From c545b546272b28e437010fea0680d738a8f7934b Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Wed, 10 May 2023 15:43:53 -0400 Subject: [PATCH 001/421] Use EXT_mesh_features to indicate feature IDs --- .../src/BatchTableToGltfFeatureMetadata.cpp | 40 +++++----- .../test/TestPntsToGltfConverter.cpp | 68 ++++++++--------- ...tUpgradeBatchTableToExtFeatureMetadata.cpp | 74 +++++++++---------- 3 files changed, 89 insertions(+), 93 deletions(-) diff --git a/Cesium3DTilesSelection/src/BatchTableToGltfFeatureMetadata.cpp b/Cesium3DTilesSelection/src/BatchTableToGltfFeatureMetadata.cpp index a8c6ea77d..656f4bb2f 100644 --- a/Cesium3DTilesSelection/src/BatchTableToGltfFeatureMetadata.cpp +++ b/Cesium3DTilesSelection/src/BatchTableToGltfFeatureMetadata.cpp @@ -3,6 +3,7 @@ #include "BatchTableHierarchyPropertyValues.h" #include "Cesium3DTilesSelection/spdlog-cesium.h" +#include #include #include #include @@ -1535,13 +1536,17 @@ ErrorList BatchTableToGltfFeatureMetadata::convertFromB3dm( primitive.attributes["_FEATURE_ID_0"] = batchIDIt->second; primitive.attributes.erase("_BATCHID"); - // Create a feature extension - ExtensionMeshPrimitiveExtFeatureMetadata& extension = - primitive.addExtension(); - FeatureIDAttribute& attribute = - extension.featureIdAttributes.emplace_back(); - attribute.featureTable = "default"; - attribute.featureIds.attribute = "_FEATURE_ID_0"; + // Create an EXT_mesh_features extension with a feature ID attribute. + ExtensionExtMeshFeatures& extension = + primitive.addExtension(); + + ExtensionExtMeshFeaturesFeatureId& featureID = + extension.featureIds.emplace_back(); + // No fast way to count the unique feature IDs in this primitive, so + // subtitute the batch table length. + featureID.featureCount = batchLength; + featureID.attribute = 0; + featureID.propertyTable = 0; } } @@ -1599,29 +1604,28 @@ ErrorList BatchTableToGltfFeatureMetadata::convertFromPnts( featureCount, result); - // Create the EXT_feature_metadata extension for the single mesh primitive. + // Create the EXT_mesh_features extension for the single mesh primitive. assert(gltf.meshes.size() == 1); Mesh& mesh = gltf.meshes[0]; assert(mesh.primitives.size() == 1); MeshPrimitive& primitive = mesh.primitives[0]; - ExtensionMeshPrimitiveExtFeatureMetadata& extension = - primitive.addExtension(); - FeatureIDAttribute& attribute = extension.featureIdAttributes.emplace_back(); - attribute.featureTable = "default"; + ExtensionExtMeshFeatures& extension = + primitive.addExtension(); + ExtensionExtMeshFeaturesFeatureId& featureID = + extension.featureIds.emplace_back(); + + // Setting the feature count is sufficient for implicit feature IDs. + featureID.featureCount = featureCount; + featureID.propertyTable = 0; auto primitiveBatchIdIt = primitive.attributes.find("_BATCHID"); if (primitiveBatchIdIt != primitive.attributes.end()) { // If _BATCHID is present, rename the _BATCHID attribute to _FEATURE_ID_0 primitive.attributes["_FEATURE_ID_0"] = primitiveBatchIdIt->second; primitive.attributes.erase("_BATCHID"); - attribute.featureIds.attribute = "_FEATURE_ID_0"; - } else { - // Otherwise, use implicit feature IDs to indicate the metadata is stored in - // per-point properties. - attribute.featureIds.constant = 0; - attribute.featureIds.divisor = 1; + featureID.attribute = 0; } return result; diff --git a/Cesium3DTilesSelection/test/TestPntsToGltfConverter.cpp b/Cesium3DTilesSelection/test/TestPntsToGltfConverter.cpp index 543397b89..8aeafa635 100644 --- a/Cesium3DTilesSelection/test/TestPntsToGltfConverter.cpp +++ b/Cesium3DTilesSelection/test/TestPntsToGltfConverter.cpp @@ -3,8 +3,8 @@ #include #include #include +#include #include -#include #include #include @@ -675,13 +675,13 @@ TEST_CASE( MeshPrimitive& primitive = mesh.primitives[0]; CHECK(primitive.mode == MeshPrimitive::Mode::POINTS); - auto primitiveExtension = - primitive.getExtension(); + auto primitiveExtension = primitive.getExtension(); REQUIRE(primitiveExtension); - REQUIRE(primitiveExtension->featureIdAttributes.size() == 1); - FeatureIDAttribute& attribute = primitiveExtension->featureIdAttributes[0]; - CHECK(attribute.featureTable == "default"); - CHECK(attribute.featureIds.attribute == "_FEATURE_ID_0"); + REQUIRE(primitiveExtension->featureIds.size() == 1); + ExtensionExtMeshFeaturesFeatureId& featureId = + primitiveExtension->featureIds[0]; + CHECK(featureId.featureCount == 8); + CHECK(featureId.attribute == 0); CHECK(gltf.materials.size() == 1); @@ -754,16 +754,14 @@ TEST_CASE("Converts point cloud with per-point properties to glTF with " MeshPrimitive& primitive = mesh.primitives[0]; CHECK(primitive.mode == MeshPrimitive::Mode::POINTS); - auto primitiveExtension = - primitive.getExtension(); + auto primitiveExtension = primitive.getExtension(); REQUIRE(primitiveExtension); - REQUIRE(primitiveExtension->featureIdAttributes.size() == 1); - FeatureIDAttribute& attribute = primitiveExtension->featureIdAttributes[0]; - CHECK(attribute.featureTable == "default"); + REQUIRE(primitiveExtension->featureIds.size() == 1); + ExtensionExtMeshFeaturesFeatureId& featureId = + primitiveExtension->featureIds[0]; // Check for implicit feature IDs - CHECK(!attribute.featureIds.attribute); - CHECK(attribute.featureIds.constant == 0); - CHECK(attribute.featureIds.divisor == 1); + CHECK(featureId.featureCount == pointsLength); + CHECK(!featureId.attribute); CHECK(gltf.materials.size() == 1); @@ -815,16 +813,14 @@ TEST_CASE("Converts point cloud with Draco compression to glTF") { MeshPrimitive& primitive = mesh.primitives[0]; CHECK(primitive.mode == MeshPrimitive::Mode::POINTS); - auto primitiveExtension = - primitive.getExtension(); + auto primitiveExtension = primitive.getExtension(); REQUIRE(primitiveExtension); - REQUIRE(primitiveExtension->featureIdAttributes.size() == 1); - FeatureIDAttribute& attribute = primitiveExtension->featureIdAttributes[0]; - CHECK(attribute.featureTable == "default"); + REQUIRE(primitiveExtension->featureIds.size() == 1); + ExtensionExtMeshFeaturesFeatureId& featureId = + primitiveExtension->featureIds[0]; // Check for implicit feature IDs - CHECK(!attribute.featureIds.attribute); - CHECK(attribute.featureIds.constant == 0); - CHECK(attribute.featureIds.divisor == 1); + CHECK(featureId.featureCount == pointsLength); + CHECK(!featureId.attribute); REQUIRE(gltf.materials.size() == 1); Material& material = gltf.materials[0]; @@ -961,16 +957,14 @@ TEST_CASE("Converts point cloud with partial Draco compression to glTF") { MeshPrimitive& primitive = mesh.primitives[0]; CHECK(primitive.mode == MeshPrimitive::Mode::POINTS); - auto primitiveExtension = - primitive.getExtension(); + auto primitiveExtension = primitive.getExtension(); REQUIRE(primitiveExtension); - REQUIRE(primitiveExtension->featureIdAttributes.size() == 1); - FeatureIDAttribute& attribute = primitiveExtension->featureIdAttributes[0]; - CHECK(attribute.featureTable == "default"); + REQUIRE(primitiveExtension->featureIds.size() == 1); + ExtensionExtMeshFeaturesFeatureId& featureId = + primitiveExtension->featureIds[0]; // Check for implicit feature IDs - CHECK(!attribute.featureIds.attribute); - CHECK(attribute.featureIds.constant == 0); - CHECK(attribute.featureIds.divisor == 1); + CHECK(featureId.featureCount == pointsLength); + CHECK(!featureId.attribute); REQUIRE(gltf.materials.size() == 1); Material& material = gltf.materials[0]; @@ -1102,13 +1096,13 @@ TEST_CASE("Converts batched point cloud with Draco compression to glTF") { MeshPrimitive& primitive = mesh.primitives[0]; CHECK(primitive.mode == MeshPrimitive::Mode::POINTS); - auto primitiveExtension = - primitive.getExtension(); + auto primitiveExtension = primitive.getExtension(); REQUIRE(primitiveExtension); - REQUIRE(primitiveExtension->featureIdAttributes.size() == 1); - FeatureIDAttribute& attribute = primitiveExtension->featureIdAttributes[0]; - CHECK(attribute.featureTable == "default"); - CHECK(attribute.featureIds.attribute == "_FEATURE_ID_0"); + REQUIRE(primitiveExtension->featureIds.size() == 1); + ExtensionExtMeshFeaturesFeatureId& featureId = + primitiveExtension->featureIds[0]; + CHECK(featureId.featureCount == 8); + CHECK(featureId.attribute == 0); CHECK(gltf.materials.size() == 1); diff --git a/Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtFeatureMetadata.cpp b/Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtFeatureMetadata.cpp index 3c2e62584..6e20c2a3d 100644 --- a/Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtFeatureMetadata.cpp +++ b/Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtFeatureMetadata.cpp @@ -3,7 +3,7 @@ #include #include -#include +#include #include #include #include @@ -360,15 +360,15 @@ TEST_CASE("Converts JSON B3DM batch table to EXT_feature_metadata") { primitive.attributes.find("_FEATURE_ID_1") == primitive.attributes.end()); - ExtensionMeshPrimitiveExtFeatureMetadata* pPrimitiveExtension = - primitive.getExtension(); + ExtensionExtMeshFeatures* pPrimitiveExtension = + primitive.getExtension(); REQUIRE(pPrimitiveExtension); - REQUIRE(pPrimitiveExtension->featureIdAttributes.size() == 1); - - FeatureIDAttribute& attribute = - pPrimitiveExtension->featureIdAttributes[0]; - CHECK(attribute.featureIds.attribute == "_FEATURE_ID_0"); - CHECK(attribute.featureTable == "default"); + REQUIRE(pPrimitiveExtension->featureIds.size() == 1); + ExtensionExtMeshFeaturesFeatureId& featureId = + pPrimitiveExtension->featureIds[0]; + CHECK(featureId.featureCount == 10); + CHECK(featureId.attribute == 0); + CHECK(featureId.propertyTable == 0); } } @@ -677,14 +677,15 @@ TEST_CASE("Converts batched PNTS batch table to EXT_feature_metadata") { CHECK( primitive.attributes.find("_FEATURE_ID_0") != primitive.attributes.end()); - ExtensionMeshPrimitiveExtFeatureMetadata* pPrimitiveExtension = - primitive.getExtension(); + ExtensionExtMeshFeatures* pPrimitiveExtension = + primitive.getExtension(); REQUIRE(pPrimitiveExtension); - REQUIRE(pPrimitiveExtension->featureIdAttributes.size() == 1); - - FeatureIDAttribute& attribute = pPrimitiveExtension->featureIdAttributes[0]; - CHECK(attribute.featureTable == "default"); - CHECK(attribute.featureIds.attribute == "_FEATURE_ID_0"); + REQUIRE(pPrimitiveExtension->featureIds.size() == 1); + ExtensionExtMeshFeaturesFeatureId& featureId = + pPrimitiveExtension->featureIds[0]; + CHECK(featureId.featureCount ==8); + CHECK(featureId.attribute == 0); + CHECK(featureId.propertyTable == 0); // Check metadata values { @@ -824,17 +825,15 @@ TEST_CASE("Converts per-point PNTS batch table to EXT_feature_metadata") { CHECK( primitive.attributes.find("_FEATURE_ID_0") == primitive.attributes.end()); - ExtensionMeshPrimitiveExtFeatureMetadata* pPrimitiveExtension = - primitive.getExtension(); + ExtensionExtMeshFeatures* pPrimitiveExtension = + primitive.getExtension(); REQUIRE(pPrimitiveExtension); - REQUIRE(pPrimitiveExtension->featureIdAttributes.size() == 1); - - FeatureIDAttribute& attribute = pPrimitiveExtension->featureIdAttributes[0]; - CHECK(attribute.featureTable == "default"); - // Check for implicit feature IDs - CHECK(!attribute.featureIds.attribute); - CHECK(attribute.featureIds.constant == 0); - CHECK(attribute.featureIds.divisor == 1); + REQUIRE(pPrimitiveExtension->featureIds.size() == 1); + ExtensionExtMeshFeaturesFeatureId& featureId = + pPrimitiveExtension->featureIds[0]; + CHECK(featureId.featureCount == 8); + CHECK(!featureId.attribute); + CHECK(featureId.propertyTable == 0); // Check metadata values { @@ -974,17 +973,16 @@ TEST_CASE("Converts Draco per-point PNTS batch table to " CHECK( primitive.attributes.find("_FEATURE_ID_0") == primitive.attributes.end()); - ExtensionMeshPrimitiveExtFeatureMetadata* pPrimitiveExtension = - primitive.getExtension(); - REQUIRE(pPrimitiveExtension); - REQUIRE(pPrimitiveExtension->featureIdAttributes.size() == 1); - FeatureIDAttribute& attribute = pPrimitiveExtension->featureIdAttributes[0]; - CHECK(attribute.featureTable == "default"); - // Check for implicit feature IDs - CHECK(!attribute.featureIds.attribute); - CHECK(attribute.featureIds.constant == 0); - CHECK(attribute.featureIds.divisor == 1); + ExtensionExtMeshFeatures* pPrimitiveExtension = + primitive.getExtension(); + REQUIRE(pPrimitiveExtension); + REQUIRE(pPrimitiveExtension->featureIds.size() == 1); + ExtensionExtMeshFeaturesFeatureId& featureId = + pPrimitiveExtension->featureIds[0]; + CHECK(featureId.featureCount == 8); + CHECK(!featureId.attribute); + CHECK(featureId.propertyTable == 0); // Check metadata values { @@ -1248,7 +1246,7 @@ TEST_CASE("Upgrade fixed json number array") { {1244, 12200000, 1222, 544662}, {123, 10, 122, 334}, {13, 45, 122, 94}, - {11, 22, 3, 4294967295}}; + {11, 22, 3, (uint32_t)4294967295}}; // clang-format on std::string expectedComponentType = "UINT32"; @@ -1408,7 +1406,7 @@ TEST_CASE("Upgrade dynamic json number array") { {1244, 12200000, 1222, 544662}, {123, 10}, {13, 45, 122, 94, 333, 212, 534, 1122}, - {11, 22, 3, 4294967295}}; + {11, 22, 3, (uint32_t)4294967295}}; // clang-format on std::string expectedComponentType = "UINT32"; From c1e852fb7a25652e3c34a321498f2b7a6156664d Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Wed, 10 May 2023 15:58:28 -0400 Subject: [PATCH 002/421] Rename file --- .../src/B3dmToGltfConverter.cpp | 4 ++-- ...=> BatchTableToGltfStructuralMetadata.cpp} | 15 ++++++------- ...h => BatchTableToGltfStructuralMetadata.h} | 2 +- .../src/PntsToGltfConverter.cpp | 10 ++++----- .../test/TestPntsToGltfConverter.cpp | 2 +- ...tUpgradeBatchTableToExtFeatureMetadata.cpp | 21 +++++++++---------- 6 files changed, 27 insertions(+), 27 deletions(-) rename Cesium3DTilesSelection/src/{BatchTableToGltfFeatureMetadata.cpp => BatchTableToGltfStructuralMetadata.cpp} (99%) rename Cesium3DTilesSelection/src/{BatchTableToGltfFeatureMetadata.h => BatchTableToGltfStructuralMetadata.h} (94%) diff --git a/Cesium3DTilesSelection/src/B3dmToGltfConverter.cpp b/Cesium3DTilesSelection/src/B3dmToGltfConverter.cpp index 6ee772ca5..dc2689d38 100644 --- a/Cesium3DTilesSelection/src/B3dmToGltfConverter.cpp +++ b/Cesium3DTilesSelection/src/B3dmToGltfConverter.cpp @@ -1,6 +1,6 @@ #include "B3dmToGltfConverter.h" -#include "BatchTableToGltfFeatureMetadata.h" +#include "BatchTableToGltfStructuralMetadata.h" #include "BinaryToGltfConverter.h" #include @@ -220,7 +220,7 @@ void convertB3dmMetadataToGltfFeatureMetadata( } // upgrade batch table to glTF feature metadata and append the result - result.errors.merge(BatchTableToGltfFeatureMetadata::convertFromB3dm( + result.errors.merge(BatchTableToGltfStructuralMetadata::convertFromB3dm( featureTableJson, batchTableJson, batchTableBinaryData, diff --git a/Cesium3DTilesSelection/src/BatchTableToGltfFeatureMetadata.cpp b/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp similarity index 99% rename from Cesium3DTilesSelection/src/BatchTableToGltfFeatureMetadata.cpp rename to Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp index 656f4bb2f..98ca77a24 100644 --- a/Cesium3DTilesSelection/src/BatchTableToGltfFeatureMetadata.cpp +++ b/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp @@ -1,10 +1,9 @@ -#include "BatchTableToGltfFeatureMetadata.h" +#include "BatchTableToGltfStructuralMetadata.h" #include "BatchTableHierarchyPropertyValues.h" #include "Cesium3DTilesSelection/spdlog-cesium.h" #include -#include #include #include #include @@ -1383,7 +1382,7 @@ void updateExtensionWithBatchTableHierarchy( } } -void convertBatchTableToGltfFeatureMetadataExtension( +void convertBatchTableToGltfStructuralMetadataExtension( const rapidjson::Document& batchTableJson, const gsl::span& batchTableBinaryData, CesiumGltf::Model& gltf, @@ -1486,7 +1485,7 @@ void convertBatchTableToGltfFeatureMetadataExtension( } // namespace -ErrorList BatchTableToGltfFeatureMetadata::convertFromB3dm( +ErrorList BatchTableToGltfStructuralMetadata::convertFromB3dm( const rapidjson::Document& featureTableJson, const rapidjson::Document& batchTableJson, const gsl::span& batchTableBinaryData, @@ -1515,7 +1514,7 @@ ErrorList BatchTableToGltfFeatureMetadata::convertFromB3dm( const int64_t batchLength = batchLengthIt->value.GetInt64(); - convertBatchTableToGltfFeatureMetadataExtension( + convertBatchTableToGltfStructuralMetadataExtension( batchTableJson, batchTableBinaryData, gltf, @@ -1546,6 +1545,7 @@ ErrorList BatchTableToGltfFeatureMetadata::convertFromB3dm( // subtitute the batch table length. featureID.featureCount = batchLength; featureID.attribute = 0; + featureID.label = "_FEATURE_ID_0"; featureID.propertyTable = 0; } } @@ -1553,7 +1553,7 @@ ErrorList BatchTableToGltfFeatureMetadata::convertFromB3dm( return result; } -ErrorList BatchTableToGltfFeatureMetadata::convertFromPnts( +ErrorList BatchTableToGltfStructuralMetadata::convertFromPnts( const rapidjson::Document& featureTableJson, const rapidjson::Document& batchTableJson, const gsl::span& batchTableBinaryData, @@ -1597,7 +1597,7 @@ ErrorList BatchTableToGltfFeatureMetadata::convertFromPnts( featureCount = pointsLengthIt->value.GetInt64(); } - convertBatchTableToGltfFeatureMetadataExtension( + convertBatchTableToGltfStructuralMetadataExtension( batchTableJson, batchTableBinaryData, gltf, @@ -1626,6 +1626,7 @@ ErrorList BatchTableToGltfFeatureMetadata::convertFromPnts( primitive.attributes["_FEATURE_ID_0"] = primitiveBatchIdIt->second; primitive.attributes.erase("_BATCHID"); featureID.attribute = 0; + featureID.label = "_FEATURE_ID_0"; } return result; diff --git a/Cesium3DTilesSelection/src/BatchTableToGltfFeatureMetadata.h b/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.h similarity index 94% rename from Cesium3DTilesSelection/src/BatchTableToGltfFeatureMetadata.h rename to Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.h index 65008f913..05517786b 100644 --- a/Cesium3DTilesSelection/src/BatchTableToGltfFeatureMetadata.h +++ b/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.h @@ -9,7 +9,7 @@ #include namespace Cesium3DTilesSelection { -struct BatchTableToGltfFeatureMetadata { +struct BatchTableToGltfStructuralMetadata { static ErrorList convertFromB3dm( const rapidjson::Document& featureTableJson, const rapidjson::Document& batchTableJson, diff --git a/Cesium3DTilesSelection/src/PntsToGltfConverter.cpp b/Cesium3DTilesSelection/src/PntsToGltfConverter.cpp index debda368f..ca49693b2 100644 --- a/Cesium3DTilesSelection/src/PntsToGltfConverter.cpp +++ b/Cesium3DTilesSelection/src/PntsToGltfConverter.cpp @@ -1,6 +1,6 @@ #include "PntsToGltfConverter.h" -#include "BatchTableToGltfFeatureMetadata.h" +#include "BatchTableToGltfStructuralMetadata.h" #include #include @@ -701,7 +701,7 @@ void parseDracoExtensionFromBatchTableJson( const std::string name = dracoPropertyIt->name.GetString(); // If there are issues with the extension, skip parsing metadata altogether. - // Otherwise, BatchTableToGltfFeatureMetadata will still try to parse the + // Otherwise, BatchTableToGltfStructuralMetadata will still try to parse the // invalid Draco-compressed properties. auto batchTablePropertyIt = batchTableJson.FindMember(name.c_str()); if (batchTablePropertyIt == batchTableJson.MemberEnd() || @@ -723,7 +723,7 @@ void parseDracoExtensionFromBatchTableJson( } // If the batch table binary property is invalid, it will also be ignored by - // BatchTableToGltfFeatureMetadata, so it's fine to continue parsing the + // BatchTableToGltfStructuralMetadata, so it's fine to continue parsing the // other properties. const rapidjson::Value& batchTableProperty = batchTablePropertyIt->value; auto byteOffsetIt = batchTableProperty.FindMember("byteOffset"); @@ -1464,7 +1464,7 @@ void addBatchIdsToGltf(PntsContent& parsedContent, CesiumGltf::Model& gltf) { Accessor::Type::SCALAR); MeshPrimitive& primitive = gltf.meshes[0].primitives[0]; - // This will be renamed by BatchTableToGltfFeatureMetadata. + // This will be renamed by BatchTableToGltfStructuralMetadata. primitive.attributes.emplace("_BATCHID", accessorId); } } @@ -1610,7 +1610,7 @@ void convertPntsContentToGltf( gsl::span(parsedContent.dracoBatchTableBinary); } - result.errors.merge(BatchTableToGltfFeatureMetadata::convertFromPnts( + result.errors.merge(BatchTableToGltfStructuralMetadata::convertFromPnts( featureTableJson, batchTableJson, batchTableBinaryData, diff --git a/Cesium3DTilesSelection/test/TestPntsToGltfConverter.cpp b/Cesium3DTilesSelection/test/TestPntsToGltfConverter.cpp index 8aeafa635..41da45719 100644 --- a/Cesium3DTilesSelection/test/TestPntsToGltfConverter.cpp +++ b/Cesium3DTilesSelection/test/TestPntsToGltfConverter.cpp @@ -1096,7 +1096,7 @@ TEST_CASE("Converts batched point cloud with Draco compression to glTF") { MeshPrimitive& primitive = mesh.primitives[0]; CHECK(primitive.mode == MeshPrimitive::Mode::POINTS); - auto primitiveExtension = primitive.getExtension(); + auto primitiveExtension = primitive.getExtension(); REQUIRE(primitiveExtension); REQUIRE(primitiveExtension->featureIds.size() == 1); ExtensionExtMeshFeaturesFeatureId& featureId = diff --git a/Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtFeatureMetadata.cpp b/Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtFeatureMetadata.cpp index 6e20c2a3d..34dfc9267 100644 --- a/Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtFeatureMetadata.cpp +++ b/Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtFeatureMetadata.cpp @@ -1,4 +1,4 @@ -#include "BatchTableToGltfFeatureMetadata.h" +#include "BatchTableToGltfStructuralMetadata.h" #include "ConvertTileToGltf.h" #include @@ -136,7 +136,7 @@ static void createTestForScalarJson( scalarProperty, batchTableJson.GetAllocator()); - auto errors = BatchTableToGltfFeatureMetadata::convertFromB3dm( + auto errors = BatchTableToGltfStructuralMetadata::convertFromB3dm( featureTableJson, batchTableJson, gsl::span(), @@ -212,7 +212,7 @@ static void createTestForArrayJson( fixedArrayProperties, batchTableJson.GetAllocator()); - auto errors = BatchTableToGltfFeatureMetadata::convertFromB3dm( + auto errors = BatchTableToGltfStructuralMetadata::convertFromB3dm( featureTableJson, batchTableJson, gsl::span(), @@ -683,7 +683,7 @@ TEST_CASE("Converts batched PNTS batch table to EXT_feature_metadata") { REQUIRE(pPrimitiveExtension->featureIds.size() == 1); ExtensionExtMeshFeaturesFeatureId& featureId = pPrimitiveExtension->featureIds[0]; - CHECK(featureId.featureCount ==8); + CHECK(featureId.featureCount == 8); CHECK(featureId.attribute == 0); CHECK(featureId.propertyTable == 0); @@ -973,8 +973,7 @@ TEST_CASE("Converts Draco per-point PNTS batch table to " CHECK( primitive.attributes.find("_FEATURE_ID_0") == primitive.attributes.end()); - - ExtensionExtMeshFeatures* pPrimitiveExtension = + ExtensionExtMeshFeatures* pPrimitiveExtension = primitive.getExtension(); REQUIRE(pPrimitiveExtension); REQUIRE(pPrimitiveExtension->featureIds.size() == 1); @@ -1133,7 +1132,7 @@ TEST_CASE("Upgrade bool json to boolean binary") { boolProperties, batchTableJson.GetAllocator()); - auto errors = BatchTableToGltfFeatureMetadata::convertFromB3dm( + auto errors = BatchTableToGltfStructuralMetadata::convertFromB3dm( featureTableJson, batchTableJson, gsl::span(), @@ -1675,7 +1674,7 @@ TEST_CASE("Converts Feature Classes 3DTILES_batch_table_hierarchy example to " rapidjson::Document batchTableParsed; batchTableParsed.Parse(batchTableJson.data(), batchTableJson.size()); - auto errors = BatchTableToGltfFeatureMetadata::convertFromB3dm( + auto errors = BatchTableToGltfStructuralMetadata::convertFromB3dm( featureTableParsed, batchTableParsed, gsl::span(), @@ -1805,7 +1804,7 @@ TEST_CASE("Converts Feature Hierarchy 3DTILES_batch_table_hierarchy example to " rapidjson::Document batchTableParsed; batchTableParsed.Parse(batchTableJson.data(), batchTableJson.size()); - BatchTableToGltfFeatureMetadata::convertFromB3dm( + BatchTableToGltfStructuralMetadata::convertFromB3dm( featureTableParsed, batchTableParsed, gsl::span(), @@ -1999,7 +1998,7 @@ TEST_CASE( auto pLog = std::make_shared(3); spdlog::default_logger()->sinks().emplace_back(pLog); - BatchTableToGltfFeatureMetadata::convertFromB3dm( + BatchTableToGltfStructuralMetadata::convertFromB3dm( featureTableParsed, batchTableParsed, gsl::span(), @@ -2091,7 +2090,7 @@ TEST_CASE( auto pLog = std::make_shared(3); spdlog::default_logger()->sinks().emplace_back(pLog); - auto errors = BatchTableToGltfFeatureMetadata::convertFromB3dm( + auto errors = BatchTableToGltfStructuralMetadata::convertFromB3dm( featureTableParsed, batchTableParsed, gsl::span(), From 14997fb03170fc8960125345c4fc4aaf044b9cd7 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Mon, 15 May 2023 16:58:17 -0400 Subject: [PATCH 003/421] Add property types for structural metadata --- .../StructuralMetadataPropertyType.h | 57 ++++ .../src/StructuralMetadataPropertyType.cpp | 214 +++++++++++++++ .../TestStructuralMetadataPropertyType.cpp | 253 ++++++++++++++++++ 3 files changed, 524 insertions(+) create mode 100644 CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyType.h create mode 100644 CesiumGltf/src/StructuralMetadataPropertyType.cpp create mode 100644 CesiumGltf/test/TestStructuralMetadataPropertyType.cpp diff --git a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyType.h b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyType.h new file mode 100644 index 000000000..67d92d232 --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyType.h @@ -0,0 +1,57 @@ +#pragma once + +#include +#include +#include + +namespace CesiumGltf { +namespace StructuralMetadata { + +enum class PropertyType { + Invalid, + Scalar, + Vec2, + Vec3, + Vec4, + Mat2, + Mat3, + Mat4, + String, + Boolean, + Enum +}; + +enum class PropertyComponentType { + None, + Int8, + Uint8, + Int16, + Uint16, + Int32, + Uint32, + Int64, + Uint64, + Float32, + Float64, +}; + +std::string +convertPropertyTypeToString(CesiumGltf::StructuralMetadata::PropertyType type); + +CesiumGltf::StructuralMetadata::PropertyType +convertStringToPropertyType(const std::string& str); + +std::string convertPropertyComponentTypeToString( + CesiumGltf::StructuralMetadata::PropertyComponentType componentType); + +CesiumGltf::StructuralMetadata::PropertyComponentType +convertStringToPropertyComponentType(const std::string& str); + +CesiumGltf::StructuralMetadata::PropertyComponentType +convertArrayOffsetTypeStringToPropertyComponentType(const std::string& str); + +CesiumGltf::StructuralMetadata::PropertyComponentType +convertStringOffsetTypeStringToPropertyComponentType(const std::string& str); + +} // namespace StructuralMetadata +} // namespace CesiumGltf diff --git a/CesiumGltf/src/StructuralMetadataPropertyType.cpp b/CesiumGltf/src/StructuralMetadataPropertyType.cpp new file mode 100644 index 000000000..56a8262b1 --- /dev/null +++ b/CesiumGltf/src/StructuralMetadataPropertyType.cpp @@ -0,0 +1,214 @@ +#include "CesiumGltf/StructuralMetadataPropertyType.h" + +#include "CesiumGltf/ExtensionExtStructuralMetadataClassProperty.h" +#include "CesiumGltf/ExtensionExtStructuralMetadataPropertyTable.h" + +namespace CesiumGltf { +namespace StructuralMetadata { + +std::string convertPropertyTypeToString(StructuralMetadata::PropertyType type) { + switch (type) { + case PropertyType::Scalar: + return ExtensionExtStructuralMetadataClassProperty::Type::SCALAR; + case PropertyType::Vec2: + return ExtensionExtStructuralMetadataClassProperty::Type::VEC2; + case PropertyType::Vec3: + return ExtensionExtStructuralMetadataClassProperty::Type::VEC3; + case PropertyType::Vec4: + return ExtensionExtStructuralMetadataClassProperty::Type::VEC4; + case PropertyType::Mat2: + return ExtensionExtStructuralMetadataClassProperty::Type::MAT2; + case PropertyType::Mat3: + return ExtensionExtStructuralMetadataClassProperty::Type::MAT3; + case PropertyType::Mat4: + return ExtensionExtStructuralMetadataClassProperty::Type::MAT4; + case PropertyType::Boolean: + return ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN; + case PropertyType::Enum: + return ExtensionExtStructuralMetadataClassProperty::Type::ENUM; + case PropertyType::String: + return ExtensionExtStructuralMetadataClassProperty::Type::STRING; + default: + return "INVALID"; + } +} + +PropertyType convertStringToPropertyType(const std::string& str) { + if (str == ExtensionExtStructuralMetadataClassProperty::Type::SCALAR) { + return PropertyType::Scalar; + } + + if (str == ExtensionExtStructuralMetadataClassProperty::Type::VEC2) { + return StructuralMetadata::PropertyType::Vec2; + } + + if (str == ExtensionExtStructuralMetadataClassProperty::Type::VEC3) { + return StructuralMetadata::PropertyType::Vec3; + } + + if (str == ExtensionExtStructuralMetadataClassProperty::Type::VEC4) { + return StructuralMetadata::PropertyType::Vec4; + } + + if (str == ExtensionExtStructuralMetadataClassProperty::Type::MAT2) { + return StructuralMetadata::PropertyType::Mat2; + } + + if (str == ExtensionExtStructuralMetadataClassProperty::Type::MAT3) { + return StructuralMetadata::PropertyType::Mat3; + } + + if (str == ExtensionExtStructuralMetadataClassProperty::Type::MAT4) { + return StructuralMetadata::PropertyType::Mat4; + } + + if (str == ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN) { + return StructuralMetadata::PropertyType::Boolean; + } + + if (str == ExtensionExtStructuralMetadataClassProperty::Type::STRING) { + return StructuralMetadata::PropertyType::String; + } + + if (str == ExtensionExtStructuralMetadataClassProperty::Type::ENUM) { + return StructuralMetadata::PropertyType::Enum; + } + + return PropertyType::Invalid; +} + +std::string convertPropertyComponentTypeToString(PropertyComponentType type) { + switch (type) { + case PropertyComponentType::None: + return "NONE"; + case PropertyComponentType::Uint8: + return ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT8; + case PropertyComponentType::Int8: + return ExtensionExtStructuralMetadataClassProperty::ComponentType::INT8; + case PropertyComponentType::Uint16: + return ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT16; + case PropertyComponentType::Int16: + return ExtensionExtStructuralMetadataClassProperty::ComponentType::INT16; + case PropertyComponentType::Uint32: + return ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT32; + case PropertyComponentType::Int32: + return ExtensionExtStructuralMetadataClassProperty::ComponentType::INT32; + case PropertyComponentType::Uint64: + return ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT64; + case PropertyComponentType::Int64: + return ExtensionExtStructuralMetadataClassProperty::ComponentType::INT64; + case PropertyComponentType::Float32: + return ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT32; + case PropertyComponentType::Float64: + return ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT64; + default: + return "NONE"; + } +} + +StructuralMetadata::PropertyComponentType +convertStringToPropertyComponentType(const std::string& str) { + if (str == + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT8) { + return PropertyComponentType::Uint8; + } + + if (str == ExtensionExtStructuralMetadataClassProperty::ComponentType::INT8) { + return PropertyComponentType::Int8; + } + + if (str == + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT16) { + return PropertyComponentType::Uint16; + } + + if (str == + ExtensionExtStructuralMetadataClassProperty::ComponentType::INT16) { + return PropertyComponentType::Int16; + } + + if (str == + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT32) { + return PropertyComponentType::Uint32; + } + + if (str == + ExtensionExtStructuralMetadataClassProperty::ComponentType::INT32) { + return PropertyComponentType::Int32; + } + + if (str == + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT64) { + return PropertyComponentType::Uint64; + } + + if (str == + ExtensionExtStructuralMetadataClassProperty::ComponentType::INT64) { + return PropertyComponentType::Int64; + } + + if (str == + ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT32) { + return PropertyComponentType::Float32; + } + + if (str == + ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT64) { + return PropertyComponentType::Float64; + } + + return PropertyComponentType::None; +} + +StructuralMetadata::PropertyComponentType +convertArrayOffsetTypeStringToPropertyComponentType(const std::string& str) { + if (str == ExtensionExtStructuralMetadataPropertyTableProperty:: + ArrayOffsetType::UINT8) { + return PropertyComponentType::Uint8; + } + + if (str == ExtensionExtStructuralMetadataPropertyTableProperty:: + ArrayOffsetType::UINT16) { + return PropertyComponentType::Uint16; + } + + if (str == ExtensionExtStructuralMetadataPropertyTableProperty:: + ArrayOffsetType::UINT32) { + return PropertyComponentType::Uint32; + } + + if (str == ExtensionExtStructuralMetadataPropertyTableProperty:: + ArrayOffsetType::UINT64) { + return PropertyComponentType::Uint64; + } + + return PropertyComponentType::None; +} + +StructuralMetadata::PropertyComponentType +convertStringOffsetTypeStringToPropertyComponentType(const std::string& str) { + if (str == ExtensionExtStructuralMetadataPropertyTableProperty:: + StringOffsetType::UINT8) { + return PropertyComponentType::Uint8; + } + + if (str == ExtensionExtStructuralMetadataPropertyTableProperty:: + StringOffsetType::UINT16) { + return PropertyComponentType::Uint16; + } + + if (str == ExtensionExtStructuralMetadataPropertyTableProperty:: + StringOffsetType::UINT32) { + return PropertyComponentType::Uint32; + } + + if (str == ExtensionExtStructuralMetadataPropertyTableProperty:: + StringOffsetType::UINT64) { + return PropertyComponentType::Uint64; + } + + return PropertyComponentType::None; +} + +} // namespace StructuralMetadata +} // namespace CesiumGltf diff --git a/CesiumGltf/test/TestStructuralMetadataPropertyType.cpp b/CesiumGltf/test/TestStructuralMetadataPropertyType.cpp new file mode 100644 index 000000000..759a485ec --- /dev/null +++ b/CesiumGltf/test/TestStructuralMetadataPropertyType.cpp @@ -0,0 +1,253 @@ +#include "CesiumGltf/ExtensionExtStructuralMetadataClassProperty.h" +#include "CesiumGltf/ExtensionExtStructuralMetadataPropertyTableProperty.h" +#include "CesiumGltf/StructuralMetadataPropertyType.h" + +#include + +using namespace CesiumGltf; +using namespace StructuralMetadata; + +TEST_CASE("Test StructuralMetadata PropertyType utilities function") { + SECTION("Convert string to PropertyType") { + REQUIRE( + convertStringToPropertyType( + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR) == + PropertyType::Scalar); + + REQUIRE( + convertStringToPropertyType( + ExtensionExtStructuralMetadataClassProperty::Type::VEC2) == + PropertyType::Vec2); + + REQUIRE( + convertStringToPropertyType( + ExtensionExtStructuralMetadataClassProperty::Type::VEC3) == + PropertyType::Vec3); + + REQUIRE( + convertStringToPropertyType( + ExtensionExtStructuralMetadataClassProperty::Type::VEC4) == + PropertyType::Vec4); + + REQUIRE( + convertStringToPropertyType( + ExtensionExtStructuralMetadataClassProperty::Type::MAT2) == + PropertyType::Mat2); + + REQUIRE( + convertStringToPropertyType( + ExtensionExtStructuralMetadataClassProperty::Type::MAT3) == + PropertyType::Mat3); + + REQUIRE( + convertStringToPropertyType( + ExtensionExtStructuralMetadataClassProperty::Type::MAT4) == + PropertyType::Mat4); + + REQUIRE( + convertStringToPropertyType( + ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN) == + PropertyType::Boolean); + + REQUIRE( + convertStringToPropertyType( + ExtensionExtStructuralMetadataClassProperty::Type::STRING) == + PropertyType::String); + + REQUIRE( + convertStringToPropertyType( + ExtensionExtStructuralMetadataClassProperty::Type::ENUM) == + PropertyType::Enum); + + REQUIRE(convertStringToPropertyType("invalid") == PropertyType::Invalid); + } + + SECTION("Convert string to PropertyComponentType") { + REQUIRE( + convertStringToPropertyComponentType( + ExtensionExtStructuralMetadataClassProperty::ComponentType:: + UINT8) == PropertyComponentType::Uint8); + + REQUIRE( + convertStringToPropertyComponentType( + ExtensionExtStructuralMetadataClassProperty::ComponentType::INT8) == + PropertyComponentType::Int8); + + REQUIRE( + convertStringToPropertyComponentType( + ExtensionExtStructuralMetadataClassProperty::ComponentType:: + UINT16) == PropertyComponentType::Uint16); + REQUIRE( + convertStringToPropertyComponentType( + ExtensionExtStructuralMetadataClassProperty::ComponentType:: + INT16) == PropertyComponentType::Int16); + + REQUIRE( + convertStringToPropertyComponentType( + ExtensionExtStructuralMetadataClassProperty::ComponentType:: + UINT32) == PropertyComponentType::Uint32); + + REQUIRE( + convertStringToPropertyComponentType( + ExtensionExtStructuralMetadataClassProperty::ComponentType:: + INT32) == PropertyComponentType::Int32); + REQUIRE( + convertStringToPropertyComponentType( + ExtensionExtStructuralMetadataClassProperty::ComponentType:: + UINT64) == PropertyComponentType::Uint64); + + REQUIRE( + convertStringToPropertyComponentType( + ExtensionExtStructuralMetadataClassProperty::ComponentType:: + INT64) == PropertyComponentType::Int64); + + REQUIRE( + convertStringToPropertyComponentType( + ExtensionExtStructuralMetadataClassProperty::ComponentType:: + FLOAT32) == PropertyComponentType::Float32); + REQUIRE( + convertStringToPropertyComponentType( + ExtensionExtStructuralMetadataClassProperty::ComponentType:: + FLOAT64) == PropertyComponentType::Float64); + + REQUIRE( + convertStringToPropertyComponentType("invalid") == + PropertyComponentType::None); + } + + SECTION("Convert PropertyType to string") { + REQUIRE( + convertPropertyTypeToString(PropertyType::Scalar) == + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR); + + REQUIRE( + convertPropertyTypeToString(PropertyType::Vec2) == + ExtensionExtStructuralMetadataClassProperty::Type::VEC2); + + REQUIRE( + convertPropertyTypeToString(PropertyType::Vec3) == + ExtensionExtStructuralMetadataClassProperty::Type::VEC3); + + REQUIRE( + convertPropertyTypeToString(PropertyType::Vec4) == + ExtensionExtStructuralMetadataClassProperty::Type::VEC4); + + REQUIRE( + convertPropertyTypeToString(PropertyType::Mat2) == + ExtensionExtStructuralMetadataClassProperty::Type::MAT2); + + REQUIRE( + convertPropertyTypeToString(PropertyType::Mat3) == + ExtensionExtStructuralMetadataClassProperty::Type::MAT3); + + REQUIRE( + convertPropertyTypeToString(PropertyType::Mat4) == + ExtensionExtStructuralMetadataClassProperty::Type::MAT4); + + REQUIRE( + convertPropertyTypeToString(PropertyType::Boolean) == + ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN); + + REQUIRE( + convertPropertyTypeToString(PropertyType::String) == + ExtensionExtStructuralMetadataClassProperty::Type::STRING); + + REQUIRE( + convertPropertyTypeToString(PropertyType::Enum) == + ExtensionExtStructuralMetadataClassProperty::Type::ENUM); + } + + SECTION("Convert PropertyComponentType to string") { + REQUIRE( + convertPropertyComponentTypeToString(PropertyComponentType::Uint8) == + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT8); + + REQUIRE( + convertPropertyComponentTypeToString(PropertyComponentType::Int8) == + ExtensionExtStructuralMetadataClassProperty::ComponentType::INT8); + + REQUIRE( + convertPropertyComponentTypeToString(PropertyComponentType::Uint16) == + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT16); + + REQUIRE( + convertPropertyComponentTypeToString(PropertyComponentType::Int16) == + ExtensionExtStructuralMetadataClassProperty::ComponentType::INT16); + + REQUIRE( + convertPropertyComponentTypeToString(PropertyComponentType::Uint32) == + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT32); + + REQUIRE( + convertPropertyComponentTypeToString(PropertyComponentType::Int32) == + ExtensionExtStructuralMetadataClassProperty::ComponentType::INT32); + + REQUIRE( + convertPropertyComponentTypeToString(PropertyComponentType::Uint64) == + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT64); + + REQUIRE( + convertPropertyComponentTypeToString(PropertyComponentType::Int64) == + ExtensionExtStructuralMetadataClassProperty::ComponentType::INT64); + + REQUIRE( + convertPropertyComponentTypeToString(PropertyComponentType::Float32) == + ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT32); + + REQUIRE( + convertPropertyComponentTypeToString(PropertyComponentType::Float64) == + ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT64); + } + + SECTION("Convert array offset type string to PropertyComponentType") { + REQUIRE( + convertArrayOffsetTypeStringToPropertyComponentType( + ExtensionExtStructuralMetadataPropertyTableProperty:: + ArrayOffsetType::UINT8) == PropertyComponentType::Uint8); + + REQUIRE( + convertArrayOffsetTypeStringToPropertyComponentType( + ExtensionExtStructuralMetadataPropertyTableProperty:: + ArrayOffsetType::UINT16) == PropertyComponentType::Uint16); + + REQUIRE( + convertArrayOffsetTypeStringToPropertyComponentType( + ExtensionExtStructuralMetadataPropertyTableProperty:: + ArrayOffsetType::UINT32) == PropertyComponentType::Uint32); + + REQUIRE( + convertArrayOffsetTypeStringToPropertyComponentType( + ExtensionExtStructuralMetadataPropertyTableProperty:: + ArrayOffsetType::UINT64) == PropertyComponentType::Uint64); + + REQUIRE( + convertArrayOffsetTypeStringToPropertyComponentType("invalid") == + PropertyComponentType::None); + } + + SECTION("Convert string offset type string to PropertyComponentType") { + REQUIRE( + convertStringOffsetTypeStringToPropertyComponentType( + ExtensionExtStructuralMetadataPropertyTableProperty:: + StringOffsetType::UINT8) == PropertyComponentType::Uint8); + + REQUIRE( + convertStringOffsetTypeStringToPropertyComponentType( + ExtensionExtStructuralMetadataPropertyTableProperty:: + StringOffsetType::UINT16) == PropertyComponentType::Uint16); + + REQUIRE( + convertStringOffsetTypeStringToPropertyComponentType( + ExtensionExtStructuralMetadataPropertyTableProperty:: + StringOffsetType::UINT32) == PropertyComponentType::Uint32); + + REQUIRE( + convertStringOffsetTypeStringToPropertyComponentType( + ExtensionExtStructuralMetadataPropertyTableProperty:: + StringOffsetType::UINT64) == PropertyComponentType::Uint64); + + REQUIRE( + convertStringOffsetTypeStringToPropertyComponentType("invalid") == + PropertyComponentType::None); + } +} From 1433243c3d159aceb5033ab3ee6410d5455c1821 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Mon, 15 May 2023 18:13:47 -0400 Subject: [PATCH 004/421] Add StructuralMetadataPropertyTypeTraits --- .../CesiumGltf/StructuralMetadataArrayView.h | 145 ++++ .../StructuralMetadataPropertyTypeTraits.h | 658 ++++++++++++++++++ ...stStructuralMetadataPropertyTypeTraits.cpp | 524 ++++++++++++++ 3 files changed, 1327 insertions(+) create mode 100644 CesiumGltf/include/CesiumGltf/StructuralMetadataArrayView.h create mode 100644 CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTypeTraits.h create mode 100644 CesiumGltf/test/TestStructuralMetadataPropertyTypeTraits.cpp diff --git a/CesiumGltf/include/CesiumGltf/StructuralMetadataArrayView.h b/CesiumGltf/include/CesiumGltf/StructuralMetadataArrayView.h new file mode 100644 index 000000000..c76abfcca --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/StructuralMetadataArrayView.h @@ -0,0 +1,145 @@ +#pragma once + +#include "CesiumGltf/StructuralMetadataPropertyType.h" + +#include + +#include + +#include +#include + +/** + * @brief A view on an array element of a + * ExtensionExtStructuralMetadataPropertyTableProperty. + * + * Provides utility to retrieve the data stored in the array of + * elements via the array index operator. + */ +namespace CesiumGltf { +namespace StructuralMetadata { + +template class MetadataArrayView { +public: + MetadataArrayView() : _values{} {} + + MetadataArrayView(const gsl::span& buffer) noexcept + : _values{ + CesiumUtility::reintepretCastSpan(buffer)} {} + + const ElementType& operator[](int64_t index) const noexcept { + return _values[index]; + } + + int64_t size() const noexcept { + return static_cast(_values.size()); + } + +private: + gsl::span _values; +}; + +template <> class MetadataArrayView { +public: + MetadataArrayView() + : _values{}, _bitOffset{0}, _instanceCount{0} {} + + MetadataArrayView( + const gsl::span& buffer, + int64_t bitOffset, + int64_t instanceCount) noexcept + : _values{buffer}, + _bitOffset{bitOffset}, + _instanceCount{instanceCount} {} + + bool operator[](int64_t index) const noexcept { + index += _bitOffset; + const int64_t byteIndex = index / 8; + const int64_t bitIndex = index % 8; + const int bitValue = + static_cast(_values[byteIndex] >> bitIndex) & 1; + return bitValue == 1; + } + + int64_t size() const noexcept { return _instanceCount; } + +private: + gsl::span _values; + int64_t _bitOffset; + int64_t _instanceCount; +}; + +template <> class MetadataArrayView { +public: + MetadataArrayView() + : _values{}, _stringOffsets{}, _stringOffsetType{}, _size{0} {} + + MetadataArrayView( + const gsl::span& values, + const gsl::span& stringOffsets, + StructuralMetadata::PropertyComponentType stringOffsetType, + int64_t size) noexcept + : _values{values}, + _stringOffsets{stringOffsets}, + _stringOffsetType{stringOffsetType}, + _size{size} {} + + std::string_view operator[](int64_t index) const noexcept { + const size_t currentOffset = getOffsetFromStringOffsetsBuffer( + index, + _stringOffsets, + _stringOffsetType); + const size_t nextOffset = getOffsetFromStringOffsetsBuffer( + index + 1, + _stringOffsets, + _stringOffsetType); + return std::string_view( + reinterpret_cast(_values.data() + currentOffset), + (nextOffset - currentOffset)); + } + + int64_t size() const noexcept { return _size; } + +private: + static size_t getOffsetFromStringOffsetsBuffer( + size_t instance, + const gsl::span& stringOffsetBuffer, + StructuralMetadata::PropertyComponentType offsetType) noexcept { + switch (offsetType) { + case StructuralMetadata::PropertyComponentType::Uint8: { + assert(instance < stringOffsetBuffer.size() / sizeof(uint8_t)); + const uint8_t offset = *reinterpret_cast( + stringOffsetBuffer.data() + instance * sizeof(uint8_t)); + return static_cast(offset); + } + case StructuralMetadata::PropertyComponentType::Uint16: { + assert(instance < stringOffsetBuffer.size() / sizeof(uint16_t)); + const uint16_t offset = *reinterpret_cast( + stringOffsetBuffer.data() + instance * sizeof(uint16_t)); + return static_cast(offset); + } + case StructuralMetadata::PropertyComponentType::Uint32: { + assert(instance < stringOffsetBuffer.size() / sizeof(uint32_t)); + const uint32_t offset = *reinterpret_cast( + stringOffsetBuffer.data() + instance * sizeof(uint32_t)); + return static_cast(offset); + } + case StructuralMetadata::PropertyComponentType::Uint64: { + assert(instance < stringOffsetBuffer.size() / sizeof(uint64_t)); + const uint64_t offset = *reinterpret_cast( + stringOffsetBuffer.data() + instance * sizeof(uint64_t)); + return static_cast(offset); + } + default: + assert(false && "Offset type has unknown type"); + return 0; + } + } + gsl::span _values; + gsl::span _stringOffsets; + StructuralMetadata::PropertyComponentType _stringOffsetType; + int64_t _size; +}; + +} // namespace StructuralMetadata +} // namespace CesiumGltf diff --git a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTypeTraits.h b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTypeTraits.h new file mode 100644 index 000000000..0998c3302 --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTypeTraits.h @@ -0,0 +1,658 @@ +#pragma once + +#include "CesiumGltf/StructuralMetadataArrayView.h" +#include "CesiumGltf/StructuralMetadataPropertyType.h" + +#include + +#include +#include + +namespace CesiumGltf { +namespace StructuralMetadata { + +/** + * @brief Check if a C++ type can be represented as a scalar property type + */ +template struct IsMetadataScalar; +template struct IsMetadataScalar : std::false_type {}; +template <> struct IsMetadataScalar : std::true_type {}; +template <> struct IsMetadataScalar : std::true_type {}; +template <> struct IsMetadataScalar : std::true_type {}; +template <> struct IsMetadataScalar : std::true_type {}; +template <> struct IsMetadataScalar : std::true_type {}; +template <> struct IsMetadataScalar : std::true_type {}; +template <> struct IsMetadataScalar : std::true_type {}; +template <> struct IsMetadataScalar : std::true_type {}; +template <> struct IsMetadataScalar : std::true_type {}; +template <> struct IsMetadataScalar : std::true_type {}; + +/** + * @brief Check if a C++ type can be represented as an integer property type + */ +template struct IsMetadataInteger; +template struct IsMetadataInteger : std::false_type {}; +template <> struct IsMetadataInteger : std::true_type {}; +template <> struct IsMetadataInteger : std::true_type {}; +template <> struct IsMetadataInteger : std::true_type {}; +template <> struct IsMetadataInteger : std::true_type {}; +template <> struct IsMetadataInteger : std::true_type {}; +template <> struct IsMetadataInteger : std::true_type {}; +template <> struct IsMetadataInteger : std::true_type {}; +template <> struct IsMetadataInteger : std::true_type {}; + +/** + * @brief Check if a C++ type can be represented as a floating-point property + * type. + */ +template struct IsMetadataFloating; +template struct IsMetadataFloating : std::false_type {}; +template <> struct IsMetadataFloating : std::true_type {}; +template <> struct IsMetadataFloating : std::true_type {}; + +/** + * @brief Check if a C++ type can be represented as a vecN type. + */ +template struct IsMetadataVecN; +template struct IsMetadataVecN : std::false_type {}; +template <> struct IsMetadataVecN : std::true_type {}; +template <> struct IsMetadataVecN : std::true_type {}; +template <> struct IsMetadataVecN : std::true_type {}; +template <> struct IsMetadataVecN : std::true_type {}; +template <> struct IsMetadataVecN : std::true_type {}; +template <> struct IsMetadataVecN : std::true_type {}; +template <> struct IsMetadataVecN : std::true_type {}; +template <> struct IsMetadataVecN : std::true_type {}; +template <> struct IsMetadataVecN : std::true_type {}; +template <> struct IsMetadataVecN : std::true_type {}; +template <> struct IsMetadataVecN : std::true_type {}; +template <> struct IsMetadataVecN : std::true_type {}; +template <> struct IsMetadataVecN : std::true_type {}; +template <> struct IsMetadataVecN : std::true_type {}; +template <> struct IsMetadataVecN : std::true_type {}; +template <> struct IsMetadataVecN : std::true_type {}; +template <> struct IsMetadataVecN : std::true_type {}; +template <> struct IsMetadataVecN : std::true_type {}; +template <> struct IsMetadataVecN : std::true_type {}; +template <> struct IsMetadataVecN : std::true_type {}; +template <> struct IsMetadataVecN : std::true_type {}; +template <> struct IsMetadataVecN : std::true_type {}; +template <> struct IsMetadataVecN : std::true_type {}; +template <> struct IsMetadataVecN : std::true_type {}; +template <> struct IsMetadataVecN : std::true_type {}; +template <> struct IsMetadataVecN : std::true_type {}; +template <> struct IsMetadataVecN : std::true_type {}; +template <> struct IsMetadataVecN : std::true_type {}; +template <> struct IsMetadataVecN : std::true_type {}; +template <> struct IsMetadataVecN : std::true_type {}; + +/** + * @brief Check if a C++ type can be represented as a matN type. + */ +template struct IsMetadataMatN; +template struct IsMetadataMatN : std::false_type {}; +template <> struct IsMetadataMatN : std::true_type {}; +template <> struct IsMetadataMatN : std::true_type {}; +template <> struct IsMetadataMatN : std::true_type {}; +template <> struct IsMetadataMatN : std::true_type {}; +template <> struct IsMetadataMatN : std::true_type {}; +template <> struct IsMetadataMatN : std::true_type {}; +template <> struct IsMetadataMatN : std::true_type {}; +template <> struct IsMetadataMatN : std::true_type {}; +template <> struct IsMetadataMatN : std::true_type {}; +template <> struct IsMetadataMatN : std::true_type {}; +template <> struct IsMetadataMatN : std::true_type {}; +template <> struct IsMetadataMatN : std::true_type {}; +template <> struct IsMetadataMatN : std::true_type {}; +template <> struct IsMetadataMatN : std::true_type {}; +template <> struct IsMetadataMatN : std::true_type {}; +template <> struct IsMetadataMatN : std::true_type {}; +template <> struct IsMetadataMatN : std::true_type {}; +template <> struct IsMetadataMatN : std::true_type {}; +template <> struct IsMetadataMatN : std::true_type {}; +template <> struct IsMetadataMatN : std::true_type {}; +template <> struct IsMetadataMatN : std::true_type {}; +template <> struct IsMetadataMatN : std::true_type {}; +template <> struct IsMetadataMatN : std::true_type {}; +template <> struct IsMetadataMatN : std::true_type {}; +template <> struct IsMetadataMatN : std::true_type {}; +template <> struct IsMetadataMatN : std::true_type {}; +template <> struct IsMetadataMatN : std::true_type {}; +template <> struct IsMetadataMatN : std::true_type {}; +template <> struct IsMetadataMatN : std::true_type {}; +template <> struct IsMetadataMatN : std::true_type {}; + +/** + * @brief Check if a C++ type can be represented as a numeric property, i.e. + * a scalar / vecN / matN type. + */ +template struct IsMetadataNumeric; +template struct IsMetadataNumeric { + static constexpr bool value = IsMetadataScalar::value || + IsMetadataVecN::value || + IsMetadataMatN::value; +}; + +/** + * @brief Check if a C++ type can be represented as a boolean property type + */ +template struct IsMetadataBoolean; +template struct IsMetadataBoolean : std::false_type {}; +template <> struct IsMetadataBoolean : std::true_type {}; + +/** + * @brief Check if a C++ type can be represented as a string property type + */ +template struct IsMetadataString; +template struct IsMetadataString : std::false_type {}; +template <> struct IsMetadataString : std::true_type {}; + +/** + * @brief Check if a C++ type can be represented as an array. + */ +template struct IsMetadataArray; +template struct IsMetadataArray : std::false_type {}; +template +struct IsMetadataArray> : std::true_type {}; + +/** + * @brief Check if a C++ type can be represented as an array of numeric elements + * property type + */ +template struct IsMetadataNumericArray; +template struct IsMetadataNumericArray : std::false_type {}; +template struct IsMetadataNumericArray> { + static constexpr bool value = IsMetadataNumeric::value; +}; + +/** + * @brief Check if a C++ type can be represented as an array of booleans + * property type + */ +template struct IsMetadataBooleanArray; +template struct IsMetadataBooleanArray : std::false_type {}; +template <> +struct IsMetadataBooleanArray> : std::true_type {}; + +/** + * @brief Check if a C++ type can be represented as an array of strings property + * type + */ +template struct IsMetadataStringArray; +template struct IsMetadataStringArray : std::false_type {}; +template <> +struct IsMetadataStringArray> + : std::true_type {}; + +/** + * @brief Retrieve the component type of a metadata array + */ +template struct MetadataArrayType; +template +struct MetadataArrayType> { + using type = T; +}; + +/** + * @brief Convert a C++ type to PropertyType and PropertyComponentType + */ +template struct TypeToPropertyType; + +#pragma region Scalar Property Types + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Uint8; + static constexpr PropertyType value = PropertyType::Scalar; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Int8; + static constexpr PropertyType value = PropertyType::Scalar; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Uint16; + static constexpr PropertyType value = PropertyType::Scalar; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Int16; + static constexpr PropertyType value = PropertyType::Scalar; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Uint32; + static constexpr PropertyType value = PropertyType::Scalar; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Int32; + static constexpr PropertyType value = PropertyType::Scalar; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Uint64; + static constexpr PropertyType value = PropertyType::Scalar; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Int64; + static constexpr PropertyType value = PropertyType::Scalar; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Float32; + static constexpr PropertyType value = PropertyType::Scalar; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Float64; + static constexpr PropertyType value = PropertyType::Scalar; +}; +#pragma endregion + +#pragma region Vec2 Property Types + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Uint8; + static constexpr PropertyType value = PropertyType::Vec2; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Int8; + static constexpr PropertyType value = PropertyType::Vec2; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Uint16; + static constexpr PropertyType value = PropertyType::Vec2; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Int16; + static constexpr PropertyType value = PropertyType::Vec2; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Uint32; + static constexpr PropertyType value = PropertyType::Vec2; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Int32; + static constexpr PropertyType value = PropertyType::Vec2; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Uint64; + static constexpr PropertyType value = PropertyType::Vec2; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Int64; + static constexpr PropertyType value = PropertyType::Vec2; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Float32; + static constexpr PropertyType value = PropertyType::Vec2; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Float64; + static constexpr PropertyType value = PropertyType::Vec2; +}; +#pragma endregion + +#pragma region Vec3 Property Types + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Uint8; + static constexpr PropertyType value = PropertyType::Vec3; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Int8; + static constexpr PropertyType value = PropertyType::Vec3; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Uint16; + static constexpr PropertyType value = PropertyType::Vec3; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Int16; + static constexpr PropertyType value = PropertyType::Vec3; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Uint32; + static constexpr PropertyType value = PropertyType::Vec3; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Int32; + static constexpr PropertyType value = PropertyType::Vec3; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Uint64; + static constexpr PropertyType value = PropertyType::Vec3; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Int64; + static constexpr PropertyType value = PropertyType::Vec3; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Float32; + static constexpr PropertyType value = PropertyType::Vec3; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Float64; + static constexpr PropertyType value = PropertyType::Vec3; +}; +#pragma endregion + +#pragma region Vec4 Property Types + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Uint8; + static constexpr PropertyType value = PropertyType::Vec4; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Int8; + static constexpr PropertyType value = PropertyType::Vec4; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Uint16; + static constexpr PropertyType value = PropertyType::Vec4; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Int16; + static constexpr PropertyType value = PropertyType::Vec4; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Uint32; + static constexpr PropertyType value = PropertyType::Vec4; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Int32; + static constexpr PropertyType value = PropertyType::Vec4; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Uint64; + static constexpr PropertyType value = PropertyType::Vec4; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Int64; + static constexpr PropertyType value = PropertyType::Vec4; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Float32; + static constexpr PropertyType value = PropertyType::Vec4; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Float64; + static constexpr PropertyType value = PropertyType::Vec4; +}; +#pragma endregion + +#pragma region Mat2 Property Types + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Uint8; + static constexpr PropertyType value = PropertyType::Mat2; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Int8; + static constexpr PropertyType value = PropertyType::Mat2; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Uint16; + static constexpr PropertyType value = PropertyType::Mat2; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Int16; + static constexpr PropertyType value = PropertyType::Mat2; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Uint32; + static constexpr PropertyType value = PropertyType::Mat2; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Int32; + static constexpr PropertyType value = PropertyType::Mat2; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Uint64; + static constexpr PropertyType value = PropertyType::Mat2; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Int64; + static constexpr PropertyType value = PropertyType::Mat2; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Float32; + static constexpr PropertyType value = PropertyType::Mat2; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Float64; + static constexpr PropertyType value = PropertyType::Mat2; +}; + +#pragma endregion + +#pragma region Mat3 Property Types + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Uint8; + static constexpr PropertyType value = PropertyType::Mat3; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Int8; + static constexpr PropertyType value = PropertyType::Mat3; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Uint16; + static constexpr PropertyType value = PropertyType::Mat3; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Int16; + static constexpr PropertyType value = PropertyType::Mat3; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Uint32; + static constexpr PropertyType value = PropertyType::Mat3; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Int32; + static constexpr PropertyType value = PropertyType::Mat3; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Uint64; + static constexpr PropertyType value = PropertyType::Mat3; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Int64; + static constexpr PropertyType value = PropertyType::Mat3; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Float32; + static constexpr PropertyType value = PropertyType::Mat3; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Float64; + static constexpr PropertyType value = PropertyType::Mat3; +}; + +#pragma endregion + +#pragma region Mat4 Property Types + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Uint8; + static constexpr PropertyType value = PropertyType::Mat4; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Int8; + static constexpr PropertyType value = PropertyType::Mat4; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Uint16; + static constexpr PropertyType value = PropertyType::Mat4; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Int16; + static constexpr PropertyType value = PropertyType::Mat4; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Uint32; + static constexpr PropertyType value = PropertyType::Mat4; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Int32; + static constexpr PropertyType value = PropertyType::Mat4; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Uint64; + static constexpr PropertyType value = PropertyType::Mat4; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Int64; + static constexpr PropertyType value = PropertyType::Mat4; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Float32; + static constexpr PropertyType value = PropertyType::Mat4; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::Float64; + static constexpr PropertyType value = PropertyType::Mat4; +}; + +#pragma endregion + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::None; + static constexpr PropertyType value = PropertyType::Boolean; +}; + +template <> struct TypeToPropertyType { + static constexpr PropertyComponentType component = + PropertyComponentType::None; + static constexpr PropertyType value = PropertyType::String; +}; + +} // namespace StructuralMetadata +} // namespace CesiumGltf diff --git a/CesiumGltf/test/TestStructuralMetadataPropertyTypeTraits.cpp b/CesiumGltf/test/TestStructuralMetadataPropertyTypeTraits.cpp new file mode 100644 index 000000000..42c0a5313 --- /dev/null +++ b/CesiumGltf/test/TestStructuralMetadataPropertyTypeTraits.cpp @@ -0,0 +1,524 @@ +#include "CesiumGltf/StructuralMetadataPropertyTypeTraits.h" + +#include + +using namespace CesiumGltf::StructuralMetadata; + +TEST_CASE("Test StructuralMetadata PropertyTypeTraits") { + SECTION("IsScalar") { + REQUIRE(IsMetadataScalar::value); + REQUIRE(IsMetadataScalar::value); + + REQUIRE(IsMetadataScalar::value); + REQUIRE(IsMetadataScalar::value); + + REQUIRE(IsMetadataScalar::value); + REQUIRE(IsMetadataScalar::value); + + REQUIRE(IsMetadataScalar::value); + REQUIRE(IsMetadataScalar::value); + + REQUIRE(IsMetadataScalar::value); + REQUIRE(IsMetadataScalar::value); + + REQUIRE(!IsMetadataScalar::value); + REQUIRE(!IsMetadataScalar::value); + REQUIRE(!IsMetadataScalar::value); + REQUIRE(!IsMetadataScalar::value); + } + + SECTION("IsVecN") { + REQUIRE(IsMetadataVecN::value); + REQUIRE(IsMetadataVecN::value); + REQUIRE(IsMetadataVecN::value); + + REQUIRE(IsMetadataVecN::value); + REQUIRE(IsMetadataVecN::value); + REQUIRE(IsMetadataVecN::value); + + REQUIRE(IsMetadataVecN::value); + REQUIRE(IsMetadataVecN::value); + REQUIRE(IsMetadataVecN::value); + + REQUIRE(IsMetadataVecN::value); + REQUIRE(IsMetadataVecN::value); + REQUIRE(IsMetadataVecN::value); + + REQUIRE(IsMetadataVecN::value); + REQUIRE(IsMetadataVecN::value); + REQUIRE(IsMetadataVecN::value); + + REQUIRE(IsMetadataVecN::value); + REQUIRE(IsMetadataVecN::value); + REQUIRE(IsMetadataVecN::value); + + REQUIRE(IsMetadataVecN::value); + REQUIRE(IsMetadataVecN::value); + REQUIRE(IsMetadataVecN::value); + + REQUIRE(IsMetadataVecN::value); + REQUIRE(IsMetadataVecN::value); + REQUIRE(IsMetadataVecN::value); + + REQUIRE(IsMetadataVecN::value); + REQUIRE(IsMetadataVecN::value); + REQUIRE(IsMetadataVecN::value); + + REQUIRE(IsMetadataVecN::value); + REQUIRE(IsMetadataVecN::value); + REQUIRE(IsMetadataVecN::value); + + REQUIRE(!IsMetadataVecN::value); + REQUIRE(!IsMetadataVecN::value); + } + + SECTION("IsMatN") { + REQUIRE(IsMetadataMatN::value); + REQUIRE(IsMetadataMatN::value); + REQUIRE(IsMetadataMatN::value); + + REQUIRE(IsMetadataMatN::value); + REQUIRE(IsMetadataMatN::value); + REQUIRE(IsMetadataMatN::value); + + REQUIRE(IsMetadataMatN::value); + REQUIRE(IsMetadataMatN::value); + REQUIRE(IsMetadataMatN::value); + + REQUIRE(IsMetadataMatN::value); + REQUIRE(IsMetadataMatN::value); + REQUIRE(IsMetadataMatN::value); + + REQUIRE(IsMetadataMatN::value); + REQUIRE(IsMetadataMatN::value); + REQUIRE(IsMetadataMatN::value); + + REQUIRE(IsMetadataMatN::value); + REQUIRE(IsMetadataMatN::value); + REQUIRE(IsMetadataMatN::value); + + REQUIRE(IsMetadataMatN::value); + REQUIRE(IsMetadataMatN::value); + REQUIRE(IsMetadataMatN::value); + + REQUIRE(IsMetadataMatN::value); + REQUIRE(IsMetadataMatN::value); + REQUIRE(IsMetadataMatN::value); + + REQUIRE(IsMetadataMatN::value); + REQUIRE(IsMetadataMatN::value); + REQUIRE(IsMetadataMatN::value); + + REQUIRE(IsMetadataMatN::value); + REQUIRE(IsMetadataMatN::value); + REQUIRE(IsMetadataMatN::value); + + REQUIRE(!IsMetadataMatN::value); + REQUIRE(!IsMetadataMatN::value); + } + + SECTION("IsBoolean") { + REQUIRE(IsMetadataBoolean::value); + REQUIRE(!IsMetadataBoolean::value); + REQUIRE(!IsMetadataBoolean::value); + } + + SECTION("IsString") { + REQUIRE(IsMetadataString::value); + REQUIRE(!IsMetadataString::value); + } + + SECTION("IsNumeric") { + REQUIRE(IsMetadataNumeric::value); + REQUIRE(IsMetadataNumeric::value); + REQUIRE(IsMetadataNumeric::value); + REQUIRE(IsMetadataNumeric::value); + REQUIRE(IsMetadataNumeric::value); + REQUIRE(IsMetadataNumeric::value); + REQUIRE(!IsMetadataNumeric::value); + REQUIRE(!IsMetadataNumeric::value); + } + + SECTION("IsNumericArray") { + REQUIRE(IsMetadataNumericArray>::value); + REQUIRE(IsMetadataNumericArray>::value); + REQUIRE(IsMetadataNumericArray>::value); + REQUIRE(!IsMetadataNumericArray>::value); + } + + SECTION("IsBooleanArray") { + REQUIRE(IsMetadataBooleanArray>::value); + REQUIRE(!IsMetadataBooleanArray>::value); + } + + SECTION("IsStringArray") { + REQUIRE(IsMetadataStringArray>::value); + REQUIRE(!IsMetadataStringArray>::value); + REQUIRE(!IsMetadataStringArray>::value); + } + + SECTION("ArrayType") { + using type = MetadataArrayType>::type; + REQUIRE(std::is_same_v); + } + + SECTION("TypeToPropertyType scalar") { + REQUIRE(TypeToPropertyType::value == PropertyType::Scalar); + REQUIRE( + TypeToPropertyType::component == PropertyComponentType::Uint8); + + REQUIRE(TypeToPropertyType::value == PropertyType::Scalar); + REQUIRE( + TypeToPropertyType::component == PropertyComponentType::Int8); + + REQUIRE(TypeToPropertyType::value == PropertyType::Scalar); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Uint16); + + REQUIRE(TypeToPropertyType::value == PropertyType::Scalar); + REQUIRE( + TypeToPropertyType::component == PropertyComponentType::Int16); + + REQUIRE(TypeToPropertyType::value == PropertyType::Scalar); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Uint32); + + REQUIRE(TypeToPropertyType::value == PropertyType::Scalar); + REQUIRE( + TypeToPropertyType::component == PropertyComponentType::Int32); + + REQUIRE(TypeToPropertyType::value == PropertyType::Scalar); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Uint64); + + REQUIRE(TypeToPropertyType::value == PropertyType::Scalar); + REQUIRE( + TypeToPropertyType::component == PropertyComponentType::Int64); + } + + SECTION("TypeToPropertyType vecN") { + // Vec2 + REQUIRE(TypeToPropertyType::value == PropertyType::Vec2); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Uint8); + + REQUIRE(TypeToPropertyType::value == PropertyType::Vec2); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Int8); + + REQUIRE(TypeToPropertyType::value == PropertyType::Vec2); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Uint16); + + REQUIRE(TypeToPropertyType::value == PropertyType::Vec2); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Int16); + + REQUIRE(TypeToPropertyType::value == PropertyType::Vec2); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Uint32); + + REQUIRE(TypeToPropertyType::value == PropertyType::Vec2); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Int32); + + REQUIRE(TypeToPropertyType::value == PropertyType::Vec2); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Uint64); + + REQUIRE(TypeToPropertyType::value == PropertyType::Vec2); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Int64); + + REQUIRE(TypeToPropertyType::value == PropertyType::Vec2); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Float32); + + REQUIRE(TypeToPropertyType::value == PropertyType::Vec2); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Float64); + + // Vec3 + REQUIRE(TypeToPropertyType::value == PropertyType::Vec3); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Uint8); + + REQUIRE(TypeToPropertyType::value == PropertyType::Vec3); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Int8); + + REQUIRE(TypeToPropertyType::value == PropertyType::Vec3); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Uint16); + + REQUIRE(TypeToPropertyType::value == PropertyType::Vec3); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Int16); + + REQUIRE(TypeToPropertyType::value == PropertyType::Vec3); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Uint32); + + REQUIRE(TypeToPropertyType::value == PropertyType::Vec3); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Int32); + + REQUIRE(TypeToPropertyType::value == PropertyType::Vec3); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Uint64); + + REQUIRE(TypeToPropertyType::value == PropertyType::Vec3); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Int64); + + REQUIRE(TypeToPropertyType::value == PropertyType::Vec3); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Float32); + + REQUIRE(TypeToPropertyType::value == PropertyType::Vec3); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Float64); + + // Vec4 + REQUIRE(TypeToPropertyType::value == PropertyType::Vec4); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Uint8); + + REQUIRE(TypeToPropertyType::value == PropertyType::Vec4); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Int8); + + REQUIRE(TypeToPropertyType::value == PropertyType::Vec4); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Uint16); + + REQUIRE(TypeToPropertyType::value == PropertyType::Vec4); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Int16); + + REQUIRE(TypeToPropertyType::value == PropertyType::Vec4); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Uint32); + + REQUIRE(TypeToPropertyType::value == PropertyType::Vec4); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Int32); + + REQUIRE(TypeToPropertyType::value == PropertyType::Vec4); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Uint64); + + REQUIRE(TypeToPropertyType::value == PropertyType::Vec4); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Int64); + + REQUIRE(TypeToPropertyType::value == PropertyType::Vec4); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Float32); + + REQUIRE(TypeToPropertyType::value == PropertyType::Vec4); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Float64); + } + + SECTION("TypeToPropertyType matN") { + // Mat2 + REQUIRE(TypeToPropertyType::value == PropertyType::Mat2); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Uint8); + + REQUIRE(TypeToPropertyType::value == PropertyType::Mat2); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Int8); + + REQUIRE(TypeToPropertyType::value == PropertyType::Mat2); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Uint16); + + REQUIRE(TypeToPropertyType::value == PropertyType::Mat2); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Int16); + + REQUIRE(TypeToPropertyType::value == PropertyType::Mat2); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Uint32); + + REQUIRE(TypeToPropertyType::value == PropertyType::Mat2); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Int32); + + REQUIRE(TypeToPropertyType::value == PropertyType::Mat2); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Uint64); + + REQUIRE(TypeToPropertyType::value == PropertyType::Mat2); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Int64); + + REQUIRE(TypeToPropertyType::value == PropertyType::Mat2); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Float32); + + REQUIRE(TypeToPropertyType::value == PropertyType::Mat2); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Float64); + + // Mat3 + REQUIRE(TypeToPropertyType::value == PropertyType::Mat3); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Uint8); + + REQUIRE(TypeToPropertyType::value == PropertyType::Mat3); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Int8); + + REQUIRE(TypeToPropertyType::value == PropertyType::Mat3); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Uint16); + + REQUIRE(TypeToPropertyType::value == PropertyType::Mat3); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Int16); + + REQUIRE(TypeToPropertyType::value == PropertyType::Mat3); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Uint32); + + REQUIRE(TypeToPropertyType::value == PropertyType::Mat3); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Int32); + + REQUIRE(TypeToPropertyType::value == PropertyType::Mat3); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Uint64); + + REQUIRE(TypeToPropertyType::value == PropertyType::Mat3); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Int64); + + REQUIRE(TypeToPropertyType::value == PropertyType::Mat3); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Float32); + + REQUIRE(TypeToPropertyType::value == PropertyType::Mat3); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Float64); + + // Mat4 + REQUIRE(TypeToPropertyType::value == PropertyType::Mat4); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Uint8); + + REQUIRE(TypeToPropertyType::value == PropertyType::Mat4); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Int8); + + REQUIRE(TypeToPropertyType::value == PropertyType::Mat4); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Uint16); + + REQUIRE(TypeToPropertyType::value == PropertyType::Mat4); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Int16); + + REQUIRE(TypeToPropertyType::value == PropertyType::Mat4); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Uint32); + + REQUIRE(TypeToPropertyType::value == PropertyType::Mat4); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Int32); + + REQUIRE(TypeToPropertyType::value == PropertyType::Mat4); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Uint64); + + REQUIRE(TypeToPropertyType::value == PropertyType::Mat4); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Int64); + + REQUIRE(TypeToPropertyType::value == PropertyType::Mat4); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Float32); + + REQUIRE(TypeToPropertyType::value == PropertyType::Mat4); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::Float64); + } + + SECTION("TypeToPropertyType boolean") { + REQUIRE(TypeToPropertyType::value == PropertyType::Boolean); + REQUIRE(TypeToPropertyType::component == PropertyComponentType::None); + } + + SECTION("TypeToPropertyType string") { + REQUIRE( + TypeToPropertyType::value == PropertyType::String); + REQUIRE( + TypeToPropertyType::component == + PropertyComponentType::None); + } +} From cf7c8bdd383a61c8544732e3dedf5214b46ec023 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Tue, 16 May 2023 22:18:37 +1000 Subject: [PATCH 005/421] Use partial specialization to reduce code duplication. --- .../StructuralMetadataPropertyTypeTraits.h | 64 ++----------------- 1 file changed, 4 insertions(+), 60 deletions(-) diff --git a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTypeTraits.h b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTypeTraits.h index 0998c3302..8f535980a 100644 --- a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTypeTraits.h +++ b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTypeTraits.h @@ -55,72 +55,16 @@ template <> struct IsMetadataFloating : std::true_type {}; */ template struct IsMetadataVecN; template struct IsMetadataVecN : std::false_type {}; -template <> struct IsMetadataVecN : std::true_type {}; -template <> struct IsMetadataVecN : std::true_type {}; -template <> struct IsMetadataVecN : std::true_type {}; -template <> struct IsMetadataVecN : std::true_type {}; -template <> struct IsMetadataVecN : std::true_type {}; -template <> struct IsMetadataVecN : std::true_type {}; -template <> struct IsMetadataVecN : std::true_type {}; -template <> struct IsMetadataVecN : std::true_type {}; -template <> struct IsMetadataVecN : std::true_type {}; -template <> struct IsMetadataVecN : std::true_type {}; -template <> struct IsMetadataVecN : std::true_type {}; -template <> struct IsMetadataVecN : std::true_type {}; -template <> struct IsMetadataVecN : std::true_type {}; -template <> struct IsMetadataVecN : std::true_type {}; -template <> struct IsMetadataVecN : std::true_type {}; -template <> struct IsMetadataVecN : std::true_type {}; -template <> struct IsMetadataVecN : std::true_type {}; -template <> struct IsMetadataVecN : std::true_type {}; -template <> struct IsMetadataVecN : std::true_type {}; -template <> struct IsMetadataVecN : std::true_type {}; -template <> struct IsMetadataVecN : std::true_type {}; -template <> struct IsMetadataVecN : std::true_type {}; -template <> struct IsMetadataVecN : std::true_type {}; -template <> struct IsMetadataVecN : std::true_type {}; -template <> struct IsMetadataVecN : std::true_type {}; -template <> struct IsMetadataVecN : std::true_type {}; -template <> struct IsMetadataVecN : std::true_type {}; -template <> struct IsMetadataVecN : std::true_type {}; -template <> struct IsMetadataVecN : std::true_type {}; -template <> struct IsMetadataVecN : std::true_type {}; +template +struct IsMetadataVecN> : IsMetadataScalar {}; /** * @brief Check if a C++ type can be represented as a matN type. */ template struct IsMetadataMatN; template struct IsMetadataMatN : std::false_type {}; -template <> struct IsMetadataMatN : std::true_type {}; -template <> struct IsMetadataMatN : std::true_type {}; -template <> struct IsMetadataMatN : std::true_type {}; -template <> struct IsMetadataMatN : std::true_type {}; -template <> struct IsMetadataMatN : std::true_type {}; -template <> struct IsMetadataMatN : std::true_type {}; -template <> struct IsMetadataMatN : std::true_type {}; -template <> struct IsMetadataMatN : std::true_type {}; -template <> struct IsMetadataMatN : std::true_type {}; -template <> struct IsMetadataMatN : std::true_type {}; -template <> struct IsMetadataMatN : std::true_type {}; -template <> struct IsMetadataMatN : std::true_type {}; -template <> struct IsMetadataMatN : std::true_type {}; -template <> struct IsMetadataMatN : std::true_type {}; -template <> struct IsMetadataMatN : std::true_type {}; -template <> struct IsMetadataMatN : std::true_type {}; -template <> struct IsMetadataMatN : std::true_type {}; -template <> struct IsMetadataMatN : std::true_type {}; -template <> struct IsMetadataMatN : std::true_type {}; -template <> struct IsMetadataMatN : std::true_type {}; -template <> struct IsMetadataMatN : std::true_type {}; -template <> struct IsMetadataMatN : std::true_type {}; -template <> struct IsMetadataMatN : std::true_type {}; -template <> struct IsMetadataMatN : std::true_type {}; -template <> struct IsMetadataMatN : std::true_type {}; -template <> struct IsMetadataMatN : std::true_type {}; -template <> struct IsMetadataMatN : std::true_type {}; -template <> struct IsMetadataMatN : std::true_type {}; -template <> struct IsMetadataMatN : std::true_type {}; -template <> struct IsMetadataMatN : std::true_type {}; +template +struct IsMetadataMatN> : IsMetadataScalar {}; /** * @brief Check if a C++ type can be represented as a numeric property, i.e. From cd9c32b591cfcd9ef407c6edc039d17f90bba832 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Tue, 16 May 2023 22:25:09 +1000 Subject: [PATCH 006/421] More partial specialization. --- .../StructuralMetadataPropertyTypeTraits.h | 375 +----------------- 1 file changed, 22 insertions(+), 353 deletions(-) diff --git a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTypeTraits.h b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTypeTraits.h index 8f535980a..f9606671f 100644 --- a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTypeTraits.h +++ b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTypeTraits.h @@ -205,387 +205,54 @@ template <> struct TypeToPropertyType { }; #pragma endregion -#pragma region Vec2 Property Types +#pragma region Vector Property Types -template <> struct TypeToPropertyType { +template +struct TypeToPropertyType> { static constexpr PropertyComponentType component = - PropertyComponentType::Uint8; - static constexpr PropertyType value = PropertyType::Vec2; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Int8; + TypeToPropertyType::component; static constexpr PropertyType value = PropertyType::Vec2; }; -template <> struct TypeToPropertyType { +template +struct TypeToPropertyType> { static constexpr PropertyComponentType component = - PropertyComponentType::Uint16; - static constexpr PropertyType value = PropertyType::Vec2; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Int16; - static constexpr PropertyType value = PropertyType::Vec2; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Uint32; - static constexpr PropertyType value = PropertyType::Vec2; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Int32; - static constexpr PropertyType value = PropertyType::Vec2; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Uint64; - static constexpr PropertyType value = PropertyType::Vec2; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Int64; - static constexpr PropertyType value = PropertyType::Vec2; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Float32; - static constexpr PropertyType value = PropertyType::Vec2; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Float64; - static constexpr PropertyType value = PropertyType::Vec2; -}; -#pragma endregion - -#pragma region Vec3 Property Types - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Uint8; - static constexpr PropertyType value = PropertyType::Vec3; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Int8; - static constexpr PropertyType value = PropertyType::Vec3; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Uint16; - static constexpr PropertyType value = PropertyType::Vec3; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Int16; + TypeToPropertyType::component; static constexpr PropertyType value = PropertyType::Vec3; }; -template <> struct TypeToPropertyType { +template +struct TypeToPropertyType> { static constexpr PropertyComponentType component = - PropertyComponentType::Uint32; - static constexpr PropertyType value = PropertyType::Vec3; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Int32; - static constexpr PropertyType value = PropertyType::Vec3; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Uint64; - static constexpr PropertyType value = PropertyType::Vec3; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Int64; - static constexpr PropertyType value = PropertyType::Vec3; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Float32; - static constexpr PropertyType value = PropertyType::Vec3; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Float64; - static constexpr PropertyType value = PropertyType::Vec3; -}; -#pragma endregion - -#pragma region Vec4 Property Types - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Uint8; - static constexpr PropertyType value = PropertyType::Vec4; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Int8; - static constexpr PropertyType value = PropertyType::Vec4; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Uint16; - static constexpr PropertyType value = PropertyType::Vec4; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Int16; - static constexpr PropertyType value = PropertyType::Vec4; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Uint32; - static constexpr PropertyType value = PropertyType::Vec4; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Int32; - static constexpr PropertyType value = PropertyType::Vec4; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Uint64; + TypeToPropertyType::component; static constexpr PropertyType value = PropertyType::Vec4; }; -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Int64; - static constexpr PropertyType value = PropertyType::Vec4; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Float32; - static constexpr PropertyType value = PropertyType::Vec4; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Float64; - static constexpr PropertyType value = PropertyType::Vec4; -}; #pragma endregion -#pragma region Mat2 Property Types +#pragma region Matrix Property Types -template <> struct TypeToPropertyType { +template +struct TypeToPropertyType> { static constexpr PropertyComponentType component = - PropertyComponentType::Uint8; + TypeToPropertyType::component; static constexpr PropertyType value = PropertyType::Mat2; }; -template <> struct TypeToPropertyType { +template +struct TypeToPropertyType> { static constexpr PropertyComponentType component = - PropertyComponentType::Int8; - static constexpr PropertyType value = PropertyType::Mat2; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Uint16; - static constexpr PropertyType value = PropertyType::Mat2; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Int16; - static constexpr PropertyType value = PropertyType::Mat2; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Uint32; - static constexpr PropertyType value = PropertyType::Mat2; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Int32; - static constexpr PropertyType value = PropertyType::Mat2; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Uint64; - static constexpr PropertyType value = PropertyType::Mat2; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Int64; - static constexpr PropertyType value = PropertyType::Mat2; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Float32; - static constexpr PropertyType value = PropertyType::Mat2; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Float64; - static constexpr PropertyType value = PropertyType::Mat2; -}; - -#pragma endregion - -#pragma region Mat3 Property Types - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Uint8; - static constexpr PropertyType value = PropertyType::Mat3; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Int8; - static constexpr PropertyType value = PropertyType::Mat3; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Uint16; - static constexpr PropertyType value = PropertyType::Mat3; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Int16; - static constexpr PropertyType value = PropertyType::Mat3; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Uint32; - static constexpr PropertyType value = PropertyType::Mat3; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Int32; - static constexpr PropertyType value = PropertyType::Mat3; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Uint64; - static constexpr PropertyType value = PropertyType::Mat3; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Int64; - static constexpr PropertyType value = PropertyType::Mat3; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Float32; - static constexpr PropertyType value = PropertyType::Mat3; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Float64; + TypeToPropertyType::component; static constexpr PropertyType value = PropertyType::Mat3; }; -#pragma endregion - -#pragma region Mat4 Property Types - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Uint8; - static constexpr PropertyType value = PropertyType::Mat4; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Int8; - static constexpr PropertyType value = PropertyType::Mat4; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Uint16; - static constexpr PropertyType value = PropertyType::Mat4; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Int16; - static constexpr PropertyType value = PropertyType::Mat4; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Uint32; - static constexpr PropertyType value = PropertyType::Mat4; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Int32; - static constexpr PropertyType value = PropertyType::Mat4; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Uint64; - static constexpr PropertyType value = PropertyType::Mat4; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Int64; - static constexpr PropertyType value = PropertyType::Mat4; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Float32; - static constexpr PropertyType value = PropertyType::Mat4; -}; - -template <> struct TypeToPropertyType { +template +struct TypeToPropertyType> { static constexpr PropertyComponentType component = - PropertyComponentType::Float64; + TypeToPropertyType::component; static constexpr PropertyType value = PropertyType::Mat4; }; -#pragma endregion - template <> struct TypeToPropertyType { static constexpr PropertyComponentType component = PropertyComponentType::None; @@ -598,5 +265,7 @@ template <> struct TypeToPropertyType { static constexpr PropertyType value = PropertyType::String; }; +#pragma endregion + } // namespace StructuralMetadata } // namespace CesiumGltf From 90377e8f6ca04ca97574e7850d5529117d24a157 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Tue, 16 May 2023 11:23:10 -0400 Subject: [PATCH 007/421] Fix formatting and move pragma --- .../CesiumGltf/StructuralMetadataArrayView.h | 17 +++++------------ .../StructuralMetadataPropertyTypeTraits.h | 4 ++-- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/CesiumGltf/include/CesiumGltf/StructuralMetadataArrayView.h b/CesiumGltf/include/CesiumGltf/StructuralMetadataArrayView.h index c76abfcca..65eaf0117 100644 --- a/CesiumGltf/include/CesiumGltf/StructuralMetadataArrayView.h +++ b/CesiumGltf/include/CesiumGltf/StructuralMetadataArrayView.h @@ -24,16 +24,13 @@ template class MetadataArrayView { MetadataArrayView() : _values{} {} MetadataArrayView(const gsl::span& buffer) noexcept - : _values{ - CesiumUtility::reintepretCastSpan(buffer)} {} + : _values{CesiumUtility::reintepretCastSpan(buffer)} {} const ElementType& operator[](int64_t index) const noexcept { return _values[index]; } - int64_t size() const noexcept { - return static_cast(_values.size()); - } + int64_t size() const noexcept { return static_cast(_values.size()); } private: gsl::span _values; @@ -41,23 +38,19 @@ template class MetadataArrayView { template <> class MetadataArrayView { public: - MetadataArrayView() - : _values{}, _bitOffset{0}, _instanceCount{0} {} + MetadataArrayView() : _values{}, _bitOffset{0}, _instanceCount{0} {} MetadataArrayView( const gsl::span& buffer, int64_t bitOffset, int64_t instanceCount) noexcept - : _values{buffer}, - _bitOffset{bitOffset}, - _instanceCount{instanceCount} {} + : _values{buffer}, _bitOffset{bitOffset}, _instanceCount{instanceCount} {} bool operator[](int64_t index) const noexcept { index += _bitOffset; const int64_t byteIndex = index / 8; const int64_t bitIndex = index % 8; - const int bitValue = - static_cast(_values[byteIndex] >> bitIndex) & 1; + const int bitValue = static_cast(_values[byteIndex] >> bitIndex) & 1; return bitValue == 1; } diff --git a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTypeTraits.h b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTypeTraits.h index f9606671f..972c6055e 100644 --- a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTypeTraits.h +++ b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTypeTraits.h @@ -253,6 +253,8 @@ struct TypeToPropertyType> { static constexpr PropertyType value = PropertyType::Mat4; }; +#pragma endregion + template <> struct TypeToPropertyType { static constexpr PropertyComponentType component = PropertyComponentType::None; @@ -265,7 +267,5 @@ template <> struct TypeToPropertyType { static constexpr PropertyType value = PropertyType::String; }; -#pragma endregion - } // namespace StructuralMetadata } // namespace CesiumGltf From da5355387c3696ae7c965c92132c16b830fd8f4b Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Tue, 16 May 2023 15:52:39 -0400 Subject: [PATCH 008/421] Fix formatting and minor changes --- .../CesiumGltf/StructuralMetadataArrayView.h | 74 ++++--------------- .../StructuralMetadataPropertyType.h | 12 +-- 2 files changed, 22 insertions(+), 64 deletions(-) diff --git a/CesiumGltf/include/CesiumGltf/StructuralMetadataArrayView.h b/CesiumGltf/include/CesiumGltf/StructuralMetadataArrayView.h index c76abfcca..a886b62c3 100644 --- a/CesiumGltf/include/CesiumGltf/StructuralMetadataArrayView.h +++ b/CesiumGltf/include/CesiumGltf/StructuralMetadataArrayView.h @@ -1,6 +1,7 @@ #pragma once #include "CesiumGltf/StructuralMetadataPropertyType.h" +#include "getOffsetFromOffsetsBuffer.h" #include @@ -9,6 +10,9 @@ #include #include +namespace CesiumGltf { +namespace StructuralMetadata { + /** * @brief A view on an array element of a * ExtensionExtStructuralMetadataPropertyTableProperty. @@ -16,57 +20,47 @@ * Provides utility to retrieve the data stored in the array of * elements via the array index operator. */ -namespace CesiumGltf { -namespace StructuralMetadata { - template class MetadataArrayView { public: MetadataArrayView() : _values{} {} MetadataArrayView(const gsl::span& buffer) noexcept - : _values{ - CesiumUtility::reintepretCastSpan(buffer)} {} + : _values{CesiumUtility::reintepretCastSpan(buffer)} {} const ElementType& operator[](int64_t index) const noexcept { return _values[index]; } - int64_t size() const noexcept { - return static_cast(_values.size()); - } + int64_t size() const noexcept { return static_cast(_values.size()); } private: - gsl::span _values; + const gsl::span _values; }; template <> class MetadataArrayView { public: - MetadataArrayView() - : _values{}, _bitOffset{0}, _instanceCount{0} {} + MetadataArrayView() : _values{}, _bitOffset{0}, _size{0} {} MetadataArrayView( const gsl::span& buffer, int64_t bitOffset, - int64_t instanceCount) noexcept - : _values{buffer}, - _bitOffset{bitOffset}, - _instanceCount{instanceCount} {} + int64_t size) noexcept + : _values{buffer}, _bitOffset{bitOffset}, _size{size} {} bool operator[](int64_t index) const noexcept { index += _bitOffset; const int64_t byteIndex = index / 8; const int64_t bitIndex = index % 8; - const int bitValue = - static_cast(_values[byteIndex] >> bitIndex) & 1; + const int bitValue = static_cast(_values[byteIndex] >> bitIndex) & 1; return bitValue == 1; } - int64_t size() const noexcept { return _instanceCount; } + int64_t size() const noexcept { return _size; } private: gsl::span _values; int64_t _bitOffset; - int64_t _instanceCount; + int64_t _size; }; template <> class MetadataArrayView { @@ -85,11 +79,9 @@ template <> class MetadataArrayView { _size{size} {} std::string_view operator[](int64_t index) const noexcept { - const size_t currentOffset = getOffsetFromStringOffsetsBuffer( - index, - _stringOffsets, - _stringOffsetType); - const size_t nextOffset = getOffsetFromStringOffsetsBuffer( + const size_t currentOffset = + getOffsetFromOffsetsBuffer(index, _stringOffsets, _stringOffsetType); + const size_t nextOffset = getOffsetFromOffsetsBuffer( index + 1, _stringOffsets, _stringOffsetType); @@ -101,40 +93,6 @@ template <> class MetadataArrayView { int64_t size() const noexcept { return _size; } private: - static size_t getOffsetFromStringOffsetsBuffer( - size_t instance, - const gsl::span& stringOffsetBuffer, - StructuralMetadata::PropertyComponentType offsetType) noexcept { - switch (offsetType) { - case StructuralMetadata::PropertyComponentType::Uint8: { - assert(instance < stringOffsetBuffer.size() / sizeof(uint8_t)); - const uint8_t offset = *reinterpret_cast( - stringOffsetBuffer.data() + instance * sizeof(uint8_t)); - return static_cast(offset); - } - case StructuralMetadata::PropertyComponentType::Uint16: { - assert(instance < stringOffsetBuffer.size() / sizeof(uint16_t)); - const uint16_t offset = *reinterpret_cast( - stringOffsetBuffer.data() + instance * sizeof(uint16_t)); - return static_cast(offset); - } - case StructuralMetadata::PropertyComponentType::Uint32: { - assert(instance < stringOffsetBuffer.size() / sizeof(uint32_t)); - const uint32_t offset = *reinterpret_cast( - stringOffsetBuffer.data() + instance * sizeof(uint32_t)); - return static_cast(offset); - } - case StructuralMetadata::PropertyComponentType::Uint64: { - assert(instance < stringOffsetBuffer.size() / sizeof(uint64_t)); - const uint64_t offset = *reinterpret_cast( - stringOffsetBuffer.data() + instance * sizeof(uint64_t)); - return static_cast(offset); - } - default: - assert(false && "Offset type has unknown type"); - return 0; - } - } gsl::span _values; gsl::span _stringOffsets; StructuralMetadata::PropertyComponentType _stringOffsetType; diff --git a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyType.h b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyType.h index 67d92d232..b33d8ddee 100644 --- a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyType.h +++ b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyType.h @@ -36,21 +36,21 @@ enum class PropertyComponentType { }; std::string -convertPropertyTypeToString(CesiumGltf::StructuralMetadata::PropertyType type); +convertPropertyTypeToString(PropertyType type); -CesiumGltf::StructuralMetadata::PropertyType +PropertyType convertStringToPropertyType(const std::string& str); std::string convertPropertyComponentTypeToString( - CesiumGltf::StructuralMetadata::PropertyComponentType componentType); + PropertyComponentType componentType); -CesiumGltf::StructuralMetadata::PropertyComponentType +PropertyComponentType convertStringToPropertyComponentType(const std::string& str); -CesiumGltf::StructuralMetadata::PropertyComponentType +PropertyComponentType convertArrayOffsetTypeStringToPropertyComponentType(const std::string& str); -CesiumGltf::StructuralMetadata::PropertyComponentType +PropertyComponentType convertStringOffsetTypeStringToPropertyComponentType(const std::string& str); } // namespace StructuralMetadata From 671033c1e11e1e1090b916a57cd533eb26d5ad13 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Tue, 16 May 2023 15:53:40 -0400 Subject: [PATCH 009/421] Add StructuralMetadataPropertyView --- .../StructuralMetadataPropertyView.h | 412 ++++++++++++++++++ .../CesiumGltf/getOffsetFromOffsetsBuffer.h | 52 +++ 2 files changed, 464 insertions(+) create mode 100644 CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyView.h create mode 100644 CesiumGltf/include/CesiumGltf/getOffsetFromOffsetsBuffer.h diff --git a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyView.h b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyView.h new file mode 100644 index 000000000..3039d9652 --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyView.h @@ -0,0 +1,412 @@ +#pragma once + +#include "CesiumGltf/StructuralMetadataArrayView.h" +#include "CesiumGltf/StructuralMetadataPropertyTypeTraits.h" + +#include + +#include +#include +#include +#include +#include + +namespace CesiumGltf { +namespace StructuralMetadata { + +/** + * @brief Indicates the status of a property view. + * + * The {@link MetadataPropertyView} constructor always completes successfully. However, + * it may not always reflect the actual content of the {@link ExtensionExtStructuralMetadataPropertyTableProperty}, but + * instead indicate that its {@link MetadataPropertyView::size} is 0. This enumeration + * provides the reason. + */ + +enum class MetadataPropertyViewStatus { + /** + * @brief This property view is valid and ready to use. + */ + Valid, + + /** + * @brief This property view does not exist in the + * ExtensionExtStructuralMetadataPropertyTable. + */ + InvalidPropertyDoesNotExist, + + /** + * @brief This property view does not have a correct type with what is + * specified in {@link ExtensionExtStructuralMetadataClassProperty::type}. + */ + InvalidTypeMismatch, + + /** + * @brief This property view does not have a correct component type with what + * is specified in {@link ExtensionExtStructuralMetadataClassProperty::componentType}. + */ + InvalidComponentTypeMismatch, + + /** + * @brief This property view does not have a valid value buffer view index. + */ + InvalidValueBufferView, + + /** + * @brief This array property view does not have a valid array offset buffer + * view index. + */ + InvalidArrayOffsetBufferViewIndex, + + /** + * @brief This string property view does not have a valid string offset buffer + * view index. + */ + InvalidStringOffsetBufferViewIndex, + + /** + * @brief This property view has a valid value buffer view, but the buffer + * view specifies an invalid buffer index. + */ + InvalidValueBufferIndex, + + /** + * @brief This property view has a valid array string buffer view, but the + * buffer view specifies an invalid buffer index. + */ + InvalidArrayOffsetBufferIndex, + + /** + * @brief This property view has a valid string offset buffer view, but the + * buffer view specifies an invalid buffer index. + */ + InvalidStringOffsetBufferIndex, + + /** + * @brief This property view has an out-of-bounds buffer view + */ + InvalidBufferViewOutOfBounds, + + /** + * @brief This property view has an invalid buffer view's length which is not + * a multiple of the size of its type or offset type + */ + InvalidBufferViewSizeNotDivisibleByTypeSize, + + /** + * @brief This property view has an invalid buffer view's length which cannot + * fit all the instances of the feature table + */ + InvalidBufferViewSizeNotFitInstanceCount, + + /** + * @brief This array property view has both count and offset buffer + * view defined. + */ + InvalidArrayCountAndOffsetBufferCoexist, + + /** + * @brief This array property view doesn't have count nor offset buffer view + * defined. + */ + InvalidArrayCountAndOffsetBufferDontExist, + + /** + * @brief This property view has an unknown array offset type. + */ + InvalidArrayOffsetType, + + /** + * @brief This property view has an unknown string offset type. + */ + InvalidStringOffsetType, + + /** + * @brief This property view's array offset values are not sorted in ascending + * order. + */ + InvalidArrayOffsetValuesNotSorted, + + /** + * @brief This property view's string offset values are not sorted in + * ascending order. + */ + InvalidStringOffsetValuesNotSorted, + + /** + * @brief This property view has an array offset that is out of bounds. + */ + InvalidArrayOffsetValueOutOfBounds, + + /** + * @brief This property view has a string offset that is out of bounds. + */ + InvalidStringOffsetValueOutOfBounds +}; + +/** + * @brief A view on the data of the + * ExtensionExtStructuralMetadataPropertyTableProperty + * + * It provides utility to retrieve the actual data stored in the + * {@link ExtensionExtStructuralMetadataPropertyTableProperty::values} like an array of elements. + * Data of each instance can be accessed through the {@link get(int64_t instance)} method + * + * @param ElementType must be a scalar (uin8_t, int8_t, uint16_t, int16_t, + * uint32_t, int32_t, uint64_t, int64_t, float, double), vecN, matN, bool, + * std::string_view, or MetadataArrayView with T as one of the aforementioned + * types. + */ +template class MetadataPropertyView { +public: + /** + * @brief Constructs a new instance viewing a non-existent property. + */ + MetadataPropertyView() + : _status{MetadataPropertyViewStatus::InvalidPropertyDoesNotExist}, + _values{}, + _componentCount{}, + _instanceCount{}, + _normalized{} {} + + /** + * @brief Construct a new instance pointing to the data specified by + * ExtensionExtStructuralMetadataPropertyTableProperty + * @param values The raw buffer specified by {@link ExtensionExtStructuralMetadataPropertyTableProperty::values} + * @param arrayOffsets The raw buffer specified by {@link ExtensionExtStructuralMetadataPropertyTableProperty::arrayOffsets} + * @param stringOffsets The raw buffer specified by {@link ExtensionExtStructuralMetadataPropertyTableProperty::stringOffsets} + * @param offsetType The offset type of arrayOffsets specified by {@link ExtensionExtStructuralMetadataPropertyTableProperty::arrayOffsetType} + * @param offsetType The offset type of stringOffsets specified by {@link ExtensionExtStructuralMetadataPropertyTableProperty::stringOffsetType} + * @param fixedArrayCount The number of elements in each array value specified by {@link ExtensionExtStructuralMetadataClassProperty::count} + * @param size The number of elements in the property table specified by {@link ExtensionExtStructuralMetadataPropertyTable::count} + * @param normalized Whether this property has a normalized integer type. + */ + MetadataPropertyView( + MetadataPropertyViewStatus status, + gsl::span values, + gsl::span arrayOffsets, + gsl::span stringOffsets, + StructuralMetadata::PropertyComponentType arrayOffsetType, + StructuralMetadata::PropertyComponentType stringOffsetType, + int64_t fixedLengthArrayCount, + int64_t size, + bool normalized) noexcept + : _status{status}, + _values{values}, + _arrayOffsets{arrayOffsets}, + _arrayOffsetType{arrayOffsetType}, + _arrayOffsetTypeSize{getOffsetTypeSize(arrayOffsetType)}, + _stringOffsets{stringOffsets}, + _stringOffsetType{stringOffsetType}, + _stringOffsetTypeSize{getOffsetTypeSize(stringOffsetType)}, + _fixedLengthArrayCount{fixedLengthArrayCount}, + _size{size}, + _normalized{normalized} {} + + /** + * @brief Gets the status of this property view. + * + * Indicates whether the view accurately reflects the property's data, or + * whether an error occurred. + */ + MetadataPropertyViewStatus status() const noexcept { return _status; } + + /** + * @brief Get the value of an element of the FeatureTable. + * @param index The element index + * @return The value of the element + */ + ElementType get(int64_t index) const noexcept { + assert( + _status == MetadataPropertyViewStatus::Valid && + "Check the status() first to make sure view is valid"); + assert( + size() > 0 && + "Check the size() of the view to make sure it's not empty"); + assert(index >= 0 && "index must be positive"); + + if constexpr (IsMetadataNumeric::value) { + return getNumericValue(index); + } + + if constexpr (IsMetadataBoolean::value) { + return getBooleanValue(index); + } + + if constexpr (IsMetadataString::value) { + return getStringValue(index); + } + + if constexpr (IsMetadataNumericArray::value) { + return getNumericArrayValues< + typename MetadataArrayType::type>(index); + } + + if constexpr (IsMetadataBooleanArray::value) { + return getBooleanArrayValues(index); + } + + if constexpr (IsMetadataStringArray::value) { + return getStringArrayValues(index); + } + } + + /** + * @brief Get the number of elements in the + * ExtensionExtStructuralMetadataPropertyTable. + * + * @return The number of elements in the + * ExtensionExtStructuralMetadataPropertyTable. + */ + int64_t size() const noexcept { return _size; } + + /** + * @brief Get the element count of the fixed-length arrays in this property. + * Only applicable when the property is an array type. + * + * @return The count of this property. + */ + int64_t getFixedLengthArrayCount() const noexcept { return _fixedArrayCount; } + + /** + * @brief Whether this property has a normalized integer type. + * + * @return Whether this property has a normalized integer type. + */ + bool isNormalized() const noexcept { return _normalized; } + +private: + ElementType getNumericValue(int64_t index) const noexcept { + return reinterpret_cast(_values.data())[index]; + } + + bool getBooleanValue(int64_t index) const noexcept { + const int64_t byteIndex = index / 8; + const int64_t bitIndex = index % 8; + const int bitValue = static_cast(_values[byteIndex] >> bitIndex) & 1; + return bitValue == 1; + } + + std::string_view getStringValue(int64_t index) const noexcept { + const size_t currentOffset = + getOffsetFromOffsetsBuffer(index, _stringOffsets, _stringOffsetType); + const size_t nextOffset = getOffsetFromOffsetsBuffer( + index + 1, + _stringOffsets, + _stringOffsetType); + return std::string_view( + reinterpret_cast(_values.data() + currentOffset), + nextOffset - currentOffset); + } + + template + MetadataArrayView getNumericArrayValues(int64_t index) const noexcept { + // Handle fixed-length arrays + if (_fixedLengthArrayCount > 0) { + size_t arraySize = _fixedLengthArrayCount * sizeof(T); + const gsl::span values( + _values.data() + index * arraySize, + arraySize); + return MetadataArrayView{values}; + } + + // Handle variable-length arrays + const size_t currentOffset = + getOffsetFromOffsetsBuffer(index, _arrayOffsets, _arrayOffsetType); + const size_t nextOffset = + getOffsetFromOffsetsBuffer(index + 1, _arrayOffsets, _arrayOffsetType); + const gsl::span values( + _values.data() + currentOffset, + nextOffset - currentOffset); + return MetadataArrayView{values}; + } + + MetadataArrayView + getStringArrayValues(int64_t index) const noexcept { + // Handle fixed-length arrays + if (_fixedLengthArrayCount > 0) { + size_t arraySize = _fixedLengthArrayCount * _stringOffsetTypeSize; + const gsl::span stringOffsetValues( + _stringOffsets.data() + index * arraySize, + arraySize); + return MetadataArrayView( + _values, + stringOffsetValues, + _stringOffsetType, + _count); + } + + const size_t currentArrayOffset = + getOffsetFromOffsetsBuffer(index, _arrayOffsets, _arrayOffsetType); + const size_t nextArrayOffset = + getOffsetFromOffsetsBuffer(index + 1, _arrayOffsets, _arrayOffsetType); + const size_t length = currentArrayOffset - nextArrayOffset; + const gsl::span stringOffsetValues( + _stringOffsets.data() + currentOffset, + length * _arrayOffsetTypeSize); + return MetadataArrayView( + _values, + stringOffsetValues, + _stringOffsetType, + length / _stringOffsetTypeSize); + } + + MetadataArrayView getBooleanArrayValues(int64_t index) const noexcept { + // Handle fixed-length arrays + if (_fixedLengthArrayCount > 0) { + const size_t offsetBits = _fixedLengthArrayCount * index; + const size_t nextOffsetBits = _fixedLengthArrayCount * (index + 1); + const gsl::span buffer( + _values.data() + offsetBits / 8, + (nextOffsetBits / 8 - offsetBits / 8 + 1)); + return MetadataArrayView( + buffer, + offsetBits % 8, + _fixedLengthArrayCount); + } + + // Handle variable-length arrays + const size_t currentOffset = + getOffsetFromOffsetsBuffer(index, _arrayOffsets, _arrayOffsetType); + const size_t nextOffset = + getOffsetFromOffsetsBuffer(index + 1, _arrayOffsets, _arrayOffsetType); + const size_t totalBits = nextOffset - currentOffset; + const gsl::span buffer( + _values.data() + currentOffset / 8, + (nextOffset / 8 - currentOffset / 8 + 1)); + return MetadataArrayView(buffer, currentOffset % 8, totalBits); + } + + static int64_t getOffsetTypeSize(PropertyComponentType offsetType) noexcept { + switch (offsetType) { + case PropertyComponentType::Uint8: + return sizeof(uint8_t); + case PropertyComponentType::Uint16: + return sizeof(uint16_t); + case PropertyComponentType::Uint32: + return sizeof(uint32_t); + case PropertyComponentType::Uint64: + return sizeof(uint64_t); + default: + return 0; + } + } + + MetadataPropertyViewStatus _status; + gsl::span _values; + + gsl::span _arrayOffsets; + PropertyComponentType _arrayOffsetType; + int64_t _arrayOffsetTypeSize; + + gsl::span _stringOffsets; + PropertyComponentType _stringOffsetType; + int64_t _stringOffsetTypeSize; + + int64_t _fixedLengthArrayCount; + int64_t _size; + bool _normalized; +}; + +} // namespace StructuralMetadata +} // namespace CesiumGltf diff --git a/CesiumGltf/include/CesiumGltf/getOffsetFromOffsetsBuffer.h b/CesiumGltf/include/CesiumGltf/getOffsetFromOffsetsBuffer.h new file mode 100644 index 000000000..5374f2423 --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/getOffsetFromOffsetsBuffer.h @@ -0,0 +1,52 @@ +#pragma once + +#include "CesiumGltf/StructuralMetadataPropertyType.h" + +#include + +#include + +#include +#include + +namespace CesiumGltf { +namespace StructuralMetadata { + +static size_t getOffsetFromOffsetsBuffer( + size_t index, + const gsl::span& offsetBuffer, + PropertyComponentType offsetType) noexcept { + switch (offsetType) { + case PropertyComponentType::Uint8: { + assert(index < offsetBuffer.size() / sizeof(uint8_t)); + const uint8_t offset = *reinterpret_cast( + offsetBuffer.data() + index * sizeof(uint8_t)); + return static_cast(offset); + } + case PropertyComponentType::Uint16: { + assert(index < offsetBuffer.size() / sizeof(uint16_t)); + const uint16_t offset = *reinterpret_cast( + offsetBuffer.data() + index * sizeof(uint16_t)); + return static_cast(offset); + } + case PropertyComponentType::Uint32: { + assert(index < offsetBuffer.size() / sizeof(uint32_t)); + const uint32_t offset = *reinterpret_cast( + offsetBuffer.data() + index * sizeof(uint32_t)); + return static_cast(offset); + } + case PropertyComponentType::Uint64: { + assert(index < offsetBuffer.size() / sizeof(uint64_t)); + const uint64_t offset = *reinterpret_cast( + offsetBuffer.data() + index * sizeof(uint64_t)); + return static_cast(offset); + } + default: + assert(false && "Offset type is invalid"); + return 0; + } + + } + +} // namespace StructuralMetadata +} // namespace CesiumGltf From 72fe4a960efec7dcb9eba95b56dba4346075554c Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 17 May 2023 07:37:26 +1000 Subject: [PATCH 010/421] Fix test failures on GCC9. --- .../include/CesiumGltf/StructuralMetadataPropertyTypeTraits.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTypeTraits.h b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTypeTraits.h index 972c6055e..535667d9a 100644 --- a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTypeTraits.h +++ b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTypeTraits.h @@ -55,7 +55,7 @@ template <> struct IsMetadataFloating : std::true_type {}; */ template struct IsMetadataVecN; template struct IsMetadataVecN : std::false_type {}; -template +template struct IsMetadataVecN> : IsMetadataScalar {}; /** @@ -63,7 +63,7 @@ struct IsMetadataVecN> : IsMetadataScalar {}; */ template struct IsMetadataMatN; template struct IsMetadataMatN : std::false_type {}; -template +template struct IsMetadataMatN> : IsMetadataScalar {}; /** From dccb99e66608f2016d11701ae5c9535845684910 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 17 May 2023 07:38:44 +1000 Subject: [PATCH 011/421] Formatting. --- CesiumGltf/test/TestStructuralMetadataPropertyType.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CesiumGltf/test/TestStructuralMetadataPropertyType.cpp b/CesiumGltf/test/TestStructuralMetadataPropertyType.cpp index 759a485ec..fbd27e44a 100644 --- a/CesiumGltf/test/TestStructuralMetadataPropertyType.cpp +++ b/CesiumGltf/test/TestStructuralMetadataPropertyType.cpp @@ -225,7 +225,7 @@ TEST_CASE("Test StructuralMetadata PropertyType utilities function") { PropertyComponentType::None); } - SECTION("Convert string offset type string to PropertyComponentType") { + SECTION("Convert string offset type string to PropertyComponentType") { REQUIRE( convertStringOffsetTypeStringToPropertyComponentType( ExtensionExtStructuralMetadataPropertyTableProperty:: From 25a3320be0ec9c582486a0f09ddecb7aafdc97c0 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Tue, 16 May 2023 18:15:05 -0400 Subject: [PATCH 012/421] Add missing file --- .../CesiumGltf/getOffsetFromOffsetsBuffer.h | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 CesiumGltf/include/CesiumGltf/getOffsetFromOffsetsBuffer.h diff --git a/CesiumGltf/include/CesiumGltf/getOffsetFromOffsetsBuffer.h b/CesiumGltf/include/CesiumGltf/getOffsetFromOffsetsBuffer.h new file mode 100644 index 000000000..5374f2423 --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/getOffsetFromOffsetsBuffer.h @@ -0,0 +1,52 @@ +#pragma once + +#include "CesiumGltf/StructuralMetadataPropertyType.h" + +#include + +#include + +#include +#include + +namespace CesiumGltf { +namespace StructuralMetadata { + +static size_t getOffsetFromOffsetsBuffer( + size_t index, + const gsl::span& offsetBuffer, + PropertyComponentType offsetType) noexcept { + switch (offsetType) { + case PropertyComponentType::Uint8: { + assert(index < offsetBuffer.size() / sizeof(uint8_t)); + const uint8_t offset = *reinterpret_cast( + offsetBuffer.data() + index * sizeof(uint8_t)); + return static_cast(offset); + } + case PropertyComponentType::Uint16: { + assert(index < offsetBuffer.size() / sizeof(uint16_t)); + const uint16_t offset = *reinterpret_cast( + offsetBuffer.data() + index * sizeof(uint16_t)); + return static_cast(offset); + } + case PropertyComponentType::Uint32: { + assert(index < offsetBuffer.size() / sizeof(uint32_t)); + const uint32_t offset = *reinterpret_cast( + offsetBuffer.data() + index * sizeof(uint32_t)); + return static_cast(offset); + } + case PropertyComponentType::Uint64: { + assert(index < offsetBuffer.size() / sizeof(uint64_t)); + const uint64_t offset = *reinterpret_cast( + offsetBuffer.data() + index * sizeof(uint64_t)); + return static_cast(offset); + } + default: + assert(false && "Offset type is invalid"); + return 0; + } + + } + +} // namespace StructuralMetadata +} // namespace CesiumGltf From c0c8e3a6195068332fb94424f6ff570461791f70 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 17 May 2023 08:44:39 +1000 Subject: [PATCH 013/421] Formatting. --- CesiumGltf/include/CesiumGltf/getOffsetFromOffsetsBuffer.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CesiumGltf/include/CesiumGltf/getOffsetFromOffsetsBuffer.h b/CesiumGltf/include/CesiumGltf/getOffsetFromOffsetsBuffer.h index 5374f2423..431430bf4 100644 --- a/CesiumGltf/include/CesiumGltf/getOffsetFromOffsetsBuffer.h +++ b/CesiumGltf/include/CesiumGltf/getOffsetFromOffsetsBuffer.h @@ -45,8 +45,7 @@ static size_t getOffsetFromOffsetsBuffer( assert(false && "Offset type is invalid"); return 0; } - - } +} } // namespace StructuralMetadata } // namespace CesiumGltf From e1305361b498e45ebaafd3b758c3822571c66311 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Wed, 17 May 2023 14:01:40 -0400 Subject: [PATCH 014/421] Add tests for new property view --- .../StructuralMetadataPropertyView.h | 29 +- .../TestStructuralMetadataPropertyView.cpp | 1052 +++++++++++++++++ 2 files changed, 1068 insertions(+), 13 deletions(-) create mode 100644 CesiumGltf/test/TestStructuralMetadataPropertyView.cpp diff --git a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyView.h b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyView.h index 3039d9652..5f356fdb6 100644 --- a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyView.h +++ b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyView.h @@ -152,15 +152,16 @@ enum class MetadataPropertyViewStatus { * {@link ExtensionExtStructuralMetadataPropertyTableProperty::values} like an array of elements. * Data of each instance can be accessed through the {@link get(int64_t instance)} method * - * @param ElementType must be a scalar (uin8_t, int8_t, uint16_t, int16_t, - * uint32_t, int32_t, uint64_t, int64_t, float, double), vecN, matN, bool, - * std::string_view, or MetadataArrayView with T as one of the aforementioned - * types. + * @param ElementType must be one of the following: a scalar (uin8_t, int8_t, + * uint16_t, int16_t, uint32_t, int32_t, uint64_t, int64_t, float, double), vecN + * composed of one of the scalar types, matN composed of one of the scalar + * types, bool, std::string_view, or MetadataArrayView with T as one of the + * aforementioned types. */ template class MetadataPropertyView { public: /** - * @brief Constructs a new instance viewing a non-existent property. + * @brief Constructs a new instance with a non-existent property. */ MetadataPropertyView() : _status{MetadataPropertyViewStatus::InvalidPropertyDoesNotExist}, @@ -171,7 +172,7 @@ template class MetadataPropertyView { /** * @brief Construct a new instance pointing to the data specified by - * ExtensionExtStructuralMetadataPropertyTableProperty + * ExtensionExtStructuralMetadataPropertyTableProperty. * @param values The raw buffer specified by {@link ExtensionExtStructuralMetadataPropertyTableProperty::values} * @param arrayOffsets The raw buffer specified by {@link ExtensionExtStructuralMetadataPropertyTableProperty::arrayOffsets} * @param stringOffsets The raw buffer specified by {@link ExtensionExtStructuralMetadataPropertyTableProperty::stringOffsets} @@ -325,30 +326,32 @@ template class MetadataPropertyView { getStringArrayValues(int64_t index) const noexcept { // Handle fixed-length arrays if (_fixedLengthArrayCount > 0) { - size_t arraySize = _fixedLengthArrayCount * _stringOffsetTypeSize; + // Copy the corresponding string offsets to pass to the MetadataArrayView. + const size_t arraySize = _fixedLengthArrayCount * _stringOffsetTypeSize; const gsl::span stringOffsetValues( _stringOffsets.data() + index * arraySize, - arraySize); + arraySize + _stringOffsetTypeSize); return MetadataArrayView( _values, stringOffsetValues, _stringOffsetType, - _count); + _fixedLengthArrayCount); } + // Handle variable-length arrays const size_t currentArrayOffset = getOffsetFromOffsetsBuffer(index, _arrayOffsets, _arrayOffsetType); const size_t nextArrayOffset = getOffsetFromOffsetsBuffer(index + 1, _arrayOffsets, _arrayOffsetType); - const size_t length = currentArrayOffset - nextArrayOffset; + const size_t arraySize = nextArrayOffset - currentArrayOffset; const gsl::span stringOffsetValues( - _stringOffsets.data() + currentOffset, - length * _arrayOffsetTypeSize); + _stringOffsets.data() + currentArrayOffset, + arraySize + _arrayOffsetTypeSize); return MetadataArrayView( _values, stringOffsetValues, _stringOffsetType, - length / _stringOffsetTypeSize); + arraySize / _arrayOffsetTypeSize); } MetadataArrayView getBooleanArrayValues(int64_t index) const noexcept { diff --git a/CesiumGltf/test/TestStructuralMetadataPropertyView.cpp b/CesiumGltf/test/TestStructuralMetadataPropertyView.cpp new file mode 100644 index 000000000..bd5ab17ec --- /dev/null +++ b/CesiumGltf/test/TestStructuralMetadataPropertyView.cpp @@ -0,0 +1,1052 @@ +#include "CesiumGltf/StructuralMetadataPropertyView.h" + +#include +#include + +#include +#include +#include +#include +#include + +using namespace CesiumGltf::StructuralMetadata; + +template static void checkNumeric(const std::vector& expected) { + std::vector data; + data.resize(expected.size() * sizeof(T)); + std::memcpy(data.data(), expected.data(), data.size()); + + MetadataPropertyView property( + MetadataPropertyViewStatus::Valid, + gsl::span(data.data(), data.size()), + gsl::span(), + gsl::span(), + PropertyComponentType::None, + PropertyComponentType::None, + 0, + static_cast(expected.size()), + false); + + for (int64_t i = 0; i < property.size(); ++i) { + REQUIRE(property.get(i) == expected[static_cast(i)]); + } +} + +template +static void checkVariableLengthArray( + const std::vector& data, + const std::vector offsets, + PropertyComponentType offsetType, + int64_t instanceCount) { + // copy data to buffer + std::vector buffer; + buffer.resize(data.size() * sizeof(DataType)); + std::memcpy(buffer.data(), data.data(), data.size() * sizeof(DataType)); + + // copy offset to buffer + std::vector offsetBuffer; + offsetBuffer.resize(offsets.size() * sizeof(OffsetType)); + std::memcpy( + offsetBuffer.data(), + offsets.data(), + offsets.size() * sizeof(OffsetType)); + + MetadataPropertyView> property( + MetadataPropertyViewStatus::Valid, + gsl::span(buffer.data(), buffer.size()), + gsl::span(offsetBuffer.data(), offsetBuffer.size()), + gsl::span(), + offsetType, + PropertyComponentType::None, + 0, + instanceCount, + false); + + size_t expectedIdx = 0; + for (int64_t i = 0; i < property.size(); ++i) { + MetadataArrayView values = property.get(i); + for (int64_t j = 0; j < values.size(); ++j) { + REQUIRE(values[j] == data[expectedIdx]); + ++expectedIdx; + } + } + + REQUIRE(expectedIdx == data.size()); +} + +template +static void checkFixedLengthArray( + const std::vector& data, + int64_t fixedLengthArrayCount, + int64_t instanceCount) { + std::vector buffer; + buffer.resize(data.size() * sizeof(T)); + std::memcpy(buffer.data(), data.data(), data.size() * sizeof(T)); + + MetadataPropertyView> property( + MetadataPropertyViewStatus::Valid, + gsl::span(buffer.data(), buffer.size()), + gsl::span(), + gsl::span(), + PropertyComponentType::None, + PropertyComponentType::None, + fixedLengthArrayCount, + instanceCount, + false); + + size_t expectedIdx = 0; + for (int64_t i = 0; i < property.size(); ++i) { + MetadataArrayView values = property.get(i); + for (int64_t j = 0; j < values.size(); ++j) { + REQUIRE(values[j] == data[expectedIdx]); + ++expectedIdx; + } + } + + REQUIRE(expectedIdx == data.size()); +} + +TEST_CASE("Check StructuralMetadata scalar numeric property view") { + SECTION("Uint8 Scalar") { + std::vector data{12, 33, 56, 67}; + checkNumeric(data); + } + + SECTION("Int32 Scalar") { + std::vector data{111222, -11133, -56000, 670000}; + checkNumeric(data); + } + + SECTION("Float Scalar") { + std::vector data{12.3333f, -12.44555f, -5.6111f, 6.7421f}; + checkNumeric(data); + } + + SECTION("Double Scalar") { + std::vector data{ + 12222.3302121, + -12000.44555, + -5000.6113111, + 6.7421}; + checkNumeric(data); + } +} + +TEST_CASE("Check StructuralMetadata vecN numeric property view") { + SECTION("Float Vec2") { + std::vector data{ + glm::vec2(10.001f, 0.005f), + glm::vec2(-20.8f, 50.0f), + glm::vec2(99.9f, -9.9f), + glm::vec2(-64.456f, 12.01f)}; + checkNumeric(data); + } + + SECTION("Int32 Vec3") { + std::vector data{ + glm::ivec3(10, 0, -3), + glm::ivec3(-20, 10, 52), + glm::ivec3(9, 9, -9), + glm::ivec3(8, -40, 2)}; + checkNumeric(data); + } + + SECTION("Uint8 Vec4") { + std::vector data{ + glm::u8vec4(1, 2, 3, 0), + glm::u8vec4(4, 5, 6, 1), + glm::u8vec4(7, 8, 9, 0), + glm::u8vec4(0, 0, 0, 1)}; + checkNumeric(data); + } +} + +TEST_CASE("Check StructuralMetadata matN numeric property view") { + SECTION("Float Mat2") { + // clang-format off + std::vector data{ + glm::mat2( + 1.0f, 2.0f, + 3.0f, 4.0f), + glm::mat2( + -10.0f, 40.0f, + 0.08f, 5.4f), + glm::mat2( + 9.99f, -2.0f, + -0.4f, 0.23f) + }; + // clang-format on + checkNumeric(data); + } + + SECTION("Int16 Mat3") { + // clang-format off + std::vector data{ + glm::i16mat3x3( + 1, 2, 3, + 4, 5, 6, + 7, 8, 9), + glm::i16mat3x3( + 10, 0, 5, + -14, 35, 16, + -2, 3, 4), + glm::i16mat3x3( + -6, 5, 2, + 14, 4, -33, + 2, 1, 0) + }; + // clang-format on + checkNumeric(data); + } + + SECTION("Double Mat4") { + // clang-format off + std::vector data{ + glm::dmat4( + 1.0, 2.0, 3.0, 4.0, + 5.0, 6.0, 7.0, 8.0, + 9.0, 10.0, 11.0, 12.0, + 13.0, 14.0, 15.0, 16.0), + glm::dmat4( + 0.1, 0.2, 0.3, 0.4, + 0.5, 0.6, 0.7, 0.8, + -9.0, -10.0, -11.0, -12.0, + 13.0, 14.0, 15.0, 16.0), + glm::dmat4( + 1.0, 0.0, 0.0, 10.0, + 0.0, 0.0, -1.0, -3.5, + 0.0, 1.0, 0.0, 20.4, + 0.0, 0.0, 0.0, 1.0) + }; + // clang-format on + checkNumeric(data); + } +} + +TEST_CASE("Check StructuralMetadata boolean property") { + std::bitset bits = 0b11110101; + unsigned long val = bits.to_ulong(); + std::vector data(sizeof(val)); + std::memcpy(data.data(), &val, sizeof(val)); + + size_t instanceCount = sizeof(unsigned long) * CHAR_BIT; + MetadataPropertyView property( + MetadataPropertyViewStatus::Valid, + gsl::span(data.data(), data.size()), + gsl::span(), + gsl::span(), + PropertyComponentType::None, + PropertyComponentType::None, + 0, + static_cast(instanceCount), + false); + for (int64_t i = 0; i < property.size(); ++i) { + REQUIRE(property.get(i) == bits[static_cast(i)]); + } +} + +TEST_CASE("Check StructuralMetadata string property") { + std::vector strings{ + "This is a fine test", + "What's going on", + "Good morning"}; + size_t totalSize = 0; + for (const auto& s : strings) { + totalSize += s.size(); + } + + uint32_t currentOffset = 0; + std::vector buffer; + buffer.resize(totalSize); + for (size_t i = 0; i < strings.size(); ++i) { + std::memcpy( + buffer.data() + currentOffset, + strings[i].data(), + strings[i].size()); + currentOffset += static_cast(strings[i].size()); + } + + // copy offset to buffer + std::vector offsetBuffer; + offsetBuffer.resize((strings.size() + 1) * sizeof(uint32_t)); + currentOffset = 0; + for (size_t i = 0; i < strings.size(); ++i) { + std::memcpy( + offsetBuffer.data() + i * sizeof(uint32_t), + ¤tOffset, + sizeof(uint32_t)); + currentOffset += static_cast(strings[i].size()); + } + std::memcpy( + offsetBuffer.data() + strings.size() * sizeof(uint32_t), + ¤tOffset, + sizeof(uint32_t)); + + MetadataPropertyView property( + MetadataPropertyViewStatus::Valid, + gsl::span(buffer.data(), buffer.size()), + gsl::span(), + gsl::span(offsetBuffer.data(), offsetBuffer.size()), + PropertyComponentType::None, + PropertyComponentType::Uint32, + 0, + static_cast(strings.size()), + false); + for (int64_t i = 0; i < property.size(); ++i) { + REQUIRE(property.get(i) == strings[static_cast(i)]); + } +} + +TEST_CASE("Check StructuralMetadata fixed-length scalar array property") { + SECTION("Fixed-length array of 4 uint8_ts") { + // clang-format off + std::vector data{ + 210, 211, 3, 42, + 122, 22, 1, 45}; + // clang-format on + checkFixedLengthArray(data, 4, static_cast(data.size() / 4)); + } + + SECTION("Fixed-length array of 3 int8_ts") { + // clang-format off + std::vector data{ + 122, -12, 3, + 44, 11, -2, + 5, 6, -22, + 5, 6, 1}; + // clang-format on + checkFixedLengthArray(data, 3, static_cast(data.size() / 3)); + } + + SECTION("Fixed-length array of 4 int16_ts") { + // clang-format off + std::vector data{ + -122, 12, 3, 44, + 11, 2, 5, -6000, + 119, 30, 51, 200, + 22000, -500, 6000, 1}; + // clang-format on + checkFixedLengthArray(data, 4, static_cast(data.size() / 4)); + } + + SECTION("Fixed-length array of 6 uint32_ts") { + // clang-format off + std::vector data{ + 122, 12, 3, 44, 34444, 2222, + 11, 2, 5, 6000, 1111, 2222, + 119, 30, 51, 200, 12534, 11, + 22000, 500, 6000, 1, 3, 7}; + // clang-format on + checkFixedLengthArray(data, 6, static_cast(data.size() / 6)); + } + + SECTION("Fixed-length array of 2 int32_ts") { + // clang-format off + std::vector data{ + 122, 12, + 3, 44}; + // clang-format on + checkFixedLengthArray(data, 2, static_cast(data.size() / 2)); + } + + SECTION("Fixed-length array of 4 uint64_ts") { + // clang-format off + std::vector data{ + 10022, 120000, 2422, 1111, + 3, 440000, 333, 1455}; + // clang-format on + checkFixedLengthArray(data, 4, static_cast(data.size() / 4)); + } + + SECTION("Fixed-length array of 4 int64_ts") { + // clang-format off + std::vector data{ + 10022, -120000, 2422, 1111, + 3, 440000, -333, 1455}; + // clang-format on + checkFixedLengthArray(data, 4, static_cast(data.size() / 4)); + } + + SECTION("Fixed-length array of 4 floats") { + // clang-format off + std::vector data{ + 10.022f, -12.43f, 242.2f, 1.111f, + 3.333f, 440000.1f, -33.3f, 14.55f}; + // clang-format on + checkFixedLengthArray(data, 4, static_cast(data.size() / 4)); + } + + SECTION("Fixed-length array of 4 double") { + // clang-format off + std::vector data{ + 10.022, -12.43, 242.2, 1.111, + 3.333, 440000.1, -33.3, 14.55}; + // clang-format on + checkFixedLengthArray(data, 4, static_cast(data.size() / 4)); + } +} + +TEST_CASE("Check StructuralMetadata fixed-length vecN array property") { + SECTION("Fixed-length array of 4 u8vec2s") { + // clang-format off + std::vector data{ + glm::u8vec2(10, 21), + glm::u8vec2(3, 42), + glm::u8vec2(122, 22), + glm::u8vec2(1, 45), + glm::u8vec2(0, 0), + glm::u8vec2(32, 12), + glm::u8vec2(8, 19), + glm::u8vec2(6, 5)}; + // clang-format on + checkFixedLengthArray(data, 4, static_cast(data.size() / 4)); + } + + SECTION("Fixed-length array of 2 i8vec3s") { + // clang-format off + std::vector data{ + glm::i8vec3(122, -12, 3), + glm::i8vec3(44, 11, -2), + glm::i8vec3(5, 6, -22), + glm::i8vec3(5, 6, 1), + glm::i8vec3(8, -7, 7), + glm::i8vec3(-4, 36, 17)}; + // clang-format on + checkFixedLengthArray(data, 2, static_cast(data.size() / 2)); + } + + SECTION("Fixed-length array of 3 vec4s") { + // clang-format off + std::vector data{ + glm::vec4(40.2f, -1.2f, 8.8f, 1.0f), + glm::vec4(1.4f, 0.11, 34.0f, 0.0f), + glm::vec4(0.0f, 0.0f, 0.0f, 1.0f), + glm::vec4(1.0f, 2.0f, 3.0f, 6.0f), + glm::vec4(1.08f, -3.71f, 18.0f, -7.0f), + glm::vec4(-17.0f, 33.0f, 8.0f, -3.0f)}; + // clang-format on + checkFixedLengthArray(data, 3, static_cast(data.size() / 3)); + } +} + +TEST_CASE("Check StructuralMetadata fixed-length matN array property") { + SECTION("Fixed-length array of 4 i8mat2x2") { + // clang-format off + std::vector data{ + glm::i8mat2x2( + 10, 21, + 1, -2), + glm::i8mat2x2( + 3, 42, + -10, 12), + glm::i8mat2x2( + 122, 22, + 80, -4), + glm::i8mat2x2( + 15, -2, + 17, 6), + glm::i8mat2x2( + 0, 0, + -1, 1), + glm::i8mat2x2( + 32, -12, + 20, 4), + glm::i8mat2x2( + 8, 19, + -7, 1), + glm::i8mat2x2( + 6, 16, + 2, 5)}; + // clang-format on + checkFixedLengthArray(data, 4, static_cast(data.size() / 4)); + } + + SECTION("Fixed-length array of 2 dmat3s") { + // clang-format off + std::vector data{ + glm::dmat3( + 1.0, 2.0, 3.0, + 0.01, 0.02, 0.03, + 4.0, 5.0, 6.0), + glm::dmat3( + 0.2, -1.0, 8.0, + 40.0, -8.0, 9.0, + 10.0, 0.2, 0.34), + glm::dmat3( + 7.2, 16.5, 4.2, + 33.0, 3.5, -20.0, + 1.22, 1.02, 30.34), + glm::dmat3( + 1.2, 0.5, 0.0, + 0.0, 3.5, 0.0, + 0.76, 0.9, 1.1), + glm::dmat3( + 25.0, 50.4, 8.8, + 16.1, 23.0, 40.0, + 0.8, 8.9, 5.0), + glm::dmat3( + -4.0, 9.4, 11.2, + 5.5, 3.09, 0.301, + 4.5, 52.4, 1.05)}; + // clang-format on + checkFixedLengthArray(data, 2, static_cast(data.size() / 2)); + } + + SECTION("Fixed-length array of 3 u8mat4x4") { + // clang-format off + std::vector data{ + glm::u8mat4x4( + 1, 2, 3, 4, + 5, 6, 7, 8, + 9, 10, 11, 12, + 13, 14, 15, 16), + glm::u8mat4x4( + 0, 4, 2, 19, + 8, 7, 3, 5, + 43, 21, 10, 9, + 3, 10, 8, 6), + glm::u8mat4x4( + 1, 0, 0, 19, + 0, 1, 0, 2, + 0, 0, 4, 0, + 0, 0, 0, 1), + glm::u8mat4x4( + 6, 2, 7, 8, + 50, 11, 18, 2, + 3, 12, 6, 9, + 4, 20, 10, 4), + glm::u8mat4x4( + 10, 2, 46, 5, + 8, 7, 20, 13, + 24, 8, 6, 9, + 2, 15, 4, 3), + glm::u8mat4x4( + 3, 2, 1, 0, + 0, 1, 2, 3, + 8, 7, 6, 5, + 4, 3, 2, 1),}; + // clang-format on + checkFixedLengthArray(data, 3, static_cast(data.size() / 3)); + } +} + +TEST_CASE("Check StructuralMetadata variable-length scalar array property") { + SECTION("Variable-length array of uint8_t") { + // clang-format off + std::vector data{ + 3, 2, + 0, 45, 2, 1, 4, + 1, 3, 2, + 1, 3, 4, 1 + }; + std::vector offsets{ + 0, 2, 7, 10, 14 + }; + // clang-format on + + checkVariableLengthArray(data, offsets, PropertyComponentType::Uint32, 4); + } + + SECTION("Variable-length array of int32_t") { + // clang-format off + std::vector data{ + 3, 200, + 0, 450, 200, 1, 4, + 1, 3, 2, + 1, 3, 4, 1 + }; + std::vector offsets{ + 0, 2 * sizeof(int32_t), 7 * sizeof(int32_t), 10 * sizeof(int32_t), 14 + * sizeof(int32_t) + }; + // clang-format on + + checkVariableLengthArray(data, offsets, PropertyComponentType::Uint32, 4); + } + + SECTION("Variable-length array of double") { + // clang-format off + std::vector data{ + 3.333, 200.2, + 0.1122, 4.50, 2.30, 1.22, 4.444, + 1.4, 3.3, 2.2, + 1.11, 3.2, 4.111, 1.44 + }; + std::vector offsets{ + 0, 2 * sizeof(double), 7 * sizeof(double), 10 * sizeof(double), 14 * + sizeof(double) + }; + // clang-format on + + checkVariableLengthArray(data, offsets, PropertyComponentType::Uint32, 4); + } +} + +TEST_CASE("Check StructuralMetadata variable-length vecN array property") { + SECTION("Variable-length array of ivec2") { + // clang-format off + std::vector data{ + glm::ivec2(3, -2), glm::ivec2(20, 3), + glm::ivec2(0, 45), glm::ivec2(-10, 2), glm::ivec2(4, 4), glm::ivec2(1, -1), + glm::ivec2(3, 1), glm::ivec2(3, 2), glm::ivec2(0, -5), + glm::ivec2(-9, 10), glm::ivec2(8, -2) + }; + // clang-format on + std::vector offsets{ + 0, + 2 * sizeof(glm::ivec2), + 6 * sizeof(glm::ivec2), + 9 * sizeof(glm::ivec2), + 11 * sizeof(glm::ivec2)}; + + checkVariableLengthArray(data, offsets, PropertyComponentType::Uint32, 4); + } + + SECTION("Variable-length array of dvec3") { + // clang-format off + std::vector data{ + glm::dvec3(-0.02, 2.0, 1.0), glm::dvec3(9.92, 9.0, -8.0), + glm::dvec3(22.0, 5.5, -3.7), glm::dvec3(1.02, 9.0, -8.0), glm::dvec3(0.0, 0.5, 1.0), + glm::dvec3(-1.3, -5.0, -90.0), + glm::dvec3(4.4, 1.0, 2.3), glm::dvec3(5.8, 7.07, -4.0), + glm::dvec3(-2.0, 0.85, 0.22), glm::dvec3(-8.8, 5.1, 0.0), glm::dvec3(12.0, 8.0, -2.2), + }; + // clang-format on + std::vector offsets{ + 0, + 2 * sizeof(glm::dvec3), + 5 * sizeof(glm::dvec3), + 6 * sizeof(glm::dvec3), + 8 * sizeof(glm::dvec3), + 11 * sizeof(glm::dvec3)}; + + checkVariableLengthArray(data, offsets, PropertyComponentType::Uint32, 5); + } + + SECTION("Variable-length array of u8vec4") { + // clang-format off + std::vector data{ + glm::u8vec4(1, 2, 3, 4), glm::u8vec4(5, 6, 7, 8), + glm::u8vec4(9, 2, 1, 0), + glm::u8vec4(8, 7, 10, 21), glm::u8vec4(3, 6, 8, 0), glm::u8vec4(0, 0, 0, 1), + glm::u8vec4(64, 8, 17, 5), glm::u8vec4(35, 23, 10, 0), + glm::u8vec4(99, 8, 1, 2) + }; + // clang-format on + + std::vector offsets{ + 0, + 2 * sizeof(glm::u8vec4), + 3 * sizeof(glm::u8vec4), + 6 * sizeof(glm::u8vec4), + 8 * sizeof(glm::u8vec4), + 9 * sizeof(glm::u8vec4)}; + + checkVariableLengthArray(data, offsets, PropertyComponentType::Uint32, 5); + } +} + +TEST_CASE("Check StructuralMetadata variable-length matN array property") { + SECTION("Variable-length array of dmat2") { + // clang-format off + std::vector data0{ + glm::dmat2( + 3.23, -2.456, + 1.0, 0.003), + glm::dmat2( + 40.0, 3.66, + 8.567, -9.8) + }; + std::vector data1{ + glm::dmat2( + 1.1, 10.02, + 7.0, 0.0), + }; + std::vector data2{ + glm::dmat2( + 18.8, 0.0, + 1.0, 17.2), + glm::dmat2( + -4.0, -0.053, + -9.0, 1.0), + glm::dmat2( + 1.1, 8.88, + -99.0, 1.905), + }; + // clang-format on + + std::vector data; + data.reserve(data0.size() + data1.size() + data2.size()); + data.insert(data.end(), data0.begin(), data0.end()); + data.insert(data.end(), data1.begin(), data1.end()); + data.insert(data.end(), data2.begin(), data2.end()); + + std::vector offsets{ + 0, + 2 * sizeof(glm::dmat2), + 3 * sizeof(glm::dmat2), + 6 * sizeof(glm::dmat2), + }; + + checkVariableLengthArray(data, offsets, PropertyComponentType::Uint32, 3); + } + + SECTION("Variable-length array of i16mat3x3") { + // clang-format off + std::vector data0{ + glm::i16mat3x3( + 1, 0, 0, + 0, -1, 0, + 0, 0, 1), + }; + std::vector data1{ + glm::i16mat3x3( + 2, 3, 0, + -9, 14, 4, + -2, -5, 10), + glm::i16mat3x3( + 0, 5, 10, + -8, 33, 2, + -9, 8, 41), + glm::i16mat3x3( + 10, -7, 8, + 21, -9, 2, + 3, 4, 5) + }; + std::vector data2{ + glm::i16mat3x3( + -10, 50, 30, + 8, 17, 2, + 16, 40, 3), + glm::i16mat3x3( + -9, 18, 8, + 20, 3, 4, + 16, 7, -9), + }; + // clang-format on + + std::vector data; + data.reserve(data0.size() + data1.size() + data2.size()); + data.insert(data.end(), data0.begin(), data0.end()); + data.insert(data.end(), data1.begin(), data1.end()); + data.insert(data.end(), data2.begin(), data2.end()); + + std::vector offsets{ + 0, + 1 * sizeof(glm::i16mat3x3), + 4 * sizeof(glm::i16mat3x3), + 6 * sizeof(glm::i16mat3x3)}; + + checkVariableLengthArray(data, offsets, PropertyComponentType::Uint32, 3); + } + + SECTION("Variable-length array of u8mat4x4") { + // clang-format off + std::vector data0{ + glm::u8mat4x4( + 1, 0, 0, 0, + 0, 4, 0, 0, + 0, 0, 1, 10, + 0, 0, 0, 1), + glm::u8mat4x4( + 10, 0, 0, 8, + 0, 5, 0, 4, + 0, 0, 1, 3, + 0, 0, 0, 1), + glm::u8mat4x4( + 8, 0, 0, 9, + 0, 3, 0, 11, + 0, 0, 20, 0, + 0, 0, 0, 1), + }; + std::vector data1{ + glm::u8mat4x4( + 1, 2, 3, 4, + 4, 3, 2, 1, + 0, 9, 8, 7, + 6, 5, 5, 6), + }; + std::vector data2{ + glm::u8mat4x4( + 4, 1, 8, 9, + 2, 6, 50, 1, + 10, 20, 30, 9, + 8, 7, 20, 4), + glm::u8mat4x4( + 0, 2, 1, 0, + 25, 19, 8, 2, + 3, 6, 40, 50, + 15, 9, 0, 3), + }; + // clang-format on + + std::vector data; + data.reserve(data0.size() + data1.size() + data2.size()); + data.insert(data.end(), data0.begin(), data0.end()); + data.insert(data.end(), data1.begin(), data1.end()); + data.insert(data.end(), data2.begin(), data2.end()); + + std::vector offsets{ + 0, + 3 * sizeof(glm::u8mat4x4), + 4 * sizeof(glm::u8mat4x4), + 6 * sizeof(glm::u8mat4x4)}; + + checkVariableLengthArray(data, offsets, PropertyComponentType::Uint32, 3); + } +} + +TEST_CASE("Check StructuralMetadata fixed-length array of string") { + std::vector strings{ + "Test 1", + "Test 2", + "Test 3", + "Test 4", + "Test 5", + "Test 6", + "This is a fine test", + "What's going on", + "Good morning"}; + size_t totalSize = 0; + for (const auto& str : strings) { + totalSize += str.size(); + } + + const size_t stringCount = strings.size(); + std::vector buffer; + buffer.resize(totalSize); + uint32_t currentStringOffset = 0; + for (size_t i = 0; i < stringCount; ++i) { + std::memcpy( + buffer.data() + currentStringOffset, + strings[i].data(), + strings[i].size()); + currentStringOffset += static_cast(strings[i].size()); + } + + // Create string offset buffer + std::vector stringOffsets; + stringOffsets.resize((stringCount + 1) * sizeof(uint32_t)); + currentStringOffset = 0; + for (size_t i = 0; i < stringCount; ++i) { + std::memcpy( + stringOffsets.data() + i * sizeof(uint32_t), + ¤tStringOffset, + sizeof(uint32_t)); + currentStringOffset += static_cast(strings[i].size()); + } + + std::memcpy( + stringOffsets.data() + stringCount * sizeof(uint32_t), + ¤tStringOffset, + sizeof(uint32_t)); + + MetadataPropertyView> property( + MetadataPropertyViewStatus::Valid, + gsl::span(buffer.data(), buffer.size()), + gsl::span(), + gsl::span(stringOffsets.data(), stringOffsets.size()), + PropertyComponentType::None, + PropertyComponentType::Uint32, + 3, + static_cast(stringCount / 3), + false); + + size_t expectedIdx = 0; + for (int64_t i = 0; i < property.size(); ++i) { + MetadataArrayView values = property.get(i); + for (int64_t j = 0; j < values.size(); ++j) { + std::string_view v = values[j]; + REQUIRE(v == strings[expectedIdx]); + ++expectedIdx; + } + } + + REQUIRE(expectedIdx == stringCount); +} + +TEST_CASE( + "Check StructuralMetadata variable-length array of strings property") { + // clang-format off + std::vector arrayOffsets{ + 0, + 4 * sizeof(uint32_t), + 7 * sizeof(uint32_t), + 11 * sizeof(uint32_t) + }; + + std::vector strings{ + "Test 1", "Test 2", "Test 3", "Test 4", + "Test 5", "Test 6", "Test 7", + "test 8", "Test 9", "Test 10", "Test 11" + }; + // clang-format on + + size_t totalSize = 0; + for (const auto& str : strings) { + totalSize += str.size(); + } + + const size_t stringCount = strings.size(); + uint32_t currentOffset = 0; + std::vector buffer; + buffer.resize(totalSize); + for (size_t i = 0; i < stringCount; ++i) { + std::memcpy( + buffer.data() + currentOffset, + strings[i].data(), + strings[i].size()); + currentOffset += static_cast(strings[i].size()); + } + + std::vector stringOffsets; + stringOffsets.resize((stringCount + 1) * sizeof(uint32_t)); + currentOffset = 0; + for (size_t i = 0; i < stringCount; ++i) { + std::memcpy( + stringOffsets.data() + i * sizeof(uint32_t), + ¤tOffset, + sizeof(uint32_t)); + currentOffset += static_cast(strings[i].size()); + } + std::memcpy( + stringOffsets.data() + stringCount * sizeof(uint32_t), + ¤tOffset, + sizeof(uint32_t)); + + MetadataPropertyView> property( + MetadataPropertyViewStatus::Valid, + gsl::span(buffer.data(), buffer.size()), + gsl::span( + reinterpret_cast(arrayOffsets.data()), + arrayOffsets.size() * sizeof(uint32_t)), + gsl::span(stringOffsets.data(), stringOffsets.size()), + PropertyComponentType::Uint32, + PropertyComponentType::Uint32, + 0, + 3, + false); + + size_t expectedIdx = 0; + for (int64_t i = 0; i < property.size(); ++i) { + MetadataArrayView values = property.get(i); + for (int64_t j = 0; j < values.size(); ++j) { + std::string_view v = values[j]; + REQUIRE(v == strings[expectedIdx]); + ++expectedIdx; + } + } + + REQUIRE(expectedIdx == stringCount); +} + +TEST_CASE("Check StructuralMetadata fixed-length boolean array property") { + std::vector buffer{ + static_cast(0b10101111), + static_cast(0b11111010), + static_cast(0b11100111)}; + + MetadataPropertyView> property( + MetadataPropertyViewStatus::Valid, + gsl::span(buffer.data(), buffer.size()), + gsl::span(), + gsl::span(), + PropertyComponentType::Uint32, + PropertyComponentType::None, + 12, + 2, + false); + + REQUIRE(property.size() == 2); + + MetadataArrayView val0 = property.get(0); + REQUIRE(val0.size() == 12); + REQUIRE(static_cast(val0[0]) == 1); + REQUIRE(static_cast(val0[1]) == 1); + REQUIRE(static_cast(val0[2]) == 1); + REQUIRE(static_cast(val0[3]) == 1); + REQUIRE(static_cast(val0[4]) == 0); + REQUIRE(static_cast(val0[5]) == 1); + REQUIRE(static_cast(val0[6]) == 0); + REQUIRE(static_cast(val0[7]) == 1); + REQUIRE(static_cast(val0[8]) == 0); + REQUIRE(static_cast(val0[9]) == 1); + REQUIRE(static_cast(val0[10]) == 0); + REQUIRE(static_cast(val0[11]) == 1); + + MetadataArrayView val1 = property.get(1); + REQUIRE(static_cast(val1[0]) == 1); + REQUIRE(static_cast(val1[1]) == 1); + REQUIRE(static_cast(val1[2]) == 1); + REQUIRE(static_cast(val1[3]) == 1); + REQUIRE(static_cast(val1[4]) == 1); + REQUIRE(static_cast(val1[5]) == 1); + REQUIRE(static_cast(val1[6]) == 1); + REQUIRE(static_cast(val1[7]) == 0); + REQUIRE(static_cast(val1[8]) == 0); + REQUIRE(static_cast(val1[9]) == 1); + REQUIRE(static_cast(val1[10]) == 1); + REQUIRE(static_cast(val1[11]) == 1); +} + +TEST_CASE("Check StructuralMetadata variable-length boolean array property") { + std::vector buffer{ + static_cast(0b10101111), + static_cast(0b11111010), + static_cast(0b11100111), + static_cast(0b11110110)}; + + std::vector offsetBuffer{0, 3, 12, 28}; + + MetadataPropertyView> property( + MetadataPropertyViewStatus::Valid, + gsl::span(buffer.data(), buffer.size()), + gsl::span( + reinterpret_cast(offsetBuffer.data()), + offsetBuffer.size() * sizeof(uint32_t)), + gsl::span(), + PropertyComponentType::Uint32, + PropertyComponentType::None, + 0, + 3, + false); + + REQUIRE(property.size() == 3); + + MetadataArrayView val0 = property.get(0); + REQUIRE(val0.size() == 3); + REQUIRE(static_cast(val0[0]) == 1); + REQUIRE(static_cast(val0[1]) == 1); + REQUIRE(static_cast(val0[2]) == 1); + + MetadataArrayView val1 = property.get(1); + REQUIRE(val1.size() == 9); + REQUIRE(static_cast(val1[0]) == 1); + REQUIRE(static_cast(val1[1]) == 0); + REQUIRE(static_cast(val1[2]) == 1); + REQUIRE(static_cast(val1[3]) == 0); + REQUIRE(static_cast(val1[4]) == 1); + REQUIRE(static_cast(val1[5]) == 0); + REQUIRE(static_cast(val1[6]) == 1); + REQUIRE(static_cast(val1[7]) == 0); + REQUIRE(static_cast(val1[8]) == 1); + + MetadataArrayView val2 = property.get(2); + REQUIRE(val2.size() == 16); + REQUIRE(static_cast(val2[0]) == 1); + REQUIRE(static_cast(val2[1]) == 1); + REQUIRE(static_cast(val2[2]) == 1); + REQUIRE(static_cast(val2[3]) == 1); + REQUIRE(static_cast(val2[4]) == 1); + REQUIRE(static_cast(val2[5]) == 1); + REQUIRE(static_cast(val2[6]) == 1); + REQUIRE(static_cast(val2[7]) == 0); + REQUIRE(static_cast(val2[8]) == 0); + REQUIRE(static_cast(val2[9]) == 1); + REQUIRE(static_cast(val2[10]) == 1); + REQUIRE(static_cast(val2[11]) == 1); + REQUIRE(static_cast(val2[12]) == 0); + REQUIRE(static_cast(val2[13]) == 1); + REQUIRE(static_cast(val2[14]) == 1); + REQUIRE(static_cast(val2[15]) == 0); +} From 2228f75d6cceb1b5b7bfe61c1513831beda0295b Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Wed, 17 May 2023 14:50:59 -0400 Subject: [PATCH 015/421] Rename enums and fix documentation --- .../StructuralMetadataPropertyView.h | 58 +++++++++---------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyView.h b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyView.h index 5f356fdb6..b2a86194c 100644 --- a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyView.h +++ b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyView.h @@ -33,115 +33,115 @@ enum class MetadataPropertyViewStatus { * @brief This property view does not exist in the * ExtensionExtStructuralMetadataPropertyTable. */ - InvalidPropertyDoesNotExist, + ErrorPropertyDoesNotExist, /** * @brief This property view does not have a correct type with what is * specified in {@link ExtensionExtStructuralMetadataClassProperty::type}. */ - InvalidTypeMismatch, + ErrorTypeMismatch, /** * @brief This property view does not have a correct component type with what * is specified in {@link ExtensionExtStructuralMetadataClassProperty::componentType}. */ - InvalidComponentTypeMismatch, + ErrorComponentTypeMismatch, /** * @brief This property view does not have a valid value buffer view index. */ - InvalidValueBufferView, + ErrorInvalidValueBufferView, /** * @brief This array property view does not have a valid array offset buffer * view index. */ - InvalidArrayOffsetBufferViewIndex, + ErrorInvalidArrayOffsetBufferView, /** * @brief This string property view does not have a valid string offset buffer * view index. */ - InvalidStringOffsetBufferViewIndex, + ErrorInvalidStringOffsetBufferView, /** * @brief This property view has a valid value buffer view, but the buffer * view specifies an invalid buffer index. */ - InvalidValueBufferIndex, + ErrorInvalidValueBuffer, /** * @brief This property view has a valid array string buffer view, but the * buffer view specifies an invalid buffer index. */ - InvalidArrayOffsetBufferIndex, + ErrorInvalidArrayOffsetBuffer, /** * @brief This property view has a valid string offset buffer view, but the * buffer view specifies an invalid buffer index. */ - InvalidStringOffsetBufferIndex, + ErrorInvalidStringOffsetBuffer, /** * @brief This property view has an out-of-bounds buffer view */ - InvalidBufferViewOutOfBounds, + ErrorBufferViewOutOfBounds, /** - * @brief This property view has an invalid buffer view's length which is not - * a multiple of the size of its type or offset type + * @brief This property view has an invalid buffer view; its length is not + * a multiple of the size of its type / offset type. */ - InvalidBufferViewSizeNotDivisibleByTypeSize, + ErrorBufferViewSizeNotDivisibleByTypeSize, /** - * @brief This property view has an invalid buffer view's length which cannot - * fit all the instances of the feature table + * @brief This property view has an invalid buffer view; its length does not + * match the size of the property table. */ - InvalidBufferViewSizeNotFitInstanceCount, + ErrorBufferViewSizeDoesNotMatchPropertyTableCount, /** * @brief This array property view has both count and offset buffer * view defined. */ - InvalidArrayCountAndOffsetBufferCoexist, + ErrorArrayCountAndOffsetBufferCoexist, /** * @brief This array property view doesn't have count nor offset buffer view * defined. */ - InvalidArrayCountAndOffsetBufferDontExist, + ErrorArrayCountAndOffsetBufferDontExist, /** * @brief This property view has an unknown array offset type. */ - InvalidArrayOffsetType, + ErrorInvalidArrayOffsetType, /** * @brief This property view has an unknown string offset type. */ - InvalidStringOffsetType, + ErrorInvalidStringOffsetType, /** * @brief This property view's array offset values are not sorted in ascending * order. */ - InvalidArrayOffsetValuesNotSorted, + ErrorArrayOffsetsNotSorted, /** * @brief This property view's string offset values are not sorted in * ascending order. */ - InvalidStringOffsetValuesNotSorted, + ErrorStringOffsetsNotSorted, /** * @brief This property view has an array offset that is out of bounds. */ - InvalidArrayOffsetValueOutOfBounds, + ErrorArrayOffsetOutOfBounds, /** * @brief This property view has a string offset that is out of bounds. */ - InvalidStringOffsetValueOutOfBounds + ErrorStringOffsetOutOfBounds }; /** @@ -152,11 +152,11 @@ enum class MetadataPropertyViewStatus { * {@link ExtensionExtStructuralMetadataPropertyTableProperty::values} like an array of elements. * Data of each instance can be accessed through the {@link get(int64_t instance)} method * - * @param ElementType must be one of the following: a scalar (uin8_t, int8_t, - * uint16_t, int16_t, uint32_t, int32_t, uint64_t, int64_t, float, double), vecN - * composed of one of the scalar types, matN composed of one of the scalar - * types, bool, std::string_view, or MetadataArrayView with T as one of the - * aforementioned types. + * @param ElementType must be one of the following: a scalar (uint8_t, int8_t, + * uint16_t, int16_t, uint32_t, int32_t, uint64_t, int64_t, float, double), a + * glm vecN composed of one of the scalar types, a glm matN composed of one of + * the scalar types, bool, std::string_view, or MetadataArrayView with T as + * one of the aforementioned types. */ template class MetadataPropertyView { public: From f52e1bab0168d73d373c6e62622d996b2197a232 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Wed, 17 May 2023 14:52:38 -0400 Subject: [PATCH 016/421] Fix incorrect variable names --- .../CesiumGltf/StructuralMetadataPropertyView.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyView.h b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyView.h index b2a86194c..ddba25835 100644 --- a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyView.h +++ b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyView.h @@ -166,8 +166,8 @@ template class MetadataPropertyView { MetadataPropertyView() : _status{MetadataPropertyViewStatus::InvalidPropertyDoesNotExist}, _values{}, - _componentCount{}, - _instanceCount{}, + _fixedLengthArrayCount{}, + _size{}, _normalized{} {} /** @@ -178,7 +178,7 @@ template class MetadataPropertyView { * @param stringOffsets The raw buffer specified by {@link ExtensionExtStructuralMetadataPropertyTableProperty::stringOffsets} * @param offsetType The offset type of arrayOffsets specified by {@link ExtensionExtStructuralMetadataPropertyTableProperty::arrayOffsetType} * @param offsetType The offset type of stringOffsets specified by {@link ExtensionExtStructuralMetadataPropertyTableProperty::stringOffsetType} - * @param fixedArrayCount The number of elements in each array value specified by {@link ExtensionExtStructuralMetadataClassProperty::count} + * @param fixedLengthArrayCount The number of elements in each array value specified by {@link ExtensionExtStructuralMetadataClassProperty::count} * @param size The number of elements in the property table specified by {@link ExtensionExtStructuralMetadataPropertyTable::count} * @param normalized Whether this property has a normalized integer type. */ @@ -267,7 +267,9 @@ template class MetadataPropertyView { * * @return The count of this property. */ - int64_t getFixedLengthArrayCount() const noexcept { return _fixedArrayCount; } + int64_t getFixedLengthArrayCount() const noexcept { + return _fixedLengthArrayCount; + } /** * @brief Whether this property has a normalized integer type. From 9f7b66c7bb67cf281c428e5a34146bd47187bc95 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Wed, 17 May 2023 15:14:35 -0400 Subject: [PATCH 017/421] Fix enum names --- .../StructuralMetadataPropertyView.h | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyView.h b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyView.h index ddba25835..cbe2dd43c 100644 --- a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyView.h +++ b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyView.h @@ -36,17 +36,23 @@ enum class MetadataPropertyViewStatus { ErrorPropertyDoesNotExist, /** - * @brief This property view does not have a correct type with what is + * @brief This property view's type does not match what is * specified in {@link ExtensionExtStructuralMetadataClassProperty::type}. */ ErrorTypeMismatch, /** - * @brief This property view does not have a correct component type with what + * @brief This property view's component type does not match what * is specified in {@link ExtensionExtStructuralMetadataClassProperty::componentType}. */ ErrorComponentTypeMismatch, + /** + * @brief This property view differs from what is specified in + * {@link ExtensionExtStructuralMetadataClassProperty::array}. + */ + ErrorArrayTypeMismatch, + /** * @brief This property view does not have a valid value buffer view index. */ @@ -83,7 +89,8 @@ enum class MetadataPropertyViewStatus { ErrorInvalidStringOffsetBuffer, /** - * @brief This property view has an out-of-bounds buffer view + * @brief This property view has a buffer view that points outside the bounds + * of its target buffer. */ ErrorBufferViewOutOfBounds, @@ -100,14 +107,14 @@ enum class MetadataPropertyViewStatus { ErrorBufferViewSizeDoesNotMatchPropertyTableCount, /** - * @brief This array property view has both count and offset buffer - * view defined. + * @brief This array property view has both a fixed length and an offset + * buffer view defined. */ ErrorArrayCountAndOffsetBufferCoexist, /** - * @brief This array property view doesn't have count nor offset buffer view - * defined. + * @brief This array property view has neither a fixed length nor an offset + * buffer view defined. */ ErrorArrayCountAndOffsetBufferDontExist, @@ -164,7 +171,7 @@ template class MetadataPropertyView { * @brief Constructs a new instance with a non-existent property. */ MetadataPropertyView() - : _status{MetadataPropertyViewStatus::InvalidPropertyDoesNotExist}, + : _status{MetadataPropertyViewStatus::ErrorPropertyDoesNotExist}, _values{}, _fixedLengthArrayCount{}, _size{}, From 5e999deef4ca1a9c01df5830bf9d9f4986f099e4 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Thu, 18 May 2023 11:18:33 -0400 Subject: [PATCH 018/421] Add StructuralMetadataPropertyTableView --- .../StructuralMetadataPropertyTableView.h | 793 ++++++++++++++++++ .../StructuralMetadataPropertyType.h | 4 + .../StructuralMetadataPropertyView.h | 3 +- .../StructuralMetadataPropertyTableView.cpp | 493 +++++++++++ .../src/StructuralMetadataPropertyType.cpp | 34 +- 5 files changed, 1314 insertions(+), 13 deletions(-) create mode 100644 CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTableView.h create mode 100644 CesiumGltf/src/StructuralMetadataPropertyTableView.cpp diff --git a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTableView.h b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTableView.h new file mode 100644 index 000000000..9e555dca5 --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTableView.h @@ -0,0 +1,793 @@ +#pragma once + +#include "CesiumGltf/ExtensionModelExtStructuralMetadata.h" +#include "CesiumGltf/Model.h" +#include "CesiumGltf/StructuralMetadataPropertyType.h" +#include "CesiumGltf/StructuralMetadataPropertyView.h" + +#include + +#include + +namespace CesiumGltf { +namespace StructuralMetadata { + +/** + * @brief Utility to retrieve the data of + * ExtensionExtStructuralMetadataPropertyTable. + * + * This should be used to get a {@link MetadataPropertyView} of a property. + * It will validate the EXT_structural_metadata format and ensure {@link MetadataPropertyView} + * does not access out of bounds. + */ + +class MetadataPropertyTableView { +public: + /** + * @brief Create an instance of MetadataPropertyTableView. + * @param pModel The Gltf Model that stores property table data + * @param pPropertyTable The ExtensionExtStructuralMetadataPropertyTable from + * which the view will retrieve data. + */ + MetadataPropertyTableView( + const Model* pModel, + const ExtensionExtStructuralMetadataPropertyTable* pPropertyTable); + + /** + * @brief Find the {@link ExtensionExtStructuralMetadataClassProperty} which + * stores the type information of a property based on the property's name. + * @param propertyName The name of the property to retrieve type info + * @return Pointer to the ExtensionExtStructuralMetadataClassProperty. Return + * nullptr if no property was found. + */ + const ExtensionExtStructuralMetadataClassProperty* + getClassProperty(const std::string& propertyName) const; + + /** + * @brief Gets a MetadataPropertyView to view the data of a property stored in + * the ExtensionExtStructuralMetadataPropertyTable. + * + * This method will validate the EXT_structural_metadata format to ensure + * MetadataPropertyView retrieves the correct data. T must be one of the + * following: a scalar (uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t, + * uint64_t, int64_t, float, double), a glm vecN composed of one of the scalar + * types, a glm matN composed of one of the scalar types, bool, + * std::string_view, or MetadataArrayView with T as one of the + * aforementioned types. + * + * @param propertyName The name of the property to retrieve data from + * @return MetadataPropertyView of a property. The property view will be + * invalid if no property is found. + */ + template + MetadataPropertyView + getPropertyView(const std::string& propertyName) const { + if (_pPropertyTable->count <= 0) { + return createInvalidPropertyView( + StructuralMetadata::MetadataPropertyViewStatus:: + ErrorPropertyDoesNotExist); + } + + const ExtensionExtStructuralMetadataClassProperty* pClassProperty = + getClassProperty(propertyName); + if (!pClassProperty) { + return createInvalidPropertyView( + StructuralMetadata::MetadataPropertyViewStatus:: + ErrorPropertyDoesNotExist); + } + + return getPropertyViewImpl(propertyName, *pClassProperty); + } + + /** + * @brief Gets a MetadataPropertyView through a callback that accepts a + * property name and a std::optional> to view the data + * of a property stored in the ExtensionExtStructuralMetadataPropertyTable. + * + * This method will validate the EXT_structural_metadata format to ensure + * MetadataPropertyView retrieves the correct data. T must be one of the + * following: a scalar (uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t, + * uint64_t, int64_t, float, double), a glm vecN composed of one of the scalar + * types, a glm matN composed of one of the scalar types, bool, + * std::string_view, or MetadataArrayView with T as one of the + * aforementioned types. If the property is invalid, std::nullopt will be + * passed to the callback. Otherwise, a valid property view will be passed to + * the callback. + * + * @param propertyName The name of the property to retrieve data from + * @tparam callback A callback function that accepts property name and + * std::optional> + */ + template + void + getPropertyView(const std::string& propertyName, Callback&& callback) const { + const ExtensionExtStructuralMetadataClassProperty* pClassProperty = + getClassProperty(propertyName); + if (!pClassProperty) { + return; + } + + PropertyType type = convertStringToPropertyType(pClassProperty->type); + PropertyComponentType componentType = + convertStringToPropertyType(pClassProperty->componentType); + + if (pClassProperty->array) { + getArrayPropertyViewImpl( + propertyName, + *pClassProperty, + componentType, + std::forward(callback)); + } else if (isPropertyTypeVecN(type)) { + getVecNPropertyViewImpl( + propertyName, + *pClassProperty, + componentType, + std::forward(callback)); + } else if (isPropertyTypeMatN(type)) { + getMatNPropertyViewImpl( + propertyName, + *pClassProperty, + componentType, + std::forward(callback)); + } else { + getPrimitivePropertyViewImpl( + propertyName, + *pClassProperty, + type, + std::forward(callback)); + } + } + + /** + * @brief Iterates over each property in the + * ExtensionExtStructuralMetadataPropertyTable with a callback that accepts a + * property name and a std::optional> to view the data + * stored in the ExtensionExtStructuralMetadataPropertyTableProperty. + * + * This method will validate the EXT_structural_metadata format to ensure + * MetadataPropertyView retrieves the correct data. T must be one of the + * following: a scalar (uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t, + * uint64_t, int64_t, float, double), a glm vecN composed of one of the scalar + * types, a glm matN composed of one of the scalar types, bool, + * std::string_view, or MetadataArrayView with T as one of the + * aforementioned types. If the property is invalid, std::nullopt will be + * passed to the callback. Otherwise, a valid property view will be passed to + * the callback. + * + * @param propertyName The name of the property to retrieve data from + * @tparam callback A callback function that accepts property name and + * std::optional> + */ + template void forEachProperty(Callback&& callback) const { + for (const auto& property : this->_pClass->properties) { + getPropertyView(property.first, std::forward(callback)); + } + } + +private: + template + void getArrayPropertyViewImpl( + const std::string& propertyName, + const ExtensionExtStructuralMetadataClassProperty& classProperty, + StructuralMetadata::PropertyType type, + Callback&& callback) const { + switch (type) { + case PropertyType::Int8: + callback( + propertyName, + getPropertyViewImpl>( + propertyName, + classProperty)); + break; + case PropertyType::Uint8: + callback( + propertyName, + getPropertyViewImpl>( + propertyName, + classProperty)); + break; + case PropertyType::Int16: + callback( + propertyName, + getPropertyViewImpl>( + propertyName, + classProperty)); + break; + case PropertyType::Uint16: + callback( + propertyName, + getPropertyViewImpl>( + propertyName, + classProperty)); + break; + case PropertyType::Int32: + callback( + propertyName, + getPropertyViewImpl>( + propertyName, + classProperty)); + break; + case PropertyType::Uint32: + callback( + propertyName, + getPropertyViewImpl>( + propertyName, + classProperty)); + break; + case PropertyType::Int64: + callback( + propertyName, + getPropertyViewImpl>( + propertyName, + classProperty)); + break; + case PropertyType::Uint64: + callback( + propertyName, + getPropertyViewImpl>( + propertyName, + classProperty)); + break; + case PropertyType::Float32: + callback( + propertyName, + getPropertyViewImpl>( + propertyName, + classProperty)); + break; + case PropertyType::Float64: + callback( + propertyName, + getPropertyViewImpl>( + propertyName, + classProperty)); + break; + case PropertyType::Boolean: + callback( + propertyName, + getPropertyViewImpl>( + propertyName, + classProperty)); + break; + case PropertyType::String: + callback( + propertyName, + getPropertyViewImpl>( + propertyName, + classProperty)); + break; + default: + break; + } + } + + template + void getVecNPropertyViewImpl( + const ExtensionExtStructuralMetadataClassProperty& classProperty, + const ExtensionExtStructuralMetadataPropertyTableProperty& + propertyTableProperty, + PropertyType type, + PropertyComponentType componentType, + Callback&& callback) { + glm::length_t N; + switch (type) { + case PropertyType::Vec2: + N = 2; + break; + case PropertyType::Vec3: + N = 3; + break; + case PropertyType::Vec4: + N = 4; + break; + default: + return; + } + + switch (componentType) { + case PropertyComponentType::Int8: + callback( + propertyName, + getPropertyViewImpl>( + propertyName, + classProperty)); + break; + case PropertyComponentType::Uint8: + callback( + propertyName, + getPropertyViewImpl>( + propertyName, + classProperty)); + break; + case PropertyComponentType::Int16: + callback( + propertyName, + getPropertyViewImpl>( + propertyName, + classProperty)); + break; + case PropertyComponentType::Uint16: + callback( + propertyName, + getPropertyViewImpl>( + propertyName, + classProperty)); + break; + case PropertyComponentType::Int32: + callback( + propertyName, + getPropertyViewImpl>( + propertyName, + classProperty)); + break; + case PropertyComponentType::Uint32: + callback( + propertyName, + getPropertyViewImpl>( + propertyName, + classProperty)); + break; + case PropertyComponentType::Int64: + callback( + propertyName, + getPropertyViewImpl>( + propertyName, + classProperty)); + break; + case PropertyComponentType::Uint64: + callback( + propertyName, + getPropertyViewImpl>( + propertyName, + classProperty)); + break; + case PropertyComponentType::Float32: + callback( + propertyName, + getPropertyViewImpl>(propertyName, classProperty)); + break; + case PropertyComponentType::Float64: + callback( + propertyName, + getPropertyViewImpl>( + propertyName, + classProperty)); + break; + default: + break; + } + } + + template + void getMatNPropertyViewImpl( + const ExtensionExtStructuralMetadataClassProperty& classProperty, + const ExtensionExtStructuralMetadataPropertyTableProperty& + propertyTableProperty, + PropertyType type, + PropertyComponentType componentType, + Callback&& callback) { + glm::length_t N; + switch (type) { + case PropertyType::Mat2: + N = 2; + break; + case PropertyType::Mat3: + N = 3; + break; + case PropertyType::Mat4: + N = 4; + break; + default: + return; + } + + switch (componentType) { + case PropertyComponentType::Int8: + callback( + propertyName, + getPropertyViewImpl < + glm::mat(propertyName, classProperty)); + break; + case PropertyComponentType::Uint8: + callback( + propertyName, + getPropertyViewImpl>( + propertyName, + classProperty)); + break; + case PropertyComponentType::Int16: + callback( + propertyName, + getPropertyViewImpl>( + propertyName, + classProperty)); + break; + case PropertyComponentType::Uint16: + callback( + propertyName, + getPropertyViewImpl>( + propertyName, + classProperty)); + break; + case PropertyComponentType::Int32: + callback( + propertyName, + getPropertyViewImpl>( + propertyName, + classProperty)); + break; + case PropertyComponentType::Uint32: + callback( + propertyName, + getPropertyViewImpl>( + propertyName, + classProperty)); + break; + case PropertyComponentType::Int64: + callback( + propertyName, + getPropertyViewImpl>( + propertyName, + classProperty)); + break; + case PropertyComponentType::Uint64: + callback( + propertyName, + getPropertyViewImp>( + propertyName, + classProperty)); + break; + case PropertyComponentType::Float32: + callback( + propertyName, + getPropertyViewImpl>( + propertyName, + classProperty)); + break; + case PropertyComponentType::Float64: + callback( + propertyName, + getPropertyViewImpl>( + propertyName, + classProperty)); + break; + default: + break; + } + } + + template + void getPrimitivePropertyViewImpl( + const ExtensionExtStructuralMetadataClassProperty& classProperty, + const ExtensionExtStructuralMetadataPropertyTableProperty& + propertyTableProperty, + PropertyComponentType type, + Callback&& callback) const { + switch (type) { + case PropertyType::Int8: + callback( + propertyName, + getPropertyViewImpl(propertyName, classProperty)); + break; + case PropertyType::Uint8: + callback( + propertyName, + getPropertyViewImpl(propertyName, classProperty)); + break; + case PropertyType::Int16: + callback( + propertyName, + getPropertyViewImpl(propertyName, classProperty)); + break; + case PropertyType::Uint16: + callback( + propertyName, + getPropertyViewImpl(propertyName, classProperty)); + break; + case PropertyType::Int32: + callback( + propertyName, + getPropertyViewImpl(propertyName, classProperty)); + break; + case PropertyType::Uint32: + callback( + propertyName, + getPropertyViewImpl(propertyName, classProperty)); + break; + case PropertyType::Int64: + callback( + propertyName, + getPropertyViewImpl(propertyName, classProperty)); + break; + case PropertyType::Uint64: + callback( + propertyName, + getPropertyViewImpl(propertyName, classProperty)); + break; + case PropertyType::Float32: + callback( + propertyName, + getPropertyViewImpl(propertyName, classProperty)); + break; + case PropertyType::Float64: + callback( + propertyName, + getPropertyViewImpl(propertyName, classProperty)); + break; + case PropertyType::Boolean: + callback( + propertyName, + getPropertyViewImpl(propertyName, classProperty)); + break; + case PropertyType::String: + callback( + propertyName, + getPropertyViewImpl(propertyName, classProperty)); + break; + default: + break; + } + } + + template + MetadataPropertyView getPropertyViewImpl( + const std::string& propertyName, + const ExtensionExtStructuralMetadataClassProperty& classProperty) const { + auto propertyTablePropertyIter = + _pPropertyTable->properties.find(propertyName); + if (propertyTablePropertyIter == _pPropertyTable->properties.end()) { + return createInvalidPropertyView( + MetadataPropertyViewStatus::ErrorPropertyDoesNotExist); + } + + const ExtensionExtStructuralMetadataPropertyTableProperty& + propertyTableProperty = propertyTablePropertyIter->second; + + if constexpr (IsMetadataNumeric::value || IsMetadataBoolean::value) { + return getNumericOrBooleanPropertyValues( + classProperty, + propertyTableProperty); + } + + if constexpr (IsMetadataString::value) { + return getStringPropertyValues(classProperty, propertyTableProperty); + } + + if constexpr ( + IsMetadataNumericArray::value || IsMetadataBooleanArray::value) { + return getPrimitiveArrayPropertyValues< + typename MetadataArrayType::type>( + classProperty, + propertyTableProperty); + } + + if constexpr (IsMetadataStringArray::value) { + return getStringArrayPropertyValues(classProperty, propertyTableProperty); + } + } + + template + StructuralMetadata::MetadataPropertyView getNumericOrBooleanPropertyValues( + const ExtensionExtStructuralMetadataClassProperty& classProperty, + const ExtensionExtStructuralMetadataPropertyTableProperty& + propertyTableProperty) const { + if (classProperty.array) { + return createInvalidPropertyView( + MetadataPropertyViewStatus::ErrorArrayTypeMismatch); + } + + const PropertyType type = convertStringToPropertyType(classProperty.type); + if (TypeToPropertyType::value != type) { + return createInvalidPropertyView( + MetadataPropertyViewStatus::ErrorTypeMismatch); + } + const PropertyComponentType componentType = + convertStringToPropertyComponentType( + classProperty.componentType.value_or("")); + if (TypeToPropertyType::component != componentType) { + return createInvalidPropertyView( + MetadataPropertyViewStatus::ErrorComponentTypeMismatch); + } + + gsl::span values; + const auto status = getBufferSafe(propertyTableProperty.values, values); + if (status != MetadataPropertyViewStatus::Valid) { + return createInvalidPropertyView(status); + } + + if (values.size() % sizeof(T) != 0) { + return createInvalidPropertyView( + StructuralMetadata::MetadataPropertyViewStatus:: + ErrorBufferViewSizeNotDivisibleByTypeSize); + } + + size_t maxRequiredBytes = 0; + if (IsMetadataBoolean::value) { + maxRequiredBytes = static_cast( + glm::ceil(static_cast(_pPropertyTable->count) / 8.0)); + } else { + maxRequiredBytes = _pPropertyTable->count * sizeof(T); + } + + if (values.size() < maxRequiredBytes) { + return createInvalidPropertyView( + MetadataPropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + } + + return MetadataPropertyView( + MetadataPropertyViewStatus::Valid, + values, + gsl::span(), + gsl::span(), + PropertyComponentType::None, + PropertyComponentType::None, + 0, + _pPropertyTable->count, + classProperty.normalized); + } + + MetadataPropertyView getStringPropertyValues( + const ExtensionExtStructuralMetadataClassProperty& classProperty, + const ExtensionExtStructuralMetadataPropertyTableProperty& + propertyTableProperty) const; + + template + MetadataPropertyView> getPrimitiveArrayPropertyValues( + const ExtensionExtStructuralMetadataClassProperty& classProperty, + const ExtensionExtStructuralMetadataPropertyTableProperty& + propertyTableProperty) const { + if (!classProperty.array) { + return createInvalidPropertyView>( + MetadataPropertyViewStatus::ErrorArrayTypeMismatch); + } + + const PropertyType type = convertStringToPropertyType(classProperty.type); + if (TypeToPropertyType::value != type) { + return createInvalidPropertyView>( + MetadataPropertyViewStatus::ErrorTypeMismatch); + } + + const PropertyComponentType componentType = + convertStringToPropertyComponentType( + classProperty.componentType.value_or("")); + if (TypeToPropertyType::component != componentType) { + return createInvalidPropertyView>( + MetadataPropertyViewStatus::ErrorComponentTypeMismatch); + } + + gsl::span values; + auto status = getBufferSafe(propertyTableProperty.values, values); + if (status != MetadataPropertyViewStatus::Valid) { + return createInvalidPropertyView>(status); + } + + if (values.size() % sizeof(T) != 0) { + return createInvalidPropertyView>( + MetadataPropertyViewStatus:: + ErrorBufferViewSizeNotDivisibleByTypeSize); + } + + const int64_t fixedLengthArrayCount = classProperty.count.value_or(0); + if (fixedLengthArrayCount > 0 && propertyTableProperty.arrayOffsets >= 0) { + return createInvalidPropertyView>( + MetadataPropertyViewStatus::ErrorArrayCountAndOffsetBufferCoexist); + } + + if (fixedLengthArrayCount <= 0 && propertyTableProperty.arrayOffsets < 0) { + return createInvalidPropertyView>( + MetadataPropertyViewStatus::ErrorArrayCountAndOffsetBufferDontExist); + } + + // Handle fixed-length arrays + if (fixedLengthArrayCount > 0) { + size_t maxRequiredBytes = 0; + if constexpr (IsMetadataBoolean::value) { + maxRequiredBytes = static_cast(glm::ceil( + static_cast( + _pPropertyTable->count * fixedLengthArrayCount) / + 8.0)); + } else { + maxRequiredBytes = static_cast( + _pPropertyTable->count * fixedLengthArrayCount * sizeof(T)); + } + + if (values.size() < maxRequiredBytes) { + return createInvalidPropertyView>( + MetadataPropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + } + + return MetadataPropertyView>( + MetadataPropertyViewStatus::Valid, + values, + gsl::span(), + gsl::span(), + PropertyComponentType::None, + PropertyComponentType::None, + static_cast(fixedLengthArrayCount), + static_cast(_pPropertyTable->count), + classProperty.normalized); + } + + // Handle variable-length arrays + const PropertyComponentType arrayOffsetType = + convertArrayOffsetTypeStringToPropertyComponentType( + propertyTableProperty.arrayOffsetType); + if (arrayOffsetType == PropertyComponentType::None) { + return createInvalidPropertyView>( + MetadataPropertyViewStatus::ErrorInvalidArrayOffsetType); + } + + constexpr bool checkBitsSize = IsMetadataBoolean::value; + gsl::span arrayOffsets; + status = getArrayOffsetsBufferSafe( + propertyTableProperty.arrayOffsets, + arrayOffsetType, + values.size(), + static_cast(_pPropertyTable->count), + checkBitsSize, + arrayOffsets); + if (status != MetadataPropertyViewStatus::Valid) { + return createInvalidPropertyView>(status); + } + + return MetadataPropertyView>( + MetadataPropertyViewStatus::Valid, + values, + arrayOffsets, + gsl::span(), + arrayOffsetType, + PropertyComponentType::None, + 0, + static_cast(_pPropertyTable->count), + classProperty.normalized); + } + + MetadataPropertyView> + getStringArrayPropertyValues( + const ExtensionExtStructuralMetadataClassProperty& classProperty, + const ExtensionExtStructuralMetadataPropertyTableProperty& + propertyTableProperty) const; + + MetadataPropertyViewStatus getBufferSafe( + int32_t bufferView, + gsl::span& buffer) const noexcept; + + MetadataPropertyViewStatus getArrayOffsetsBufferSafe( + int32_t arrayOffsetsBufferView, + PropertyComponentType arrayOffsetType, + size_t valuesBufferSize, + size_t propertyTableCount, + bool checkBitsSize, + gsl::span& arrayOffsetsBuffer) const noexcept; + + MetadataPropertyViewStatus getStringOffsetsBufferSafe( + int32_t stringOffsetsBufferView, + PropertyComponentType stringOffsetType, + size_t valuesBufferSize, + size_t propertyTableCount, + gsl::span& stringOffsetsBuffer) const noexcept; + + template + static MetadataPropertyView + createInvalidPropertyView(MetadataPropertyViewStatus invalidStatus) noexcept { + return MetadataPropertyView( + invalidStatus, + gsl::span(), + gsl::span(), + gsl::span(), + PropertyComponentType::None, + PropertyComponentType::None, + 0, + 0, + false); + } + + const Model* _pModel; + const ExtensionExtStructuralMetadataPropertyTable* _pPropertyTable; + const ExtensionExtStructuralMetadataClass* _pClass; +}; + +} // namespace StructuralMetadata +} // namespace CesiumGltf diff --git a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyType.h b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyType.h index 3233d52a3..4648c7c12 100644 --- a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyType.h +++ b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyType.h @@ -51,5 +51,9 @@ convertArrayOffsetTypeStringToPropertyComponentType(const std::string& str); PropertyComponentType convertStringOffsetTypeStringToPropertyComponentType(const std::string& str); +bool isPropertyTypeVecN(PropertyType type); + +bool isPropertyTypeMatN(PropertyType type); + } // namespace StructuralMetadata } // namespace CesiumGltf diff --git a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyView.h b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyView.h index cbe2dd43c..529e092df 100644 --- a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyView.h +++ b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyView.h @@ -231,7 +231,8 @@ template class MetadataPropertyView { assert( size() > 0 && "Check the size() of the view to make sure it's not empty"); - assert(index >= 0 && "index must be positive"); + assert(index >= 0 && "index must be non-negative"); + assert(index < size() && "index must be less than size"); if constexpr (IsMetadataNumeric::value) { return getNumericValue(index); diff --git a/CesiumGltf/src/StructuralMetadataPropertyTableView.cpp b/CesiumGltf/src/StructuralMetadataPropertyTableView.cpp new file mode 100644 index 000000000..4843fabe2 --- /dev/null +++ b/CesiumGltf/src/StructuralMetadataPropertyTableView.cpp @@ -0,0 +1,493 @@ +#include "CesiumGltf/StructuralMetadataPropertyTableView.h" + +namespace CesiumGltf { +namespace StructuralMetadata { + +template +static MetadataPropertyViewStatus checkOffsetsBuffer( + const gsl::span& offsetBuffer, + size_t valueBufferSize, + size_t instanceCount, + bool checkBitSize, + MetadataPropertyViewStatus offsetsNotSortedError, + MetadataPropertyViewStatus offsetOutOfBoundsError) noexcept { + if (offsetBuffer.size() % sizeof(T) != 0) { + return MetadataPropertyViewStatus:: + ErrorBufferViewSizeNotDivisibleByTypeSize; + } + + const size_t size = offsetBuffer.size() / sizeof(T); + if (size != instanceCount + 1) { + return MetadataPropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount; + } + + const gsl::span offsetValues( + reinterpret_cast(offsetBuffer.data()), + size); + + for (size_t i = 1; i < offsetValues.size(); ++i) { + if (offsetValues[i] < offsetValues[i - 1]) { + return offsetsNotSortedError; + } + } + + if (checkBitSize) { + if (offsetValues.back() / 8 <= valueBufferSize) { + return MetadataPropertyViewStatus::Valid; + } + + return offsetOutOfBoundsError; + } + + if (offsetValues.back() <= valueBufferSize) { + return MetadataPropertyViewStatus::Valid; + } + + return offsetOutOfBoundsError; +} + +template +static MetadataPropertyViewStatus checkStringAndArrayOffsetsBuffers( + const gsl::span& arrayOffsets, + const gsl::span& stringOffsets, + size_t valueBufferSize, + PropertyComponentType stringOffsetType, + size_t propertyTableCount) noexcept { + const auto status = checkOffsetsBuffer( + arrayOffsets, + stringOffsets.size(), + propertyTableCount, + false, + MetadataPropertyViewStatus::ErrorArrayOffsetsNotSorted, + MetadataPropertyViewStatus::ErrorArrayOffsetOutOfBounds); + + if (status != MetadataPropertyViewStatus::Valid) { + return status; + } + + const T* pValue = reinterpret_cast(arrayOffsets.data()); + + switch (stringOffsetType) { + case PropertyComponentType::Uint8: + return checkOffsetsBuffer( + stringOffsets, + valueBufferSize, + pValue[propertyTableCount] / sizeof(T), + false, + MetadataPropertyViewStatus::ErrorStringOffsetsNotSorted, + MetadataPropertyViewStatus::ErrorStringOffsetOutOfBounds); + case PropertyComponentType::Uint16: + return checkOffsetsBuffer( + stringOffsets, + valueBufferSize, + pValue[propertyTableCount] / sizeof(T), + false, + MetadataPropertyViewStatus::ErrorStringOffsetsNotSorted, + MetadataPropertyViewStatus::ErrorStringOffsetOutOfBounds); + case PropertyComponentType::Uint32: + return checkOffsetsBuffer( + stringOffsets, + valueBufferSize, + pValue[propertyTableCount] / sizeof(T), + false, + MetadataPropertyViewStatus::ErrorStringOffsetsNotSorted, + MetadataPropertyViewStatus::ErrorStringOffsetOutOfBounds); + case PropertyComponentType::Uint64: + return checkOffsetsBuffer( + stringOffsets, + valueBufferSize, + pValue[propertyTableCount] / sizeof(T), + false, + MetadataPropertyViewStatus::ErrorStringOffsetsNotSorted, + MetadataPropertyViewStatus::ErrorStringOffsetOutOfBounds); + default: + return MetadataPropertyViewStatus::ErrorInvalidStringOffsetType; + } +} + +MetadataPropertyTableView::MetadataPropertyTableView( + const Model* pModel, + const ExtensionExtStructuralMetadataPropertyTable* pPropertyTable) + : _pModel{pModel}, _pPropertyTable{pPropertyTable}, _pClass{nullptr} { + assert(pModel != nullptr && "pModel must not be nullptr"); + assert(pPropertyTable != nullptr && "pPropertyTable must not be nullptr"); + + const ExtensionModelExtStructuralMetadata* pMetadata = + pModel->getExtension(); + assert( + pMetadata != nullptr && + "Model must contain ExtensionModelExtStructuralMetadata to use " + "MetadataPropertyTableView"); + + const std::optional& schema = + pMetadata->schema; + assert( + schema != std::nullopt && + "ExtensionModelExtStructuralMetadata must contain " + "Schema to use MetadataPropertyTableView"); + + auto classIter = schema->classes.find(_pPropertyTable->classProperty); + if (classIter != schema->classes.end()) { + _pClass = &classIter->second; + } +} + +const ExtensionExtStructuralMetadataClassProperty* +MetadataPropertyTableView::getClassProperty( + const std::string& propertyName) const { + if (_pClass == nullptr) { + return nullptr; + } + + auto propertyIter = _pClass->properties.find(propertyName); + if (propertyIter == _pClass->properties.end()) { + return nullptr; + } + + return &propertyIter->second; +} + +MetadataPropertyViewStatus MetadataPropertyTableView::getBufferSafe( + int32_t bufferViewIdx, + gsl::span& buffer) const noexcept { + buffer = {}; + + const BufferView* pBufferView = + _pModel->getSafe(&_pModel->bufferViews, bufferViewIdx); + if (!pBufferView) { + return MetadataPropertyViewStatus::ErrorInvalidValueBufferView; + } + + const Buffer* pBuffer = + _pModel->getSafe(&_pModel->buffers, pBufferView->buffer); + if (!pBuffer) { + return MetadataPropertyViewStatus::ErrorInvalidValueBuffer; + } + + if (pBufferView->byteOffset + pBufferView->byteLength > + static_cast(pBuffer->cesium.data.size())) { + return MetadataPropertyViewStatus::ErrorBufferViewOutOfBounds; + } + + buffer = gsl::span( + pBuffer->cesium.data.data() + pBufferView->byteOffset, + static_cast(pBufferView->byteLength)); + return MetadataPropertyViewStatus::Valid; +} + +MetadataPropertyViewStatus MetadataPropertyTableView::getArrayOffsetsBufferSafe( + int32_t arrayOffsetsBufferView, + PropertyComponentType arrayOffsetType, + size_t valueBufferSize, + size_t propertyTableCount, + bool checkBitsSize, + gsl::span& arrayOffsetsBuffer) const noexcept { + const MetadataPropertyViewStatus bufferStatus = + getBufferSafe(arrayOffsetsBufferView, arrayOffsetsBuffer); + if (bufferStatus != MetadataPropertyViewStatus::Valid) { + return bufferStatus; + } + + switch (arrayOffsetType) { + case PropertyComponentType::Uint8: + return checkOffsetsBuffer( + arrayOffsetsBuffer, + valueBufferSize, + propertyTableCount, + checkBitsSize, + MetadataPropertyViewStatus::ErrorArrayOffsetsNotSorted, + MetadataPropertyViewStatus::ErrorArrayOffsetOutOfBounds); + case PropertyComponentType::Uint16: + return checkOffsetsBuffer( + arrayOffsetsBuffer, + valueBufferSize, + propertyTableCount, + checkBitsSize, + MetadataPropertyViewStatus::ErrorArrayOffsetsNotSorted, + MetadataPropertyViewStatus::ErrorArrayOffsetOutOfBounds); + case PropertyComponentType::Uint32: + return checkOffsetsBuffer( + arrayOffsetsBuffer, + valueBufferSize, + propertyTableCount, + checkBitsSize, + MetadataPropertyViewStatus::ErrorArrayOffsetsNotSorted, + MetadataPropertyViewStatus::ErrorArrayOffsetOutOfBounds); + case PropertyComponentType::Uint64: + return checkOffsetsBuffer( + arrayOffsetsBuffer, + valueBufferSize, + propertyTableCount, + checkBitsSize, + MetadataPropertyViewStatus::ErrorArrayOffsetsNotSorted, + MetadataPropertyViewStatus::ErrorArrayOffsetOutOfBounds); + default: + return MetadataPropertyViewStatus::ErrorInvalidArrayOffsetType; + } +} + +MetadataPropertyViewStatus +MetadataPropertyTableView::getStringOffsetsBufferSafe( + int32_t stringOffsetsBufferView, + PropertyComponentType stringOffsetType, + size_t valueBufferSize, + size_t propertyTableCount, + gsl::span& stringOffsetsBuffer) const noexcept { + const MetadataPropertyViewStatus bufferStatus = + getBufferSafe(stringOffsetsBufferView, stringOffsetsBuffer); + if (bufferStatus != MetadataPropertyViewStatus::Valid) { + return bufferStatus; + } + + switch (stringOffsetType) { + case PropertyComponentType::Uint8: + return checkOffsetsBuffer( + stringOffsetsBuffer, + valueBufferSize, + propertyTableCount, + false, + MetadataPropertyViewStatus::ErrorStringOffsetsNotSorted, + MetadataPropertyViewStatus::ErrorStringOffsetOutOfBounds); + case PropertyComponentType::Uint16: + return checkOffsetsBuffer( + stringOffsetsBuffer, + valueBufferSize, + propertyTableCount, + false, + MetadataPropertyViewStatus::ErrorStringOffsetsNotSorted, + MetadataPropertyViewStatus::ErrorStringOffsetOutOfBounds); + case PropertyComponentType::Uint32: + return checkOffsetsBuffer( + stringOffsetsBuffer, + valueBufferSize, + propertyTableCount, + false, + MetadataPropertyViewStatus::ErrorStringOffsetsNotSorted, + MetadataPropertyViewStatus::ErrorStringOffsetOutOfBounds); + case PropertyComponentType::Uint64: + return checkOffsetsBuffer( + stringOffsetsBuffer, + valueBufferSize, + propertyTableCount, + false, + MetadataPropertyViewStatus::ErrorStringOffsetsNotSorted, + MetadataPropertyViewStatus::ErrorStringOffsetOutOfBounds); + default: + return MetadataPropertyViewStatus::ErrorInvalidStringOffsetType; + } +} + +MetadataPropertyView +MetadataPropertyTableView::getStringPropertyValues( + const ExtensionExtStructuralMetadataClassProperty& classProperty, + const ExtensionExtStructuralMetadataPropertyTableProperty& + propertyTableProperty) const { + if (classProperty.array) { + return createInvalidPropertyView( + MetadataPropertyViewStatus::ErrorArrayTypeMismatch); + } + + if (classProperty.type != + ExtensionExtStructuralMetadataClassProperty::Type::STRING) { + return createInvalidPropertyView( + MetadataPropertyViewStatus::ErrorTypeMismatch); + } + + gsl::span values; + auto status = getBufferSafe(propertyTableProperty.values, values); + if (status != MetadataPropertyViewStatus::Valid) { + return createInvalidPropertyView(status); + } + + const PropertyComponentType offsetType = + convertStringOffsetTypeStringToPropertyComponentType( + propertyTableProperty.stringOffsetType); + if (offsetType == PropertyComponentType::None) { + return createInvalidPropertyView( + MetadataPropertyViewStatus::ErrorInvalidStringOffsetType); + } + + gsl::span stringOffsets; + status = getStringOffsetsBufferSafe( + propertyTableProperty.stringOffsets, + offsetType, + values.size(), + static_cast(_pPropertyTable->count), + stringOffsets); + if (status != MetadataPropertyViewStatus::Valid) { + return createInvalidPropertyView(status); + } + + return MetadataPropertyView( + MetadataPropertyViewStatus::Valid, + values, + gsl::span(), + stringOffsets, + PropertyComponentType::None, + offsetType, + 0, + _pPropertyTable->count, + classProperty.normalized); +} + +MetadataPropertyView> +MetadataPropertyTableView::getStringArrayPropertyValues( + const ExtensionExtStructuralMetadataClassProperty& classProperty, + const ExtensionExtStructuralMetadataPropertyTableProperty& + propertyTableProperty) const { + if (!classProperty.array) { + return createInvalidPropertyView>( + MetadataPropertyViewStatus::ErrorTypeMismatch); + } + + if (classProperty.type != + ExtensionExtStructuralMetadataClassProperty::Type::STRING) { + return createInvalidPropertyView>( + MetadataPropertyViewStatus::ErrorTypeMismatch); + } + + gsl::span values; + auto status = getBufferSafe(propertyTableProperty.values, values); + if (status != MetadataPropertyViewStatus::Valid) { + return createInvalidPropertyView>( + status); + } + + // Check if array is fixed or variable length + const int64_t fixedLengthArrayCount = classProperty.count.value_or(0); + if (fixedLengthArrayCount > 0 && propertyTableProperty.arrayOffsets >= 0) { + return createInvalidPropertyView>( + MetadataPropertyViewStatus::ErrorArrayCountAndOffsetBufferCoexist); + } + + if (fixedLengthArrayCount <= 0 && propertyTableProperty.arrayOffsets < 0) { + return createInvalidPropertyView>( + MetadataPropertyViewStatus::ErrorArrayCountAndOffsetBufferDontExist); + } + + // Get offset types + const PropertyComponentType arrayOffsetType = + convertArrayOffsetTypeStringToPropertyComponentType( + propertyTableProperty.arrayOffsetType); + if (arrayOffsetType == PropertyComponentType::None) { + return createInvalidPropertyView>( + MetadataPropertyViewStatus::ErrorInvalidArrayOffsetType); + } + + const PropertyComponentType stringOffsetType = + convertStringOffsetTypeStringToPropertyComponentType( + propertyTableProperty.stringOffsetType); + if (stringOffsetType == PropertyComponentType::None) { + return createInvalidPropertyView>( + MetadataPropertyViewStatus::ErrorInvalidStringOffsetType); + } + + if (propertyTableProperty.arrayOffsets < 0) { + return createInvalidPropertyView>( + MetadataPropertyViewStatus::ErrorInvalidArrayOffsetBuffer); + } + + if (propertyTableProperty.stringOffsets < 0) { + return createInvalidPropertyView>( + MetadataPropertyViewStatus::ErrorInvalidStringOffsetBuffer); + } + + // Handle fixed-length arrays + if (fixedLengthArrayCount > 0) { + gsl::span stringOffsets; + status = getStringOffsetsBufferSafe( + propertyTableProperty.stringOffsets, + stringOffsetType, + values.size(), + static_cast(_pPropertyTable->count * fixedLengthArrayCount), + stringOffsets); + if (status != MetadataPropertyViewStatus::Valid) { + return createInvalidPropertyView>( + status); + } + + return MetadataPropertyView>( + MetadataPropertyViewStatus::Valid, + values, + gsl::span(), + stringOffsets, + PropertyComponentType::None, + stringOffsetType, + fixedLengthArrayCount, + _pPropertyTable->count, + classProperty.normalized); + } + + // Handle variable-length arrays + gsl::span stringOffsets; + status = getBufferSafe(propertyTableProperty.stringOffsets, stringOffsets); + if (status != MetadataPropertyViewStatus::Valid) { + return createInvalidPropertyView>( + status); + } + + gsl::span arrayOffsets; + status = getBufferSafe(propertyTableProperty.arrayOffsets, arrayOffsets); + if (status != MetadataPropertyViewStatus::Valid) { + return createInvalidPropertyView>( + status); + } + + switch (arrayOffsetType) { + case PropertyComponentType::Uint8: + status = checkStringAndArrayOffsetsBuffers( + arrayOffsets, + stringOffsets, + values.size(), + stringOffsetType, + static_cast(_pPropertyTable->count)); + break; + case PropertyComponentType::Uint16: + status = checkStringAndArrayOffsetsBuffers( + arrayOffsets, + stringOffsets, + values.size(), + stringOffsetType, + static_cast(_pPropertyTable->count)); + break; + case PropertyComponentType::Uint32: + status = checkStringAndArrayOffsetsBuffers( + arrayOffsets, + stringOffsets, + values.size(), + stringOffsetType, + static_cast(_pPropertyTable->count)); + break; + case PropertyComponentType::Uint64: + status = checkStringAndArrayOffsetsBuffers( + arrayOffsets, + stringOffsets, + values.size(), + stringOffsetType, + static_cast(_pPropertyTable->count)); + break; + default: + status = MetadataPropertyViewStatus::ErrorInvalidArrayOffsetType; + break; + } + + if (status != MetadataPropertyViewStatus::Valid) { + return createInvalidPropertyView>( + status); + } + + return MetadataPropertyView>( + MetadataPropertyViewStatus::Valid, + values, + arrayOffsets, + stringOffsets, + arrayOffsetType, + stringOffsetType, + 0, + _pPropertyTable->count, + classProperty.normalized); +} + +} // namespace StructuralMetadata +} // namespace CesiumGltf diff --git a/CesiumGltf/src/StructuralMetadataPropertyType.cpp b/CesiumGltf/src/StructuralMetadataPropertyType.cpp index 56a8262b1..ca7613023 100644 --- a/CesiumGltf/src/StructuralMetadataPropertyType.cpp +++ b/CesiumGltf/src/StructuralMetadataPropertyType.cpp @@ -39,39 +39,39 @@ PropertyType convertStringToPropertyType(const std::string& str) { } if (str == ExtensionExtStructuralMetadataClassProperty::Type::VEC2) { - return StructuralMetadata::PropertyType::Vec2; + return PropertyType::Vec2; } if (str == ExtensionExtStructuralMetadataClassProperty::Type::VEC3) { - return StructuralMetadata::PropertyType::Vec3; + return PropertyType::Vec3; } if (str == ExtensionExtStructuralMetadataClassProperty::Type::VEC4) { - return StructuralMetadata::PropertyType::Vec4; + return PropertyType::Vec4; } if (str == ExtensionExtStructuralMetadataClassProperty::Type::MAT2) { - return StructuralMetadata::PropertyType::Mat2; + return PropertyType::Mat2; } if (str == ExtensionExtStructuralMetadataClassProperty::Type::MAT3) { - return StructuralMetadata::PropertyType::Mat3; + return PropertyType::Mat3; } if (str == ExtensionExtStructuralMetadataClassProperty::Type::MAT4) { - return StructuralMetadata::PropertyType::Mat4; + return PropertyType::Mat4; } if (str == ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN) { - return StructuralMetadata::PropertyType::Boolean; + return PropertyType::Boolean; } if (str == ExtensionExtStructuralMetadataClassProperty::Type::STRING) { - return StructuralMetadata::PropertyType::String; + return PropertyType::String; } if (str == ExtensionExtStructuralMetadataClassProperty::Type::ENUM) { - return StructuralMetadata::PropertyType::Enum; + return PropertyType::Enum; } return PropertyType::Invalid; @@ -106,7 +106,7 @@ std::string convertPropertyComponentTypeToString(PropertyComponentType type) { } } -StructuralMetadata::PropertyComponentType +PropertyComponentType convertStringToPropertyComponentType(const std::string& str) { if (str == ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT8) { @@ -160,7 +160,7 @@ convertStringToPropertyComponentType(const std::string& str) { return PropertyComponentType::None; } -StructuralMetadata::PropertyComponentType +PropertyComponentType convertArrayOffsetTypeStringToPropertyComponentType(const std::string& str) { if (str == ExtensionExtStructuralMetadataPropertyTableProperty:: ArrayOffsetType::UINT8) { @@ -185,7 +185,7 @@ convertArrayOffsetTypeStringToPropertyComponentType(const std::string& str) { return PropertyComponentType::None; } -StructuralMetadata::PropertyComponentType +PropertyComponentType convertStringOffsetTypeStringToPropertyComponentType(const std::string& str) { if (str == ExtensionExtStructuralMetadataPropertyTableProperty:: StringOffsetType::UINT8) { @@ -210,5 +210,15 @@ convertStringOffsetTypeStringToPropertyComponentType(const std::string& str) { return PropertyComponentType::None; } +bool isPropertyTypeVecN(PropertyType type) { + return type == PropertyType::Vec2 || type == PropertyType::Vec3 || + type == PropertyType::Vec4; +} + +bool isPropertyTypeMatN(PropertyType type) { + return type == PropertyType::Mat2 || type == PropertyType::Mat3 || + type == PropertyType::Mat4; +} + } // namespace StructuralMetadata } // namespace CesiumGltf From 84f2e8f55014943ee9c0ffdc7107976748f25a1e Mon Sep 17 00:00:00 2001 From: Joseph Kaile Date: Thu, 18 May 2023 13:04:48 -0400 Subject: [PATCH 019/421] automatically unzip data if gzipped data is detected --- .gitmodules | 3 + CHANGES.md | 1 + CMakeLists.txt | 2 + CesiumAsync/src/CachingAssetAccessor.cpp | 65 ++++++++++++++++++++ CesiumUtility/CMakeLists.txt | 3 + CesiumUtility/include/CesiumUtility/Gunzip.h | 20 ++++++ CesiumUtility/src/Gunzip.cpp | 44 +++++++++++++ ThirdParty.json | 6 ++ extern/CMakeLists.txt | 2 + extern/zlib | 1 + 10 files changed, 147 insertions(+) create mode 100644 CesiumUtility/include/CesiumUtility/Gunzip.h create mode 100644 CesiumUtility/src/Gunzip.cpp create mode 160000 extern/zlib diff --git a/.gitmodules b/.gitmodules index 545040938..244d6e8ba 100644 --- a/.gitmodules +++ b/.gitmodules @@ -62,3 +62,6 @@ [submodule "extern/libjpeg-turbo"] path = extern/libjpeg-turbo url = https://github.com/CesiumGS/libjpeg-turbo.git +[submodule "extern/zlib"] + path = extern/zlib + url = https://github.com/madler/zlib.git diff --git a/CHANGES.md b/CHANGES.md index 01b60fe82..7403c2ecd 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,6 +5,7 @@ ##### Additions :tada: - Added support for the `KHR_materials_variants` extension to the glTF reader and writer. +- The Caching Asset Accessor will now automatically unzip data if gzipped data is detected. ### v0.24.0 - 2023-05-01 diff --git a/CMakeLists.txt b/CMakeLists.txt index ae4c8464d..c868a75a7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -198,6 +198,8 @@ install(TARGETS webpdecoder) install(DIRECTORY $ TYPE LIB) +install(DIRECTORY $ TYPE LIB) + install(DIRECTORY ${CESIUM_NATIVE_RAPIDJSON_INCLUDE_DIR}/rapidjson TYPE INCLUDE) install(TARGETS s2geometry) diff --git a/CesiumAsync/src/CachingAssetAccessor.cpp b/CesiumAsync/src/CachingAssetAccessor.cpp index c3bd25135..e4dbffa0c 100644 --- a/CesiumAsync/src/CachingAssetAccessor.cpp +++ b/CesiumAsync/src/CachingAssetAccessor.cpp @@ -3,6 +3,7 @@ #include "CesiumAsync/AsyncSystem.h" #include "CesiumAsync/CacheItem.h" #include "CesiumAsync/IAssetResponse.h" +#include "CesiumUtility/Gunzip.h" #include "InternalTimegm.h" #include "ResponseCacheControl.h" @@ -71,6 +72,63 @@ class CacheAssetRequest : public IAssetRequest { CacheAssetResponse _response; }; +class GunzippedAssetResponse : public IAssetResponse { +public: + GunzippedAssetResponse(const IAssetResponse* pOther) noexcept + : _pAssetResponse{pOther} { + _dataValid = CesiumUtility::gunzip( + this->_pAssetResponse->data(), + this->_gunzippedData); + } + + virtual uint16_t statusCode() const noexcept override { + return this->_pAssetResponse->statusCode(); + } + + virtual std::string contentType() const override { + return this->_pAssetResponse->contentType(); + } + + virtual const HttpHeaders& headers() const noexcept override { + return this->_pAssetResponse->headers(); + } + + virtual gsl::span data() const noexcept override { + return _dataValid ? gsl::span(_gunzippedData.data(), _gunzippedData.size()) + : _pAssetResponse->data(); + } + +private: + const IAssetResponse* _pAssetResponse; + std::vector _gunzippedData; + bool _dataValid; +}; + +class GunzippedAssetRequest : public IAssetRequest { +public: + GunzippedAssetRequest(std::shared_ptr& pOther) + : _pAssetRequest(pOther), _AssetResponse(pOther->response()){}; + virtual const std::string& method() const noexcept override { + return this->_pAssetRequest->method(); + } + + virtual const std::string& url() const noexcept override { + return this->_pAssetRequest->url(); + } + + virtual const HttpHeaders& headers() const noexcept override { + return this->_pAssetRequest->headers(); + } + + virtual const IAssetResponse* response() const noexcept override { + return &this->_AssetResponse; + } + +private: + std::shared_ptr _pAssetRequest; + GunzippedAssetResponse _AssetResponse; +}; + static std::time_t convertHttpDateToTime(const std::string& httpDate); static bool shouldRevalidateCache(const CacheItem& cacheItem); @@ -149,6 +207,13 @@ Future> CachingAssetAccessor::get( return std::move(pCompletedRequest); } + if (CesiumUtility::isGzip(pResponse->data())) { + pCompletedRequest = + std::make_shared( + GunzippedAssetRequest(pCompletedRequest)); + pResponse = pCompletedRequest->response(); + } + const std::optional cacheControl = ResponseCacheControl::parseFromResponseHeaders( pResponse->headers()); diff --git a/CesiumUtility/CMakeLists.txt b/CesiumUtility/CMakeLists.txt index 8b566b8c6..36ae9c04d 100644 --- a/CesiumUtility/CMakeLists.txt +++ b/CesiumUtility/CMakeLists.txt @@ -35,6 +35,8 @@ target_include_directories( ${CESIUM_NATIVE_RAPIDJSON_INCLUDE_DIR} PRIVATE ${CMAKE_CURRENT_LIST_DIR}/src + ${CMAKE_CURRENT_LIST_DIR}/../extern/zlib + ${CMAKE_CURRENT_BINARY_DIR}/../extern/zlib ) # GLM erroneously does not declare its include a `SYSTEM` include, so @@ -50,6 +52,7 @@ target_link_libraries_system( target_link_libraries(CesiumUtility PUBLIC GSL + zlibstatic ) install(TARGETS CesiumUtility diff --git a/CesiumUtility/include/CesiumUtility/Gunzip.h b/CesiumUtility/include/CesiumUtility/Gunzip.h new file mode 100644 index 000000000..f98a731bd --- /dev/null +++ b/CesiumUtility/include/CesiumUtility/Gunzip.h @@ -0,0 +1,20 @@ +#pragma once +#include + +#include +#include + +namespace CesiumUtility { +static bool isGzip(const gsl::span& data) { + if (data.size() < 3) { + return false; + } + return data[0] == std::byte{31} && data[1] == std::byte{139}; +} + +/** +* Gunzip data. If successful, it will return true and the result will be in the provided vector. +*/ +extern bool +gunzip(const gsl::span& data, std::vector& out); +} // namespace CesiumUtility diff --git a/CesiumUtility/src/Gunzip.cpp b/CesiumUtility/src/Gunzip.cpp new file mode 100644 index 000000000..795631a86 --- /dev/null +++ b/CesiumUtility/src/Gunzip.cpp @@ -0,0 +1,44 @@ +#include "CesiumUtility/Gunzip.h" +#define ZLIB_CONST +#include "zlib.h" + +#define CHUNK 65536 + +bool CesiumUtility::gunzip( + const gsl::span& data, + std::vector& out) { + int ret; + int index = 0; + z_stream strm; + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + strm.avail_in = 0; + strm.next_in = Z_NULL; + ret = inflateInit2(&strm, 16 + MAX_WBITS); + if (ret != Z_OK) { + return false; + } + + strm.avail_in = static_cast(data.size()); + strm.next_in = reinterpret_cast(data.data()); + + do { + out.resize(index + CHUNK); + strm.next_out = reinterpret_cast(&out[index]); + strm.avail_out = CHUNK; + ret = inflate(&strm, Z_NO_FLUSH); + switch (ret) { + case Z_NEED_DICT: + case Z_DATA_ERROR: + case Z_MEM_ERROR: + inflateEnd(&strm); + return false; + } + index += CHUNK - strm.avail_out; + } while (ret != Z_STREAM_END); + + inflateEnd(&strm); + out.resize(index); + return true; +} diff --git a/ThirdParty.json b/ThirdParty.json index cfe95d296..41622a037 100644 --- a/ThirdParty.json +++ b/ThirdParty.json @@ -125,5 +125,11 @@ "url": "https://github.com/uriparser/uriparser", "version": "0.9.6", "license": ["BSD-3-Clause"] + }, + { + "name": "zlib", + "url": "https://github.com/madler/zlib", + "version": "1.2.13", + "license": ["zlib"] } ] diff --git a/extern/CMakeLists.txt b/extern/CMakeLists.txt index 528512555..9fee817b1 100644 --- a/extern/CMakeLists.txt +++ b/extern/CMakeLists.txt @@ -158,6 +158,8 @@ set(CESIUM_NATIVE_LIBMORTON_INCUDE_DIR "${CMAKE_CURRENT_LIST_DIR}/libmorton/incl set(BUILD_TESTING OFF CACHE INTERNAL "Disable libmorton Testing") add_subdirectory(libmorton) +add_subdirectory(zlib) + if(DEFINED CMAKE_TOOLCHAIN_FILE) if(NOT IS_ABSOLUTE ${CMAKE_TOOLCHAIN_FILE}) set(TJ_TOOLCHAIN_FILE ${CMAKE_SOURCE_DIR}/${CMAKE_TOOLCHAIN_FILE}) diff --git a/extern/zlib b/extern/zlib new file mode 160000 index 000000000..04f42ceca --- /dev/null +++ b/extern/zlib @@ -0,0 +1 @@ +Subproject commit 04f42ceca40f73e2978b50e93806c2a18c1281fc From ce479a202f578f9765a612bbea0edc8af0f96235 Mon Sep 17 00:00:00 2001 From: Joseph Kaile Date: Thu, 18 May 2023 13:37:42 -0400 Subject: [PATCH 020/421] formatting --- CesiumUtility/include/CesiumUtility/Gunzip.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CesiumUtility/include/CesiumUtility/Gunzip.h b/CesiumUtility/include/CesiumUtility/Gunzip.h index f98a731bd..65727f1ae 100644 --- a/CesiumUtility/include/CesiumUtility/Gunzip.h +++ b/CesiumUtility/include/CesiumUtility/Gunzip.h @@ -1,7 +1,6 @@ #pragma once #include -#include #include namespace CesiumUtility { @@ -13,8 +12,9 @@ static bool isGzip(const gsl::span& data) { } /** -* Gunzip data. If successful, it will return true and the result will be in the provided vector. -*/ + * Gunzip data. If successful, it will return true and the result will be in the + * provided vector. + */ extern bool gunzip(const gsl::span& data, std::vector& out); } // namespace CesiumUtility From 80025f1540584ec46d2d0ea933cf27a06d3c9250 Mon Sep 17 00:00:00 2001 From: Joseph Kaile Date: Thu, 18 May 2023 14:07:32 -0400 Subject: [PATCH 021/421] change gzip function from static to extern --- CesiumUtility/include/CesiumUtility/Gunzip.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CesiumUtility/include/CesiumUtility/Gunzip.h b/CesiumUtility/include/CesiumUtility/Gunzip.h index 65727f1ae..de52db68c 100644 --- a/CesiumUtility/include/CesiumUtility/Gunzip.h +++ b/CesiumUtility/include/CesiumUtility/Gunzip.h @@ -4,7 +4,7 @@ #include namespace CesiumUtility { -static bool isGzip(const gsl::span& data) { +extern bool isGzip(const gsl::span& data) { if (data.size() < 3) { return false; } From 3dd709a8746d1b66ab078226189252d8c5f857ae Mon Sep 17 00:00:00 2001 From: Joseph Kaile Date: Thu, 18 May 2023 14:27:36 -0400 Subject: [PATCH 022/421] fix ci --- CesiumUtility/include/CesiumUtility/Gunzip.h | 8 +------- CesiumUtility/src/Gunzip.cpp | 7 +++++++ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/CesiumUtility/include/CesiumUtility/Gunzip.h b/CesiumUtility/include/CesiumUtility/Gunzip.h index de52db68c..fa7eeae09 100644 --- a/CesiumUtility/include/CesiumUtility/Gunzip.h +++ b/CesiumUtility/include/CesiumUtility/Gunzip.h @@ -4,13 +4,7 @@ #include namespace CesiumUtility { -extern bool isGzip(const gsl::span& data) { - if (data.size() < 3) { - return false; - } - return data[0] == std::byte{31} && data[1] == std::byte{139}; -} - +extern bool isGzip(const gsl::span& data); /** * Gunzip data. If successful, it will return true and the result will be in the * provided vector. diff --git a/CesiumUtility/src/Gunzip.cpp b/CesiumUtility/src/Gunzip.cpp index 795631a86..d66bec3d6 100644 --- a/CesiumUtility/src/Gunzip.cpp +++ b/CesiumUtility/src/Gunzip.cpp @@ -4,6 +4,13 @@ #define CHUNK 65536 +bool CesiumUtility::isGzip(const gsl::span& data) { + if (data.size() < 3) { + return false; + } + return data[0] == std::byte{31} && data[1] == std::byte{139}; +} + bool CesiumUtility::gunzip( const gsl::span& data, std::vector& out) { From 3b4879c0328b4106c361f2962a03f9610d9c14fb Mon Sep 17 00:00:00 2001 From: Joseph Kaile Date: Thu, 18 May 2023 14:55:20 -0400 Subject: [PATCH 023/421] fix signedness error --- CesiumUtility/src/Gunzip.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CesiumUtility/src/Gunzip.cpp b/CesiumUtility/src/Gunzip.cpp index d66bec3d6..378ed94e5 100644 --- a/CesiumUtility/src/Gunzip.cpp +++ b/CesiumUtility/src/Gunzip.cpp @@ -15,7 +15,7 @@ bool CesiumUtility::gunzip( const gsl::span& data, std::vector& out) { int ret; - int index = 0; + unsigned int index = 0; z_stream strm; strm.zalloc = Z_NULL; strm.zfree = Z_NULL; From 62ad87b7f991f74075b6002ad3328405d5067f64 Mon Sep 17 00:00:00 2001 From: Joseph Kaile Date: Thu, 18 May 2023 15:12:31 -0400 Subject: [PATCH 024/421] always log warning/errors --- CHANGES.md | 3 +++ .../src/TilesetContentManager.cpp | 15 +++++++-------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 01b60fe82..23cf8fcfd 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,6 +6,9 @@ - Added support for the `KHR_materials_variants` extension to the glTF reader and writer. +##### Fixes :wrench: +- Upon Tileset Load Failure, cesium-native will always log warning/error messages even if the failure callback is set. + ### v0.24.0 - 2023-05-01 ##### Additions :tada: diff --git a/Cesium3DTilesSelection/src/TilesetContentManager.cpp b/Cesium3DTilesSelection/src/TilesetContentManager.cpp index dfb97c2c2..bba7efa28 100644 --- a/Cesium3DTilesSelection/src/TilesetContentManager.cpp +++ b/Cesium3DTilesSelection/src/TilesetContentManager.cpp @@ -1425,15 +1425,14 @@ void TilesetContentManager::propagateTilesetContentLoaderResult( type, result.statusCode, CesiumUtility::joinToString(result.errors.errors, "\n- ")}); - } else { - result.errors.logError( - this->_externals.pLogger, - "Errors when loading tileset"); - - result.errors.logWarning( - this->_externals.pLogger, - "Warnings when loading tileset"); } + result.errors.logError( + this->_externals.pLogger, + "Errors when loading tileset"); + + result.errors.logWarning( + this->_externals.pLogger, + "Warnings when loading tileset"); } else { this->_tilesetCredits.reserve( this->_tilesetCredits.size() + result.credits.size()); From 2b8e5349fa3b8ae8c2e932cca0e2eae3f1e368ff Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Fri, 19 May 2023 15:41:54 -0400 Subject: [PATCH 025/421] Add tests for StructuralMetadataPropertyTableView --- .../StructuralMetadataPropertyTableView.h | 407 ++- .../StructuralMetadataPropertyTableView.cpp | 32 +- ...estStructuralMetadataPropertyTableView.cpp | 2591 +++++++++++++++++ 3 files changed, 2914 insertions(+), 116 deletions(-) create mode 100644 CesiumGltf/test/TestStructuralMetadataPropertyTableView.cpp diff --git a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTableView.h b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTableView.h index 9e555dca5..b656b37c3 100644 --- a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTableView.h +++ b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTableView.h @@ -115,6 +115,7 @@ class MetadataPropertyTableView { getArrayPropertyViewImpl( propertyName, *pClassProperty, + type, componentType, std::forward(callback)); } else if (isPropertyTypeVecN(type)) { @@ -134,6 +135,7 @@ class MetadataPropertyTableView { propertyName, *pClassProperty, type, + componentType, std::forward(callback)); } } @@ -165,94 +167,184 @@ class MetadataPropertyTableView { } private: + glm::length_t getDimensionsFromType(PropertyType type) { + switch (type) { + case PropertyType::Vec2: + case PropertyType::Mat2: + return 2; + case PropertyType::Vec3: + case PropertyType::Mat3: + return 3; + case PropertyType::Vec4: + case PropertyType::Mat4: + return 4; + default: + return 0; + } + } + template - void getArrayPropertyViewImpl( + void getScalarArrayPropertyViewImpl( const std::string& propertyName, const ExtensionExtStructuralMetadataClassProperty& classProperty, - StructuralMetadata::PropertyType type, + PropertyComponentType componentType, Callback&& callback) const { - switch (type) { - case PropertyType::Int8: + switch (componentType) { + case PropertyComponentType::Int8: callback( propertyName, getPropertyViewImpl>( propertyName, classProperty)); break; - case PropertyType::Uint8: + case PropertyComponentType::Uint8: callback( propertyName, getPropertyViewImpl>( propertyName, classProperty)); break; - case PropertyType::Int16: + case PropertyComponentType::Int16: callback( propertyName, getPropertyViewImpl>( propertyName, classProperty)); break; - case PropertyType::Uint16: + case PropertyComponentType::Uint16: callback( propertyName, getPropertyViewImpl>( propertyName, classProperty)); break; - case PropertyType::Int32: + case PropertyComponentType::Int32: callback( propertyName, getPropertyViewImpl>( propertyName, classProperty)); break; - case PropertyType::Uint32: + case PropertyComponentType::Uint32: callback( propertyName, getPropertyViewImpl>( propertyName, classProperty)); break; - case PropertyType::Int64: + case PropertyComponentType::Int64: callback( propertyName, getPropertyViewImpl>( propertyName, classProperty)); break; - case PropertyType::Uint64: + case PropertyComponentType::Uint64: callback( propertyName, getPropertyViewImpl>( propertyName, classProperty)); break; - case PropertyType::Float32: + case PropertyComponentType::Float32: callback( propertyName, getPropertyViewImpl>( propertyName, classProperty)); break; - case PropertyType::Float64: + case PropertyComponentType::Float64: callback( propertyName, getPropertyViewImpl>( propertyName, classProperty)); break; - case PropertyType::Boolean: + default: + break; + } + } + + template + void getVecNArrayPropertyViewImpl( + const std::string& propertyName, + const ExtensionExtStructuralMetadataClassProperty& classProperty, + PropertyType type, + PropertyComponentType componentType, + Callback&& callback) const { + glm::length_t N = getDimensionsFromType(type); + if (N <= 1) { + return; + } + + switch (componentType) { + case PropertyComponentType::Int8: + callback( + propertyName, + getPropertyViewImpl>>( + propertyName, + classProperty)); + break; + case PropertyComponentType::Uint8: callback( propertyName, - getPropertyViewImpl>( + getPropertyViewImpl>>( propertyName, classProperty)); break; - case PropertyType::String: + case PropertyComponentType::Int16: callback( propertyName, - getPropertyViewImpl>( + getPropertyViewImpl>>( + propertyName, + classProperty)); + break; + case PropertyComponentType::Uint16: + callback( + propertyName, + getPropertyViewImpl>>( + propertyName, + classProperty)); + break; + case PropertyComponentType::Int32: + callback( + propertyName, + getPropertyViewImpl>>( + propertyName, + classProperty)); + break; + case PropertyComponentType::Uint32: + callback( + propertyName, + getPropertyViewImpl>>( + propertyName, + classProperty)); + break; + case PropertyComponentType::Int64: + callback( + propertyName, + getPropertyViewImpl>>( + propertyName, + classProperty)); + break; + case PropertyComponentType::Uint64: + callback( + propertyName, + getPropertyViewImpl>>( + propertyName, + classProperty)); + break; + case PropertyComponentType::Float32: + callback( + propertyName, + getPropertyViewImpl>>( + propertyName, + classProperty)); + break; + case PropertyComponentType::Float64: + callback( + propertyName, + getPropertyViewImpl>>( propertyName, classProperty)); break; @@ -262,25 +354,147 @@ class MetadataPropertyTableView { } template - void getVecNPropertyViewImpl( + void getMatNArrayPropertyViewImpl( + const std::string& propertyName, const ExtensionExtStructuralMetadataClassProperty& classProperty, - const ExtensionExtStructuralMetadataPropertyTableProperty& - propertyTableProperty, PropertyType type, PropertyComponentType componentType, - Callback&& callback) { - glm::length_t N; - switch (type) { - case PropertyType::Vec2: - N = 2; + Callback&& callback) const { + glm::length_t N = getDimensionsFromType(type); + if (N <= 1) { + return; + } + + switch (componentType) { + case PropertyComponentType::Int8: + callback( + propertyName, + getPropertyViewImpl>>( + propertyName, + classProperty)); break; - case PropertyType::Vec3: - N = 3; + case PropertyComponentType::Uint8: + callback( + propertyName, + getPropertyViewImpl>>( + propertyName, + classProperty)); break; - case PropertyType::Vec4: - N = 4; + case PropertyComponentType::Int16: + callback( + propertyName, + getPropertyViewImpl>>( + propertyName, + classProperty)); + break; + case PropertyComponentType::Uint16: + callback( + propertyName, + getPropertyViewImpl>>( + propertyName, + classProperty)); + break; + case PropertyComponentType::Int32: + callback( + propertyName, + getPropertyViewImpl>>( + propertyName, + classProperty)); + break; + case PropertyComponentType::Uint32: + callback( + propertyName, + getPropertyViewImpl>>( + propertyName, + classProperty)); + break; + case PropertyComponentType::Int64: + callback( + propertyName, + getPropertyViewImpl>>( + propertyName, + classProperty)); + break; + case PropertyComponentType::Uint64: + callback( + propertyName, + getPropertyViewImpl>>( + propertyName, + classProperty)); + break; + case PropertyComponentType::Float32: + callback( + propertyName, + getPropertyViewImpl>>( + propertyName, + classProperty)); + break; + case PropertyComponentType::Float64: + callback( + propertyName, + getPropertyViewImpl>>( + propertyName, + classProperty)); break; default: + break; + } + } + + template + void getArrayPropertyViewImpl( + const std::string& propertyName, + const ExtensionExtStructuralMetadataClassProperty& classProperty, + PropertyType type, + PropertyComponentType componentType, + Callback&& callback) const { + if (type == PropertyType::Scalar) { + getScalarArrayPropertyViewImpl( + propertyName, + classProperty, + componentType, + std::forward(callback)) + } else if (isPropertyTypeVecN(type)) { + getVecNArrayPropertyViewImpl( + propertyName, + classProperty, + type, + componentType, + std::forward(callback)); + } else if (isPropertyTypeMatN(type)) { + getMatNArrayPropertyViewImpl( + propertyName, + classProperty, + type, + componentType, + std::forward(callback)); + + } else if (type == PropertyType::Boolean) { + callback( + propertyName, + getPropertyViewImpl>( + propertyName, + classProperty)); + + } else if (type == PropertyType::String) { + callback( + propertyName, + getPropertyViewImpl>( + propertyName, + classProperty)); + } + } + + template + void getVecNPropertyViewImpl( + const ExtensionExtStructuralMetadataClassProperty& classProperty, + const ExtensionExtStructuralMetadataPropertyTableProperty& + propertyTableProperty, + PropertyType type, + PropertyComponentType componentType, + Callback&& callback) { + glm::length_t N = getDimensionsFromType(type); + if (N <= 1) { return; } @@ -366,18 +580,8 @@ class MetadataPropertyTableView { PropertyType type, PropertyComponentType componentType, Callback&& callback) { - glm::length_t N; - switch (type) { - case PropertyType::Mat2: - N = 2; - break; - case PropertyType::Mat3: - N = 3; - break; - case PropertyType::Mat4: - N = 4; - break; - default: + glm::length_t N = getDimensionsFromType(type); + if (N <= 1) { return; } @@ -461,71 +665,72 @@ class MetadataPropertyTableView { const ExtensionExtStructuralMetadataClassProperty& classProperty, const ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty, - PropertyComponentType type, + PropertyType type, + PropertyComponentType componentType, Callback&& callback) const { - switch (type) { - case PropertyType::Int8: - callback( - propertyName, - getPropertyViewImpl(propertyName, classProperty)); - break; - case PropertyType::Uint8: - callback( - propertyName, - getPropertyViewImpl(propertyName, classProperty)); - break; - case PropertyType::Int16: - callback( - propertyName, - getPropertyViewImpl(propertyName, classProperty)); - break; - case PropertyType::Uint16: - callback( - propertyName, - getPropertyViewImpl(propertyName, classProperty)); - break; - case PropertyType::Int32: - callback( - propertyName, - getPropertyViewImpl(propertyName, classProperty)); - break; - case PropertyType::Uint32: - callback( - propertyName, - getPropertyViewImpl(propertyName, classProperty)); - break; - case PropertyType::Int64: - callback( - propertyName, - getPropertyViewImpl(propertyName, classProperty)); - break; - case PropertyType::Uint64: - callback( - propertyName, - getPropertyViewImpl(propertyName, classProperty)); - break; - case PropertyType::Float32: - callback( - propertyName, - getPropertyViewImpl(propertyName, classProperty)); - break; - case PropertyType::Float64: + if (type == PropertyType::Scalar) { + switch (componentType) { + case PropertyComponentType::Int8: + callback( + propertyName, + getPropertyViewImpl(propertyName, classProperty)); + break; + case PropertyComponentType::Uint8: + callback( + propertyName, + getPropertyViewImpl(propertyName, classProperty)); + break; + case PropertyComponentType::Int16: + callback( + propertyName, + getPropertyViewImpl(propertyName, classProperty)); + break; + case PropertyComponentType::Uint16: + callback( + propertyName, + getPropertyViewImpl(propertyName, classProperty)); + break; + case PropertyComponentType::Int32: + callback( + propertyName, + getPropertyViewImpl(propertyName, classProperty)); + break; + case PropertyComponentType::Uint32: + callback( + propertyName, + getPropertyViewImpl(propertyName, classProperty)); + break; + case PropertyComponentType::Int64: + callback( + propertyName, + getPropertyViewImpl(propertyName, classProperty)); + break; + case PropertyComponentType::Uint64: + callback( + propertyName, + getPropertyViewImpl(propertyName, classProperty)); + break; + case PropertyComponentType::Float32: + callback( + propertyName, + getPropertyViewImpl(propertyName, classProperty)); + break; + case PropertyComponentType::Float64: + callback( + propertyName, + getPropertyViewImpl(propertyName, classProperty)); + break; + default: + break; + } + } else if (type == PropertyType::String) { callback( propertyName, - getPropertyViewImpl(propertyName, classProperty)); - break; - case PropertyType::Boolean: + getPropertyViewImpl(propertyName, classProperty)); + } else if (type == PropertyType::Boolean) { callback( propertyName, getPropertyViewImpl(propertyName, classProperty)); - break; - case PropertyType::String: - callback( - propertyName, - getPropertyViewImpl(propertyName, classProperty)); - break; - default: - break; } } @@ -567,7 +772,7 @@ class MetadataPropertyTableView { } template - StructuralMetadata::MetadataPropertyView getNumericOrBooleanPropertyValues( + MetadataPropertyView getNumericOrBooleanPropertyValues( const ExtensionExtStructuralMetadataClassProperty& classProperty, const ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty) const { diff --git a/CesiumGltf/src/StructuralMetadataPropertyTableView.cpp b/CesiumGltf/src/StructuralMetadataPropertyTableView.cpp index 4843fabe2..2c17c1259 100644 --- a/CesiumGltf/src/StructuralMetadataPropertyTableView.cpp +++ b/CesiumGltf/src/StructuralMetadataPropertyTableView.cpp @@ -366,15 +366,7 @@ MetadataPropertyTableView::getStringArrayPropertyValues( MetadataPropertyViewStatus::ErrorArrayCountAndOffsetBufferDontExist); } - // Get offset types - const PropertyComponentType arrayOffsetType = - convertArrayOffsetTypeStringToPropertyComponentType( - propertyTableProperty.arrayOffsetType); - if (arrayOffsetType == PropertyComponentType::None) { - return createInvalidPropertyView>( - MetadataPropertyViewStatus::ErrorInvalidArrayOffsetType); - } - + // Get string offset type const PropertyComponentType stringOffsetType = convertStringOffsetTypeStringToPropertyComponentType( propertyTableProperty.stringOffsetType); @@ -383,14 +375,9 @@ MetadataPropertyTableView::getStringArrayPropertyValues( MetadataPropertyViewStatus::ErrorInvalidStringOffsetType); } - if (propertyTableProperty.arrayOffsets < 0) { - return createInvalidPropertyView>( - MetadataPropertyViewStatus::ErrorInvalidArrayOffsetBuffer); - } - if (propertyTableProperty.stringOffsets < 0) { return createInvalidPropertyView>( - MetadataPropertyViewStatus::ErrorInvalidStringOffsetBuffer); + MetadataPropertyViewStatus::ErrorInvalidStringOffsetBufferView); } // Handle fixed-length arrays @@ -419,6 +406,21 @@ MetadataPropertyTableView::getStringArrayPropertyValues( classProperty.normalized); } + // Get array offset type + const PropertyComponentType arrayOffsetType = + convertArrayOffsetTypeStringToPropertyComponentType( + propertyTableProperty.arrayOffsetType); + if (arrayOffsetType == PropertyComponentType::None) { + return createInvalidPropertyView>( + MetadataPropertyViewStatus::ErrorInvalidArrayOffsetType); + } + + if (propertyTableProperty.arrayOffsets < 0) { + return createInvalidPropertyView>( + MetadataPropertyViewStatus::ErrorInvalidArrayOffsetBufferView); + } + + // Handle variable-length arrays gsl::span stringOffsets; status = getBufferSafe(propertyTableProperty.stringOffsets, stringOffsets); diff --git a/CesiumGltf/test/TestStructuralMetadataPropertyTableView.cpp b/CesiumGltf/test/TestStructuralMetadataPropertyTableView.cpp new file mode 100644 index 000000000..e948a5dc6 --- /dev/null +++ b/CesiumGltf/test/TestStructuralMetadataPropertyTableView.cpp @@ -0,0 +1,2591 @@ +#include "CesiumGltf/StructuralMetadataPropertyTableView.h" + +#include + +#include + +using namespace CesiumGltf; +using namespace CesiumGltf::StructuralMetadata; + +TEST_CASE("Test StructuralMetadata scalar property") { + Model model; + + std::vector values = {12, 34, 30, 11, 34, 34, 11, 33, 122, 33}; + + size_t valueBufferIndex = 0; + size_t valueBufferViewIndex = 0; + + // Buffers are constructed in scope to ensure that the tests don't use their + // temporary variables. + { + Buffer& valueBuffer = model.buffers.emplace_back(); + valueBuffer.cesium.data.resize(values.size() * sizeof(uint32_t)); + valueBuffer.byteLength = + static_cast(valueBuffer.cesium.data.size()); + std::memcpy( + valueBuffer.cesium.data.data(), + values.data(), + valueBuffer.cesium.data.size()); + valueBufferIndex = model.buffers.size() - 1; + + BufferView& valueBufferView = model.bufferViews.emplace_back(); + valueBufferView.buffer = static_cast(model.buffers.size() - 1); + valueBufferView.byteOffset = 0; + valueBufferView.byteLength = valueBuffer.byteLength; + valueBufferViewIndex = model.bufferViews.size() - 1; + } + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); + ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; + ExtensionExtStructuralMetadataClassProperty& testClassProperty = + testClass.properties["TestClassProperty"]; + testClassProperty.type = + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR; + testClassProperty.componentType = + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT32; + + ExtensionExtStructuralMetadataPropertyTable& propertyTable = + metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast(values.size()); + + ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = static_cast(valueBufferViewIndex); + + MetadataPropertyTableView view(&model, &propertyTable); + const ExtensionExtStructuralMetadataClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE( + classProperty->type == + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR); + REQUIRE( + classProperty->componentType == + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT32); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->array); + + SECTION("Access correct type") { + MetadataPropertyView uint32Property = + view.getPropertyView("TestClassProperty"); + REQUIRE(uint32Property.status() == MetadataPropertyViewStatus::Valid); + REQUIRE(uint32Property.size() > 0); + + for (int64_t i = 0; i < uint32Property.size(); ++i) { + REQUIRE(uint32Property.get(i) == values[static_cast(i)]); + } + } + + SECTION("Access wrong type") { + MetadataPropertyView uvec3Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uvec3Invalid.status() == MetadataPropertyViewStatus::ErrorTypeMismatch); + + MetadataPropertyView u32mat3x3Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + u32mat3x3Invalid.status() == + MetadataPropertyViewStatus::ErrorTypeMismatch); + + MetadataPropertyView boolInvalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + boolInvalid.status() == MetadataPropertyViewStatus::ErrorTypeMismatch); + + MetadataPropertyView stringInvalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + stringInvalid.status() == + MetadataPropertyViewStatus::ErrorTypeMismatch); + } + + SECTION("Access wrong component type") { + MetadataPropertyView uint8Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uint8Invalid.status() == + MetadataPropertyViewStatus::ErrorComponentTypeMismatch); + + MetadataPropertyView int32Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + int32Invalid.status() == + MetadataPropertyViewStatus::ErrorComponentTypeMismatch); + + MetadataPropertyView uint64Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uint64Invalid.status() == + MetadataPropertyViewStatus::ErrorComponentTypeMismatch); + } + + SECTION("Access incorrectly as array") { + MetadataPropertyView> uint32ArrayInvalid = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + uint32ArrayInvalid.status() == + MetadataPropertyViewStatus::ErrorArrayTypeMismatch); + } + + SECTION("Wrong buffer index") { + model.bufferViews[valueBufferViewIndex].buffer = 2; + MetadataPropertyView uint32Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uint32Property.status() == + MetadataPropertyViewStatus::ErrorInvalidValueBuffer); + } + + SECTION("Wrong buffer view index") { + propertyTableProperty.values = -1; + MetadataPropertyView uint32Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uint32Property.status() == + MetadataPropertyViewStatus::ErrorInvalidValueBufferView); + } + + SECTION("Buffer view points outside of the real buffer length") { + model.buffers[valueBufferIndex].cesium.data.resize(12); + MetadataPropertyView uint32Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uint32Property.status() == + MetadataPropertyViewStatus::ErrorBufferViewOutOfBounds); + } + + SECTION("Buffer view length isn't multiple of sizeof(T)") { + model.bufferViews[valueBufferViewIndex].byteLength = 13; + MetadataPropertyView uint32Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uint32Property.status() == + MetadataPropertyViewStatus::ErrorBufferViewSizeNotDivisibleByTypeSize); + } + + SECTION("Buffer view length doesn't match with propertyTableCount") { + model.bufferViews[valueBufferViewIndex].byteLength = 12; + MetadataPropertyView uint32Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uint32Property.status() == + MetadataPropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + } +} + +TEST_CASE("Test StructuralMetadata vecN property") { + Model model; + + std::vector values = { + glm::ivec3(-12, 34, 30), + glm::ivec3(11, 73, 0), + glm::ivec3(-2, 6, 12), + glm::ivec3(-4, 8, -13)}; + + size_t valueBufferIndex = 0; + size_t valueBufferViewIndex = 0; + + // Buffers are constructed in scope to ensure that the tests don't use their + // temporary variables. + { + Buffer& valueBuffer = model.buffers.emplace_back(); + valueBuffer.cesium.data.resize(values.size() * sizeof(glm::ivec3)); + valueBuffer.byteLength = + static_cast(valueBuffer.cesium.data.size()); + std::memcpy( + valueBuffer.cesium.data.data(), + values.data(), + valueBuffer.cesium.data.size()); + valueBufferIndex = model.buffers.size() - 1; + + BufferView& valueBufferView = model.bufferViews.emplace_back(); + valueBufferView.buffer = static_cast(model.buffers.size() - 1); + valueBufferView.byteOffset = 0; + valueBufferView.byteLength = valueBuffer.byteLength; + valueBufferViewIndex = model.bufferViews.size() - 1; + } + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); + ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; + ExtensionExtStructuralMetadataClassProperty& testClassProperty = + testClass.properties["TestClassProperty"]; + testClassProperty.type = + ExtensionExtStructuralMetadataClassProperty::Type::VEC3; + testClassProperty.componentType = + ExtensionExtStructuralMetadataClassProperty::ComponentType::INT32; + + ExtensionExtStructuralMetadataPropertyTable& propertyTable = + metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast(values.size()); + + ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = static_cast(valueBufferViewIndex); + + MetadataPropertyTableView view(&model, &propertyTable); + const ExtensionExtStructuralMetadataClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE( + classProperty->type == + ExtensionExtStructuralMetadataClassProperty::Type::VEC3); + REQUIRE( + classProperty->componentType == + ExtensionExtStructuralMetadataClassProperty::ComponentType::INT32); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->array); + + SECTION("Access correct type") { + MetadataPropertyView ivec3Property = + view.getPropertyView("TestClassProperty"); + REQUIRE(ivec3Property.status() == MetadataPropertyViewStatus::Valid); + REQUIRE(ivec3Property.size() > 0); + + for (int64_t i = 0; i < ivec3Property.size(); ++i) { + REQUIRE(ivec3Property.get(i) == values[i]); + } + } + + SECTION("Access wrong type") { + MetadataPropertyView int32Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + int32Invalid.status() == MetadataPropertyViewStatus::ErrorTypeMismatch); + + MetadataPropertyView ivec2Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + ivec2Invalid.status() == MetadataPropertyViewStatus::ErrorTypeMismatch); + + MetadataPropertyView i32mat3x3Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + i32mat3x3Invalid.status() == + MetadataPropertyViewStatus::ErrorTypeMismatch); + + MetadataPropertyView boolInvalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + boolInvalid.status() == MetadataPropertyViewStatus::ErrorTypeMismatch); + + MetadataPropertyView stringInvalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + stringInvalid.status() == + MetadataPropertyViewStatus::ErrorTypeMismatch); + } + + SECTION("Access wrong component type") { + MetadataPropertyView u8vec3Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + u8vec3Invalid.status() == + MetadataPropertyViewStatus::ErrorComponentTypeMismatch); + + MetadataPropertyView i16vec3Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + i16vec3Invalid.status() == + MetadataPropertyViewStatus::ErrorComponentTypeMismatch); + + MetadataPropertyView vec3Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + vec3Invalid.status() == + MetadataPropertyViewStatus::ErrorComponentTypeMismatch); + } + + SECTION("Access incorrectly as array") { + MetadataPropertyView> ivec3ArrayInvalid = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + ivec3ArrayInvalid.status() == + MetadataPropertyViewStatus::ErrorArrayTypeMismatch); + } + + SECTION("Wrong buffer index") { + model.bufferViews[valueBufferViewIndex].buffer = 2; + MetadataPropertyView ivec3Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + ivec3Property.status() == + MetadataPropertyViewStatus::ErrorInvalidValueBuffer); + } + + SECTION("Wrong buffer view index") { + propertyTableProperty.values = -1; + MetadataPropertyView ivec3Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + ivec3Property.status() == + MetadataPropertyViewStatus::ErrorInvalidValueBufferView); + } + + SECTION("Buffer view points outside of the real buffer length") { + model.buffers[valueBufferIndex].cesium.data.resize(12); + MetadataPropertyView ivec3Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + ivec3Property.status() == + MetadataPropertyViewStatus::ErrorBufferViewOutOfBounds); + } + + SECTION("Buffer view length isn't multiple of sizeof(T)") { + model.bufferViews[valueBufferViewIndex].byteLength = 11; + MetadataPropertyView ivec3Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + ivec3Property.status() == + MetadataPropertyViewStatus::ErrorBufferViewSizeNotDivisibleByTypeSize); + } + + SECTION("Buffer view length doesn't match with propertyTableCount") { + model.bufferViews[valueBufferViewIndex].byteLength = 12; + MetadataPropertyView ivec3Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + ivec3Property.status() == + MetadataPropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + } +} + +TEST_CASE("Test StructuralMetadata matN property") { + Model model; + + // clang-format off + std::vector values = { + glm::u32mat2x2( + 12, 34, + 30, 1), + glm::u32mat2x2( + 11, 8, + 73, 102), + glm::u32mat2x2( + 1, 0, + 63, 2), + glm::u32mat2x2( + 4, 8, + 3, 23)}; + // clang-format on + + size_t valueBufferIndex = 0; + size_t valueBufferViewIndex = 0; + + // Buffers are constructed in scope to ensure that the tests don't use their + // temporary variables. + { + Buffer& valueBuffer = model.buffers.emplace_back(); + valueBuffer.cesium.data.resize(values.size() * sizeof(glm::u32mat2x2)); + valueBuffer.byteLength = + static_cast(valueBuffer.cesium.data.size()); + std::memcpy( + valueBuffer.cesium.data.data(), + values.data(), + valueBuffer.cesium.data.size()); + valueBufferIndex = model.buffers.size() - 1; + + BufferView& valueBufferView = model.bufferViews.emplace_back(); + valueBufferView.buffer = static_cast(model.buffers.size() - 1); + valueBufferView.byteOffset = 0; + valueBufferView.byteLength = valueBuffer.byteLength; + valueBufferViewIndex = model.bufferViews.size() - 1; + } + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); + ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; + ExtensionExtStructuralMetadataClassProperty& testClassProperty = + testClass.properties["TestClassProperty"]; + testClassProperty.type = + ExtensionExtStructuralMetadataClassProperty::Type::MAT2; + testClassProperty.componentType = + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT32; + + ExtensionExtStructuralMetadataPropertyTable& propertyTable = + metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast(values.size()); + + ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = static_cast(valueBufferViewIndex); + + MetadataPropertyTableView view(&model, &propertyTable); + const ExtensionExtStructuralMetadataClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE( + classProperty->type == + ExtensionExtStructuralMetadataClassProperty::Type::MAT2); + REQUIRE( + classProperty->componentType == + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT32); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->array); + + SECTION("Access correct type") { + MetadataPropertyView u32mat2x2Property = + view.getPropertyView("TestClassProperty"); + REQUIRE(u32mat2x2Property.status() == MetadataPropertyViewStatus::Valid); + REQUIRE(u32mat2x2Property.size() > 0); + + for (int64_t i = 0; i < u32mat2x2Property.size(); ++i) { + REQUIRE(u32mat2x2Property.get(i) == values[i]); + } + } + + SECTION("Access wrong type") { + MetadataPropertyView uint32Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uint32Invalid.status() == + MetadataPropertyViewStatus::ErrorTypeMismatch); + + MetadataPropertyView uvec2Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uvec2Invalid.status() == MetadataPropertyViewStatus::ErrorTypeMismatch); + + MetadataPropertyView u32mat4x4Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + u32mat4x4Invalid.status() == + MetadataPropertyViewStatus::ErrorTypeMismatch); + + MetadataPropertyView boolInvalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + boolInvalid.status() == MetadataPropertyViewStatus::ErrorTypeMismatch); + + MetadataPropertyView stringInvalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + stringInvalid.status() == + MetadataPropertyViewStatus::ErrorTypeMismatch); + } + + SECTION("Access wrong component type") { + MetadataPropertyView u8mat2x2Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + u8mat2x2Invalid.status() == + MetadataPropertyViewStatus::ErrorComponentTypeMismatch); + + MetadataPropertyView i32mat2x2Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + i32mat2x2Invalid.status() == + MetadataPropertyViewStatus::ErrorComponentTypeMismatch); + + MetadataPropertyView mat2Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + mat2Invalid.status() == + MetadataPropertyViewStatus::ErrorComponentTypeMismatch); + } + + SECTION("Access incorrectly as array") { + MetadataPropertyView> + u32mat2x2ArrayInvalid = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + u32mat2x2ArrayInvalid.status() == + MetadataPropertyViewStatus::ErrorArrayTypeMismatch); + } + + SECTION("Wrong buffer index") { + model.bufferViews[valueBufferViewIndex].buffer = 2; + MetadataPropertyView u32mat2x2Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + u32mat2x2Property.status() == + MetadataPropertyViewStatus::ErrorInvalidValueBuffer); + } + + SECTION("Wrong buffer view index") { + propertyTableProperty.values = -1; + MetadataPropertyView u32mat2x2Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + u32mat2x2Property.status() == + MetadataPropertyViewStatus::ErrorInvalidValueBufferView); + } + + SECTION("Buffer view points outside of the real buffer length") { + model.buffers[valueBufferIndex].cesium.data.resize(sizeof(glm::u32mat2x2)); + MetadataPropertyView u32mat2x2Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + u32mat2x2Property.status() == + MetadataPropertyViewStatus::ErrorBufferViewOutOfBounds); + } + + SECTION("Buffer view length isn't multiple of sizeof(T)") { + model.bufferViews[valueBufferViewIndex].byteLength = + sizeof(glm::u32mat2x2) * 4 - 1; + MetadataPropertyView u32mat2x2Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + u32mat2x2Property.status() == + MetadataPropertyViewStatus::ErrorBufferViewSizeNotDivisibleByTypeSize); + } + + SECTION("Buffer view length doesn't match with propertyTableCount") { + model.bufferViews[valueBufferViewIndex].byteLength = sizeof(glm::u32mat2x2); + + MetadataPropertyView u32mat2x2Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + u32mat2x2Property.status() == + MetadataPropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + } +} + +TEST_CASE("Test StructuralMetadata boolean properties") { + Model model; + + int64_t instanceCount = 21; + std::vector expected; + std::vector values; + values.resize(3); + for (int64_t i = 0; i < instanceCount; ++i) { + if (i % 2 == 0) { + expected.emplace_back(true); + } else { + expected.emplace_back(false); + } + + uint8_t expectedValue = expected.back(); + int64_t byteIndex = i / 8; + int64_t bitIndex = i % 8; + values[static_cast(byteIndex)] = static_cast( + (expectedValue << bitIndex) | values[static_cast(byteIndex)]); + } + + // Buffers are constructed in scope to ensure that the tests don't use their + // temporary variables. + { + Buffer& valueBuffer = model.buffers.emplace_back(); + valueBuffer.cesium.data.resize(values.size()); + valueBuffer.byteLength = + static_cast(valueBuffer.cesium.data.size()); + std::memcpy( + valueBuffer.cesium.data.data(), + values.data(), + valueBuffer.cesium.data.size()); + + BufferView& valueBufferView = model.bufferViews.emplace_back(); + valueBufferView.buffer = static_cast(model.buffers.size() - 1); + valueBufferView.byteOffset = 0; + valueBufferView.byteLength = valueBuffer.byteLength; + } + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); + ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; + ExtensionExtStructuralMetadataClassProperty& testClassProperty = + testClass.properties["TestClassProperty"]; + testClassProperty.type = + ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN; + + ExtensionExtStructuralMetadataPropertyTable& propertyTable = + metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast(instanceCount); + + ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = + static_cast(model.bufferViews.size() - 1); + + MetadataPropertyTableView view(&model, &propertyTable); + const ExtensionExtStructuralMetadataClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE( + classProperty->type == + ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN); + REQUIRE(classProperty->componentType == std::nullopt); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->array); + + SECTION("Access correct type") { + MetadataPropertyView boolProperty = + view.getPropertyView("TestClassProperty"); + REQUIRE(boolProperty.status() == MetadataPropertyViewStatus::Valid); + REQUIRE(boolProperty.size() == instanceCount); + for (int64_t i = 0; i < boolProperty.size(); ++i) { + bool expectedValue = expected[static_cast(i)]; + REQUIRE(boolProperty.get(i) == expectedValue); + } + } + + SECTION("Buffer size doesn't match with propertyTableCount") { + propertyTable.count = 66; + MetadataPropertyView boolProperty = + view.getPropertyView("TestClassProperty"); + REQUIRE( + boolProperty.status() == + MetadataPropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + } +} + +TEST_CASE("Test StructuralMetadata string property") { + Model model; + + std::vector expected{"What's up", "Test_0", "Test_1", "", "Hi"}; + size_t totalBytes = 0; + for (const std::string& expectedValue : expected) { + totalBytes += expectedValue.size(); + } + + std::vector stringOffsets( + (expected.size() + 1) * sizeof(uint32_t)); + std::vector values(totalBytes); + uint32_t* offsetValue = reinterpret_cast(stringOffsets.data()); + for (size_t i = 0; i < expected.size(); ++i) { + const std::string& expectedValue = expected[i]; + std::memcpy( + values.data() + offsetValue[i], + expectedValue.c_str(), + expectedValue.size()); + offsetValue[i + 1] = + offsetValue[i] + static_cast(expectedValue.size()); + } + + // Buffers are constructed in scope to ensure that the tests don't use their + // temporary variables. + size_t valueBufferIndex = 0; + size_t valueBufferViewIndex = 0; + { + Buffer& valueBuffer = model.buffers.emplace_back(); + valueBuffer.byteLength = static_cast(values.size()); + valueBuffer.cesium.data = std::move(values); + valueBufferIndex = model.buffers.size() - 1; + + BufferView& valueBufferView = model.bufferViews.emplace_back(); + valueBufferView.buffer = static_cast(valueBufferIndex); + valueBufferView.byteOffset = 0; + valueBufferView.byteLength = valueBuffer.byteLength; + valueBufferViewIndex = model.bufferViews.size() - 1; + } + + size_t offsetBufferIndex = 0; + size_t offsetBufferViewIndex = 0; + { + Buffer& offsetBuffer = model.buffers.emplace_back(); + offsetBuffer.byteLength = static_cast(stringOffsets.size()); + offsetBuffer.cesium.data = std::move(stringOffsets); + offsetBufferIndex = model.buffers.size() - 1; + + BufferView& offsetBufferView = model.bufferViews.emplace_back(); + offsetBufferView.buffer = static_cast(offsetBufferIndex); + offsetBufferView.byteOffset = 0; + offsetBufferView.byteLength = offsetBuffer.byteLength; + offsetBufferViewIndex = model.bufferViews.size() - 1; + } + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); + ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; + ExtensionExtStructuralMetadataClassProperty& testClassProperty = + testClass.properties["TestClassProperty"]; + testClassProperty.type = + ExtensionExtStructuralMetadataClassProperty::Type::STRING; + + ExtensionExtStructuralMetadataPropertyTable& propertyTable = + metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast(expected.size()); + + ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.stringOffsetType = + ExtensionExtStructuralMetadataPropertyTableProperty::StringOffsetType:: + UINT32; + propertyTableProperty.values = static_cast(valueBufferViewIndex); + propertyTableProperty.stringOffsets = + static_cast(offsetBufferViewIndex); + + MetadataPropertyTableView view(&model, &propertyTable); + const ExtensionExtStructuralMetadataClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE( + classProperty->type == + ExtensionExtStructuralMetadataClassProperty::Type::STRING); + REQUIRE(classProperty->componentType == std::nullopt); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->array); + + SECTION("Access correct type") { + MetadataPropertyView stringProperty = + view.getPropertyView("TestClassProperty"); + REQUIRE(stringProperty.status() == MetadataPropertyViewStatus::Valid); + for (size_t i = 0; i < expected.size(); ++i) { + REQUIRE(stringProperty.get(static_cast(i)) == expected[i]); + } + } + + SECTION("Wrong offset type") { + propertyTableProperty.stringOffsetType = + ExtensionExtStructuralMetadataPropertyTableProperty::StringOffsetType:: + UINT8; + MetadataPropertyView stringProperty = + view.getPropertyView("TestClassProperty"); + REQUIRE( + stringProperty.status() == + MetadataPropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + + propertyTableProperty.stringOffsetType = + ExtensionExtStructuralMetadataPropertyTableProperty::StringOffsetType:: + UINT64; + stringProperty = + view.getPropertyView("TestClassProperty"); + REQUIRE( + stringProperty.status() == + MetadataPropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + + propertyTableProperty.stringOffsetType = "NONSENSE"; + stringProperty = + view.getPropertyView("TestClassProperty"); + REQUIRE( + stringProperty.status() == + MetadataPropertyViewStatus::ErrorInvalidStringOffsetType); + + propertyTableProperty.stringOffsetType = ""; + propertyTableProperty.arrayOffsetType = + ExtensionExtStructuralMetadataPropertyTableProperty::StringOffsetType:: + UINT32; + stringProperty = + view.getPropertyView("TestClassProperty"); + REQUIRE( + stringProperty.status() == + MetadataPropertyViewStatus::ErrorInvalidStringOffsetType); + } + + SECTION("Offset values are not sorted ascending") { + uint32_t* offset = reinterpret_cast( + model.buffers[offsetBufferIndex].cesium.data.data()); + offset[2] = + static_cast(model.buffers[valueBufferIndex].byteLength + 4); + MetadataPropertyView stringProperty = + view.getPropertyView("TestClassProperty"); + REQUIRE( + stringProperty.status() == + MetadataPropertyViewStatus::ErrorStringOffsetsNotSorted); + } + + SECTION("Offset value points outside of value buffer") { + uint32_t* offset = reinterpret_cast( + model.buffers[offsetBufferIndex].cesium.data.data()); + offset[propertyTable.count] = + static_cast(model.buffers[valueBufferIndex].byteLength + 4); + MetadataPropertyView stringProperty = + view.getPropertyView("TestClassProperty"); + REQUIRE( + stringProperty.status() == + MetadataPropertyViewStatus::ErrorStringOffsetOutOfBounds); + } +} + +TEST_CASE("Test StructuralMetadata fixed-length scalar array") { + Model model; + + std::vector values = + {12, 34, 30, 11, 34, 34, 11, 33, 122, 33, 223, 11}; + + size_t valueBufferViewIndex = 0; + { + Buffer& valueBuffer = model.buffers.emplace_back(); + valueBuffer.cesium.data.resize(values.size() * sizeof(uint32_t)); + valueBuffer.byteLength = + static_cast(valueBuffer.cesium.data.size()); + std::memcpy( + valueBuffer.cesium.data.data(), + values.data(), + valueBuffer.cesium.data.size()); + + BufferView& valueBufferView = model.bufferViews.emplace_back(); + valueBufferView.buffer = static_cast(model.buffers.size() - 1); + valueBufferView.byteOffset = 0; + valueBufferView.byteLength = valueBuffer.byteLength; + valueBufferViewIndex = model.bufferViews.size() - 1; + } + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); + ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; + ExtensionExtStructuralMetadataClassProperty& testClassProperty = + testClass.properties["TestClassProperty"]; + testClassProperty.type = + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR; + testClassProperty.componentType = + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT32; + testClassProperty.array = true; + testClassProperty.count = 3; + + ExtensionExtStructuralMetadataPropertyTable& propertyTable = + metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast( + values.size() / static_cast(testClassProperty.count.value())); + + ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = + static_cast(model.bufferViews.size() - 1); + + MetadataPropertyTableView view(&model, &propertyTable); + const ExtensionExtStructuralMetadataClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE( + classProperty->type == + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR); + REQUIRE( + classProperty->componentType == + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT32); + REQUIRE(classProperty->array); + REQUIRE(classProperty->count == 3); + + SECTION("Access the right type") { + MetadataPropertyView> arrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE(arrayProperty.status() == MetadataPropertyViewStatus::Valid); + + for (int64_t i = 0; i < arrayProperty.size(); ++i) { + MetadataArrayView member = arrayProperty.get(i); + for (int64_t j = 0; j < member.size(); ++j) { + REQUIRE(member[j] == values[static_cast(i * 3 + j)]); + } + } + } + + SECTION("Wrong type") { + MetadataPropertyView> boolArrayInvalid = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + boolArrayInvalid.status() == + MetadataPropertyViewStatus::ErrorTypeMismatch); + + MetadataPropertyView> uvec2ArrayInvalid = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + uvec2ArrayInvalid.status() == + MetadataPropertyViewStatus::ErrorTypeMismatch); + } + + SECTION("Wrong component type") { + MetadataPropertyView> int32ArrayInvalid = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + int32ArrayInvalid.status() == + MetadataPropertyViewStatus::ErrorComponentTypeMismatch); + } + + SECTION("Not an array type") { + MetadataPropertyView uint32Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uint32Invalid.status() == + MetadataPropertyViewStatus::ErrorArrayTypeMismatch); + } + + SECTION("Buffer size is not a multiple of type size") { + model.bufferViews[valueBufferViewIndex].byteLength = 13; + MetadataPropertyView> arrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + arrayProperty.status() == + MetadataPropertyViewStatus::ErrorBufferViewSizeNotDivisibleByTypeSize); + } + + SECTION("Negative component count") { + testClassProperty.count = -1; + MetadataPropertyView> arrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + arrayProperty.status() == + MetadataPropertyViewStatus::ErrorArrayCountAndOffsetBufferDontExist); + } + + SECTION("Value buffer doesn't fit into property table count") { + testClassProperty.count = 55; + MetadataPropertyView> arrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + arrayProperty.status() == + MetadataPropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + } +} + +TEST_CASE("Test Structural Metadata variable-length scalar array") { + Model model; + + std::vector> expected{ + {12, 33, 11, 344, 112, 444, 1}, + {}, + {}, + {122, 23, 333, 12}, + {}, + {333, 311, 22, 34}, + {}, + {33, 1888, 233, 33019}}; + size_t numOfElements = 0; + for (const auto& expectedMember : expected) { + numOfElements += expectedMember.size(); + } + + std::vector values(numOfElements * sizeof(uint16_t)); + std::vector offsets((expected.size() + 1) * sizeof(uint64_t)); + uint64_t* offsetValue = reinterpret_cast(offsets.data()); + for (size_t i = 0; i < expected.size(); ++i) { + std::memcpy( + values.data() + offsetValue[i], + expected[i].data(), + expected[i].size() * sizeof(uint16_t)); + offsetValue[i + 1] = offsetValue[i] + expected[i].size() * sizeof(uint16_t); + } + + size_t valueBufferIndex = 0; + size_t valueBufferViewIndex = 0; + { + Buffer& valueBuffer = model.buffers.emplace_back(); + valueBuffer.byteLength = static_cast(values.size()); + valueBuffer.cesium.data = std::move(values); + valueBufferIndex = model.buffers.size() - 1; + + BufferView& valueBufferView = model.bufferViews.emplace_back(); + valueBufferView.buffer = static_cast(model.buffers.size() - 1); + valueBufferView.byteOffset = 0; + valueBufferView.byteLength = valueBuffer.byteLength; + valueBufferViewIndex = model.bufferViews.size() - 1; + } + + size_t offsetBufferIndex = 0; + size_t offsetBufferViewIndex = 0; + { + Buffer& offsetBuffer = model.buffers.emplace_back(); + offsetBuffer.byteLength = static_cast(offsets.size()); + offsetBuffer.cesium.data = std::move(offsets); + offsetBufferIndex = model.buffers.size() - 1; + + BufferView& offsetBufferView = model.bufferViews.emplace_back(); + offsetBufferView.buffer = static_cast(model.buffers.size() - 1); + offsetBufferView.byteOffset = 0; + offsetBufferView.byteLength = offsetBuffer.byteLength; + offsetBufferViewIndex = model.bufferViews.size() - 1; + } + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); + ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; + ExtensionExtStructuralMetadataClassProperty& testClassProperty = + testClass.properties["TestClassProperty"]; + testClassProperty.type = + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR; + testClassProperty.componentType = + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT16; + testClassProperty.array = true; + + ExtensionExtStructuralMetadataPropertyTable& propertyTable = + metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast(expected.size()); + + ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = static_cast(valueBufferViewIndex); + propertyTableProperty.arrayOffsets = + static_cast(offsetBufferViewIndex); + propertyTableProperty.arrayOffsetType = + ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: + UINT64; + + MetadataPropertyTableView view(&model, &propertyTable); + const ExtensionExtStructuralMetadataClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE( + classProperty->type == + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR); + REQUIRE( + classProperty->componentType == + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT16); + REQUIRE(classProperty->array); + REQUIRE(!classProperty->count); + + SECTION("Access the correct type") { + MetadataPropertyView> property = + view.getPropertyView>("TestClassProperty"); + REQUIRE(property.status() == MetadataPropertyViewStatus::Valid); + for (size_t i = 0; i < expected.size(); ++i) { + MetadataArrayView valueMember = + property.get(static_cast(i)); + REQUIRE(valueMember.size() == static_cast(expected[i].size())); + for (size_t j = 0; j < expected[i].size(); ++j) { + REQUIRE(expected[i][j] == valueMember[static_cast(j)]); + } + } + } + SECTION("Wrong offset type") { + propertyTableProperty.arrayOffsetType = + ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: + UINT8; + MetadataPropertyView> arrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + arrayProperty.status() == + MetadataPropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + + propertyTableProperty.arrayOffsetType = + ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: + UINT16; + arrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + arrayProperty.status() == + MetadataPropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + + propertyTableProperty.arrayOffsetType = "NONSENSE"; + arrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + arrayProperty.status() == + MetadataPropertyViewStatus::ErrorInvalidArrayOffsetType); + + propertyTableProperty.arrayOffsetType = ""; + propertyTableProperty.stringOffsetType = + ExtensionExtStructuralMetadataPropertyTableProperty::StringOffsetType:: + UINT64; + arrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + arrayProperty.status() == + MetadataPropertyViewStatus::ErrorInvalidArrayOffsetType); + } + + SECTION("Offset values are not sorted ascending") { + propertyTableProperty.arrayOffsetType = + ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: + UINT64; + uint64_t* offset = reinterpret_cast( + model.buffers[offsetBufferIndex].cesium.data.data()); + offset[propertyTable.count] = 0; + MetadataPropertyView> arrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + arrayProperty.status() == + MetadataPropertyViewStatus::ErrorArrayOffsetsNotSorted); + } + + SECTION("Offset value points outside of value buffer") { + uint64_t* offset = reinterpret_cast( + model.buffers[offsetBufferIndex].cesium.data.data()); + offset[propertyTable.count] = + static_cast(model.buffers[valueBufferIndex].byteLength + 4); + MetadataPropertyView> arrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + arrayProperty.status() == + MetadataPropertyViewStatus::ErrorArrayOffsetOutOfBounds); + } + + SECTION("Count and offset buffer are both present") { + testClassProperty.count = 3; + MetadataPropertyView> property = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + property.status() == + MetadataPropertyViewStatus::ErrorArrayCountAndOffsetBufferCoexist); + } +} + +TEST_CASE("Test StructuralMetadata fixed-length vecN array") { + Model model; + + std::vector values = { + glm::ivec3(12, 34, -30), + glm::ivec3(-2, 0, 1), + glm::ivec3(1, 2, 8), + glm::ivec3(-100, 84, 6), + glm::ivec3(2, -2, -2), + glm::ivec3(40, 61, 3), + }; + + size_t valueBufferViewIndex = 0; + { + Buffer& valueBuffer = model.buffers.emplace_back(); + valueBuffer.cesium.data.resize(values.size() * sizeof(glm::ivec3)); + valueBuffer.byteLength = + static_cast(valueBuffer.cesium.data.size()); + std::memcpy( + valueBuffer.cesium.data.data(), + values.data(), + valueBuffer.cesium.data.size()); + + BufferView& valueBufferView = model.bufferViews.emplace_back(); + valueBufferView.buffer = static_cast(model.buffers.size() - 1); + valueBufferView.byteOffset = 0; + valueBufferView.byteLength = valueBuffer.byteLength; + valueBufferViewIndex = model.bufferViews.size() - 1; + } + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); + ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; + ExtensionExtStructuralMetadataClassProperty& testClassProperty = + testClass.properties["TestClassProperty"]; + testClassProperty.type = + ExtensionExtStructuralMetadataClassProperty::Type::VEC3; + testClassProperty.componentType = + ExtensionExtStructuralMetadataClassProperty::ComponentType::INT32; + testClassProperty.array = true; + testClassProperty.count = 2; + + ExtensionExtStructuralMetadataPropertyTable& propertyTable = + metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast( + values.size() / static_cast(testClassProperty.count.value())); + + ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = + static_cast(model.bufferViews.size() - 1); + + MetadataPropertyTableView view(&model, &propertyTable); + const ExtensionExtStructuralMetadataClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE( + classProperty->type == + ExtensionExtStructuralMetadataClassProperty::Type::VEC3); + REQUIRE( + classProperty->componentType == + ExtensionExtStructuralMetadataClassProperty::ComponentType::INT32); + REQUIRE(classProperty->array); + REQUIRE(classProperty->count == 2); + + SECTION("Access the right type") { + MetadataPropertyView> arrayProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE(arrayProperty.status() == MetadataPropertyViewStatus::Valid); + + for (int64_t i = 0; i < arrayProperty.size(); ++i) { + MetadataArrayView member = arrayProperty.get(i); + for (int64_t j = 0; j < member.size(); ++j) { + REQUIRE(member[j] == values[static_cast(i * 2 + j)]); + } + } + } + + SECTION("Wrong type") { + MetadataPropertyView> int32ArrayInvalid = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + int32ArrayInvalid.status() == + MetadataPropertyViewStatus::ErrorTypeMismatch); + + MetadataPropertyView> ivec2ArrayInvalid = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + ivec2ArrayInvalid.status() == + MetadataPropertyViewStatus::ErrorTypeMismatch); + } + + SECTION("Wrong component type") { + MetadataPropertyView> uvec3ArrayInvalid = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + uvec3ArrayInvalid.status() == + MetadataPropertyViewStatus::ErrorComponentTypeMismatch); + } + + SECTION("Not an array type") { + MetadataPropertyView ivec3Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + ivec3Invalid.status() == + MetadataPropertyViewStatus::ErrorArrayTypeMismatch); + } + + SECTION("Buffer size is not a multiple of type size") { + model.bufferViews[valueBufferViewIndex].byteLength = 13; + MetadataPropertyView> arrayProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + MetadataPropertyViewStatus::ErrorBufferViewSizeNotDivisibleByTypeSize); + } + + SECTION("Negative component count") { + testClassProperty.count = -1; + MetadataPropertyView> arrayProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + MetadataPropertyViewStatus::ErrorArrayCountAndOffsetBufferDontExist); + } + + SECTION("Value buffer doesn't fit into property table count") { + testClassProperty.count = 55; + MetadataPropertyView> arrayProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + MetadataPropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + } +} + +TEST_CASE("Test Structural Metadata variable-length vecN array") { + Model model; + + // clang-format off + std::vector> expected{ + { glm::ivec3(12, 34, -30), glm::ivec3(-2, 0, 1) }, + { glm::ivec3(1, 2, 8), }, + {}, + { glm::ivec3(-100, 84, 6), glm::ivec3(2, -2, -2), glm::ivec3(40, 61, 3) }, + { glm::ivec3(-1, 4, -7) }, + }; + // clang-format on + + size_t numOfElements = 0; + for (const auto& expectedMember : expected) { + numOfElements += expectedMember.size(); + } + + std::vector values(numOfElements * sizeof(glm::ivec3)); + std::vector offsets((expected.size() + 1) * sizeof(uint64_t)); + uint64_t* offsetValue = reinterpret_cast(offsets.data()); + for (size_t i = 0; i < expected.size(); ++i) { + std::memcpy( + values.data() + offsetValue[i], + expected[i].data(), + expected[i].size() * sizeof(glm::ivec3)); + offsetValue[i + 1] = + offsetValue[i] + expected[i].size() * sizeof(glm::ivec3); + } + + size_t valueBufferIndex = 0; + size_t valueBufferViewIndex = 0; + { + Buffer& valueBuffer = model.buffers.emplace_back(); + valueBuffer.byteLength = static_cast(values.size()); + valueBuffer.cesium.data = std::move(values); + valueBufferIndex = model.buffers.size() - 1; + + BufferView& valueBufferView = model.bufferViews.emplace_back(); + valueBufferView.buffer = static_cast(model.buffers.size() - 1); + valueBufferView.byteOffset = 0; + valueBufferView.byteLength = valueBuffer.byteLength; + valueBufferViewIndex = model.bufferViews.size() - 1; + } + + size_t offsetBufferIndex = 0; + size_t offsetBufferViewIndex = 0; + { + Buffer& offsetBuffer = model.buffers.emplace_back(); + offsetBuffer.byteLength = static_cast(offsets.size()); + offsetBuffer.cesium.data = std::move(offsets); + offsetBufferIndex = model.buffers.size() - 1; + + BufferView& offsetBufferView = model.bufferViews.emplace_back(); + offsetBufferView.buffer = static_cast(model.buffers.size() - 1); + offsetBufferView.byteOffset = 0; + offsetBufferView.byteLength = offsetBuffer.byteLength; + offsetBufferViewIndex = model.bufferViews.size() - 1; + } + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); + ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; + ExtensionExtStructuralMetadataClassProperty& testClassProperty = + testClass.properties["TestClassProperty"]; + testClassProperty.type = + ExtensionExtStructuralMetadataClassProperty::Type::VEC3; + testClassProperty.componentType = + ExtensionExtStructuralMetadataClassProperty::ComponentType::INT32; + testClassProperty.array = true; + + ExtensionExtStructuralMetadataPropertyTable& propertyTable = + metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast(expected.size()); + + ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = static_cast(valueBufferViewIndex); + propertyTableProperty.arrayOffsets = + static_cast(offsetBufferViewIndex); + propertyTableProperty.arrayOffsetType = + ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: + UINT64; + + MetadataPropertyTableView view(&model, &propertyTable); + const ExtensionExtStructuralMetadataClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE( + classProperty->type == + ExtensionExtStructuralMetadataClassProperty::Type::VEC3); + REQUIRE( + classProperty->componentType == + ExtensionExtStructuralMetadataClassProperty::ComponentType::INT32); + REQUIRE(classProperty->array); + REQUIRE(!classProperty->count); + + SECTION("Access the correct type") { + MetadataPropertyView> property = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE(property.status() == MetadataPropertyViewStatus::Valid); + for (size_t i = 0; i < expected.size(); ++i) { + MetadataArrayView valueMember = + property.get(static_cast(i)); + REQUIRE(valueMember.size() == static_cast(expected[i].size())); + for (size_t j = 0; j < expected[i].size(); ++j) { + REQUIRE(expected[i][j] == valueMember[static_cast(j)]); + } + } + } + + SECTION("Wrong offset type") { + propertyTableProperty.arrayOffsetType = + ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: + UINT8; + MetadataPropertyView> arrayProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + MetadataPropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + + propertyTableProperty.arrayOffsetType = + ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: + UINT16; + arrayProperty = view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + MetadataPropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + + propertyTableProperty.arrayOffsetType = "NONSENSE"; + arrayProperty = view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + MetadataPropertyViewStatus::ErrorInvalidArrayOffsetType); + + propertyTableProperty.arrayOffsetType = ""; + propertyTableProperty.stringOffsetType = + ExtensionExtStructuralMetadataPropertyTableProperty::StringOffsetType:: + UINT64; + arrayProperty = view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + MetadataPropertyViewStatus::ErrorInvalidArrayOffsetType); + } + + SECTION("Offset values are not sorted ascending") { + propertyTableProperty.arrayOffsetType = + ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: + UINT64; + uint64_t* offset = reinterpret_cast( + model.buffers[offsetBufferIndex].cesium.data.data()); + offset[propertyTable.count] = 0; + MetadataPropertyView> arrayProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + MetadataPropertyViewStatus::ErrorArrayOffsetsNotSorted); + } + + SECTION("Offset value points outside of value buffer") { + uint64_t* offset = reinterpret_cast( + model.buffers[offsetBufferIndex].cesium.data.data()); + offset[propertyTable.count] = + static_cast(model.buffers[valueBufferIndex].byteLength + 4); + MetadataPropertyView> arrayProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + MetadataPropertyViewStatus::ErrorArrayOffsetOutOfBounds); + } + + SECTION("Count and offset buffer are both present") { + testClassProperty.count = 3; + MetadataPropertyView> property = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + property.status() == + MetadataPropertyViewStatus::ErrorArrayCountAndOffsetBufferCoexist); + } +} + +TEST_CASE("Test StructuralMetadata fixed-length matN array") { + Model model; + + // clang-format off + std::vector values = { + glm::i32mat2x2( + 12, 34, + -30, 20), + glm::i32mat2x2( + -2, -2, + 0, 1), + glm::i32mat2x2( + 1, 2, + 8, 5), + glm::i32mat2x2( + -100, 3, + 84, 6), + glm::i32mat2x2( + 2, 12, + -2, -2), + glm::i32mat2x2( + 40, 61, + 7, -3), + }; + // clang-format on + + size_t valueBufferViewIndex = 0; + { + Buffer& valueBuffer = model.buffers.emplace_back(); + valueBuffer.cesium.data.resize(values.size() * sizeof(glm::i32mat2x2)); + valueBuffer.byteLength = + static_cast(valueBuffer.cesium.data.size()); + std::memcpy( + valueBuffer.cesium.data.data(), + values.data(), + valueBuffer.cesium.data.size()); + + BufferView& valueBufferView = model.bufferViews.emplace_back(); + valueBufferView.buffer = static_cast(model.buffers.size() - 1); + valueBufferView.byteOffset = 0; + valueBufferView.byteLength = valueBuffer.byteLength; + valueBufferViewIndex = model.bufferViews.size() - 1; + } + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); + ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; + ExtensionExtStructuralMetadataClassProperty& testClassProperty = + testClass.properties["TestClassProperty"]; + testClassProperty.type = + ExtensionExtStructuralMetadataClassProperty::Type::MAT2; + testClassProperty.componentType = + ExtensionExtStructuralMetadataClassProperty::ComponentType::INT32; + testClassProperty.array = true; + testClassProperty.count = 2; + + ExtensionExtStructuralMetadataPropertyTable& propertyTable = + metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast( + values.size() / static_cast(testClassProperty.count.value())); + + ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = + static_cast(model.bufferViews.size() - 1); + + MetadataPropertyTableView view(&model, &propertyTable); + const ExtensionExtStructuralMetadataClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE( + classProperty->type == + ExtensionExtStructuralMetadataClassProperty::Type::MAT2); + REQUIRE( + classProperty->componentType == + ExtensionExtStructuralMetadataClassProperty::ComponentType::INT32); + REQUIRE(classProperty->array); + REQUIRE(classProperty->count == 2); + + SECTION("Access the right type") { + MetadataPropertyView> arrayProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE(arrayProperty.status() == MetadataPropertyViewStatus::Valid); + + for (int64_t i = 0; i < arrayProperty.size(); ++i) { + MetadataArrayView member = arrayProperty.get(i); + for (int64_t j = 0; j < member.size(); ++j) { + REQUIRE(member[j] == values[static_cast(i * 2 + j)]); + } + } + } + + SECTION("Wrong type") { + MetadataPropertyView> int32ArrayInvalid = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + int32ArrayInvalid.status() == + MetadataPropertyViewStatus::ErrorTypeMismatch); + + MetadataPropertyView> ivec2ArrayInvalid = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + ivec2ArrayInvalid.status() == + MetadataPropertyViewStatus::ErrorTypeMismatch); + } + + SECTION("Wrong component type") { + MetadataPropertyView> + u32mat2x2ArrayInvalid = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + u32mat2x2ArrayInvalid.status() == + MetadataPropertyViewStatus::ErrorComponentTypeMismatch); + } + + SECTION("Not an array type") { + MetadataPropertyView ivec3Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + ivec3Invalid.status() == + MetadataPropertyViewStatus::ErrorArrayTypeMismatch); + } + + SECTION("Buffer size is not a multiple of type size") { + model.bufferViews[valueBufferViewIndex].byteLength = 13; + MetadataPropertyView> arrayProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + MetadataPropertyViewStatus::ErrorBufferViewSizeNotDivisibleByTypeSize); + } + + SECTION("Negative component count") { + testClassProperty.count = -1; + MetadataPropertyView> arrayProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + MetadataPropertyViewStatus::ErrorArrayCountAndOffsetBufferDontExist); + } + + SECTION("Value buffer doesn't fit into property table count") { + testClassProperty.count = 55; + MetadataPropertyView> arrayProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + MetadataPropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + } +} + +TEST_CASE("Test Structural Metadata variable-length matN array") { + Model model; + + // clang-format off + std::vector data0{ + glm::i32mat2x2( + 3, -2, + 1, 0), + glm::i32mat2x2( + 40, 3, + 8, -9) + }; + std::vector data1{ + glm::i32mat2x2( + 1, 10, + 7, 8), + }; + std::vector data2{ + glm::i32mat2x2( + 18, 0, + 1, 17), + glm::i32mat2x2( + -4, -2, + -9, 1), + glm::i32mat2x2( + 1, 8, + -99, 3), + }; + // clang-format on + + std::vector> + expected{data0, {}, data1, data2, {}}; + + size_t numOfElements = 0; + for (const auto& expectedMember : expected) { + numOfElements += expectedMember.size(); + } + + std::vector values(numOfElements * sizeof(glm::i32mat2x2)); + std::vector offsets((expected.size() + 1) * sizeof(uint64_t)); + uint64_t* offsetValue = reinterpret_cast(offsets.data()); + for (size_t i = 0; i < expected.size(); ++i) { + std::memcpy( + values.data() + offsetValue[i], + expected[i].data(), + expected[i].size() * sizeof(glm::i32mat2x2)); + offsetValue[i + 1] = + offsetValue[i] + expected[i].size() * sizeof(glm::i32mat2x2); + } + + size_t valueBufferIndex = 0; + size_t valueBufferViewIndex = 0; + { + Buffer& valueBuffer = model.buffers.emplace_back(); + valueBuffer.byteLength = static_cast(values.size()); + valueBuffer.cesium.data = std::move(values); + valueBufferIndex = model.buffers.size() - 1; + + BufferView& valueBufferView = model.bufferViews.emplace_back(); + valueBufferView.buffer = static_cast(model.buffers.size() - 1); + valueBufferView.byteOffset = 0; + valueBufferView.byteLength = valueBuffer.byteLength; + valueBufferViewIndex = model.bufferViews.size() - 1; + } + + size_t offsetBufferIndex = 0; + size_t offsetBufferViewIndex = 0; + { + Buffer& offsetBuffer = model.buffers.emplace_back(); + offsetBuffer.byteLength = static_cast(offsets.size()); + offsetBuffer.cesium.data = std::move(offsets); + offsetBufferIndex = model.buffers.size() - 1; + + BufferView& offsetBufferView = model.bufferViews.emplace_back(); + offsetBufferView.buffer = static_cast(model.buffers.size() - 1); + offsetBufferView.byteOffset = 0; + offsetBufferView.byteLength = offsetBuffer.byteLength; + offsetBufferViewIndex = model.bufferViews.size() - 1; + } + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); + ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; + ExtensionExtStructuralMetadataClassProperty& testClassProperty = + testClass.properties["TestClassProperty"]; + testClassProperty.type = + ExtensionExtStructuralMetadataClassProperty::Type::MAT2; + testClassProperty.componentType = + ExtensionExtStructuralMetadataClassProperty::ComponentType::INT32; + testClassProperty.array = true; + + ExtensionExtStructuralMetadataPropertyTable& propertyTable = + metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast(expected.size()); + + ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = static_cast(valueBufferViewIndex); + propertyTableProperty.arrayOffsets = + static_cast(offsetBufferViewIndex); + propertyTableProperty.arrayOffsetType = + ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: + UINT64; + + MetadataPropertyTableView view(&model, &propertyTable); + const ExtensionExtStructuralMetadataClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE( + classProperty->type == + ExtensionExtStructuralMetadataClassProperty::Type::MAT2); + REQUIRE( + classProperty->componentType == + ExtensionExtStructuralMetadataClassProperty::ComponentType::INT32); + REQUIRE(classProperty->array); + REQUIRE(!classProperty->count); + + SECTION("Access the correct type") { + MetadataPropertyView> property = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE(property.status() == MetadataPropertyViewStatus::Valid); + for (size_t i = 0; i < expected.size(); ++i) { + MetadataArrayView valueMember = + property.get(static_cast(i)); + REQUIRE(valueMember.size() == static_cast(expected[i].size())); + for (size_t j = 0; j < expected[i].size(); ++j) { + REQUIRE(expected[i][j] == valueMember[static_cast(j)]); + } + } + } + + SECTION("Wrong offset type") { + propertyTableProperty.arrayOffsetType = + ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: + UINT8; + MetadataPropertyView> arrayProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + MetadataPropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + + propertyTableProperty.arrayOffsetType = + ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: + UINT16; + arrayProperty = view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + MetadataPropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + + propertyTableProperty.arrayOffsetType = "NONSENSE"; + arrayProperty = view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + MetadataPropertyViewStatus::ErrorInvalidArrayOffsetType); + + propertyTableProperty.arrayOffsetType = ""; + propertyTableProperty.stringOffsetType = + ExtensionExtStructuralMetadataPropertyTableProperty::StringOffsetType:: + UINT64; + arrayProperty = view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + MetadataPropertyViewStatus::ErrorInvalidArrayOffsetType); + } + + SECTION("Offset values are not sorted ascending") { + propertyTableProperty.arrayOffsetType = + ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: + UINT64; + uint64_t* offset = reinterpret_cast( + model.buffers[offsetBufferIndex].cesium.data.data()); + offset[propertyTable.count] = 0; + MetadataPropertyView> arrayProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + MetadataPropertyViewStatus::ErrorArrayOffsetsNotSorted); + } + + SECTION("Offset value points outside of value buffer") { + uint64_t* offset = reinterpret_cast( + model.buffers[offsetBufferIndex].cesium.data.data()); + offset[propertyTable.count] = + static_cast(model.buffers[valueBufferIndex].byteLength + 4); + MetadataPropertyView> arrayProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + MetadataPropertyViewStatus::ErrorArrayOffsetOutOfBounds); + } + + SECTION("Count and offset buffer are both present") { + testClassProperty.count = 3; + MetadataPropertyView> property = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + property.status() == + MetadataPropertyViewStatus::ErrorArrayCountAndOffsetBufferCoexist); + } +} + +TEST_CASE("Test StructuralMetadata fixed-length boolean array") { + Model model; + + std::vector expected = { + true, + false, + false, + true, + false, + false, + true, + true, + true, + false, + false, + true}; + std::vector values; + size_t requiredBytesSize = static_cast( + glm::ceil(static_cast(expected.size()) / 8.0)); + values.resize(requiredBytesSize); + for (size_t i = 0; i < expected.size(); ++i) { + uint8_t expectedValue = expected[i]; + size_t byteIndex = i / 8; + size_t bitIndex = i % 8; + values[byteIndex] = + static_cast((expectedValue << bitIndex) | values[byteIndex]); + } + + { + Buffer& valueBuffer = model.buffers.emplace_back(); + valueBuffer.cesium.data.resize(values.size()); + valueBuffer.byteLength = + static_cast(valueBuffer.cesium.data.size()); + std::memcpy( + valueBuffer.cesium.data.data(), + values.data(), + valueBuffer.cesium.data.size()); + + BufferView& valueBufferView = model.bufferViews.emplace_back(); + valueBufferView.buffer = static_cast(model.buffers.size() - 1); + valueBufferView.byteOffset = 0; + valueBufferView.byteLength = valueBuffer.byteLength; + } + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); + ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; + ExtensionExtStructuralMetadataClassProperty& testClassProperty = + testClass.properties["TestClassProperty"]; + testClassProperty.type = + ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN; + testClassProperty.array = true; + testClassProperty.count = 3; + + ExtensionExtStructuralMetadataPropertyTable& propertyTable = + metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast( + expected.size() / static_cast(testClassProperty.count.value())); + + ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = + static_cast(model.bufferViews.size() - 1); + + MetadataPropertyTableView view(&model, &propertyTable); + const ExtensionExtStructuralMetadataClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE( + classProperty->type == + ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN); + REQUIRE(classProperty->array); + REQUIRE(classProperty->count == 3); + + SECTION("Access correct type") { + MetadataPropertyView> boolArrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE(boolArrayProperty.status() == MetadataPropertyViewStatus::Valid); + REQUIRE(boolArrayProperty.size() == propertyTable.count); + REQUIRE(boolArrayProperty.size() > 0); + for (int64_t i = 0; i < boolArrayProperty.size(); ++i) { + MetadataArrayView valueMember = boolArrayProperty.get(i); + for (int64_t j = 0; j < valueMember.size(); ++j) { + REQUIRE(valueMember[j] == expected[static_cast(i * 3 + j)]); + } + } + } + + SECTION("Wrong type") { + MetadataPropertyView> uint8ArrayInvalid = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + uint8ArrayInvalid.status() == + MetadataPropertyViewStatus::ErrorTypeMismatch); + } + + SECTION("View is not array type") { + MetadataPropertyView boolInvalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + boolInvalid.status() == + MetadataPropertyViewStatus::ErrorArrayTypeMismatch); + } + + SECTION("Value buffer doesn't have enough required bytes") { + testClassProperty.count = 11; + MetadataPropertyView> boolArrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + boolArrayProperty.status() == + MetadataPropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + } + + SECTION("Count is negative") { + testClassProperty.count = -1; + MetadataPropertyView> boolArrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + boolArrayProperty.status() == + MetadataPropertyViewStatus::ErrorArrayCountAndOffsetBufferDontExist); + } +} + +TEST_CASE("Test StructuralMetadata variable-length boolean array") { + Model model; + + std::vector> expected{ + {true, false, true, true, false, true, true}, + {}, + {}, + {}, + {false, false, false, false}, + {true, false, true}, + {false}, + {true, true, true, true, true, false, false}}; + size_t numOfElements = 0; + for (const auto& expectedMember : expected) { + numOfElements += expectedMember.size(); + } + + size_t requiredBytesSize = + static_cast(glm::ceil(static_cast(numOfElements) / 8.0)); + std::vector values(requiredBytesSize); + std::vector offsets((expected.size() + 1) * sizeof(uint64_t)); + uint64_t* offsetValue = reinterpret_cast(offsets.data()); + size_t indexSoFar = 0; + for (size_t i = 0; i < expected.size(); ++i) { + for (size_t j = 0; j < expected[i].size(); ++j) { + uint8_t expectedValue = expected[i][j]; + size_t byteIndex = indexSoFar / 8; + size_t bitIndex = indexSoFar % 8; + values[byteIndex] = static_cast( + (expectedValue << bitIndex) | static_cast(values[byteIndex])); + ++indexSoFar; + } + offsetValue[i + 1] = offsetValue[i] + expected[i].size(); + } + + size_t valueBufferViewIndex = 0; + size_t valueBufferIndex = 0; + { + Buffer& valueBuffer = model.buffers.emplace_back(); + valueBuffer.byteLength = static_cast(values.size()); + valueBuffer.cesium.data = std::move(values); + valueBufferIndex = model.buffers.size() - 1; + + BufferView& valueBufferView = model.bufferViews.emplace_back(); + valueBufferView.buffer = static_cast(valueBufferIndex); + valueBufferView.byteOffset = 0; + valueBufferView.byteLength = valueBuffer.byteLength; + valueBufferViewIndex = model.bufferViews.size() - 1; + } + + size_t offsetBufferIndex = 0; + size_t offsetBufferViewIndex = 0; + { + Buffer& offsetBuffer = model.buffers.emplace_back(); + offsetBuffer.byteLength = static_cast(offsets.size()); + offsetBuffer.cesium.data = std::move(offsets); + offsetBufferIndex = model.buffers.size() - 1; + + BufferView& offsetBufferView = model.bufferViews.emplace_back(); + offsetBufferView.buffer = static_cast(offsetBufferIndex); + offsetBufferView.byteOffset = 0; + offsetBufferView.byteLength = offsetBuffer.byteLength; + offsetBufferViewIndex = model.bufferViews.size() - 1; + } + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); + ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; + ExtensionExtStructuralMetadataClassProperty& testClassProperty = + testClass.properties["TestClassProperty"]; + testClassProperty.type = + ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN; + testClassProperty.array = true; + + ExtensionExtStructuralMetadataPropertyTable& propertyTable = + metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast(expected.size()); + + ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = static_cast(valueBufferViewIndex); + propertyTableProperty.arrayOffsets = + static_cast(offsetBufferViewIndex); + propertyTableProperty.arrayOffsetType = + ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: + UINT64; + + MetadataPropertyTableView view(&model, &propertyTable); + const ExtensionExtStructuralMetadataClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE( + classProperty->type == + ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN); + REQUIRE(classProperty->array); + REQUIRE(!classProperty->count); + + SECTION("Access correct type") { + MetadataPropertyView> boolArrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE(boolArrayProperty.status() == MetadataPropertyViewStatus::Valid); + for (size_t i = 0; i < expected.size(); ++i) { + MetadataArrayView arrayMember = + boolArrayProperty.get(static_cast(i)); + REQUIRE(arrayMember.size() == static_cast(expected[i].size())); + for (size_t j = 0; j < expected[i].size(); ++j) { + REQUIRE(expected[i][j] == arrayMember[static_cast(j)]); + } + } + } + + SECTION("Wrong offset type") { + propertyTableProperty.arrayOffsetType = + ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: + UINT8; + MetadataPropertyView> arrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + arrayProperty.status() == + MetadataPropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + + propertyTableProperty.arrayOffsetType = + ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: + UINT16; + arrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + arrayProperty.status() == + MetadataPropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + + propertyTableProperty.arrayOffsetType = "NONSENSE"; + arrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + arrayProperty.status() == + MetadataPropertyViewStatus::ErrorInvalidArrayOffsetType); + + propertyTableProperty.arrayOffsetType = ""; + propertyTableProperty.stringOffsetType = + ExtensionExtStructuralMetadataPropertyTableProperty::StringOffsetType:: + UINT64; + arrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + arrayProperty.status() == + MetadataPropertyViewStatus::ErrorInvalidArrayOffsetType); + } + + SECTION("Offset values are not sorted ascending") { + propertyTableProperty.arrayOffsetType = + ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: + UINT64; + uint64_t* offset = reinterpret_cast( + model.buffers[offsetBufferIndex].cesium.data.data()); + offset[propertyTable.count] = 0; + MetadataPropertyView> arrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + arrayProperty.status() == + MetadataPropertyViewStatus::ErrorArrayOffsetsNotSorted); + } + + SECTION("Offset value points outside of value buffer") { + uint64_t* offset = reinterpret_cast( + model.buffers[offsetBufferIndex].cesium.data.data()); + offset[propertyTable.count] = static_cast( + model.buffers[valueBufferIndex].byteLength * 8 + 20); + MetadataPropertyView> arrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + arrayProperty.status() == + MetadataPropertyViewStatus::ErrorArrayOffsetOutOfBounds); + } + + SECTION("Count and offset buffer both present") { + testClassProperty.count = 3; + MetadataPropertyView> boolArrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + boolArrayProperty.status() == + MetadataPropertyViewStatus::ErrorArrayCountAndOffsetBufferCoexist); + } +} + +TEST_CASE("Test StructuralMetadata fixed-length arrays of strings") { + Model model; + + std::vector expected{ + "What's up", + "Breaking news!!! Aliens no longer attacks the US first", + "But they still abduct my cows! Those milk thiefs! 👽 🐮", + "I'm not crazy. My mother had me tested 🤪", + "I love you, meat bags! ❤️", + "Book in the freezer"}; + + size_t totalBytes = 0; + for (const std::string& expectedValue : expected) { + totalBytes += expectedValue.size(); + } + + std::vector offsets((expected.size() + 1) * sizeof(uint32_t)); + std::vector values(totalBytes); + uint32_t* offsetValue = reinterpret_cast(offsets.data()); + for (size_t i = 0; i < expected.size(); ++i) { + const std::string& expectedValue = expected[i]; + std::memcpy( + values.data() + offsetValue[i], + expectedValue.c_str(), + expectedValue.size()); + offsetValue[i + 1] = + offsetValue[i] + static_cast(expectedValue.size()); + } + + size_t valueBufferViewIndex = 0; + { + Buffer& valueBuffer = model.buffers.emplace_back(); + valueBuffer.byteLength = static_cast(values.size()); + valueBuffer.cesium.data = std::move(values); + + BufferView& valueBufferView = model.bufferViews.emplace_back(); + valueBufferView.buffer = static_cast(model.buffers.size() - 1); + valueBufferView.byteOffset = 0; + valueBufferView.byteLength = valueBuffer.byteLength; + valueBufferViewIndex = model.bufferViews.size() - 1; + } + + size_t offsetBufferViewIndex = 0; + { + Buffer& offsetBuffer = model.buffers.emplace_back(); + offsetBuffer.byteLength = static_cast(offsets.size()); + offsetBuffer.cesium.data = std::move(offsets); + + BufferView& offsetBufferView = model.bufferViews.emplace_back(); + offsetBufferView.buffer = static_cast(model.buffers.size() - 1); + offsetBufferView.byteOffset = 0; + offsetBufferView.byteLength = offsetBuffer.byteLength; + offsetBufferViewIndex = model.bufferViews.size() - 1; + } + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); + ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; + ExtensionExtStructuralMetadataClassProperty& testClassProperty = + testClass.properties["TestClassProperty"]; + testClassProperty.type = + ExtensionExtStructuralMetadataClassProperty::Type::STRING; + testClassProperty.array = true; + testClassProperty.count = 2; + + ExtensionExtStructuralMetadataPropertyTable& propertyTable = + metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast( + expected.size() / static_cast(testClassProperty.count.value())); + + ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.stringOffsetType = + ExtensionExtStructuralMetadataPropertyTableProperty::StringOffsetType:: + UINT32; + propertyTableProperty.values = static_cast(valueBufferViewIndex); + propertyTableProperty.stringOffsets = + static_cast(offsetBufferViewIndex); + + MetadataPropertyTableView view(&model, &propertyTable); + const ExtensionExtStructuralMetadataClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE( + classProperty->type == + ExtensionExtStructuralMetadataClassProperty::Type::STRING); + REQUIRE(classProperty->array); + REQUIRE(classProperty->count == 2); + + SECTION("Access correct type") { + MetadataPropertyView> stringProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE(stringProperty.status() == MetadataPropertyViewStatus::Valid); + REQUIRE(stringProperty.size() == 3); + + MetadataArrayView v0 = stringProperty.get(0); + REQUIRE(v0.size() == 2); + REQUIRE(v0[0] == "What's up"); + REQUIRE(v0[1] == "Breaking news!!! Aliens no longer attacks the US first"); + + MetadataArrayView v1 = stringProperty.get(1); + REQUIRE(v1.size() == 2); + REQUIRE(v1[0] == "But they still abduct my cows! Those milk thiefs! 👽 🐮"); + REQUIRE(v1[1] == "I'm not crazy. My mother had me tested 🤪"); + + MetadataArrayView v2 = stringProperty.get(2); + REQUIRE(v2.size() == 2); + REQUIRE(v2[0] == "I love you, meat bags! ❤️"); + REQUIRE(v2[1] == "Book in the freezer"); + } + + SECTION("Array type mismatch") { + MetadataPropertyView stringProperty = + view.getPropertyView("TestClassProperty"); + REQUIRE( + stringProperty.status() == + MetadataPropertyViewStatus::ErrorArrayTypeMismatch); + } + + SECTION("Count is negative") { + testClassProperty.count = -1; + MetadataPropertyView> stringProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + stringProperty.status() == + MetadataPropertyViewStatus::ErrorArrayCountAndOffsetBufferDontExist); + } + + SECTION("Offset type is unknown") { + propertyTableProperty.stringOffsetType = "NONSENSE"; + MetadataPropertyView> stringProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + stringProperty.status() == + MetadataPropertyViewStatus::ErrorInvalidStringOffsetType); + + propertyTableProperty.stringOffsetType = ""; + propertyTableProperty.arrayOffsetType = + ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: + UINT32; + stringProperty = view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + stringProperty.status() == + MetadataPropertyViewStatus::ErrorInvalidStringOffsetType); + } + + SECTION("String offsets don't exist") { + propertyTableProperty.stringOffsets = -1; + MetadataPropertyView> stringProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + stringProperty.status() == + MetadataPropertyViewStatus::ErrorInvalidStringOffsetBufferView); + } +} + +TEST_CASE("Test StructuralMetadata variable-length arrays of strings") { + Model model; + + std::vector> expected{ + {"What's up"}, + {"Breaking news!!! Aliens no longer attacks the US first", + "But they still abduct my cows! Those milk thiefs! 👽 🐮"}, + {"I'm not crazy. My mother had me tested 🤪", + "I love you, meat bags! ❤️", + "Book in the freezer"}}; + + size_t totalBytes = 0; + size_t numOfElements = 0; + for (const auto& expectedValues : expected) { + for (const auto& value : expectedValues) { + totalBytes += value.size(); + } + + numOfElements += expectedValues.size(); + } + + std::vector offsets((expected.size() + 1) * sizeof(uint32_t)); + std::vector stringOffsets((numOfElements + 1) * sizeof(uint32_t)); + std::vector values(totalBytes); + uint32_t* offsetValue = reinterpret_cast(offsets.data()); + uint32_t* stringOffsetValue = + reinterpret_cast(stringOffsets.data()); + size_t strOffsetIdx = 0; + for (size_t i = 0; i < expected.size(); ++i) { + for (size_t j = 0; j < expected[i].size(); ++j) { + const std::string& expectedValue = expected[i][j]; + std::memcpy( + values.data() + stringOffsetValue[strOffsetIdx], + expectedValue.c_str(), + expectedValue.size()); + + stringOffsetValue[strOffsetIdx + 1] = + stringOffsetValue[strOffsetIdx] + + static_cast(expectedValue.size()); + ++strOffsetIdx; + } + + offsetValue[i + 1] = + offsetValue[i] + + static_cast(expected[i].size() * sizeof(uint32_t)); + } + + size_t valueBufferViewIndex = 0; + { + Buffer& valueBuffer = model.buffers.emplace_back(); + valueBuffer.byteLength = static_cast(values.size()); + valueBuffer.cesium.data = std::move(values); + + BufferView& valueBufferView = model.bufferViews.emplace_back(); + valueBufferView.buffer = static_cast(model.buffers.size() - 1); + valueBufferView.byteOffset = 0; + valueBufferView.byteLength = valueBuffer.byteLength; + valueBufferViewIndex = model.bufferViews.size() - 1; + } + + size_t arrayOffsetBuffer = 0; + size_t arrayOffsetBufferView = 0; + { + Buffer& offsetBuffer = model.buffers.emplace_back(); + offsetBuffer.byteLength = static_cast(offsets.size()); + offsetBuffer.cesium.data = std::move(offsets); + arrayOffsetBuffer = model.buffers.size() - 1; + + BufferView& offsetBufferView = model.bufferViews.emplace_back(); + offsetBufferView.buffer = static_cast(arrayOffsetBuffer); + offsetBufferView.byteOffset = 0; + offsetBufferView.byteLength = offsetBuffer.byteLength; + arrayOffsetBufferView = model.bufferViews.size() - 1; + } + + size_t stringOffsetBuffer = 0; + size_t stringOffsetBufferView = 0; + { + Buffer& strOffsetBuffer = model.buffers.emplace_back(); + strOffsetBuffer.byteLength = static_cast(stringOffsets.size()); + strOffsetBuffer.cesium.data = std::move(stringOffsets); + stringOffsetBuffer = model.buffers.size() - 1; + + BufferView& strOffsetBufferView = model.bufferViews.emplace_back(); + strOffsetBufferView.buffer = static_cast(stringOffsetBuffer); + strOffsetBufferView.byteOffset = 0; + strOffsetBufferView.byteLength = strOffsetBuffer.byteLength; + stringOffsetBufferView = model.bufferViews.size() - 1; + } + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); + ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; + ExtensionExtStructuralMetadataClassProperty& testClassProperty = + testClass.properties["TestClassProperty"]; + testClassProperty.type = + ExtensionExtStructuralMetadataClassProperty::Type::STRING; + testClassProperty.array = true; + + ExtensionExtStructuralMetadataPropertyTable& propertyTable = + metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast(expected.size()); + + ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.arrayOffsetType = + ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: + UINT32; + propertyTableProperty.stringOffsetType = + ExtensionExtStructuralMetadataPropertyTableProperty::StringOffsetType:: + UINT32; + propertyTableProperty.values = static_cast(valueBufferViewIndex); + propertyTableProperty.arrayOffsets = + static_cast(arrayOffsetBufferView); + propertyTableProperty.stringOffsets = + static_cast(stringOffsetBufferView); + + MetadataPropertyTableView view(&model, &propertyTable); + const ExtensionExtStructuralMetadataClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE( + classProperty->type == + ExtensionExtStructuralMetadataClassProperty::Type::STRING); + REQUIRE(classProperty->array); + REQUIRE(!classProperty->componentType); + REQUIRE(!classProperty->count); + + SECTION("Access correct type") { + MetadataPropertyView> stringProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE(stringProperty.status() == MetadataPropertyViewStatus::Valid); + for (size_t i = 0; i < expected.size(); ++i) { + MetadataArrayView stringArray = + stringProperty.get(static_cast(i)); + for (size_t j = 0; j < expected[i].size(); ++j) { + REQUIRE(stringArray[static_cast(j)] == expected[i][j]); + } + } + } + + SECTION("Wrong array offset type") { + propertyTableProperty.arrayOffsetType = + ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: + UINT8; + MetadataPropertyView> arrayProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + MetadataPropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + + propertyTableProperty.arrayOffsetType = + ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: + UINT16; + arrayProperty = view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + MetadataPropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + + propertyTableProperty.arrayOffsetType = "NONSENSE"; + arrayProperty = view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + MetadataPropertyViewStatus::ErrorInvalidArrayOffsetType); + propertyTableProperty.arrayOffsetType = + ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: + UINT32; + } + + SECTION("Wrong string offset type") { + propertyTableProperty.stringOffsetType = + ExtensionExtStructuralMetadataPropertyTableProperty::StringOffsetType:: + UINT8; + MetadataPropertyView> arrayProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + MetadataPropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + + propertyTableProperty.stringOffsetType = + ExtensionExtStructuralMetadataPropertyTableProperty::StringOffsetType:: + UINT16; + arrayProperty = view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + MetadataPropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + + propertyTableProperty.stringOffsetType = "NONSENSE"; + arrayProperty = view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + MetadataPropertyViewStatus::ErrorInvalidStringOffsetType); + propertyTableProperty.stringOffsetType = + ExtensionExtStructuralMetadataPropertyTableProperty::StringOffsetType:: + UINT32; + } + + SECTION("Array offset values are not sorted ascending") { + uint32_t* offset = reinterpret_cast( + model.buffers[arrayOffsetBuffer].cesium.data.data()); + offset[0] = static_cast(1000); + MetadataPropertyView> arrayProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + MetadataPropertyViewStatus::ErrorArrayOffsetsNotSorted); + offset[0] = 0; + } + + SECTION("String offset values are not sorted ascending") { + uint32_t* offset = reinterpret_cast( + model.buffers[stringOffsetBuffer].cesium.data.data()); + offset[0] = static_cast(1000); + MetadataPropertyView> arrayProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + MetadataPropertyViewStatus::ErrorStringOffsetsNotSorted); + offset[0] = 0; + } + + SECTION("Array offset value points outside of value buffer") { + uint32_t* offset = reinterpret_cast( + model.buffers[arrayOffsetBuffer].cesium.data.data()); + uint32_t previousValue = offset[propertyTable.count]; + offset[propertyTable.count] = static_cast(100000); + MetadataPropertyView> arrayProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + MetadataPropertyViewStatus::ErrorArrayOffsetOutOfBounds); + offset[propertyTable.count] = previousValue; + } + + SECTION("String offset value points outside of value buffer") { + uint32_t* offset = reinterpret_cast( + model.buffers[stringOffsetBuffer].cesium.data.data()); + uint32_t previousValue = offset[6]; + offset[6] = static_cast(100000); + MetadataPropertyView> arrayProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + MetadataPropertyViewStatus::ErrorStringOffsetOutOfBounds); + offset[6] = previousValue; + } + + SECTION("Count and offset buffer both present") { + testClassProperty.count = 3; + MetadataPropertyView> + boolArrayProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + boolArrayProperty.status() == + MetadataPropertyViewStatus::ErrorArrayCountAndOffsetBufferCoexist); + } +} From 352d0d20bb22a586acc0806a5eb5825d644ab4ab Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Fri, 19 May 2023 16:20:38 -0400 Subject: [PATCH 026/421] Try to fix compile errors --- .../StructuralMetadataPropertyTableView.h | 17 +++++++++++------ .../src/StructuralMetadataPropertyTableView.cpp | 1 - 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTableView.h b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTableView.h index b656b37c3..372b0a87c 100644 --- a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTableView.h +++ b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTableView.h @@ -108,8 +108,11 @@ class MetadataPropertyTableView { } PropertyType type = convertStringToPropertyType(pClassProperty->type); - PropertyComponentType componentType = - convertStringToPropertyType(pClassProperty->componentType); + PropertyComponentType componentType = PropertyComponentType::None; + if (pClassProperty->componentType) { + componentType = + convertStringToPropertyComponentType(*pClassProperty->componentType); + } if (pClassProperty->array) { getArrayPropertyViewImpl( @@ -167,7 +170,7 @@ class MetadataPropertyTableView { } private: - glm::length_t getDimensionsFromType(PropertyType type) { + glm::length_t getDimensionsFromType(PropertyType type) const { switch (type) { case PropertyType::Vec2: case PropertyType::Mat2: @@ -360,7 +363,7 @@ class MetadataPropertyTableView { PropertyType type, PropertyComponentType componentType, Callback&& callback) const { - glm::length_t N = getDimensionsFromType(type); + const glm::length_t N = getDimensionsFromType(type); if (N <= 1) { return; } @@ -453,7 +456,7 @@ class MetadataPropertyTableView { propertyName, classProperty, componentType, - std::forward(callback)) + std::forward(callback)); } else if (isPropertyTypeVecN(type)) { getVecNArrayPropertyViewImpl( propertyName, @@ -487,13 +490,14 @@ class MetadataPropertyTableView { template void getVecNPropertyViewImpl( + const std::string& propertyName, const ExtensionExtStructuralMetadataClassProperty& classProperty, const ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty, PropertyType type, PropertyComponentType componentType, Callback&& callback) { - glm::length_t N = getDimensionsFromType(type); + const glm::length_t N = getDimensionsFromType(type); if (N <= 1) { return; } @@ -574,6 +578,7 @@ class MetadataPropertyTableView { template void getMatNPropertyViewImpl( + const std::string& propertyName, const ExtensionExtStructuralMetadataClassProperty& classProperty, const ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty, diff --git a/CesiumGltf/src/StructuralMetadataPropertyTableView.cpp b/CesiumGltf/src/StructuralMetadataPropertyTableView.cpp index 2c17c1259..115ad0129 100644 --- a/CesiumGltf/src/StructuralMetadataPropertyTableView.cpp +++ b/CesiumGltf/src/StructuralMetadataPropertyTableView.cpp @@ -420,7 +420,6 @@ MetadataPropertyTableView::getStringArrayPropertyValues( MetadataPropertyViewStatus::ErrorInvalidArrayOffsetBufferView); } - // Handle variable-length arrays gsl::span stringOffsets; status = getBufferSafe(propertyTableProperty.stringOffsets, stringOffsets); From 8fe85716c0f9433d3502207e6a27866889165805 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Mon, 22 May 2023 13:54:16 +1000 Subject: [PATCH 027/421] Implement upsampling for meshes with UNSIGNED_BYTE indices. --- CHANGES.md | 4 + .../src/upsampleGltfForRasterOverlays.cpp | 10 + .../test/TestUpsampleGltfForRasterOverlay.cpp | 225 +++++++++++++++++- 3 files changed, 238 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 01b60fe82..85321179f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,6 +6,10 @@ - Added support for the `KHR_materials_variants` extension to the glTF reader and writer. +##### Fixes :wrench: + +- Fixed a bug that caused meshes to be missing entirely when upsampled from a parent with `UNSIGNED_BYTE` indices. + ### v0.24.0 - 2023-05-01 ##### Additions :tada: diff --git a/Cesium3DTilesSelection/src/upsampleGltfForRasterOverlays.cpp b/Cesium3DTilesSelection/src/upsampleGltfForRasterOverlays.cpp index 7d3d60563..3a0409607 100644 --- a/Cesium3DTilesSelection/src/upsampleGltfForRasterOverlays.cpp +++ b/Cesium3DTilesSelection/src/upsampleGltfForRasterOverlays.cpp @@ -1163,6 +1163,16 @@ static bool upsamplePrimitiveForRasterOverlays( const Accessor& indicesAccessorGltf = parentModel.accessors[static_cast(primitive.indices)]; if (indicesAccessorGltf.componentType == + Accessor::ComponentType::UNSIGNED_BYTE) { + return upsamplePrimitiveForRasterOverlays( + parentModel, + model, + mesh, + primitive, + childID, + textureCoordinateIndex); + } else if ( + indicesAccessorGltf.componentType == Accessor::ComponentType::UNSIGNED_SHORT) { return upsamplePrimitiveForRasterOverlays( parentModel, diff --git a/Cesium3DTilesSelection/test/TestUpsampleGltfForRasterOverlay.cpp b/Cesium3DTilesSelection/test/TestUpsampleGltfForRasterOverlay.cpp index 3446e6866..252717ac4 100644 --- a/Cesium3DTilesSelection/test/TestUpsampleGltfForRasterOverlay.cpp +++ b/Cesium3DTilesSelection/test/TestUpsampleGltfForRasterOverlay.cpp @@ -38,7 +38,7 @@ static void checkSkirt( Math::equalsEpsilon(expectedPosition.z, skirtPosition.z, Math::Epsilon7)); } -TEST_CASE("Test upsample tile without skirts") { +TEST_CASE("Test upsample tile") { const Ellipsoid& ellipsoid = CesiumGeospatial::Ellipsoid::WGS84; Cartographic bottomLeftCart{glm::radians(110.0), glm::radians(32.0), 0.0}; Cartographic topLeftCart{ @@ -940,3 +940,226 @@ TEST_CASE("Test upsample tile without skirts") { } } } + +TEST_CASE("Test upsample tile with UNSIGNED_BYTE indices") { + const Ellipsoid& ellipsoid = CesiumGeospatial::Ellipsoid::WGS84; + Cartographic bottomLeftCart{glm::radians(110.0), glm::radians(32.0), 0.0}; + Cartographic topLeftCart{ + bottomLeftCart.longitude, + bottomLeftCart.latitude + glm::radians(1.0), + 0.0}; + Cartographic topRightCart{ + bottomLeftCart.longitude + glm::radians(1.0), + bottomLeftCart.latitude + glm::radians(1.0), + 0.0}; + Cartographic bottomRightCart{ + bottomLeftCart.longitude + glm::radians(1.0), + bottomLeftCart.latitude, + 0.0}; + Cartographic centerCart{ + (bottomLeftCart.longitude + topRightCart.longitude) / 2.0, + (bottomLeftCart.latitude + topRightCart.latitude) / 2.0, + 0.0}; + glm::dvec3 center = ellipsoid.cartographicToCartesian(centerCart); + std::vector positions{ + static_cast( + ellipsoid.cartographicToCartesian(bottomLeftCart) - center), + static_cast( + ellipsoid.cartographicToCartesian(topLeftCart) - center), + static_cast( + ellipsoid.cartographicToCartesian(topRightCart) - center), + static_cast( + ellipsoid.cartographicToCartesian(bottomRightCart) - center), + }; + std::vector uvs{ + glm::vec2{0.0, 0.0}, + glm::vec2{0.0, 1.0}, + glm::vec2{1.0, 0.0}, + glm::vec2{1.0, 1.0}}; + std::vector indices{0, 2, 1, 1, 2, 3}; + uint32_t positionsBufferSize = + static_cast(positions.size() * sizeof(glm::vec3)); + uint32_t uvsBufferSize = + static_cast(uvs.size() * sizeof(glm::vec2)); + uint32_t indicesBufferSize = + static_cast(indices.size() * sizeof(uint8_t)); + + Model model; + + // create buffer + model.buffers.emplace_back(); + Buffer& buffer = model.buffers.back(); + buffer.cesium.data.resize( + positionsBufferSize + uvsBufferSize + indicesBufferSize); + std::memcpy(buffer.cesium.data.data(), positions.data(), positionsBufferSize); + std::memcpy( + buffer.cesium.data.data() + positionsBufferSize, + uvs.data(), + uvsBufferSize); + std::memcpy( + buffer.cesium.data.data() + positionsBufferSize + uvsBufferSize, + indices.data(), + indicesBufferSize); + + // create position + model.bufferViews.emplace_back(); + BufferView& positionBufferView = model.bufferViews.emplace_back(); + positionBufferView.buffer = static_cast(model.buffers.size() - 1); + positionBufferView.byteOffset = 0; + positionBufferView.byteLength = positionsBufferSize; + + model.accessors.emplace_back(); + Accessor& positionAccessor = model.accessors.back(); + positionAccessor.bufferView = + static_cast(model.bufferViews.size() - 1); + positionAccessor.byteOffset = 0; + positionAccessor.count = static_cast(positions.size()); + positionAccessor.componentType = Accessor::ComponentType::FLOAT; + positionAccessor.type = Accessor::Type::VEC3; + + int32_t positionAccessorIdx = + static_cast(model.accessors.size() - 1); + + // create uv + model.bufferViews.emplace_back(); + BufferView& uvBufferView = model.bufferViews.emplace_back(); + uvBufferView.buffer = static_cast(model.buffers.size() - 1); + uvBufferView.byteOffset = positionsBufferSize; + uvBufferView.byteLength = uvsBufferSize; + + model.accessors.emplace_back(); + Accessor& uvAccessor = model.accessors.back(); + uvAccessor.bufferView = static_cast(model.bufferViews.size() - 1); + uvAccessor.byteOffset = 0; + uvAccessor.count = static_cast(uvs.size()); + uvAccessor.componentType = Accessor::ComponentType::FLOAT; + uvAccessor.type = Accessor::Type::VEC2; + + int32_t uvAccessorIdx = static_cast(model.accessors.size() - 1); + + // create indices + model.bufferViews.emplace_back(); + BufferView& indicesBufferView = model.bufferViews.emplace_back(); + indicesBufferView.buffer = static_cast(model.buffers.size() - 1); + indicesBufferView.byteOffset = positionsBufferSize + uvsBufferSize; + indicesBufferView.byteLength = indicesBufferSize; + + model.accessors.emplace_back(); + Accessor& indicesAccessor = model.accessors.back(); + indicesAccessor.bufferView = static_cast(model.bufferViews.size() - 1); + indicesAccessor.byteOffset = 0; + indicesAccessor.count = static_cast(indices.size()); + indicesAccessor.componentType = Accessor::ComponentType::UNSIGNED_BYTE; + indicesAccessor.type = Accessor::Type::SCALAR; + + int indicesAccessorIdx = static_cast(model.accessors.size() - 1); + + model.meshes.emplace_back(); + Mesh& mesh = model.meshes.back(); + mesh.primitives.emplace_back(); + + MeshPrimitive& primitive = mesh.primitives.back(); + primitive.mode = MeshPrimitive::Mode::TRIANGLES; + primitive.attributes["_CESIUMOVERLAY_0"] = uvAccessorIdx; + primitive.attributes["POSITION"] = positionAccessorIdx; + primitive.indices = indicesAccessorIdx; + + // create node and update bounding volume + model.nodes.emplace_back(); + Node& node = model.nodes[0]; + node.mesh = static_cast(model.meshes.size() - 1); + node.matrix = { + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + -1.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + center.x, + center.z, + -center.y, + 1.0}; + + CesiumGeometry::UpsampledQuadtreeNode lowerLeft{ + CesiumGeometry::QuadtreeTileID(1, 0, 0)}; + CesiumGeometry::UpsampledQuadtreeNode upperLeft{ + CesiumGeometry::QuadtreeTileID(1, 0, 1)}; + CesiumGeometry::UpsampledQuadtreeNode lowerRight{ + CesiumGeometry::QuadtreeTileID(1, 1, 0)}; + CesiumGeometry::UpsampledQuadtreeNode upperRight{ + CesiumGeometry::QuadtreeTileID(1, 1, 1)}; + + Model upsampledModel = *upsampleGltfForRasterOverlays(model, lowerLeft); + + REQUIRE(upsampledModel.meshes.size() == 1); + const Mesh& upsampledMesh = upsampledModel.meshes.back(); + + REQUIRE(upsampledMesh.primitives.size() == 1); + const MeshPrimitive& upsampledPrimitive = upsampledMesh.primitives.back(); + + REQUIRE(upsampledPrimitive.indices >= 0); + REQUIRE( + upsampledPrimitive.attributes.find("POSITION") != + upsampledPrimitive.attributes.end()); + AccessorView upsampledPosition( + upsampledModel, + upsampledPrimitive.attributes.at("POSITION")); + AccessorView upsampledIndices( + upsampledModel, + upsampledPrimitive.indices); + + glm::vec3 p0 = upsampledPosition[0]; + REQUIRE( + glm::epsilonEqual( + p0, + positions[0], + glm::vec3(static_cast(Math::Epsilon7))) == glm::bvec3(true)); + + glm::vec3 p1 = upsampledPosition[1]; + REQUIRE( + glm::epsilonEqual( + p1, + (positions[0] + positions[2]) * 0.5f, + glm::vec3(static_cast(Math::Epsilon7))) == glm::bvec3(true)); + + glm::vec3 p2 = upsampledPosition[2]; + REQUIRE( + glm::epsilonEqual( + p2, + (upsampledPosition[1] + positions[1]) * 0.5f, + glm::vec3(static_cast(Math::Epsilon7))) == glm::bvec3(true)); + + glm::vec3 p3 = upsampledPosition[3]; + REQUIRE( + glm::epsilonEqual( + p3, + (positions[0] + positions[1]) * 0.5f, + glm::vec3(static_cast(Math::Epsilon7))) == glm::bvec3(true)); + + glm::vec3 p4 = upsampledPosition[4]; + REQUIRE( + glm::epsilonEqual( + p4, + (positions[0] + positions[2]) * 0.5f, + glm::vec3(static_cast(Math::Epsilon7))) == glm::bvec3(true)); + + glm::vec3 p5 = upsampledPosition[5]; + REQUIRE( + glm::epsilonEqual( + p5, + (positions[1] + positions[2]) * 0.5f, + glm::vec3(static_cast(Math::Epsilon7))) == glm::bvec3(true)); + + glm::vec3 p6 = upsampledPosition[6]; + REQUIRE( + glm::epsilonEqual( + p6, + (upsampledPosition[4] + positions[1]) * 0.5f, + glm::vec3(static_cast(Math::Epsilon7))) == glm::bvec3(true)); +} From 9dedad33448bc41ae071d898cc5bc22b5cda52a3 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Mon, 22 May 2023 15:34:49 +1000 Subject: [PATCH 028/421] Remove unused code. --- .../test/TestUpsampleGltfForRasterOverlay.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Cesium3DTilesSelection/test/TestUpsampleGltfForRasterOverlay.cpp b/Cesium3DTilesSelection/test/TestUpsampleGltfForRasterOverlay.cpp index 252717ac4..baebb6cbb 100644 --- a/Cesium3DTilesSelection/test/TestUpsampleGltfForRasterOverlay.cpp +++ b/Cesium3DTilesSelection/test/TestUpsampleGltfForRasterOverlay.cpp @@ -1088,12 +1088,6 @@ TEST_CASE("Test upsample tile with UNSIGNED_BYTE indices") { CesiumGeometry::UpsampledQuadtreeNode lowerLeft{ CesiumGeometry::QuadtreeTileID(1, 0, 0)}; - CesiumGeometry::UpsampledQuadtreeNode upperLeft{ - CesiumGeometry::QuadtreeTileID(1, 0, 1)}; - CesiumGeometry::UpsampledQuadtreeNode lowerRight{ - CesiumGeometry::QuadtreeTileID(1, 1, 0)}; - CesiumGeometry::UpsampledQuadtreeNode upperRight{ - CesiumGeometry::QuadtreeTileID(1, 1, 1)}; Model upsampledModel = *upsampleGltfForRasterOverlays(model, lowerLeft); From d7f58dab33889884bda5202ea0c54be715618e54 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Mon, 22 May 2023 11:58:37 -0400 Subject: [PATCH 029/421] Rewrite how vecN / matN are handled --- .../StructuralMetadataPropertyTableView.h | 177 +++++++++++++++--- 1 file changed, 146 insertions(+), 31 deletions(-) diff --git a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTableView.h b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTableView.h index 372b0a87c..155d758fd 100644 --- a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTableView.h +++ b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTableView.h @@ -125,12 +125,14 @@ class MetadataPropertyTableView { getVecNPropertyViewImpl( propertyName, *pClassProperty, + type, componentType, std::forward(callback)); } else if (isPropertyTypeMatN(type)) { getMatNPropertyViewImpl( propertyName, *pClassProperty, + type, componentType, std::forward(callback)); } else { @@ -268,18 +270,12 @@ class MetadataPropertyTableView { } } - template + template void getVecNArrayPropertyViewImpl( const std::string& propertyName, const ExtensionExtStructuralMetadataClassProperty& classProperty, - PropertyType type, PropertyComponentType componentType, Callback&& callback) const { - glm::length_t N = getDimensionsFromType(type); - if (N <= 1) { - return; - } - switch (componentType) { case PropertyComponentType::Int8: callback( @@ -357,17 +353,46 @@ class MetadataPropertyTableView { } template - void getMatNArrayPropertyViewImpl( + void getVecNArrayPropertyViewImpl( const std::string& propertyName, const ExtensionExtStructuralMetadataClassProperty& classProperty, PropertyType type, PropertyComponentType componentType, Callback&& callback) const { - const glm::length_t N = getDimensionsFromType(type); - if (N <= 1) { - return; + glm::length_t N = getDimensionsFromType(type); + switch (N) { + case 2: + getVecNArrayPropertyViewImpl( + propertyName, + classProperty, + componentType, + callback); + break; + case 3: + getVecNArrayPropertyViewImpl( + propertyName, + classProperty, + componentType, + callback); + break; + case 4: + getVecNArrayPropertyViewImpl( + propertyName, + classProperty, + componentType, + callback); + break; + default: + break; } + } + template + void getMatNArrayPropertyViewImpl( + const std::string& propertyName, + const ExtensionExtStructuralMetadataClassProperty& classProperty, + PropertyComponentType componentType, + Callback&& callback) const { switch (componentType) { case PropertyComponentType::Int8: callback( @@ -444,6 +469,41 @@ class MetadataPropertyTableView { } } + template + void getMatNArrayPropertyViewImpl( + const std::string& propertyName, + const ExtensionExtStructuralMetadataClassProperty& classProperty, + PropertyType type, + PropertyComponentType componentType, + Callback&& callback) const { + const glm::length_t N = getDimensionsFromType(type); + switch (N) { + case 2: + getMatNArrayPropertyViewImpl( + propertyName, + classProperty, + componentType, + callback); + break; + case 3: + getMatNArrayPropertyViewImpl( + propertyName, + classProperty, + componentType, + callback); + break; + case 4: + getMatNArrayPropertyViewImpl( + propertyName, + classProperty, + componentType, + callback); + break; + default: + break; + } + } + template void getArrayPropertyViewImpl( const std::string& propertyName, @@ -488,19 +548,12 @@ class MetadataPropertyTableView { } } - template + template void getVecNPropertyViewImpl( const std::string& propertyName, const ExtensionExtStructuralMetadataClassProperty& classProperty, - const ExtensionExtStructuralMetadataPropertyTableProperty& - propertyTableProperty, - PropertyType type, PropertyComponentType componentType, - Callback&& callback) { - const glm::length_t N = getDimensionsFromType(type); - if (N <= 1) { - return; - } + Callback&& callback) const { switch (componentType) { case PropertyComponentType::Int8: @@ -577,25 +630,53 @@ class MetadataPropertyTableView { } template - void getMatNPropertyViewImpl( + void getVecNPropertyViewImpl( const std::string& propertyName, const ExtensionExtStructuralMetadataClassProperty& classProperty, - const ExtensionExtStructuralMetadataPropertyTableProperty& - propertyTableProperty, PropertyType type, PropertyComponentType componentType, - Callback&& callback) { - glm::length_t N = getDimensionsFromType(type); - if (N <= 1) { - return; + Callback&& callback) const { + const glm::length_t N = getDimensionsFromType(type); + switch (N) { + case 2: + getVecNPropertyViewImpl( + propertyName, + classProperty, + componentType, + callback); + break; + case 3: + getVecNPropertyViewImpl( + propertyName, + classProperty, + componentType, + callback); + break; + case 4: + getVecNPropertyViewImpl( + propertyName, + classProperty, + componentType, + callback); + break; + default: + break; } + } + template + void getMatNPropertyViewImpl( + const std::string& propertyName, + const ExtensionExtStructuralMetadataClassProperty& classProperty, + PropertyComponentType componentType, + Callback&& callback) const { switch (componentType) { case PropertyComponentType::Int8: callback( propertyName, - getPropertyViewImpl < - glm::mat(propertyName, classProperty)); + getPropertyViewImpl>( + propertyName, + classProperty)); break; case PropertyComponentType::Uint8: callback( @@ -665,11 +746,45 @@ class MetadataPropertyTableView { } } + template + void getMatNPropertyViewImpl( + const std::string& propertyName, + const ExtensionExtStructuralMetadataClassProperty& classProperty, + PropertyType type, + PropertyComponentType componentType, + Callback&& callback) const { + glm::length_t N = getDimensionsFromType(type); + switch (N) { + case 2: + getMatNPropertyViewImpl( + propertyName, + classProperty, + componentType, + callback); + break; + case 3: + getMatNPropertyViewImpl( + propertyName, + classProperty, + componentType, + callback); + break; + case 4: + getMatNPropertyViewImpl( + propertyName, + classProperty, + componentType, + callback); + break; + default: + break; + } + } + template void getPrimitivePropertyViewImpl( + const std::string& propertyName, const ExtensionExtStructuralMetadataClassProperty& classProperty, - const ExtensionExtStructuralMetadataPropertyTableProperty& - propertyTableProperty, PropertyType type, PropertyComponentType componentType, Callback&& callback) const { From efb95c565990a095672ac8047ee5da7c12b989db Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Mon, 22 May 2023 15:29:14 -0400 Subject: [PATCH 030/421] Verify that callbacks work with unit tests --- .../StructuralMetadataPropertyTableView.h | 26 +- ...estStructuralMetadataPropertyTableView.cpp | 471 ++++++++++++++++++ 2 files changed, 484 insertions(+), 13 deletions(-) diff --git a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTableView.h b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTableView.h index 155d758fd..4a7664c40 100644 --- a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTableView.h +++ b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTableView.h @@ -366,21 +366,21 @@ class MetadataPropertyTableView { propertyName, classProperty, componentType, - callback); + std::forward(callback)); break; case 3: getVecNArrayPropertyViewImpl( propertyName, classProperty, componentType, - callback); + std::forward(callback)); break; case 4: getVecNArrayPropertyViewImpl( propertyName, classProperty, componentType, - callback); + std::forward(callback)); break; default: break; @@ -483,21 +483,21 @@ class MetadataPropertyTableView { propertyName, classProperty, componentType, - callback); + std::forward(callback)); break; case 3: getMatNArrayPropertyViewImpl( propertyName, classProperty, componentType, - callback); + std::forward(callback)); break; case 4: getMatNArrayPropertyViewImpl( propertyName, classProperty, componentType, - callback); + std::forward(callback)); break; default: break; @@ -643,21 +643,21 @@ class MetadataPropertyTableView { propertyName, classProperty, componentType, - callback); + std::forward(callback)); break; case 3: getVecNPropertyViewImpl( propertyName, classProperty, componentType, - callback); + std::forward(callback)); break; case 4: getVecNPropertyViewImpl( propertyName, classProperty, componentType, - callback); + std::forward(callback)); break; default: break; @@ -723,7 +723,7 @@ class MetadataPropertyTableView { case PropertyComponentType::Uint64: callback( propertyName, - getPropertyViewImp>( + getPropertyViewImpl>( propertyName, classProperty)); break; @@ -760,21 +760,21 @@ class MetadataPropertyTableView { propertyName, classProperty, componentType, - callback); + std::forward(callback)); break; case 3: getMatNPropertyViewImpl( propertyName, classProperty, componentType, - callback); + std::forward(callback)); break; case 4: getMatNPropertyViewImpl( propertyName, classProperty, componentType, - callback); + std::forward(callback)); break; default: break; diff --git a/CesiumGltf/test/TestStructuralMetadataPropertyTableView.cpp b/CesiumGltf/test/TestStructuralMetadataPropertyTableView.cpp index e948a5dc6..f0303449a 100644 --- a/CesiumGltf/test/TestStructuralMetadataPropertyTableView.cpp +++ b/CesiumGltf/test/TestStructuralMetadataPropertyTableView.cpp @@ -2589,3 +2589,474 @@ TEST_CASE("Test StructuralMetadata variable-length arrays of strings") { MetadataPropertyViewStatus::ErrorArrayCountAndOffsetBufferCoexist); } } + +TEST_CASE("Test StructuralMetadata callback for scalar property") { + Model model; + std::vector values = {12, 34, 30, 11, 34, 34, 11, 33, 122, 33}; + + size_t valueBufferIndex = 0; + size_t valueBufferViewIndex = 0; + + // Buffers are constructed in scope to ensure that the tests don't use their + // temporary variables. + { + Buffer& valueBuffer = model.buffers.emplace_back(); + valueBuffer.cesium.data.resize(values.size() * sizeof(uint32_t)); + valueBuffer.byteLength = + static_cast(valueBuffer.cesium.data.size()); + std::memcpy( + valueBuffer.cesium.data.data(), + values.data(), + valueBuffer.cesium.data.size()); + valueBufferIndex = model.buffers.size() - 1; + + BufferView& valueBufferView = model.bufferViews.emplace_back(); + valueBufferView.buffer = static_cast(model.buffers.size() - 1); + valueBufferView.byteOffset = 0; + valueBufferView.byteLength = valueBuffer.byteLength; + valueBufferViewIndex = model.bufferViews.size() - 1; + } + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); + ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; + ExtensionExtStructuralMetadataClassProperty& testClassProperty = + testClass.properties["TestClassProperty"]; + testClassProperty.type = + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR; + testClassProperty.componentType = + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT32; + + ExtensionExtStructuralMetadataPropertyTable& propertyTable = + metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast(values.size()); + + ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = static_cast(valueBufferViewIndex); + + MetadataPropertyTableView view(&model, &propertyTable); + const ExtensionExtStructuralMetadataClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE( + classProperty->type == + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR); + REQUIRE( + classProperty->componentType == + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT32); + REQUIRE(!classProperty->array); + REQUIRE(classProperty->count == std::nullopt); + + view.getPropertyView( + "TestClassProperty", + [&values]( + const std::string& /*propertyName*/, + auto propertyValue) mutable { + REQUIRE(propertyValue.status() == MetadataPropertyViewStatus::Valid); + REQUIRE(propertyValue.size() > 0); + + if constexpr (std::is_same_v< + MetadataPropertyView, + decltype(propertyValue)>) { + for (int64_t i = 0; i < propertyValue.size(); ++i) { + REQUIRE( + static_cast(propertyValue.get(i)) == + values[static_cast(i)]); + } + } else { + FAIL("getPropertyView returned MetadataPropertyView of incorrect " + "type for TestClassProperty."); + } + }); +} + +TEST_CASE("Test StructuralMetadata callback for vecN property") { + Model model; + + std::vector values = { + glm::ivec3(-12, 34, 30), + glm::ivec3(11, 73, 0), + glm::ivec3(-2, 6, 12), + glm::ivec3(-4, 8, -13)}; + + size_t valueBufferIndex = 0; + size_t valueBufferViewIndex = 0; + + // Buffers are constructed in scope to ensure that the tests don't use their + // temporary variables. + { + Buffer& valueBuffer = model.buffers.emplace_back(); + valueBuffer.cesium.data.resize(values.size() * sizeof(glm::ivec3)); + valueBuffer.byteLength = + static_cast(valueBuffer.cesium.data.size()); + std::memcpy( + valueBuffer.cesium.data.data(), + values.data(), + valueBuffer.cesium.data.size()); + valueBufferIndex = model.buffers.size() - 1; + + BufferView& valueBufferView = model.bufferViews.emplace_back(); + valueBufferView.buffer = static_cast(model.buffers.size() - 1); + valueBufferView.byteOffset = 0; + valueBufferView.byteLength = valueBuffer.byteLength; + valueBufferViewIndex = model.bufferViews.size() - 1; + } + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); + ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; + ExtensionExtStructuralMetadataClassProperty& testClassProperty = + testClass.properties["TestClassProperty"]; + testClassProperty.type = + ExtensionExtStructuralMetadataClassProperty::Type::VEC3; + testClassProperty.componentType = + ExtensionExtStructuralMetadataClassProperty::ComponentType::INT32; + + ExtensionExtStructuralMetadataPropertyTable& propertyTable = + metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast(values.size()); + + ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = static_cast(valueBufferViewIndex); + + MetadataPropertyTableView view(&model, &propertyTable); + const ExtensionExtStructuralMetadataClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE( + classProperty->type == + ExtensionExtStructuralMetadataClassProperty::Type::VEC3); + REQUIRE( + classProperty->componentType == + ExtensionExtStructuralMetadataClassProperty::ComponentType::INT32); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->array); + + view.getPropertyView( + "TestClassProperty", + [&values]( + const std::string& /*propertyName*/, + auto propertyValue) mutable { + REQUIRE(propertyValue.status() == MetadataPropertyViewStatus::Valid); + REQUIRE(propertyValue.size() > 0); + + if constexpr (std::is_same_v< + MetadataPropertyView, + decltype(propertyValue)>) { + for (int64_t i = 0; i < propertyValue.size(); ++i) { + REQUIRE( + static_cast(propertyValue.get(i)) == + values[static_cast(i)]); + } + } else { + FAIL("getPropertyView returned MetadataPropertyView of incorrect " + "type for TestClassProperty."); + } + }); +} + +TEST_CASE("Test StructuralMetadata callback for matN property") { + Model model; + + // clang-format off + std::vector values = { + glm::u32mat2x2( + 12, 34, + 30, 1), + glm::u32mat2x2( + 11, 8, + 73, 102), + glm::u32mat2x2( + 1, 0, + 63, 2), + glm::u32mat2x2( + 4, 8, + 3, 23)}; + // clang-format on + + size_t valueBufferViewIndex = 0; + + // Buffers are constructed in scope to ensure that the tests don't use their + // temporary variables. + { + Buffer& valueBuffer = model.buffers.emplace_back(); + valueBuffer.cesium.data.resize(values.size() * sizeof(glm::u32mat2x2)); + valueBuffer.byteLength = + static_cast(valueBuffer.cesium.data.size()); + std::memcpy( + valueBuffer.cesium.data.data(), + values.data(), + valueBuffer.cesium.data.size()); + + BufferView& valueBufferView = model.bufferViews.emplace_back(); + valueBufferView.buffer = static_cast(model.buffers.size() - 1); + valueBufferView.byteOffset = 0; + valueBufferView.byteLength = valueBuffer.byteLength; + valueBufferViewIndex = model.bufferViews.size() - 1; + } + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); + ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; + ExtensionExtStructuralMetadataClassProperty& testClassProperty = + testClass.properties["TestClassProperty"]; + testClassProperty.type = + ExtensionExtStructuralMetadataClassProperty::Type::MAT2; + testClassProperty.componentType = + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT32; + + ExtensionExtStructuralMetadataPropertyTable& propertyTable = + metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast(values.size()); + + ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = static_cast(valueBufferViewIndex); + + MetadataPropertyTableView view(&model, &propertyTable); + const ExtensionExtStructuralMetadataClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE( + classProperty->type == + ExtensionExtStructuralMetadataClassProperty::Type::MAT2); + REQUIRE( + classProperty->componentType == + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT32); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->array); + + view.getPropertyView( + "TestClassProperty", + [&values]( + const std::string& /*propertyName*/, + auto propertyValue) mutable { + REQUIRE(propertyValue.status() == MetadataPropertyViewStatus::Valid); + REQUIRE(propertyValue.size() > 0); + + if constexpr (std::is_same_v< + MetadataPropertyView, + decltype(propertyValue)>) { + for (int64_t i = 0; i < propertyValue.size(); ++i) { + REQUIRE( + static_cast(propertyValue.get(i)) == + values[static_cast(i)]); + } + } else { + FAIL("getPropertyView returned MetadataPropertyView of incorrect " + "type for TestClassProperty."); + } + }); +} + +TEST_CASE("Test StructuralMetadata callback for boolean property") { + Model model; + + int64_t instanceCount = 21; + std::vector expected; + std::vector values; + values.resize(3); + for (int64_t i = 0; i < instanceCount; ++i) { + if (i % 2 == 0) { + expected.emplace_back(true); + } else { + expected.emplace_back(false); + } + + uint8_t expectedValue = expected.back(); + int64_t byteIndex = i / 8; + int64_t bitIndex = i % 8; + values[static_cast(byteIndex)] = static_cast( + (expectedValue << bitIndex) | values[static_cast(byteIndex)]); + } + + // Buffers are constructed in scope to ensure that the tests don't use their + // temporary variables. + { + Buffer& valueBuffer = model.buffers.emplace_back(); + valueBuffer.cesium.data.resize(values.size()); + valueBuffer.byteLength = + static_cast(valueBuffer.cesium.data.size()); + std::memcpy( + valueBuffer.cesium.data.data(), + values.data(), + valueBuffer.cesium.data.size()); + + BufferView& valueBufferView = model.bufferViews.emplace_back(); + valueBufferView.buffer = static_cast(model.buffers.size() - 1); + valueBufferView.byteOffset = 0; + valueBufferView.byteLength = valueBuffer.byteLength; + } + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); + ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; + ExtensionExtStructuralMetadataClassProperty& testClassProperty = + testClass.properties["TestClassProperty"]; + testClassProperty.type = + ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN; + + ExtensionExtStructuralMetadataPropertyTable& propertyTable = + metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast(instanceCount); + + ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = + static_cast(model.bufferViews.size() - 1); + + MetadataPropertyTableView view(&model, &propertyTable); + const ExtensionExtStructuralMetadataClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE( + classProperty->type == + ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN); + REQUIRE(classProperty->componentType == std::nullopt); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->array); + + view.getPropertyView( + "TestClassProperty", + [&expected]( + const std::string& /*propertyName*/, + auto propertyValue) mutable { + REQUIRE(propertyValue.status() == MetadataPropertyViewStatus::Valid); + REQUIRE(propertyValue.size() > 0); + + if constexpr (std::is_same_v< + MetadataPropertyView, + decltype(propertyValue)>) { + for (int64_t i = 0; i < propertyValue.size(); ++i) { + REQUIRE( + static_cast(propertyValue.get(i)) == + expected[static_cast(i)]); + } + } else { + FAIL("getPropertyView returned MetadataPropertyView of incorrect " + "type for TestClassProperty."); + } + }); +} + +TEST_CASE("Test StructuralMetadata callback for string property") { + Model model; + + std::vector expected{"What's up", "Test_0", "Test_1", "", "Hi"}; + size_t totalBytes = 0; + for (const std::string& expectedValue : expected) { + totalBytes += expectedValue.size(); + } + + std::vector stringOffsets( + (expected.size() + 1) * sizeof(uint32_t)); + std::vector values(totalBytes); + uint32_t* offsetValue = reinterpret_cast(stringOffsets.data()); + for (size_t i = 0; i < expected.size(); ++i) { + const std::string& expectedValue = expected[i]; + std::memcpy( + values.data() + offsetValue[i], + expectedValue.c_str(), + expectedValue.size()); + offsetValue[i + 1] = + offsetValue[i] + static_cast(expectedValue.size()); + } + + // Buffers are constructed in scope to ensure that the tests don't use their + // temporary variables. + size_t valueBufferIndex = 0; + size_t valueBufferViewIndex = 0; + { + Buffer& valueBuffer = model.buffers.emplace_back(); + valueBuffer.byteLength = static_cast(values.size()); + valueBuffer.cesium.data = std::move(values); + valueBufferIndex = model.buffers.size() - 1; + + BufferView& valueBufferView = model.bufferViews.emplace_back(); + valueBufferView.buffer = static_cast(valueBufferIndex); + valueBufferView.byteOffset = 0; + valueBufferView.byteLength = valueBuffer.byteLength; + valueBufferViewIndex = model.bufferViews.size() - 1; + } + + size_t offsetBufferIndex = 0; + size_t offsetBufferViewIndex = 0; + { + Buffer& offsetBuffer = model.buffers.emplace_back(); + offsetBuffer.byteLength = static_cast(stringOffsets.size()); + offsetBuffer.cesium.data = std::move(stringOffsets); + offsetBufferIndex = model.buffers.size() - 1; + + BufferView& offsetBufferView = model.bufferViews.emplace_back(); + offsetBufferView.buffer = static_cast(offsetBufferIndex); + offsetBufferView.byteOffset = 0; + offsetBufferView.byteLength = offsetBuffer.byteLength; + offsetBufferViewIndex = model.bufferViews.size() - 1; + } + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); + ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; + ExtensionExtStructuralMetadataClassProperty& testClassProperty = + testClass.properties["TestClassProperty"]; + testClassProperty.type = + ExtensionExtStructuralMetadataClassProperty::Type::STRING; + + ExtensionExtStructuralMetadataPropertyTable& propertyTable = + metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast(expected.size()); + + ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.stringOffsetType = + ExtensionExtStructuralMetadataPropertyTableProperty::StringOffsetType:: + UINT32; + propertyTableProperty.values = static_cast(valueBufferViewIndex); + propertyTableProperty.stringOffsets = + static_cast(offsetBufferViewIndex); + + MetadataPropertyTableView view(&model, &propertyTable); + const ExtensionExtStructuralMetadataClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE( + classProperty->type == + ExtensionExtStructuralMetadataClassProperty::Type::STRING); + REQUIRE(classProperty->componentType == std::nullopt); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->array); + + view.getPropertyView( + "TestClassProperty", + [&expected]( + const std::string& /*propertyName*/, + auto propertyValue) mutable { + REQUIRE(propertyValue.status() == MetadataPropertyViewStatus::Valid); + REQUIRE(propertyValue.size() > 0); + + if constexpr (std::is_same_v< + MetadataPropertyView, + decltype(propertyValue)>) { + for (int64_t i = 0; i < propertyValue.size(); ++i) { + REQUIRE( + static_cast(propertyValue.get(i)) == + expected[static_cast(i)]); + } + } else { + FAIL("getPropertyView returned MetadataPropertyView of incorrect " + "type for TestClassProperty."); + } + }); +} From 3dd83ab8d05fedb633d2f42eb58b523ee9470f80 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Mon, 22 May 2023 17:26:11 -0400 Subject: [PATCH 031/421] Add callback tests --- .../StructuralMetadataPropertyTableView.h | 197 +++++++++++------- ...estStructuralMetadataPropertyTableView.cpp | 140 ++++++++++++- 2 files changed, 252 insertions(+), 85 deletions(-) diff --git a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTableView.h b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTableView.h index 4a7664c40..7caf8282c 100644 --- a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTableView.h +++ b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTableView.h @@ -81,7 +81,7 @@ class MetadataPropertyTableView { /** * @brief Gets a MetadataPropertyView through a callback that accepts a - * property name and a std::optional> to view the data + * property name and a MetadataPropertyView to view the data * of a property stored in the ExtensionExtStructuralMetadataPropertyTable. * * This method will validate the EXT_structural_metadata format to ensure @@ -90,13 +90,13 @@ class MetadataPropertyTableView { * uint64_t, int64_t, float, double), a glm vecN composed of one of the scalar * types, a glm matN composed of one of the scalar types, bool, * std::string_view, or MetadataArrayView with T as one of the - * aforementioned types. If the property is invalid, std::nullopt will be - * passed to the callback. Otherwise, a valid property view will be passed to - * the callback. + * aforementioned types. If the property is invalid, an empty + * MetadataPropertyView with an error status code will be passed to the + * callback. Otherwise, a valid property view will be passed to the callback. * * @param propertyName The name of the property to retrieve data from * @tparam callback A callback function that accepts property name and - * std::optional> + * MetadataPropertyView */ template void @@ -121,6 +121,12 @@ class MetadataPropertyTableView { type, componentType, std::forward(callback)); + } else if (type == PropertyType::Scalar) { + getScalarPropertyViewImpl( + propertyName, + *pClassProperty, + componentType, + std::forward(callback)); } else if (isPropertyTypeVecN(type)) { getVecNPropertyViewImpl( propertyName, @@ -135,20 +141,26 @@ class MetadataPropertyTableView { type, componentType, std::forward(callback)); + } else if (type == PropertyType::String) { + callback( + propertyName, + getPropertyViewImpl(propertyName, *pClassProperty)); + } else if (type == PropertyType::Boolean) { + callback( + propertyName, + getPropertyViewImpl(propertyName, *pClassProperty)); } else { - getPrimitivePropertyViewImpl( + callback( propertyName, - *pClassProperty, - type, - componentType, - std::forward(callback)); + createInvalidPropertyView( + MetadataPropertyViewStatus::ErrorTypeMismatch)); } } /** * @brief Iterates over each property in the * ExtensionExtStructuralMetadataPropertyTable with a callback that accepts a - * property name and a std::optional> to view the data + * property name and a MetadataPropertyView to view the data * stored in the ExtensionExtStructuralMetadataPropertyTableProperty. * * This method will validate the EXT_structural_metadata format to ensure @@ -157,13 +169,14 @@ class MetadataPropertyTableView { * uint64_t, int64_t, float, double), a glm vecN composed of one of the scalar * types, a glm matN composed of one of the scalar types, bool, * std::string_view, or MetadataArrayView with T as one of the - * aforementioned types. If the property is invalid, std::nullopt will be - * passed to the callback. Otherwise, a valid property view will be passed to + * aforementioned types. If the property is invalid, an empty + * MetadataPropertyView with an error status code will be passed to the + * callback. Otherwise, a valid property view will be passed to * the callback. * * @param propertyName The name of the property to retrieve data from * @tparam callback A callback function that accepts property name and - * std::optional> + * MetadataPropertyView */ template void forEachProperty(Callback&& callback) const { for (const auto& property : this->_pClass->properties) { @@ -266,6 +279,10 @@ class MetadataPropertyTableView { classProperty)); break; default: + callback( + propertyName, + createInvalidPropertyView( + MetadataPropertyViewStatus::ErrorComponentTypeMismatch)); break; } } @@ -348,6 +365,10 @@ class MetadataPropertyTableView { classProperty)); break; default: + callback( + propertyName, + createInvalidPropertyView( + MetadataPropertyViewStatus::ErrorComponentTypeMismatch)); break; } } @@ -383,6 +404,10 @@ class MetadataPropertyTableView { std::forward(callback)); break; default: + callback( + propertyName, + createInvalidPropertyView( + MetadataPropertyViewStatus::ErrorTypeMismatch)); break; } } @@ -465,6 +490,10 @@ class MetadataPropertyTableView { classProperty)); break; default: + callback( + propertyName, + createInvalidPropertyView( + MetadataPropertyViewStatus::ErrorComponentTypeMismatch)); break; } } @@ -500,6 +529,10 @@ class MetadataPropertyTableView { std::forward(callback)); break; default: + callback( + propertyName, + createInvalidPropertyView( + MetadataPropertyViewStatus::ErrorTypeMismatch)); break; } } @@ -545,6 +578,11 @@ class MetadataPropertyTableView { getPropertyViewImpl>( propertyName, classProperty)); + } else { + callback( + propertyName, + createInvalidPropertyView( + MetadataPropertyViewStatus::ErrorTypeMismatch)); } } @@ -625,6 +663,10 @@ class MetadataPropertyTableView { classProperty)); break; default: + callback( + propertyName, + createInvalidPropertyView( + MetadataPropertyViewStatus::ErrorComponentTypeMismatch)); break; } } @@ -660,6 +702,10 @@ class MetadataPropertyTableView { std::forward(callback)); break; default: + callback( + propertyName, + createInvalidPropertyView( + MetadataPropertyViewStatus::ErrorTypeMismatch)); break; } } @@ -742,6 +788,10 @@ class MetadataPropertyTableView { classProperty)); break; default: + callback( + propertyName, + createInvalidPropertyView( + MetadataPropertyViewStatus::ErrorComponentTypeMismatch)); break; } } @@ -777,80 +827,77 @@ class MetadataPropertyTableView { std::forward(callback)); break; default: + callback( + propertyName, + createInvalidPropertyView( + MetadataPropertyViewStatus::ErrorTypeMismatch)); break; } } template - void getPrimitivePropertyViewImpl( + void getScalarPropertyViewImpl( const std::string& propertyName, const ExtensionExtStructuralMetadataClassProperty& classProperty, - PropertyType type, PropertyComponentType componentType, Callback&& callback) const { - if (type == PropertyType::Scalar) { - switch (componentType) { - case PropertyComponentType::Int8: - callback( - propertyName, - getPropertyViewImpl(propertyName, classProperty)); - break; - case PropertyComponentType::Uint8: - callback( - propertyName, - getPropertyViewImpl(propertyName, classProperty)); - break; - case PropertyComponentType::Int16: - callback( - propertyName, - getPropertyViewImpl(propertyName, classProperty)); - break; - case PropertyComponentType::Uint16: - callback( - propertyName, - getPropertyViewImpl(propertyName, classProperty)); - break; - case PropertyComponentType::Int32: - callback( - propertyName, - getPropertyViewImpl(propertyName, classProperty)); - break; - case PropertyComponentType::Uint32: - callback( - propertyName, - getPropertyViewImpl(propertyName, classProperty)); - break; - case PropertyComponentType::Int64: - callback( - propertyName, - getPropertyViewImpl(propertyName, classProperty)); - break; - case PropertyComponentType::Uint64: - callback( - propertyName, - getPropertyViewImpl(propertyName, classProperty)); - break; - case PropertyComponentType::Float32: - callback( - propertyName, - getPropertyViewImpl(propertyName, classProperty)); - break; - case PropertyComponentType::Float64: - callback( - propertyName, - getPropertyViewImpl(propertyName, classProperty)); - break; - default: - break; - } - } else if (type == PropertyType::String) { + switch (componentType) { + case PropertyComponentType::Int8: callback( propertyName, - getPropertyViewImpl(propertyName, classProperty)); - } else if (type == PropertyType::Boolean) { + getPropertyViewImpl(propertyName, classProperty)); + return; + case PropertyComponentType::Uint8: callback( propertyName, - getPropertyViewImpl(propertyName, classProperty)); + getPropertyViewImpl(propertyName, classProperty)); + return; + case PropertyComponentType::Int16: + callback( + propertyName, + getPropertyViewImpl(propertyName, classProperty)); + return; + case PropertyComponentType::Uint16: + callback( + propertyName, + getPropertyViewImpl(propertyName, classProperty)); + break; + case PropertyComponentType::Int32: + callback( + propertyName, + getPropertyViewImpl(propertyName, classProperty)); + break; + case PropertyComponentType::Uint32: + callback( + propertyName, + getPropertyViewImpl(propertyName, classProperty)); + break; + case PropertyComponentType::Int64: + callback( + propertyName, + getPropertyViewImpl(propertyName, classProperty)); + break; + case PropertyComponentType::Uint64: + callback( + propertyName, + getPropertyViewImpl(propertyName, classProperty)); + break; + case PropertyComponentType::Float32: + callback( + propertyName, + getPropertyViewImpl(propertyName, classProperty)); + break; + case PropertyComponentType::Float64: + callback( + propertyName, + getPropertyViewImpl(propertyName, classProperty)); + break; + default: + callback( + propertyName, + createInvalidPropertyView( + MetadataPropertyViewStatus::ErrorComponentTypeMismatch)); + break; } } diff --git a/CesiumGltf/test/TestStructuralMetadataPropertyTableView.cpp b/CesiumGltf/test/TestStructuralMetadataPropertyTableView.cpp index f0303449a..28e7fc7ee 100644 --- a/CesiumGltf/test/TestStructuralMetadataPropertyTableView.cpp +++ b/CesiumGltf/test/TestStructuralMetadataPropertyTableView.cpp @@ -250,7 +250,7 @@ TEST_CASE("Test StructuralMetadata vecN property") { REQUIRE(ivec3Property.size() > 0); for (int64_t i = 0; i < ivec3Property.size(); ++i) { - REQUIRE(ivec3Property.get(i) == values[i]); + REQUIRE(ivec3Property.get(i) == values[static_cast(i)]); } } @@ -441,7 +441,7 @@ TEST_CASE("Test StructuralMetadata matN property") { REQUIRE(u32mat2x2Property.size() > 0); for (int64_t i = 0; i < u32mat2x2Property.size(); ++i) { - REQUIRE(u32mat2x2Property.get(i) == values[i]); + REQUIRE(u32mat2x2Property.get(i) == values[static_cast(i)]); } } @@ -2590,11 +2590,51 @@ TEST_CASE("Test StructuralMetadata variable-length arrays of strings") { } } +TEST_CASE("Test StructuralMetadata callback for invalid property") { + Model model; + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); + ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; + ExtensionExtStructuralMetadataClassProperty& testClassProperty = + testClass.properties["InvalidProperty"]; + testClassProperty.type = + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR; + testClassProperty.componentType = + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT32; + + ExtensionExtStructuralMetadataPropertyTable& propertyTable = + metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast(5); + + ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + propertyTable.properties["InvalidProperty"]; + propertyTableProperty.values = static_cast(-1); + + MetadataPropertyTableView view(&model, &propertyTable); + const ExtensionExtStructuralMetadataClassProperty* classProperty = + view.getClassProperty("InvalidProperty"); + REQUIRE(classProperty); + + classProperty = view.getClassProperty("NonexistentProperty"); + REQUIRE(!classProperty); + + auto testCallback = [](const std::string& /*propertyName*/, + auto propertyValue) mutable { + REQUIRE(propertyValue.status() != MetadataPropertyViewStatus::Valid); + REQUIRE(propertyValue.size() == 0); + }; + + view.getPropertyView("InvalidProperty", testCallback); + view.getPropertyView("NonexistentProperty", testCallback); +} + TEST_CASE("Test StructuralMetadata callback for scalar property") { Model model; std::vector values = {12, 34, 30, 11, 34, 34, 11, 33, 122, 33}; - size_t valueBufferIndex = 0; size_t valueBufferViewIndex = 0; // Buffers are constructed in scope to ensure that the tests don't use their @@ -2608,7 +2648,6 @@ TEST_CASE("Test StructuralMetadata callback for scalar property") { valueBuffer.cesium.data.data(), values.data(), valueBuffer.cesium.data.size()); - valueBufferIndex = model.buffers.size() - 1; BufferView& valueBufferView = model.bufferViews.emplace_back(); valueBufferView.buffer = static_cast(model.buffers.size() - 1); @@ -2655,12 +2694,12 @@ TEST_CASE("Test StructuralMetadata callback for scalar property") { [&values]( const std::string& /*propertyName*/, auto propertyValue) mutable { - REQUIRE(propertyValue.status() == MetadataPropertyViewStatus::Valid); - REQUIRE(propertyValue.size() > 0); - if constexpr (std::is_same_v< MetadataPropertyView, decltype(propertyValue)>) { + REQUIRE(propertyValue.status() == MetadataPropertyViewStatus::Valid); + REQUIRE(propertyValue.size() > 0); + for (int64_t i = 0; i < propertyValue.size(); ++i) { REQUIRE( static_cast(propertyValue.get(i)) == @@ -2682,7 +2721,6 @@ TEST_CASE("Test StructuralMetadata callback for vecN property") { glm::ivec3(-2, 6, 12), glm::ivec3(-4, 8, -13)}; - size_t valueBufferIndex = 0; size_t valueBufferViewIndex = 0; // Buffers are constructed in scope to ensure that the tests don't use their @@ -2696,7 +2734,6 @@ TEST_CASE("Test StructuralMetadata callback for vecN property") { valueBuffer.cesium.data.data(), values.data(), valueBuffer.cesium.data.size()); - valueBufferIndex = model.buffers.size() - 1; BufferView& valueBufferView = model.bufferViews.emplace_back(); valueBufferView.buffer = static_cast(model.buffers.size() - 1); @@ -3038,7 +3075,7 @@ TEST_CASE("Test StructuralMetadata callback for string property") { REQUIRE(classProperty->count == std::nullopt); REQUIRE(!classProperty->array); - view.getPropertyView( + view.getPropertyView( "TestClassProperty", [&expected]( const std::string& /*propertyName*/, @@ -3060,3 +3097,86 @@ TEST_CASE("Test StructuralMetadata callback for string property") { } }); } + +TEST_CASE("Test StructuralMetadata callback for scalar array") { + Model model; + + std::vector values = + {12, 34, 30, 11, 34, 34, 11, 33, 122, 33, 223, 11}; + + size_t valueBufferViewIndex = 0; + { + Buffer& valueBuffer = model.buffers.emplace_back(); + valueBuffer.cesium.data.resize(values.size() * sizeof(uint32_t)); + valueBuffer.byteLength = + static_cast(valueBuffer.cesium.data.size()); + std::memcpy( + valueBuffer.cesium.data.data(), + values.data(), + valueBuffer.cesium.data.size()); + + BufferView& valueBufferView = model.bufferViews.emplace_back(); + valueBufferView.buffer = static_cast(model.buffers.size() - 1); + valueBufferView.byteOffset = 0; + valueBufferView.byteLength = valueBuffer.byteLength; + valueBufferViewIndex = model.bufferViews.size() - 1; + } + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); + ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; + ExtensionExtStructuralMetadataClassProperty& testClassProperty = + testClass.properties["TestClassProperty"]; + testClassProperty.type = + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR; + testClassProperty.componentType = + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT32; + testClassProperty.array = true; + testClassProperty.count = 3; + + ExtensionExtStructuralMetadataPropertyTable& propertyTable = + metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast( + values.size() / static_cast(testClassProperty.count.value())); + + ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = + static_cast(model.bufferViews.size() - 1); + + MetadataPropertyTableView view(&model, &propertyTable); + const ExtensionExtStructuralMetadataClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE( + classProperty->type == + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR); + REQUIRE( + classProperty->componentType == + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT32); + REQUIRE(classProperty->array); + REQUIRE(classProperty->count == 3); + + view.getPropertyView( + "TestClassProperty", + [&values](const std::string& /*propertyName*/, auto propertyValue) { + REQUIRE(propertyValue.status() == MetadataPropertyViewStatus::Valid); + REQUIRE(propertyValue.size() > 0); + + if constexpr (std::is_same_v< + MetadataPropertyView>, + decltype(propertyValue)>) { + for (int64_t i = 0; i < propertyValue.size(); ++i) { + MetadataArrayView member = propertyValue.get(i); + for (int64_t j = 0; j < member.size(); ++j) { + REQUIRE(member[j] == values[static_cast(i * 3 + j)]); + } + } + } else { + FAIL("getPropertyView returned MetadataPropertyView of incorrect " + "type for TestClassProperty."); + } + }); +} From 64b8378019d129bf7d1bce17c6010b5d010f389a Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Mon, 22 May 2023 17:46:34 -0400 Subject: [PATCH 032/421] Add tests for callback on array property --- ...estStructuralMetadataPropertyTableView.cpp | 417 ++++++++++++++++++ 1 file changed, 417 insertions(+) diff --git a/CesiumGltf/test/TestStructuralMetadataPropertyTableView.cpp b/CesiumGltf/test/TestStructuralMetadataPropertyTableView.cpp index 28e7fc7ee..122908c1b 100644 --- a/CesiumGltf/test/TestStructuralMetadataPropertyTableView.cpp +++ b/CesiumGltf/test/TestStructuralMetadataPropertyTableView.cpp @@ -3180,3 +3180,420 @@ TEST_CASE("Test StructuralMetadata callback for scalar array") { } }); } + +TEST_CASE("Test StructuralMetadata callback for vecN array") { + Model model; + + std::vector values = { + glm::ivec3(12, 34, -30), + glm::ivec3(-2, 0, 1), + glm::ivec3(1, 2, 8), + glm::ivec3(-100, 84, 6), + glm::ivec3(2, -2, -2), + glm::ivec3(40, 61, 3), + }; + + size_t valueBufferViewIndex = 0; + { + Buffer& valueBuffer = model.buffers.emplace_back(); + valueBuffer.cesium.data.resize(values.size() * sizeof(glm::ivec3)); + valueBuffer.byteLength = + static_cast(valueBuffer.cesium.data.size()); + std::memcpy( + valueBuffer.cesium.data.data(), + values.data(), + valueBuffer.cesium.data.size()); + + BufferView& valueBufferView = model.bufferViews.emplace_back(); + valueBufferView.buffer = static_cast(model.buffers.size() - 1); + valueBufferView.byteOffset = 0; + valueBufferView.byteLength = valueBuffer.byteLength; + valueBufferViewIndex = model.bufferViews.size() - 1; + } + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); + ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; + ExtensionExtStructuralMetadataClassProperty& testClassProperty = + testClass.properties["TestClassProperty"]; + testClassProperty.type = + ExtensionExtStructuralMetadataClassProperty::Type::VEC3; + testClassProperty.componentType = + ExtensionExtStructuralMetadataClassProperty::ComponentType::INT32; + testClassProperty.array = true; + testClassProperty.count = 2; + + ExtensionExtStructuralMetadataPropertyTable& propertyTable = + metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast( + values.size() / static_cast(testClassProperty.count.value())); + + ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = + static_cast(model.bufferViews.size() - 1); + + MetadataPropertyTableView view(&model, &propertyTable); + const ExtensionExtStructuralMetadataClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE( + classProperty->type == + ExtensionExtStructuralMetadataClassProperty::Type::VEC3); + REQUIRE( + classProperty->componentType == + ExtensionExtStructuralMetadataClassProperty::ComponentType::INT32); + REQUIRE(classProperty->array); + REQUIRE(classProperty->count == 2); + + view.getPropertyView( + "TestClassProperty", + [&values](const std::string& /*propertyName*/, auto propertyValue) { + REQUIRE(propertyValue.status() == MetadataPropertyViewStatus::Valid); + REQUIRE(propertyValue.size() > 0); + + if constexpr (std::is_same_v< + MetadataPropertyView>, + decltype(propertyValue)>) { + for (int64_t i = 0; i < propertyValue.size(); ++i) { + MetadataArrayView member = propertyValue.get(i); + for (int64_t j = 0; j < member.size(); ++j) { + REQUIRE(member[j] == values[static_cast(i * 2 + j)]); + } + } + } else { + FAIL("getPropertyView returned MetadataPropertyView of incorrect " + "type for TestClassProperty."); + } + }); +} + +TEST_CASE("Test StructuralMetadata callback for matN array") { + Model model; + + // clang-format off + std::vector values = { + glm::i32mat2x2( + 12, 34, + -30, 20), + glm::i32mat2x2( + -2, -2, + 0, 1), + glm::i32mat2x2( + 1, 2, + 8, 5), + glm::i32mat2x2( + -100, 3, + 84, 6), + glm::i32mat2x2( + 2, 12, + -2, -2), + glm::i32mat2x2( + 40, 61, + 7, -3), + }; + // clang-format on + + size_t valueBufferViewIndex = 0; + { + Buffer& valueBuffer = model.buffers.emplace_back(); + valueBuffer.cesium.data.resize(values.size() * sizeof(glm::i32mat2x2)); + valueBuffer.byteLength = + static_cast(valueBuffer.cesium.data.size()); + std::memcpy( + valueBuffer.cesium.data.data(), + values.data(), + valueBuffer.cesium.data.size()); + + BufferView& valueBufferView = model.bufferViews.emplace_back(); + valueBufferView.buffer = static_cast(model.buffers.size() - 1); + valueBufferView.byteOffset = 0; + valueBufferView.byteLength = valueBuffer.byteLength; + valueBufferViewIndex = model.bufferViews.size() - 1; + } + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); + ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; + ExtensionExtStructuralMetadataClassProperty& testClassProperty = + testClass.properties["TestClassProperty"]; + testClassProperty.type = + ExtensionExtStructuralMetadataClassProperty::Type::MAT2; + testClassProperty.componentType = + ExtensionExtStructuralMetadataClassProperty::ComponentType::INT32; + testClassProperty.array = true; + testClassProperty.count = 2; + + ExtensionExtStructuralMetadataPropertyTable& propertyTable = + metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast( + values.size() / static_cast(testClassProperty.count.value())); + + ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = + static_cast(model.bufferViews.size() - 1); + + MetadataPropertyTableView view(&model, &propertyTable); + const ExtensionExtStructuralMetadataClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE( + classProperty->type == + ExtensionExtStructuralMetadataClassProperty::Type::MAT2); + REQUIRE( + classProperty->componentType == + ExtensionExtStructuralMetadataClassProperty::ComponentType::INT32); + REQUIRE(classProperty->array); + REQUIRE(classProperty->count == 2); + + view.getPropertyView( + "TestClassProperty", + [&values](const std::string& /*propertyName*/, auto propertyValue) { + REQUIRE(propertyValue.status() == MetadataPropertyViewStatus::Valid); + REQUIRE(propertyValue.size() > 0); + + if constexpr (std::is_same_v< + MetadataPropertyView< + MetadataArrayView>, + decltype(propertyValue)>) { + for (int64_t i = 0; i < propertyValue.size(); ++i) { + MetadataArrayView member = propertyValue.get(i); + for (int64_t j = 0; j < member.size(); ++j) { + REQUIRE(member[j] == values[static_cast(i * 2 + j)]); + } + } + } else { + FAIL("getPropertyView returned MetadataPropertyView of incorrect " + "type for TestClassProperty."); + } + }); +} + +TEST_CASE("Test StructuralMetadata callback for boolean array") { + Model model; + + std::vector expected = { + true, + false, + false, + true, + false, + false, + true, + true, + true, + false, + false, + true}; + std::vector values; + size_t requiredBytesSize = static_cast( + glm::ceil(static_cast(expected.size()) / 8.0)); + values.resize(requiredBytesSize); + for (size_t i = 0; i < expected.size(); ++i) { + uint8_t expectedValue = expected[i]; + size_t byteIndex = i / 8; + size_t bitIndex = i % 8; + values[byteIndex] = + static_cast((expectedValue << bitIndex) | values[byteIndex]); + } + + { + Buffer& valueBuffer = model.buffers.emplace_back(); + valueBuffer.cesium.data.resize(values.size()); + valueBuffer.byteLength = + static_cast(valueBuffer.cesium.data.size()); + std::memcpy( + valueBuffer.cesium.data.data(), + values.data(), + valueBuffer.cesium.data.size()); + + BufferView& valueBufferView = model.bufferViews.emplace_back(); + valueBufferView.buffer = static_cast(model.buffers.size() - 1); + valueBufferView.byteOffset = 0; + valueBufferView.byteLength = valueBuffer.byteLength; + } + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); + ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; + ExtensionExtStructuralMetadataClassProperty& testClassProperty = + testClass.properties["TestClassProperty"]; + testClassProperty.type = + ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN; + testClassProperty.array = true; + testClassProperty.count = 3; + + ExtensionExtStructuralMetadataPropertyTable& propertyTable = + metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast( + expected.size() / static_cast(testClassProperty.count.value())); + + ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = + static_cast(model.bufferViews.size() - 1); + + MetadataPropertyTableView view(&model, &propertyTable); + const ExtensionExtStructuralMetadataClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE( + classProperty->type == + ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN); + REQUIRE(classProperty->array); + REQUIRE(classProperty->count == 3); + + view.getPropertyView( + "TestClassProperty", + [&expected](const std::string& /*propertyName*/, auto propertyValue) { + REQUIRE(propertyValue.status() == MetadataPropertyViewStatus::Valid); + REQUIRE(propertyValue.size() > 0); + + if constexpr (std::is_same_v< + MetadataPropertyView>, + decltype(propertyValue)>) { + for (int64_t i = 0; i < propertyValue.size(); ++i) { + MetadataArrayView member = propertyValue.get(i); + for (int64_t j = 0; j < member.size(); ++j) { + REQUIRE(member[j] == expected[static_cast(i * 3 + j)]); + } + } + } else { + FAIL("getPropertyView returned MetadataPropertyView of incorrect " + "type for TestClassProperty."); + } + }); +} + +TEST_CASE("Test StructuralMetadata callback for array of strings") { + Model model; + + std::vector expected{ + "What's up", + "Breaking news!!! Aliens no longer attacks the US first", + "But they still abduct my cows! Those milk thiefs! 👽 🐮", + "I'm not crazy. My mother had me tested 🤪", + "I love you, meat bags! ❤️", + "Book in the freezer"}; + + size_t totalBytes = 0; + for (const std::string& expectedValue : expected) { + totalBytes += expectedValue.size(); + } + + std::vector offsets((expected.size() + 1) * sizeof(uint32_t)); + std::vector values(totalBytes); + uint32_t* offsetValue = reinterpret_cast(offsets.data()); + for (size_t i = 0; i < expected.size(); ++i) { + const std::string& expectedValue = expected[i]; + std::memcpy( + values.data() + offsetValue[i], + expectedValue.c_str(), + expectedValue.size()); + offsetValue[i + 1] = + offsetValue[i] + static_cast(expectedValue.size()); + } + + size_t valueBufferViewIndex = 0; + { + Buffer& valueBuffer = model.buffers.emplace_back(); + valueBuffer.byteLength = static_cast(values.size()); + valueBuffer.cesium.data = std::move(values); + + BufferView& valueBufferView = model.bufferViews.emplace_back(); + valueBufferView.buffer = static_cast(model.buffers.size() - 1); + valueBufferView.byteOffset = 0; + valueBufferView.byteLength = valueBuffer.byteLength; + valueBufferViewIndex = model.bufferViews.size() - 1; + } + + size_t offsetBufferViewIndex = 0; + { + Buffer& offsetBuffer = model.buffers.emplace_back(); + offsetBuffer.byteLength = static_cast(offsets.size()); + offsetBuffer.cesium.data = std::move(offsets); + + BufferView& offsetBufferView = model.bufferViews.emplace_back(); + offsetBufferView.buffer = static_cast(model.buffers.size() - 1); + offsetBufferView.byteOffset = 0; + offsetBufferView.byteLength = offsetBuffer.byteLength; + offsetBufferViewIndex = model.bufferViews.size() - 1; + } + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); + ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; + ExtensionExtStructuralMetadataClassProperty& testClassProperty = + testClass.properties["TestClassProperty"]; + testClassProperty.type = + ExtensionExtStructuralMetadataClassProperty::Type::STRING; + testClassProperty.array = true; + testClassProperty.count = 2; + + ExtensionExtStructuralMetadataPropertyTable& propertyTable = + metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast( + expected.size() / static_cast(testClassProperty.count.value())); + + ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.stringOffsetType = + ExtensionExtStructuralMetadataPropertyTableProperty::StringOffsetType:: + UINT32; + propertyTableProperty.values = static_cast(valueBufferViewIndex); + propertyTableProperty.stringOffsets = + static_cast(offsetBufferViewIndex); + + MetadataPropertyTableView view(&model, &propertyTable); + const ExtensionExtStructuralMetadataClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE( + classProperty->type == + ExtensionExtStructuralMetadataClassProperty::Type::STRING); + REQUIRE(classProperty->array); + REQUIRE(classProperty->count == 2); + + view.getPropertyView( + "TestClassProperty", + [&expected](const std::string& /*propertyName*/, auto propertyValue) { + REQUIRE(propertyValue.status() == MetadataPropertyViewStatus::Valid); + + if constexpr (std::is_same_v< + MetadataPropertyView< + MetadataArrayView>, + decltype(propertyValue)>) { + REQUIRE(propertyValue.size() == 3); + + MetadataArrayView v0 = propertyValue.get(0); + REQUIRE(v0.size() == 2); + REQUIRE(v0[0] == "What's up"); + REQUIRE( + v0[1] == + "Breaking news!!! Aliens no longer attacks the US first"); + + MetadataArrayView v1 = propertyValue.get(1); + REQUIRE(v1.size() == 2); + REQUIRE( + v1[0] == "But they still abduct my cows! Those milk thiefs! 👽 🐮"); + REQUIRE(v1[1] == "I'm not crazy. My mother had me tested 🤪"); + + MetadataArrayView v2 = propertyValue.get(2); + REQUIRE(v2.size() == 2); + REQUIRE(v2[0] == "I love you, meat bags! ❤️"); + REQUIRE(v2[1] == "Book in the freezer"); + } else { + FAIL("getPropertyView returned MetadataPropertyView of incorrect " + "type for TestClassProperty."); + } + }); +} From 551316928006a23f8b4382d6aa112af3d2fde5e6 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Mon, 22 May 2023 17:50:38 -0400 Subject: [PATCH 033/421] Remove unused variables --- CesiumGltf/test/TestStructuralMetadataPropertyTableView.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/CesiumGltf/test/TestStructuralMetadataPropertyTableView.cpp b/CesiumGltf/test/TestStructuralMetadataPropertyTableView.cpp index 122908c1b..64d8b8bce 100644 --- a/CesiumGltf/test/TestStructuralMetadataPropertyTableView.cpp +++ b/CesiumGltf/test/TestStructuralMetadataPropertyTableView.cpp @@ -3104,7 +3104,6 @@ TEST_CASE("Test StructuralMetadata callback for scalar array") { std::vector values = {12, 34, 30, 11, 34, 34, 11, 33, 122, 33, 223, 11}; - size_t valueBufferViewIndex = 0; { Buffer& valueBuffer = model.buffers.emplace_back(); valueBuffer.cesium.data.resize(values.size() * sizeof(uint32_t)); @@ -3119,7 +3118,6 @@ TEST_CASE("Test StructuralMetadata callback for scalar array") { valueBufferView.buffer = static_cast(model.buffers.size() - 1); valueBufferView.byteOffset = 0; valueBufferView.byteLength = valueBuffer.byteLength; - valueBufferViewIndex = model.bufferViews.size() - 1; } ExtensionModelExtStructuralMetadata& metadata = @@ -3193,7 +3191,6 @@ TEST_CASE("Test StructuralMetadata callback for vecN array") { glm::ivec3(40, 61, 3), }; - size_t valueBufferViewIndex = 0; { Buffer& valueBuffer = model.buffers.emplace_back(); valueBuffer.cesium.data.resize(values.size() * sizeof(glm::ivec3)); @@ -3208,7 +3205,6 @@ TEST_CASE("Test StructuralMetadata callback for vecN array") { valueBufferView.buffer = static_cast(model.buffers.size() - 1); valueBufferView.byteOffset = 0; valueBufferView.byteLength = valueBuffer.byteLength; - valueBufferViewIndex = model.bufferViews.size() - 1; } ExtensionModelExtStructuralMetadata& metadata = From 7e08eadf0730f686e5153b28adb1950598c254ef Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Tue, 23 May 2023 10:39:26 -0400 Subject: [PATCH 034/421] Remove final unused variable --- CesiumGltf/test/TestStructuralMetadataPropertyTableView.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/CesiumGltf/test/TestStructuralMetadataPropertyTableView.cpp b/CesiumGltf/test/TestStructuralMetadataPropertyTableView.cpp index 64d8b8bce..d372b6c5e 100644 --- a/CesiumGltf/test/TestStructuralMetadataPropertyTableView.cpp +++ b/CesiumGltf/test/TestStructuralMetadataPropertyTableView.cpp @@ -3292,7 +3292,6 @@ TEST_CASE("Test StructuralMetadata callback for matN array") { }; // clang-format on - size_t valueBufferViewIndex = 0; { Buffer& valueBuffer = model.buffers.emplace_back(); valueBuffer.cesium.data.resize(values.size() * sizeof(glm::i32mat2x2)); @@ -3307,7 +3306,6 @@ TEST_CASE("Test StructuralMetadata callback for matN array") { valueBufferView.buffer = static_cast(model.buffers.size() - 1); valueBufferView.byteOffset = 0; valueBufferView.byteLength = valueBuffer.byteLength; - valueBufferViewIndex = model.bufferViews.size() - 1; } ExtensionModelExtStructuralMetadata& metadata = From b8b3edea5144a29eb482a0a6f08e2123e0acf0c2 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Tue, 23 May 2023 11:42:34 -0400 Subject: [PATCH 035/421] Code cleanup --- .../StructuralMetadataPropertyTableView.h | 51 ++++++++----------- .../StructuralMetadataPropertyView.h | 24 +++++++++ .../StructuralMetadataPropertyTableView.cpp | 2 +- ...estStructuralMetadataPropertyTableView.cpp | 14 ++++- .../TestStructuralMetadataPropertyView.cpp | 10 ---- 5 files changed, 58 insertions(+), 43 deletions(-) diff --git a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTableView.h b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTableView.h index 7caf8282c..e92a5aa2a 100644 --- a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTableView.h +++ b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTableView.h @@ -16,36 +16,38 @@ namespace StructuralMetadata { * @brief Utility to retrieve the data of * ExtensionExtStructuralMetadataPropertyTable. * - * This should be used to get a {@link MetadataPropertyView} of a property. + * This should be used to get a {@link MetadataPropertyView} of a property in the property table. * It will validate the EXT_structural_metadata format and ensure {@link MetadataPropertyView} - * does not access out of bounds. + * does not access out of bounds. */ class MetadataPropertyTableView { public: /** - * @brief Create an instance of MetadataPropertyTableView. - * @param pModel The Gltf Model that stores property table data - * @param pPropertyTable The ExtensionExtStructuralMetadataPropertyTable from - * which the view will retrieve data. + * @brief Creates an instance of MetadataPropertyTableView. + * @param pModel A pointer to the Gltf Model that contains property table + * data. + * @param pPropertyTable A pointer to the + * ExtensionExtStructuralMetadataPropertyTable from which the view will + * retrieve data. */ MetadataPropertyTableView( const Model* pModel, const ExtensionExtStructuralMetadataPropertyTable* pPropertyTable); /** - * @brief Find the {@link ExtensionExtStructuralMetadataClassProperty} which - * stores the type information of a property based on the property's name. - * @param propertyName The name of the property to retrieve type info - * @return Pointer to the ExtensionExtStructuralMetadataClassProperty. Return - * nullptr if no property was found. + * @brief Finds the {@link ExtensionExtStructuralMetadataClassProperty} that + * describes the type information of the property with the specified name. + * @param propertyName The name of the property to retrieve the class for. + * @return A pointer to the ExtensionExtStructuralMetadataClassProperty. + * Return nullptr if no property was found. */ const ExtensionExtStructuralMetadataClassProperty* getClassProperty(const std::string& propertyName) const; /** - * @brief Gets a MetadataPropertyView to view the data of a property stored in - * the ExtensionExtStructuralMetadataPropertyTable. + * @brief Gets a MetadataPropertyView that views the data of a property stored + * in the ExtensionExtStructuralMetadataPropertyTable. * * This method will validate the EXT_structural_metadata format to ensure * MetadataPropertyView retrieves the correct data. T must be one of the @@ -56,8 +58,8 @@ class MetadataPropertyTableView { * aforementioned types. * * @param propertyName The name of the property to retrieve data from - * @return MetadataPropertyView of a property. The property view will be - * invalid if no property is found. + * @return A MetadataPropertyView of the property. If no valid property is + * found, the property view will be invalid. */ template MetadataPropertyView @@ -81,8 +83,8 @@ class MetadataPropertyTableView { /** * @brief Gets a MetadataPropertyView through a callback that accepts a - * property name and a MetadataPropertyView to view the data - * of a property stored in the ExtensionExtStructuralMetadataPropertyTable. + * property name and a MetadataPropertyView that views the data + * of the property with the specified name. * * This method will validate the EXT_structural_metadata format to ensure * MetadataPropertyView retrieves the correct data. T must be one of the @@ -91,11 +93,11 @@ class MetadataPropertyTableView { * types, a glm matN composed of one of the scalar types, bool, * std::string_view, or MetadataArrayView with T as one of the * aforementioned types. If the property is invalid, an empty - * MetadataPropertyView with an error status code will be passed to the + * MetadataPropertyView with an error status will be passed to the * callback. Otherwise, a valid property view will be passed to the callback. * * @param propertyName The name of the property to retrieve data from - * @tparam callback A callback function that accepts property name and + * @tparam callback A callback function that accepts a property name and a * MetadataPropertyView */ template @@ -564,7 +566,6 @@ class MetadataPropertyTableView { type, componentType, std::forward(callback)); - } else if (type == PropertyType::Boolean) { callback( propertyName, @@ -990,11 +991,6 @@ class MetadataPropertyTableView { return MetadataPropertyView( MetadataPropertyViewStatus::Valid, values, - gsl::span(), - gsl::span(), - PropertyComponentType::None, - PropertyComponentType::None, - 0, _pPropertyTable->count, classProperty.normalized); } @@ -1147,11 +1143,6 @@ class MetadataPropertyTableView { return MetadataPropertyView( invalidStatus, gsl::span(), - gsl::span(), - gsl::span(), - PropertyComponentType::None, - PropertyComponentType::None, - 0, 0, false); } diff --git a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyView.h b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyView.h index 529e092df..1be6dd014 100644 --- a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyView.h +++ b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyView.h @@ -177,6 +177,30 @@ template class MetadataPropertyView { _size{}, _normalized{} {} + /** + * @brief Construct a new instance pointing to non-array data specified by + * ExtensionExtStructuralMetadataPropertyTableProperty. + * @param values The raw buffer specified by {@link ExtensionExtStructuralMetadataPropertyTableProperty::values} + * @param size The number of elements in the property table specified by {@link ExtensionExtStructuralMetadataPropertyTable::count} + * @param normalized Whether this property has a normalized integer type. + */ + MetadataPropertyView( + MetadataPropertyViewStatus status, + gsl::span values, + int64_t size, + bool normalized) noexcept + : _status{status}, + _values{values}, + _arrayOffsets{}, + _arrayOffsetType{PropertyComponentType::None}, + _arrayOffsetTypeSize{0}, + _stringOffsets{}, + _stringOffsetType{PropertyComponentType::None}, + _stringOffsetTypeSize{0}, + _fixedLengthArrayCount{0}, + _size{size}, + _normalized{normalized} {} + /** * @brief Construct a new instance pointing to the data specified by * ExtensionExtStructuralMetadataPropertyTableProperty. diff --git a/CesiumGltf/src/StructuralMetadataPropertyTableView.cpp b/CesiumGltf/src/StructuralMetadataPropertyTableView.cpp index 115ad0129..ed0fa157b 100644 --- a/CesiumGltf/src/StructuralMetadataPropertyTableView.cpp +++ b/CesiumGltf/src/StructuralMetadataPropertyTableView.cpp @@ -338,7 +338,7 @@ MetadataPropertyTableView::getStringArrayPropertyValues( propertyTableProperty) const { if (!classProperty.array) { return createInvalidPropertyView>( - MetadataPropertyViewStatus::ErrorTypeMismatch); + MetadataPropertyViewStatus::ErrorArrayTypeMismatch); } if (classProperty.type != diff --git a/CesiumGltf/test/TestStructuralMetadataPropertyTableView.cpp b/CesiumGltf/test/TestStructuralMetadataPropertyTableView.cpp index d372b6c5e..cf2d6a567 100644 --- a/CesiumGltf/test/TestStructuralMetadataPropertyTableView.cpp +++ b/CesiumGltf/test/TestStructuralMetadataPropertyTableView.cpp @@ -554,7 +554,7 @@ TEST_CASE("Test StructuralMetadata matN property") { } } -TEST_CASE("Test StructuralMetadata boolean properties") { +TEST_CASE("Test StructuralMetadata boolean property") { Model model; int64_t instanceCount = 21; @@ -743,6 +743,16 @@ TEST_CASE("Test StructuralMetadata string property") { } } + SECTION("Wrong array type") { + MetadataPropertyView> + stringArrayInvalid = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + stringArrayInvalid.status() == + MetadataPropertyViewStatus::ErrorArrayTypeMismatch); + } + SECTION("Wrong offset type") { propertyTableProperty.stringOffsetType = ExtensionExtStructuralMetadataPropertyTableProperty::StringOffsetType:: @@ -3559,7 +3569,7 @@ TEST_CASE("Test StructuralMetadata callback for array of strings") { view.getPropertyView( "TestClassProperty", - [&expected](const std::string& /*propertyName*/, auto propertyValue) { + [](const std::string& /*propertyName*/, auto propertyValue) { REQUIRE(propertyValue.status() == MetadataPropertyViewStatus::Valid); if constexpr (std::is_same_v< diff --git a/CesiumGltf/test/TestStructuralMetadataPropertyView.cpp b/CesiumGltf/test/TestStructuralMetadataPropertyView.cpp index bd5ab17ec..4c8103b08 100644 --- a/CesiumGltf/test/TestStructuralMetadataPropertyView.cpp +++ b/CesiumGltf/test/TestStructuralMetadataPropertyView.cpp @@ -19,11 +19,6 @@ template static void checkNumeric(const std::vector& expected) { MetadataPropertyView property( MetadataPropertyViewStatus::Valid, gsl::span(data.data(), data.size()), - gsl::span(), - gsl::span(), - PropertyComponentType::None, - PropertyComponentType::None, - 0, static_cast(expected.size()), false); @@ -233,11 +228,6 @@ TEST_CASE("Check StructuralMetadata boolean property") { MetadataPropertyView property( MetadataPropertyViewStatus::Valid, gsl::span(data.data(), data.size()), - gsl::span(), - gsl::span(), - PropertyComponentType::None, - PropertyComponentType::None, - 0, static_cast(instanceCount), false); for (int64_t i = 0; i < property.size(); ++i) { From c773d1000b2c28757352b9614ea1db166509542f Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 24 May 2023 18:40:06 +1000 Subject: [PATCH 036/421] Add method to transform between LocalHorizontalCoordinateSystems. --- .../LocalHorizontalCoordinateSystem.h | 24 +++++++++++++++---- .../src/LocalHorizontalCoordinateSystem.cpp | 14 +++++++---- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/CesiumGeospatial/include/CesiumGeospatial/LocalHorizontalCoordinateSystem.h b/CesiumGeospatial/include/CesiumGeospatial/LocalHorizontalCoordinateSystem.h index b13556479..1f608e8a3 100644 --- a/CesiumGeospatial/include/CesiumGeospatial/LocalHorizontalCoordinateSystem.h +++ b/CesiumGeospatial/include/CesiumGeospatial/LocalHorizontalCoordinateSystem.h @@ -101,7 +101,8 @@ class CESIUMGEOSPATIAL_API LocalHorizontalCoordinateSystem { * @param localPosition The position in the local coordinate system. * @return The equivalent position in the ECEF coordinate system. */ - glm::dvec3 localPositionToEcef(const glm::dvec3& localPosition) const; + glm::dvec3 + localPositionToEcef(const glm::dvec3& localPosition) const noexcept; /** * @brief Converts a position in the Earth-Centered, Earth-Fixed (ECEF) @@ -111,7 +112,7 @@ class CESIUMGEOSPATIAL_API LocalHorizontalCoordinateSystem { * @param ecefPosition The position in the ECEF coordinate system. * @return The equivalent position in the local coordinate system. */ - glm::dvec3 ecefPositionToLocal(const glm::dvec3& ecefPosition) const; + glm::dvec3 ecefPositionToLocal(const glm::dvec3& ecefPosition) const noexcept; /** * @brief Converts a direction in the local horizontal coordinate system @@ -123,7 +124,8 @@ class CESIUMGEOSPATIAL_API LocalHorizontalCoordinateSystem { * @param localPosition The direction in the local coordinate system. * @return The equivalent direction in the ECEF coordinate system. */ - glm::dvec3 localDirectionToEcef(const glm::dvec3& localDirection) const; + glm::dvec3 + localDirectionToEcef(const glm::dvec3& localDirection) const noexcept; /** * @brief Converts a direction in the Earth-Centered, Earth-Fixed (ECEF) @@ -136,7 +138,21 @@ class CESIUMGEOSPATIAL_API LocalHorizontalCoordinateSystem { * @param ecefPosition The direction in the ECEF coordinate system. * @return The equivalent direction in the local coordinate system. */ - glm::dvec3 ecefDirectionToLocal(const glm::dvec3& ecefDirection) const; + glm::dvec3 + ecefDirectionToLocal(const glm::dvec3& ecefDirection) const noexcept; + + /** + * @brief Computes the transformation matrix from this local horizontal + * coordinate system to another one. For example, if the returned matrix is + * multiplied with a vector representing a position in this coordinate system, + * the result will be a new vector representing the position in the target + * coordinate system. + * + * @param target The other local horizontal coordinate system. + * @return The transformation. + */ + glm::dmat4 computeTransformationToAnotherLocal( + const LocalHorizontalCoordinateSystem& target) const noexcept; private: glm::dmat4 _ecefToLocal; diff --git a/CesiumGeospatial/src/LocalHorizontalCoordinateSystem.cpp b/CesiumGeospatial/src/LocalHorizontalCoordinateSystem.cpp index e09c82682..910f3aca4 100644 --- a/CesiumGeospatial/src/LocalHorizontalCoordinateSystem.cpp +++ b/CesiumGeospatial/src/LocalHorizontalCoordinateSystem.cpp @@ -90,21 +90,27 @@ LocalHorizontalCoordinateSystem::LocalHorizontalCoordinateSystem( } glm::dvec3 LocalHorizontalCoordinateSystem::localPositionToEcef( - const glm::dvec3& localPosition) const { + const glm::dvec3& localPosition) const noexcept { return glm::dvec3(this->_localToEcef * glm::dvec4(localPosition, 1.0)); } glm::dvec3 LocalHorizontalCoordinateSystem::ecefPositionToLocal( - const glm::dvec3& ecefPosition) const { + const glm::dvec3& ecefPosition) const noexcept { return glm::dvec3(this->_ecefToLocal * glm::dvec4(ecefPosition, 1.0)); } glm::dvec3 LocalHorizontalCoordinateSystem::localDirectionToEcef( - const glm::dvec3& localDirection) const { + const glm::dvec3& localDirection) const noexcept { return glm::dvec3(this->_localToEcef * glm::dvec4(localDirection, 0.0)); } glm::dvec3 LocalHorizontalCoordinateSystem::ecefDirectionToLocal( - const glm::dvec3& ecefDirection) const { + const glm::dvec3& ecefDirection) const noexcept { return glm::dvec3(this->_ecefToLocal * glm::dvec4(ecefDirection, 0.0)); } + +glm::dmat4 LocalHorizontalCoordinateSystem::computeTransformationToAnotherLocal( + const LocalHorizontalCoordinateSystem& target) const noexcept { + return target.getEcefToLocalTransformation() * + this->getLocalToEcefTransformation(); +} From 1c3f930e6f572e40d76b364d9ded4d162c9f1810 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 24 May 2023 18:52:31 +1000 Subject: [PATCH 037/421] Add test and update CHANGES.md. --- CHANGES.md | 6 +++++ .../TestLocalHorizontalCoordinateSystem.cpp | 24 +++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index a6db856cb..e22a5e1f1 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,11 @@ # Change Log +### ? - ? + +##### Additions :tada: + +- Added `computeTransformationToAnotherLocal` method to `LocalHorizontalCoordinateSystem`. + ### v0.24.0 - 2023-05-01 ##### Additions :tada: diff --git a/CesiumGeospatial/test/TestLocalHorizontalCoordinateSystem.cpp b/CesiumGeospatial/test/TestLocalHorizontalCoordinateSystem.cpp index 94f35f2a5..b1cafc371 100644 --- a/CesiumGeospatial/test/TestLocalHorizontalCoordinateSystem.cpp +++ b/CesiumGeospatial/test/TestLocalHorizontalCoordinateSystem.cpp @@ -144,4 +144,28 @@ TEST_CASE("LocalHorizontalCoordinateSystem") { 0.0, 1e-10)); } + + SECTION("computeTransformationToAnotherLocal") { + LocalHorizontalCoordinateSystem original( + nullIsland, + LocalDirection::East, + LocalDirection::South, + LocalDirection::Up); + + LocalHorizontalCoordinateSystem target( + Cartographic::fromDegrees(12.0, 23.0, 1000.0), + LocalDirection::East, + LocalDirection::South, + LocalDirection::Up); + + glm::dvec3 somePointInOriginal = glm::dvec3(1781.0, 373.0, 7777.2); + glm::dvec3 samePointInEcef = + original.localPositionToEcef(somePointInOriginal); + glm::dvec3 samePointInTarget = target.ecefPositionToLocal(samePointInEcef); + + glm::dmat4 transform = original.computeTransformationToAnotherLocal(target); + glm::dvec3 computedByTransform = + glm::dvec3(transform * glm::dvec4(somePointInOriginal, 1.0)); + CHECK(Math::equalsEpsilon(computedByTransform, samePointInTarget, 1e-15)); + } } From 7e178f2c1072f49d539a290bb33b3b4e0ac9919b Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 24 May 2023 19:10:27 +1000 Subject: [PATCH 038/421] Small optimization to LocalHorizontalCoordinateSystem construction. --- .../src/LocalHorizontalCoordinateSystem.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/CesiumGeospatial/src/LocalHorizontalCoordinateSystem.cpp b/CesiumGeospatial/src/LocalHorizontalCoordinateSystem.cpp index 910f3aca4..cb553c1ba 100644 --- a/CesiumGeospatial/src/LocalHorizontalCoordinateSystem.cpp +++ b/CesiumGeospatial/src/LocalHorizontalCoordinateSystem.cpp @@ -77,15 +77,12 @@ LocalHorizontalCoordinateSystem::LocalHorizontalCoordinateSystem( GlobeTransforms::eastNorthUpToFixedFrame(originEcef, ellipsoid); // Construct a matrix to swap and invert axes as necessary - glm::dmat4 localToEnu( - glm::dvec4(directionToEnuVector(xAxisDirection), 0.0), - glm::dvec4(directionToEnuVector(yAxisDirection), 0.0), - glm::dvec4(directionToEnuVector(zAxisDirection), 0.0), - glm::dvec4(0.0, 0.0, 0.0, 1.0)); + glm::dmat3 localToEnuAndScale( + scaleToMeters * directionToEnuVector(xAxisDirection), + scaleToMeters * directionToEnuVector(yAxisDirection), + scaleToMeters * directionToEnuVector(zAxisDirection)); - glm::dmat4 scale{glm::dmat3(scaleToMeters)}; - - this->_localToEcef = enuToFixed * localToEnu * scale; + this->_localToEcef = enuToFixed * glm::dmat4(localToEnuAndScale); this->_ecefToLocal = glm::affineInverse(this->_localToEcef); } From 1cce7f82020038d7da4b53f36cf9408473f7a9b5 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Wed, 24 May 2023 11:04:45 -0400 Subject: [PATCH 039/421] Use references instead of pointers, add missing doc links --- .../CesiumGltf/StructuralMetadataArrayView.h | 2 +- .../StructuralMetadataPropertyTableView.h | 70 +++++++++---------- .../StructuralMetadataPropertyView.h | 14 ++-- .../StructuralMetadataPropertyTableView.cpp | 36 +++++----- ...estStructuralMetadataPropertyTableView.cpp | 52 +++++++------- 5 files changed, 83 insertions(+), 91 deletions(-) diff --git a/CesiumGltf/include/CesiumGltf/StructuralMetadataArrayView.h b/CesiumGltf/include/CesiumGltf/StructuralMetadataArrayView.h index a886b62c3..e315584f4 100644 --- a/CesiumGltf/include/CesiumGltf/StructuralMetadataArrayView.h +++ b/CesiumGltf/include/CesiumGltf/StructuralMetadataArrayView.h @@ -15,7 +15,7 @@ namespace StructuralMetadata { /** * @brief A view on an array element of a - * ExtensionExtStructuralMetadataPropertyTableProperty. + * {@link ExtensionExtStructuralMetadataPropertyTableProperty}. * * Provides utility to retrieve the data stored in the array of * elements via the array index operator. diff --git a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTableView.h b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTableView.h index e92a5aa2a..492fca629 100644 --- a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTableView.h +++ b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTableView.h @@ -14,32 +14,29 @@ namespace StructuralMetadata { /** * @brief Utility to retrieve the data of - * ExtensionExtStructuralMetadataPropertyTable. + * {@link ExtensionExtStructuralMetadataPropertyTable}. * * This should be used to get a {@link MetadataPropertyView} of a property in the property table. * It will validate the EXT_structural_metadata format and ensure {@link MetadataPropertyView} * does not access out of bounds. */ - class MetadataPropertyTableView { public: /** * @brief Creates an instance of MetadataPropertyTableView. - * @param pModel A pointer to the Gltf Model that contains property table - * data. - * @param pPropertyTable A pointer to the - * ExtensionExtStructuralMetadataPropertyTable from which the view will - * retrieve data. + * @param model The Gltf Model that contains property table data. + * @param propertyTable The {@link ExtensionExtStructuralMetadataPropertyTable} + * from which the view will retrieve data. */ MetadataPropertyTableView( - const Model* pModel, - const ExtensionExtStructuralMetadataPropertyTable* pPropertyTable); + const Model& model, + const ExtensionExtStructuralMetadataPropertyTable& propertyTable); /** * @brief Finds the {@link ExtensionExtStructuralMetadataClassProperty} that * describes the type information of the property with the specified name. * @param propertyName The name of the property to retrieve the class for. - * @return A pointer to the ExtensionExtStructuralMetadataClassProperty. + * @return A pointer to the {@link ExtensionExtStructuralMetadataClassProperty}. * Return nullptr if no property was found. */ const ExtensionExtStructuralMetadataClassProperty* @@ -64,7 +61,7 @@ class MetadataPropertyTableView { template MetadataPropertyView getPropertyView(const std::string& propertyName) const { - if (_pPropertyTable->count <= 0) { + if (_propertyTable.count <= 0) { return createInvalidPropertyView( StructuralMetadata::MetadataPropertyViewStatus:: ErrorPropertyDoesNotExist); @@ -82,23 +79,23 @@ class MetadataPropertyTableView { } /** - * @brief Gets a MetadataPropertyView through a callback that accepts a - * property name and a MetadataPropertyView that views the data + * @brief Gets a {@link MetadataPropertyView} through a callback that accepts a + * property name and a {@link MetadataPropertyView} that views the data * of the property with the specified name. * * This method will validate the EXT_structural_metadata format to ensure - * MetadataPropertyView retrieves the correct data. T must be one of the + * {@link MetadataPropertyView} retrieves the correct data. T must be one of the * following: a scalar (uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t, * uint64_t, int64_t, float, double), a glm vecN composed of one of the scalar * types, a glm matN composed of one of the scalar types, bool, - * std::string_view, or MetadataArrayView with T as one of the + * std::string_view, or {@link MetadataArrayView} with T as one of the * aforementioned types. If the property is invalid, an empty - * MetadataPropertyView with an error status will be passed to the + * {@link MetadataPropertyView} with an error status will be passed to the * callback. Otherwise, a valid property view will be passed to the callback. * * @param propertyName The name of the property to retrieve data from * @tparam callback A callback function that accepts a property name and a - * MetadataPropertyView + * {@link MetadataPropertyView} */ template void @@ -161,24 +158,24 @@ class MetadataPropertyTableView { /** * @brief Iterates over each property in the - * ExtensionExtStructuralMetadataPropertyTable with a callback that accepts a - * property name and a MetadataPropertyView to view the data - * stored in the ExtensionExtStructuralMetadataPropertyTableProperty. + * {@link ExtensionExtStructuralMetadataPropertyTable} with a callback that accepts a + * property name and a {@link MetadataPropertyView} to view the data + * stored in the {@link ExtensionExtStructuralMetadataPropertyTableProperty}. * * This method will validate the EXT_structural_metadata format to ensure - * MetadataPropertyView retrieves the correct data. T must be one of the + * {@link MetadataPropertyView} retrieves the correct data. T must be one of the * following: a scalar (uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t, * uint64_t, int64_t, float, double), a glm vecN composed of one of the scalar * types, a glm matN composed of one of the scalar types, bool, - * std::string_view, or MetadataArrayView with T as one of the + * std::string_view, or {@link MetadataArrayView} with T as one of the * aforementioned types. If the property is invalid, an empty - * MetadataPropertyView with an error status code will be passed to the + * {@link MetadataPropertyView} with an error status code will be passed to the * callback. Otherwise, a valid property view will be passed to * the callback. * * @param propertyName The name of the property to retrieve data from * @tparam callback A callback function that accepts property name and - * MetadataPropertyView + * {@link MetadataPropertyView} */ template void forEachProperty(Callback&& callback) const { for (const auto& property : this->_pClass->properties) { @@ -907,8 +904,8 @@ class MetadataPropertyTableView { const std::string& propertyName, const ExtensionExtStructuralMetadataClassProperty& classProperty) const { auto propertyTablePropertyIter = - _pPropertyTable->properties.find(propertyName); - if (propertyTablePropertyIter == _pPropertyTable->properties.end()) { + _propertyTable.properties.find(propertyName); + if (propertyTablePropertyIter == _propertyTable.properties.end()) { return createInvalidPropertyView( MetadataPropertyViewStatus::ErrorPropertyDoesNotExist); } @@ -977,9 +974,9 @@ class MetadataPropertyTableView { size_t maxRequiredBytes = 0; if (IsMetadataBoolean::value) { maxRequiredBytes = static_cast( - glm::ceil(static_cast(_pPropertyTable->count) / 8.0)); + glm::ceil(static_cast(_propertyTable.count) / 8.0)); } else { - maxRequiredBytes = _pPropertyTable->count * sizeof(T); + maxRequiredBytes = _propertyTable.count * sizeof(T); } if (values.size() < maxRequiredBytes) { @@ -991,7 +988,7 @@ class MetadataPropertyTableView { return MetadataPropertyView( MetadataPropertyViewStatus::Valid, values, - _pPropertyTable->count, + _propertyTable.count, classProperty.normalized); } @@ -1052,12 +1049,11 @@ class MetadataPropertyTableView { size_t maxRequiredBytes = 0; if constexpr (IsMetadataBoolean::value) { maxRequiredBytes = static_cast(glm::ceil( - static_cast( - _pPropertyTable->count * fixedLengthArrayCount) / + static_cast(_propertyTable.count * fixedLengthArrayCount) / 8.0)); } else { maxRequiredBytes = static_cast( - _pPropertyTable->count * fixedLengthArrayCount * sizeof(T)); + _propertyTable.count * fixedLengthArrayCount * sizeof(T)); } if (values.size() < maxRequiredBytes) { @@ -1074,7 +1070,7 @@ class MetadataPropertyTableView { PropertyComponentType::None, PropertyComponentType::None, static_cast(fixedLengthArrayCount), - static_cast(_pPropertyTable->count), + static_cast(_propertyTable.count), classProperty.normalized); } @@ -1093,7 +1089,7 @@ class MetadataPropertyTableView { propertyTableProperty.arrayOffsets, arrayOffsetType, values.size(), - static_cast(_pPropertyTable->count), + static_cast(_propertyTable.count), checkBitsSize, arrayOffsets); if (status != MetadataPropertyViewStatus::Valid) { @@ -1108,7 +1104,7 @@ class MetadataPropertyTableView { arrayOffsetType, PropertyComponentType::None, 0, - static_cast(_pPropertyTable->count), + static_cast(_propertyTable.count), classProperty.normalized); } @@ -1147,8 +1143,8 @@ class MetadataPropertyTableView { false); } - const Model* _pModel; - const ExtensionExtStructuralMetadataPropertyTable* _pPropertyTable; + const Model& _model; + const ExtensionExtStructuralMetadataPropertyTable& _propertyTable; const ExtensionExtStructuralMetadataClass* _pClass; }; diff --git a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyView.h b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyView.h index 1be6dd014..e98134dd1 100644 --- a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyView.h +++ b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyView.h @@ -31,7 +31,7 @@ enum class MetadataPropertyViewStatus { /** * @brief This property view does not exist in the - * ExtensionExtStructuralMetadataPropertyTable. + * {@link ExtensionExtStructuralMetadataPropertyTable}. */ ErrorPropertyDoesNotExist, @@ -153,7 +153,7 @@ enum class MetadataPropertyViewStatus { /** * @brief A view on the data of the - * ExtensionExtStructuralMetadataPropertyTableProperty + * {@link ExtensionExtStructuralMetadataPropertyTableProperty} * * It provides utility to retrieve the actual data stored in the * {@link ExtensionExtStructuralMetadataPropertyTableProperty::values} like an array of elements. @@ -179,7 +179,7 @@ template class MetadataPropertyView { /** * @brief Construct a new instance pointing to non-array data specified by - * ExtensionExtStructuralMetadataPropertyTableProperty. + * {@link ExtensionExtStructuralMetadataPropertyTableProperty}. * @param values The raw buffer specified by {@link ExtensionExtStructuralMetadataPropertyTableProperty::values} * @param size The number of elements in the property table specified by {@link ExtensionExtStructuralMetadataPropertyTable::count} * @param normalized Whether this property has a normalized integer type. @@ -203,7 +203,7 @@ template class MetadataPropertyView { /** * @brief Construct a new instance pointing to the data specified by - * ExtensionExtStructuralMetadataPropertyTableProperty. + * {@link ExtensionExtStructuralMetadataPropertyTableProperty}. * @param values The raw buffer specified by {@link ExtensionExtStructuralMetadataPropertyTableProperty::values} * @param arrayOffsets The raw buffer specified by {@link ExtensionExtStructuralMetadataPropertyTableProperty::arrayOffsets} * @param stringOffsets The raw buffer specified by {@link ExtensionExtStructuralMetadataPropertyTableProperty::stringOffsets} @@ -244,7 +244,7 @@ template class MetadataPropertyView { MetadataPropertyViewStatus status() const noexcept { return _status; } /** - * @brief Get the value of an element of the FeatureTable. + * @brief Get the value of an element of the {@link ExtensionExtStructuralMetadataPropertyTable}. * @param index The element index * @return The value of the element */ @@ -286,10 +286,10 @@ template class MetadataPropertyView { /** * @brief Get the number of elements in the - * ExtensionExtStructuralMetadataPropertyTable. + * {@link ExtensionExtStructuralMetadataPropertyTable}. * * @return The number of elements in the - * ExtensionExtStructuralMetadataPropertyTable. + * {@link ExtensionExtStructuralMetadataPropertyTable}. */ int64_t size() const noexcept { return _size; } diff --git a/CesiumGltf/src/StructuralMetadataPropertyTableView.cpp b/CesiumGltf/src/StructuralMetadataPropertyTableView.cpp index ed0fa157b..e48f26f6c 100644 --- a/CesiumGltf/src/StructuralMetadataPropertyTableView.cpp +++ b/CesiumGltf/src/StructuralMetadataPropertyTableView.cpp @@ -107,14 +107,11 @@ static MetadataPropertyViewStatus checkStringAndArrayOffsetsBuffers( } MetadataPropertyTableView::MetadataPropertyTableView( - const Model* pModel, - const ExtensionExtStructuralMetadataPropertyTable* pPropertyTable) - : _pModel{pModel}, _pPropertyTable{pPropertyTable}, _pClass{nullptr} { - assert(pModel != nullptr && "pModel must not be nullptr"); - assert(pPropertyTable != nullptr && "pPropertyTable must not be nullptr"); - + const Model& model, + const ExtensionExtStructuralMetadataPropertyTable& propertyTable) + : _model{model}, _propertyTable{propertyTable}, _pClass{nullptr} { const ExtensionModelExtStructuralMetadata* pMetadata = - pModel->getExtension(); + model.getExtension(); assert( pMetadata != nullptr && "Model must contain ExtensionModelExtStructuralMetadata to use " @@ -127,7 +124,7 @@ MetadataPropertyTableView::MetadataPropertyTableView( "ExtensionModelExtStructuralMetadata must contain " "Schema to use MetadataPropertyTableView"); - auto classIter = schema->classes.find(_pPropertyTable->classProperty); + auto classIter = schema->classes.find(_propertyTable.classProperty); if (classIter != schema->classes.end()) { _pClass = &classIter->second; } @@ -154,13 +151,12 @@ MetadataPropertyViewStatus MetadataPropertyTableView::getBufferSafe( buffer = {}; const BufferView* pBufferView = - _pModel->getSafe(&_pModel->bufferViews, bufferViewIdx); + _model.getSafe(&_model.bufferViews, bufferViewIdx); if (!pBufferView) { return MetadataPropertyViewStatus::ErrorInvalidValueBufferView; } - const Buffer* pBuffer = - _pModel->getSafe(&_pModel->buffers, pBufferView->buffer); + const Buffer* pBuffer = _model.getSafe(&_model.buffers, pBufferView->buffer); if (!pBuffer) { return MetadataPropertyViewStatus::ErrorInvalidValueBuffer; } @@ -313,7 +309,7 @@ MetadataPropertyTableView::getStringPropertyValues( propertyTableProperty.stringOffsets, offsetType, values.size(), - static_cast(_pPropertyTable->count), + static_cast(_propertyTable.count), stringOffsets); if (status != MetadataPropertyViewStatus::Valid) { return createInvalidPropertyView(status); @@ -327,7 +323,7 @@ MetadataPropertyTableView::getStringPropertyValues( PropertyComponentType::None, offsetType, 0, - _pPropertyTable->count, + _propertyTable.count, classProperty.normalized); } @@ -387,7 +383,7 @@ MetadataPropertyTableView::getStringArrayPropertyValues( propertyTableProperty.stringOffsets, stringOffsetType, values.size(), - static_cast(_pPropertyTable->count * fixedLengthArrayCount), + static_cast(_propertyTable.count * fixedLengthArrayCount), stringOffsets); if (status != MetadataPropertyViewStatus::Valid) { return createInvalidPropertyView>( @@ -402,7 +398,7 @@ MetadataPropertyTableView::getStringArrayPropertyValues( PropertyComponentType::None, stringOffsetType, fixedLengthArrayCount, - _pPropertyTable->count, + _propertyTable.count, classProperty.normalized); } @@ -442,7 +438,7 @@ MetadataPropertyTableView::getStringArrayPropertyValues( stringOffsets, values.size(), stringOffsetType, - static_cast(_pPropertyTable->count)); + static_cast(_propertyTable.count)); break; case PropertyComponentType::Uint16: status = checkStringAndArrayOffsetsBuffers( @@ -450,7 +446,7 @@ MetadataPropertyTableView::getStringArrayPropertyValues( stringOffsets, values.size(), stringOffsetType, - static_cast(_pPropertyTable->count)); + static_cast(_propertyTable.count)); break; case PropertyComponentType::Uint32: status = checkStringAndArrayOffsetsBuffers( @@ -458,7 +454,7 @@ MetadataPropertyTableView::getStringArrayPropertyValues( stringOffsets, values.size(), stringOffsetType, - static_cast(_pPropertyTable->count)); + static_cast(_propertyTable.count)); break; case PropertyComponentType::Uint64: status = checkStringAndArrayOffsetsBuffers( @@ -466,7 +462,7 @@ MetadataPropertyTableView::getStringArrayPropertyValues( stringOffsets, values.size(), stringOffsetType, - static_cast(_pPropertyTable->count)); + static_cast(_propertyTable.count)); break; default: status = MetadataPropertyViewStatus::ErrorInvalidArrayOffsetType; @@ -486,7 +482,7 @@ MetadataPropertyTableView::getStringArrayPropertyValues( arrayOffsetType, stringOffsetType, 0, - _pPropertyTable->count, + _propertyTable.count, classProperty.normalized); } diff --git a/CesiumGltf/test/TestStructuralMetadataPropertyTableView.cpp b/CesiumGltf/test/TestStructuralMetadataPropertyTableView.cpp index cf2d6a567..1bcdd26dc 100644 --- a/CesiumGltf/test/TestStructuralMetadataPropertyTableView.cpp +++ b/CesiumGltf/test/TestStructuralMetadataPropertyTableView.cpp @@ -56,7 +56,7 @@ TEST_CASE("Test StructuralMetadata scalar property") { propertyTable.properties["TestClassProperty"]; propertyTableProperty.values = static_cast(valueBufferViewIndex); - MetadataPropertyTableView view(&model, &propertyTable); + MetadataPropertyTableView view(model, propertyTable); const ExtensionExtStructuralMetadataClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE( @@ -231,7 +231,7 @@ TEST_CASE("Test StructuralMetadata vecN property") { propertyTable.properties["TestClassProperty"]; propertyTableProperty.values = static_cast(valueBufferViewIndex); - MetadataPropertyTableView view(&model, &propertyTable); + MetadataPropertyTableView view(model, propertyTable); const ExtensionExtStructuralMetadataClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE( @@ -422,7 +422,7 @@ TEST_CASE("Test StructuralMetadata matN property") { propertyTable.properties["TestClassProperty"]; propertyTableProperty.values = static_cast(valueBufferViewIndex); - MetadataPropertyTableView view(&model, &propertyTable); + MetadataPropertyTableView view(model, propertyTable); const ExtensionExtStructuralMetadataClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE( @@ -613,7 +613,7 @@ TEST_CASE("Test StructuralMetadata boolean property") { propertyTableProperty.values = static_cast(model.bufferViews.size() - 1); - MetadataPropertyTableView view(&model, &propertyTable); + MetadataPropertyTableView view(model, propertyTable); const ExtensionExtStructuralMetadataClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE( @@ -724,7 +724,7 @@ TEST_CASE("Test StructuralMetadata string property") { propertyTableProperty.stringOffsets = static_cast(offsetBufferViewIndex); - MetadataPropertyTableView view(&model, &propertyTable); + MetadataPropertyTableView view(model, propertyTable); const ExtensionExtStructuralMetadataClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE( @@ -866,7 +866,7 @@ TEST_CASE("Test StructuralMetadata fixed-length scalar array") { propertyTableProperty.values = static_cast(model.bufferViews.size() - 1); - MetadataPropertyTableView view(&model, &propertyTable); + MetadataPropertyTableView view(model, propertyTable); const ExtensionExtStructuralMetadataClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE( @@ -1036,7 +1036,7 @@ TEST_CASE("Test Structural Metadata variable-length scalar array") { ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: UINT64; - MetadataPropertyTableView view(&model, &propertyTable); + MetadataPropertyTableView view(model, propertyTable); const ExtensionExtStructuralMetadataClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE( @@ -1191,7 +1191,7 @@ TEST_CASE("Test StructuralMetadata fixed-length vecN array") { propertyTableProperty.values = static_cast(model.bufferViews.size() - 1); - MetadataPropertyTableView view(&model, &propertyTable); + MetadataPropertyTableView view(model, propertyTable); const ExtensionExtStructuralMetadataClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE( @@ -1368,7 +1368,7 @@ TEST_CASE("Test Structural Metadata variable-length vecN array") { ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: UINT64; - MetadataPropertyTableView view(&model, &propertyTable); + MetadataPropertyTableView view(model, propertyTable); const ExtensionExtStructuralMetadataClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE( @@ -1543,7 +1543,7 @@ TEST_CASE("Test StructuralMetadata fixed-length matN array") { propertyTableProperty.values = static_cast(model.bufferViews.size() - 1); - MetadataPropertyTableView view(&model, &propertyTable); + MetadataPropertyTableView view(model, propertyTable); const ExtensionExtStructuralMetadataClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE( @@ -1741,7 +1741,7 @@ TEST_CASE("Test Structural Metadata variable-length matN array") { ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: UINT64; - MetadataPropertyTableView view(&model, &propertyTable); + MetadataPropertyTableView view(model, propertyTable); const ExtensionExtStructuralMetadataClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE( @@ -1914,7 +1914,7 @@ TEST_CASE("Test StructuralMetadata fixed-length boolean array") { propertyTableProperty.values = static_cast(model.bufferViews.size() - 1); - MetadataPropertyTableView view(&model, &propertyTable); + MetadataPropertyTableView view(model, propertyTable); const ExtensionExtStructuralMetadataClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE( @@ -2063,7 +2063,7 @@ TEST_CASE("Test StructuralMetadata variable-length boolean array") { ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: UINT64; - MetadataPropertyTableView view(&model, &propertyTable); + MetadataPropertyTableView view(model, propertyTable); const ExtensionExtStructuralMetadataClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE( @@ -2243,7 +2243,7 @@ TEST_CASE("Test StructuralMetadata fixed-length arrays of strings") { propertyTableProperty.stringOffsets = static_cast(offsetBufferViewIndex); - MetadataPropertyTableView view(&model, &propertyTable); + MetadataPropertyTableView view(model, propertyTable); const ExtensionExtStructuralMetadataClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE( @@ -2444,7 +2444,7 @@ TEST_CASE("Test StructuralMetadata variable-length arrays of strings") { propertyTableProperty.stringOffsets = static_cast(stringOffsetBufferView); - MetadataPropertyTableView view(&model, &propertyTable); + MetadataPropertyTableView view(model, propertyTable); const ExtensionExtStructuralMetadataClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE( @@ -2623,7 +2623,7 @@ TEST_CASE("Test StructuralMetadata callback for invalid property") { propertyTable.properties["InvalidProperty"]; propertyTableProperty.values = static_cast(-1); - MetadataPropertyTableView view(&model, &propertyTable); + MetadataPropertyTableView view(model, propertyTable); const ExtensionExtStructuralMetadataClassProperty* classProperty = view.getClassProperty("InvalidProperty"); REQUIRE(classProperty); @@ -2687,7 +2687,7 @@ TEST_CASE("Test StructuralMetadata callback for scalar property") { propertyTable.properties["TestClassProperty"]; propertyTableProperty.values = static_cast(valueBufferViewIndex); - MetadataPropertyTableView view(&model, &propertyTable); + MetadataPropertyTableView view(model, propertyTable); const ExtensionExtStructuralMetadataClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE( @@ -2773,7 +2773,7 @@ TEST_CASE("Test StructuralMetadata callback for vecN property") { propertyTable.properties["TestClassProperty"]; propertyTableProperty.values = static_cast(valueBufferViewIndex); - MetadataPropertyTableView view(&model, &propertyTable); + MetadataPropertyTableView view(model, propertyTable); const ExtensionExtStructuralMetadataClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE( @@ -2869,7 +2869,7 @@ TEST_CASE("Test StructuralMetadata callback for matN property") { propertyTable.properties["TestClassProperty"]; propertyTableProperty.values = static_cast(valueBufferViewIndex); - MetadataPropertyTableView view(&model, &propertyTable); + MetadataPropertyTableView view(model, propertyTable); const ExtensionExtStructuralMetadataClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE( @@ -2963,7 +2963,7 @@ TEST_CASE("Test StructuralMetadata callback for boolean property") { propertyTableProperty.values = static_cast(model.bufferViews.size() - 1); - MetadataPropertyTableView view(&model, &propertyTable); + MetadataPropertyTableView view(model, propertyTable); const ExtensionExtStructuralMetadataClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE( @@ -3075,7 +3075,7 @@ TEST_CASE("Test StructuralMetadata callback for string property") { propertyTableProperty.stringOffsets = static_cast(offsetBufferViewIndex); - MetadataPropertyTableView view(&model, &propertyTable); + MetadataPropertyTableView view(model, propertyTable); const ExtensionExtStructuralMetadataClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE( @@ -3155,7 +3155,7 @@ TEST_CASE("Test StructuralMetadata callback for scalar array") { propertyTableProperty.values = static_cast(model.bufferViews.size() - 1); - MetadataPropertyTableView view(&model, &propertyTable); + MetadataPropertyTableView view(model, propertyTable); const ExtensionExtStructuralMetadataClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE( @@ -3242,7 +3242,7 @@ TEST_CASE("Test StructuralMetadata callback for vecN array") { propertyTableProperty.values = static_cast(model.bufferViews.size() - 1); - MetadataPropertyTableView view(&model, &propertyTable); + MetadataPropertyTableView view(model, propertyTable); const ExtensionExtStructuralMetadataClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE( @@ -3343,7 +3343,7 @@ TEST_CASE("Test StructuralMetadata callback for matN array") { propertyTableProperty.values = static_cast(model.bufferViews.size() - 1); - MetadataPropertyTableView view(&model, &propertyTable); + MetadataPropertyTableView view(model, propertyTable); const ExtensionExtStructuralMetadataClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE( @@ -3445,7 +3445,7 @@ TEST_CASE("Test StructuralMetadata callback for boolean array") { propertyTableProperty.values = static_cast(model.bufferViews.size() - 1); - MetadataPropertyTableView view(&model, &propertyTable); + MetadataPropertyTableView view(model, propertyTable); const ExtensionExtStructuralMetadataClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE( @@ -3558,7 +3558,7 @@ TEST_CASE("Test StructuralMetadata callback for array of strings") { propertyTableProperty.stringOffsets = static_cast(offsetBufferViewIndex); - MetadataPropertyTableView view(&model, &propertyTable); + MetadataPropertyTableView view(model, propertyTable); const ExtensionExtStructuralMetadataClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE( From d9e234ab5f3723c8fd96d88b80c21364f2a6c96f Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Wed, 24 May 2023 11:48:13 -0400 Subject: [PATCH 040/421] Refactor error handling in MetadataPropertyTableView --- .../StructuralMetadataPropertyTableView.h | 77 +++++++++++++++++-- .../StructuralMetadataPropertyView.h | 33 +++++--- .../StructuralMetadataPropertyTableView.cpp | 30 +++++--- 3 files changed, 111 insertions(+), 29 deletions(-) diff --git a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTableView.h b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTableView.h index 492fca629..7cae91744 100644 --- a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTableView.h +++ b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTableView.h @@ -12,6 +12,38 @@ namespace CesiumGltf { namespace StructuralMetadata { +/** + * @brief Indicates the status of a property table view. + * + * The {@link MetadataPropertyTableView} constructor always completes successfully. + * However, it may not always reflect the actual content of the + * {@link ExtensionExtStructuralMetadataPropertyTable}, but instead indicate that its + * {@link MetadataPropertyTableView::size} is 0. This enumeration provides the reason. + */ +enum class MetadataPropertyTableViewStatus { + /** + * @brief This property table view is valid and ready to use. + */ + Valid, + + /** + * @brief The property table view's model does not contain an + * EXT_structural_metadata extension. + */ + ErrorNoStructuralMetadataExtension, + + /** + * @brief The property table view's model does not have a schema in its + * EXT_structural_metadata extension. + */ + ErrorNoSchema, + + /** + * @brief The class of the property table does not exist in the schema. + */ + ErrorPropertyTableClassDoesNotExist +}; + /** * @brief Utility to retrieve the data of * {@link ExtensionExtStructuralMetadataPropertyTable}. @@ -32,39 +64,63 @@ class MetadataPropertyTableView { const Model& model, const ExtensionExtStructuralMetadataPropertyTable& propertyTable); + /** + * @brief Gets the status of this property table view. + * + * Indicates whether the view accurately reflects the property table's data, + * or whether an error occurred. + * + * @return The status of this property table view. + */ + MetadataPropertyTableViewStatus status() const noexcept { return _status; } + + /** + * @brief Get the number of elements in this MetadataPropertyTableView. If the + * view is valid, this returns + * {@link ExtensionExtStructuralMetadataPropertyTable::count}. Otherwise, this returns 0. + * + * @return The number of elements in this MetadataPropertyTableView. + */ + int64_t size() const noexcept { + return _status == MetadataPropertyTableViewStatus::Valid + ? _propertyTable.count + : 0; + } + /** * @brief Finds the {@link ExtensionExtStructuralMetadataClassProperty} that * describes the type information of the property with the specified name. * @param propertyName The name of the property to retrieve the class for. * @return A pointer to the {@link ExtensionExtStructuralMetadataClassProperty}. - * Return nullptr if no property was found. + * Return nullptr if the MetadataPropertyTableView is invalid or if no class + * property was found. */ const ExtensionExtStructuralMetadataClassProperty* getClassProperty(const std::string& propertyName) const; /** - * @brief Gets a MetadataPropertyView that views the data of a property stored - * in the ExtensionExtStructuralMetadataPropertyTable. + * @brief Gets a {@link MetadataPropertyView} that views the data of a property stored + * in the {@link ExtensionExtStructuralMetadataPropertyTable}. * * This method will validate the EXT_structural_metadata format to ensure - * MetadataPropertyView retrieves the correct data. T must be one of the + * {@link MetadataPropertyView} retrieves the correct data. T must be one of the * following: a scalar (uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t, * uint64_t, int64_t, float, double), a glm vecN composed of one of the scalar * types, a glm matN composed of one of the scalar types, bool, - * std::string_view, or MetadataArrayView with T as one of the + * std::string_view, or {@link MetadataArrayView} with T as one of the * aforementioned types. * * @param propertyName The name of the property to retrieve data from - * @return A MetadataPropertyView of the property. If no valid property is + * @return A {@link MetadataPropertyView} of the property. If no valid property is * found, the property view will be invalid. */ template MetadataPropertyView getPropertyView(const std::string& propertyName) const { - if (_propertyTable.count <= 0) { + if (this->size() <= 0) { return createInvalidPropertyView( StructuralMetadata::MetadataPropertyViewStatus:: - ErrorPropertyDoesNotExist); + ErrorInvalidPropertyTable); } const ExtensionExtStructuralMetadataClassProperty* pClassProperty = @@ -103,6 +159,10 @@ class MetadataPropertyTableView { const ExtensionExtStructuralMetadataClassProperty* pClassProperty = getClassProperty(propertyName); if (!pClassProperty) { + callback( + propertyName, + createInvalidPropertyView( + MetadataPropertyViewStatus::ErrorPropertyDoesNotExist)); return; } @@ -1146,6 +1206,7 @@ class MetadataPropertyTableView { const Model& _model; const ExtensionExtStructuralMetadataPropertyTable& _propertyTable; const ExtensionExtStructuralMetadataClass* _pClass; + MetadataPropertyTableViewStatus _status; }; } // namespace StructuralMetadata diff --git a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyView.h b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyView.h index e98134dd1..4ad34057b 100644 --- a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyView.h +++ b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyView.h @@ -17,18 +17,24 @@ namespace StructuralMetadata { /** * @brief Indicates the status of a property view. * - * The {@link MetadataPropertyView} constructor always completes successfully. However, - * it may not always reflect the actual content of the {@link ExtensionExtStructuralMetadataPropertyTableProperty}, but - * instead indicate that its {@link MetadataPropertyView::size} is 0. This enumeration + * The {@link MetadataPropertyView} constructor always completes successfully. + * However, it may not always reflect the actual content of the + * {@link ExtensionExtStructuralMetadataPropertyTableProperty}, but instead + * indicate that its {@link MetadataPropertyView::size} is 0. This enumeration * provides the reason. */ - enum class MetadataPropertyViewStatus { /** * @brief This property view is valid and ready to use. */ Valid, + /** + * @brief This property view was attempting to view an invalid + * {@link ExtensionExtStructuralMetadataPropertyTable}. + */ + ErrorInvalidPropertyTable, + /** * @brief This property view does not exist in the * {@link ExtensionExtStructuralMetadataPropertyTable}. @@ -153,11 +159,12 @@ enum class MetadataPropertyViewStatus { /** * @brief A view on the data of the - * {@link ExtensionExtStructuralMetadataPropertyTableProperty} + * {@link ExtensionExtStructuralMetadataPropertyTableProperty that is created by + * a {@link MetadataPropertyTableView}. * * It provides utility to retrieve the actual data stored in the * {@link ExtensionExtStructuralMetadataPropertyTableProperty::values} like an array of elements. - * Data of each instance can be accessed through the {@link get(int64_t instance)} method + * Data of each instance can be accessed through the {@link get(int64_t instance)} method. * * @param ElementType must be one of the following: a scalar (uint8_t, int8_t, * uint16_t, int16_t, uint32_t, int32_t, uint64_t, int64_t, float, double), a @@ -240,6 +247,8 @@ template class MetadataPropertyView { * * Indicates whether the view accurately reflects the property's data, or * whether an error occurred. + * + * @return The status of this property view. */ MetadataPropertyViewStatus status() const noexcept { return _status; } @@ -285,13 +294,15 @@ template class MetadataPropertyView { } /** - * @brief Get the number of elements in the - * {@link ExtensionExtStructuralMetadataPropertyTable}. + * @brief Get the number of elements in this MetadataPropertyView. If the view + * is valid, this returns + * {@link ExtensionExtStructuralMetadataPropertyTable::count}. Otherwise, this returns 0. * - * @return The number of elements in the - * {@link ExtensionExtStructuralMetadataPropertyTable}. + * @return The number of elements in this MetadataPropertyView. */ - int64_t size() const noexcept { return _size; } + int64_t size() const noexcept { + return status() == MetadataPropertyViewStatus::Valid ? _size : 0; + } /** * @brief Get the element count of the fixed-length arrays in this property. diff --git a/CesiumGltf/src/StructuralMetadataPropertyTableView.cpp b/CesiumGltf/src/StructuralMetadataPropertyTableView.cpp index e48f26f6c..d47d48a0c 100644 --- a/CesiumGltf/src/StructuralMetadataPropertyTableView.cpp +++ b/CesiumGltf/src/StructuralMetadataPropertyTableView.cpp @@ -109,31 +109,41 @@ static MetadataPropertyViewStatus checkStringAndArrayOffsetsBuffers( MetadataPropertyTableView::MetadataPropertyTableView( const Model& model, const ExtensionExtStructuralMetadataPropertyTable& propertyTable) - : _model{model}, _propertyTable{propertyTable}, _pClass{nullptr} { + : _model{model}, + _propertyTable{propertyTable}, + _pClass{nullptr}, + _status() { const ExtensionModelExtStructuralMetadata* pMetadata = model.getExtension(); - assert( - pMetadata != nullptr && - "Model must contain ExtensionModelExtStructuralMetadata to use " - "MetadataPropertyTableView"); + + if (!pMetadata) { + _status = + MetadataPropertyTableViewStatus::ErrorNoStructuralMetadataExtension; + return; + } const std::optional& schema = pMetadata->schema; - assert( - schema != std::nullopt && - "ExtensionModelExtStructuralMetadata must contain " - "Schema to use MetadataPropertyTableView"); + if (!schema) { + _status = MetadataPropertyTableViewStatus::ErrorNoSchema; + return; + } auto classIter = schema->classes.find(_propertyTable.classProperty); if (classIter != schema->classes.end()) { _pClass = &classIter->second; } + + if (!_pClass) { + _status = + MetadataPropertyTableViewStatus::ErrorPropertyTableClassDoesNotExist; + } } const ExtensionExtStructuralMetadataClassProperty* MetadataPropertyTableView::getClassProperty( const std::string& propertyName) const { - if (_pClass == nullptr) { + if (_status != MetadataPropertyTableViewStatus::Valid) { return nullptr; } From bae45489ed9b342fd65851862f36534e2e484852 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Wed, 24 May 2023 12:36:28 -0400 Subject: [PATCH 041/421] Add tests --- .../StructuralMetadataPropertyTableView.h | 9 + ...estStructuralMetadataPropertyTableView.cpp | 311 +++++++++++++++++- 2 files changed, 306 insertions(+), 14 deletions(-) diff --git a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTableView.h b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTableView.h index 7cae91744..6d8b0cca7 100644 --- a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTableView.h +++ b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTableView.h @@ -156,6 +156,15 @@ class MetadataPropertyTableView { template void getPropertyView(const std::string& propertyName, Callback&& callback) const { + if (this->size() <= 0) { + callback( + propertyName, + createInvalidPropertyView( + StructuralMetadata::MetadataPropertyViewStatus:: + ErrorInvalidPropertyTable)); + return; + } + const ExtensionExtStructuralMetadataClassProperty* pClassProperty = getClassProperty(propertyName); if (!pClassProperty) { diff --git a/CesiumGltf/test/TestStructuralMetadataPropertyTableView.cpp b/CesiumGltf/test/TestStructuralMetadataPropertyTableView.cpp index 1bcdd26dc..4fb419302 100644 --- a/CesiumGltf/test/TestStructuralMetadataPropertyTableView.cpp +++ b/CesiumGltf/test/TestStructuralMetadataPropertyTableView.cpp @@ -7,6 +7,88 @@ using namespace CesiumGltf; using namespace CesiumGltf::StructuralMetadata; +TEST_CASE("Test model without EXT_structural_metadata extension") { + Model model; + + // Create an erroneously isolated property table. + ExtensionExtStructuralMetadataPropertyTable propertyTable; + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast(10); + + ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = static_cast(0); + + MetadataPropertyTableView view(model, propertyTable); + REQUIRE( + view.status() == + MetadataPropertyTableViewStatus::ErrorNoStructuralMetadataExtension); + REQUIRE(view.size() == 0); + + const ExtensionExtStructuralMetadataClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(!classProperty); +} + +TEST_CASE("Test model without metadata schema") { + Model model; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + ExtensionExtStructuralMetadataPropertyTable& propertyTable = + metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast(10); + + ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = static_cast(0); + + MetadataPropertyTableView view(model, propertyTable); + REQUIRE(view.status() == MetadataPropertyTableViewStatus::ErrorNoSchema); + REQUIRE(view.size() == 0); + + const ExtensionExtStructuralMetadataClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(!classProperty); +} + +TEST_CASE("Test property table with nonexistent class") { + Model model; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); + ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; + ExtensionExtStructuralMetadataClassProperty& testClassProperty = + testClass.properties["TestClassProperty"]; + testClassProperty.type = + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR; + testClassProperty.componentType = + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT32; + + ExtensionExtStructuralMetadataPropertyTable& propertyTable = + metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "I Don't Exist"; + propertyTable.count = static_cast(10); + + ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = static_cast(0); + + MetadataPropertyTableView view(model, propertyTable); + REQUIRE( + view.status() == + MetadataPropertyTableViewStatus::ErrorPropertyTableClassDoesNotExist); + REQUIRE(view.size() == 0); + + const ExtensionExtStructuralMetadataClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(!classProperty); +} + TEST_CASE("Test StructuralMetadata scalar property") { Model model; @@ -57,8 +139,12 @@ TEST_CASE("Test StructuralMetadata scalar property") { propertyTableProperty.values = static_cast(valueBufferViewIndex); MetadataPropertyTableView view(model, propertyTable); + REQUIRE(view.status() == MetadataPropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + const ExtensionExtStructuralMetadataClassProperty* classProperty = view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); REQUIRE( classProperty->type == ExtensionExtStructuralMetadataClassProperty::Type::SCALAR); @@ -232,8 +318,12 @@ TEST_CASE("Test StructuralMetadata vecN property") { propertyTableProperty.values = static_cast(valueBufferViewIndex); MetadataPropertyTableView view(model, propertyTable); + REQUIRE(view.status() == MetadataPropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + const ExtensionExtStructuralMetadataClassProperty* classProperty = view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); REQUIRE( classProperty->type == ExtensionExtStructuralMetadataClassProperty::Type::VEC3); @@ -423,8 +513,12 @@ TEST_CASE("Test StructuralMetadata matN property") { propertyTableProperty.values = static_cast(valueBufferViewIndex); MetadataPropertyTableView view(model, propertyTable); + REQUIRE(view.status() == MetadataPropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + const ExtensionExtStructuralMetadataClassProperty* classProperty = view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); REQUIRE( classProperty->type == ExtensionExtStructuralMetadataClassProperty::Type::MAT2); @@ -614,8 +708,12 @@ TEST_CASE("Test StructuralMetadata boolean property") { static_cast(model.bufferViews.size() - 1); MetadataPropertyTableView view(model, propertyTable); + REQUIRE(view.status() == MetadataPropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + const ExtensionExtStructuralMetadataClassProperty* classProperty = view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); REQUIRE( classProperty->type == ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN); @@ -725,8 +823,12 @@ TEST_CASE("Test StructuralMetadata string property") { static_cast(offsetBufferViewIndex); MetadataPropertyTableView view(model, propertyTable); + REQUIRE(view.status() == MetadataPropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + const ExtensionExtStructuralMetadataClassProperty* classProperty = view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); REQUIRE( classProperty->type == ExtensionExtStructuralMetadataClassProperty::Type::STRING); @@ -867,8 +969,12 @@ TEST_CASE("Test StructuralMetadata fixed-length scalar array") { static_cast(model.bufferViews.size() - 1); MetadataPropertyTableView view(model, propertyTable); + REQUIRE(view.status() == MetadataPropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + const ExtensionExtStructuralMetadataClassProperty* classProperty = view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); REQUIRE( classProperty->type == ExtensionExtStructuralMetadataClassProperty::Type::SCALAR); @@ -1037,8 +1143,12 @@ TEST_CASE("Test Structural Metadata variable-length scalar array") { UINT64; MetadataPropertyTableView view(model, propertyTable); + REQUIRE(view.status() == MetadataPropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + const ExtensionExtStructuralMetadataClassProperty* classProperty = view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); REQUIRE( classProperty->type == ExtensionExtStructuralMetadataClassProperty::Type::SCALAR); @@ -1192,8 +1302,12 @@ TEST_CASE("Test StructuralMetadata fixed-length vecN array") { static_cast(model.bufferViews.size() - 1); MetadataPropertyTableView view(model, propertyTable); + REQUIRE(view.status() == MetadataPropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + const ExtensionExtStructuralMetadataClassProperty* classProperty = view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); REQUIRE( classProperty->type == ExtensionExtStructuralMetadataClassProperty::Type::VEC3); @@ -1369,8 +1483,12 @@ TEST_CASE("Test Structural Metadata variable-length vecN array") { UINT64; MetadataPropertyTableView view(model, propertyTable); + REQUIRE(view.status() == MetadataPropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + const ExtensionExtStructuralMetadataClassProperty* classProperty = view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); REQUIRE( classProperty->type == ExtensionExtStructuralMetadataClassProperty::Type::VEC3); @@ -1544,8 +1662,12 @@ TEST_CASE("Test StructuralMetadata fixed-length matN array") { static_cast(model.bufferViews.size() - 1); MetadataPropertyTableView view(model, propertyTable); + REQUIRE(view.status() == MetadataPropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + const ExtensionExtStructuralMetadataClassProperty* classProperty = view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); REQUIRE( classProperty->type == ExtensionExtStructuralMetadataClassProperty::Type::MAT2); @@ -1742,8 +1864,12 @@ TEST_CASE("Test Structural Metadata variable-length matN array") { UINT64; MetadataPropertyTableView view(model, propertyTable); + REQUIRE(view.status() == MetadataPropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + const ExtensionExtStructuralMetadataClassProperty* classProperty = view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); REQUIRE( classProperty->type == ExtensionExtStructuralMetadataClassProperty::Type::MAT2); @@ -1915,8 +2041,12 @@ TEST_CASE("Test StructuralMetadata fixed-length boolean array") { static_cast(model.bufferViews.size() - 1); MetadataPropertyTableView view(model, propertyTable); + REQUIRE(view.status() == MetadataPropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + const ExtensionExtStructuralMetadataClassProperty* classProperty = view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); REQUIRE( classProperty->type == ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN); @@ -2064,8 +2194,12 @@ TEST_CASE("Test StructuralMetadata variable-length boolean array") { UINT64; MetadataPropertyTableView view(model, propertyTable); + REQUIRE(view.status() == MetadataPropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + const ExtensionExtStructuralMetadataClassProperty* classProperty = view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); REQUIRE( classProperty->type == ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN); @@ -2244,8 +2378,12 @@ TEST_CASE("Test StructuralMetadata fixed-length arrays of strings") { static_cast(offsetBufferViewIndex); MetadataPropertyTableView view(model, propertyTable); + REQUIRE(view.status() == MetadataPropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + const ExtensionExtStructuralMetadataClassProperty* classProperty = view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); REQUIRE( classProperty->type == ExtensionExtStructuralMetadataClassProperty::Type::STRING); @@ -2445,8 +2583,12 @@ TEST_CASE("Test StructuralMetadata variable-length arrays of strings") { static_cast(stringOffsetBufferView); MetadataPropertyTableView view(model, propertyTable); + REQUIRE(view.status() == MetadataPropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + const ExtensionExtStructuralMetadataClassProperty* classProperty = view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); REQUIRE( classProperty->type == ExtensionExtStructuralMetadataClassProperty::Type::STRING); @@ -2600,6 +2742,48 @@ TEST_CASE("Test StructuralMetadata variable-length arrays of strings") { } } +TEST_CASE("Test StructuralMetadata callback on invalid property table view") { + Model model; + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + metadata.schema.emplace(); + + // Property table has a nonexistent class. + ExtensionExtStructuralMetadataPropertyTable& propertyTable = + metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast(5); + + ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = static_cast(-1); + + MetadataPropertyTableView view(model, propertyTable); + REQUIRE( + view.status() == + MetadataPropertyTableViewStatus::ErrorPropertyTableClassDoesNotExist); + REQUIRE(view.size() == 0); + + const ExtensionExtStructuralMetadataClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(!classProperty); + + uint32_t invokedCallbackCount = 0; + view.getPropertyView( + "TestClassProperty", + [&invokedCallbackCount]( + const std::string& /*propertyName*/, + auto propertyValue) mutable { + invokedCallbackCount++; + REQUIRE( + propertyValue.status() == + MetadataPropertyViewStatus::ErrorInvalidPropertyTable); + REQUIRE(propertyValue.size() == 0); + }); + + REQUIRE(invokedCallbackCount == 1); +} + TEST_CASE("Test StructuralMetadata callback for invalid property") { Model model; ExtensionModelExtStructuralMetadata& metadata = @@ -2624,6 +2808,9 @@ TEST_CASE("Test StructuralMetadata callback for invalid property") { propertyTableProperty.values = static_cast(-1); MetadataPropertyTableView view(model, propertyTable); + REQUIRE(view.status() == MetadataPropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + const ExtensionExtStructuralMetadataClassProperty* classProperty = view.getClassProperty("InvalidProperty"); REQUIRE(classProperty); @@ -2631,14 +2818,19 @@ TEST_CASE("Test StructuralMetadata callback for invalid property") { classProperty = view.getClassProperty("NonexistentProperty"); REQUIRE(!classProperty); - auto testCallback = [](const std::string& /*propertyName*/, - auto propertyValue) mutable { + uint32_t invokedCallbackCount = 0; + auto testCallback = [&invokedCallbackCount]( + const std::string& /*propertyName*/, + auto propertyValue) mutable { + invokedCallbackCount++; REQUIRE(propertyValue.status() != MetadataPropertyViewStatus::Valid); REQUIRE(propertyValue.size() == 0); }; view.getPropertyView("InvalidProperty", testCallback); view.getPropertyView("NonexistentProperty", testCallback); + + REQUIRE(invokedCallbackCount == 2); } TEST_CASE("Test StructuralMetadata callback for scalar property") { @@ -2688,8 +2880,12 @@ TEST_CASE("Test StructuralMetadata callback for scalar property") { propertyTableProperty.values = static_cast(valueBufferViewIndex); MetadataPropertyTableView view(model, propertyTable); + REQUIRE(view.status() == MetadataPropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + const ExtensionExtStructuralMetadataClassProperty* classProperty = view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); REQUIRE( classProperty->type == ExtensionExtStructuralMetadataClassProperty::Type::SCALAR); @@ -2699,11 +2895,13 @@ TEST_CASE("Test StructuralMetadata callback for scalar property") { REQUIRE(!classProperty->array); REQUIRE(classProperty->count == std::nullopt); + uint32_t invokedCallbackCount = 0; view.getPropertyView( "TestClassProperty", - [&values]( + [&values, &invokedCallbackCount]( const std::string& /*propertyName*/, auto propertyValue) mutable { + invokedCallbackCount++; if constexpr (std::is_same_v< MetadataPropertyView, decltype(propertyValue)>) { @@ -2720,6 +2918,8 @@ TEST_CASE("Test StructuralMetadata callback for scalar property") { "type for TestClassProperty."); } }); + + REQUIRE(invokedCallbackCount == 1); } TEST_CASE("Test StructuralMetadata callback for vecN property") { @@ -2774,8 +2974,12 @@ TEST_CASE("Test StructuralMetadata callback for vecN property") { propertyTableProperty.values = static_cast(valueBufferViewIndex); MetadataPropertyTableView view(model, propertyTable); + REQUIRE(view.status() == MetadataPropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + const ExtensionExtStructuralMetadataClassProperty* classProperty = view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); REQUIRE( classProperty->type == ExtensionExtStructuralMetadataClassProperty::Type::VEC3); @@ -2785,11 +2989,14 @@ TEST_CASE("Test StructuralMetadata callback for vecN property") { REQUIRE(classProperty->count == std::nullopt); REQUIRE(!classProperty->array); + uint32_t invokedCallbackCount = 0; + view.getPropertyView( "TestClassProperty", - [&values]( + [&values, &invokedCallbackCount]( const std::string& /*propertyName*/, auto propertyValue) mutable { + invokedCallbackCount++; REQUIRE(propertyValue.status() == MetadataPropertyViewStatus::Valid); REQUIRE(propertyValue.size() > 0); @@ -2806,6 +3013,8 @@ TEST_CASE("Test StructuralMetadata callback for vecN property") { "type for TestClassProperty."); } }); + + REQUIRE(invokedCallbackCount == 1); } TEST_CASE("Test StructuralMetadata callback for matN property") { @@ -2870,8 +3079,12 @@ TEST_CASE("Test StructuralMetadata callback for matN property") { propertyTableProperty.values = static_cast(valueBufferViewIndex); MetadataPropertyTableView view(model, propertyTable); + REQUIRE(view.status() == MetadataPropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + const ExtensionExtStructuralMetadataClassProperty* classProperty = view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); REQUIRE( classProperty->type == ExtensionExtStructuralMetadataClassProperty::Type::MAT2); @@ -2881,9 +3094,10 @@ TEST_CASE("Test StructuralMetadata callback for matN property") { REQUIRE(classProperty->count == std::nullopt); REQUIRE(!classProperty->array); + uint32_t invokedCallbackCount = 0; view.getPropertyView( "TestClassProperty", - [&values]( + [&values, &invokedCallbackCount]( const std::string& /*propertyName*/, auto propertyValue) mutable { REQUIRE(propertyValue.status() == MetadataPropertyViewStatus::Valid); @@ -2901,7 +3115,10 @@ TEST_CASE("Test StructuralMetadata callback for matN property") { FAIL("getPropertyView returned MetadataPropertyView of incorrect " "type for TestClassProperty."); } + invokedCallbackCount++; }); + + REQUIRE(invokedCallbackCount == 1); } TEST_CASE("Test StructuralMetadata callback for boolean property") { @@ -2964,8 +3181,12 @@ TEST_CASE("Test StructuralMetadata callback for boolean property") { static_cast(model.bufferViews.size() - 1); MetadataPropertyTableView view(model, propertyTable); + REQUIRE(view.status() == MetadataPropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + const ExtensionExtStructuralMetadataClassProperty* classProperty = view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); REQUIRE( classProperty->type == ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN); @@ -2973,11 +3194,14 @@ TEST_CASE("Test StructuralMetadata callback for boolean property") { REQUIRE(classProperty->count == std::nullopt); REQUIRE(!classProperty->array); + uint32_t invokedCallbackCount = 0; view.getPropertyView( "TestClassProperty", - [&expected]( + [&expected, &invokedCallbackCount]( const std::string& /*propertyName*/, auto propertyValue) mutable { + invokedCallbackCount++; + REQUIRE(propertyValue.status() == MetadataPropertyViewStatus::Valid); REQUIRE(propertyValue.size() > 0); @@ -2994,6 +3218,8 @@ TEST_CASE("Test StructuralMetadata callback for boolean property") { "type for TestClassProperty."); } }); + + REQUIRE(invokedCallbackCount == 1); } TEST_CASE("Test StructuralMetadata callback for string property") { @@ -3076,8 +3302,12 @@ TEST_CASE("Test StructuralMetadata callback for string property") { static_cast(offsetBufferViewIndex); MetadataPropertyTableView view(model, propertyTable); + REQUIRE(view.status() == MetadataPropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + const ExtensionExtStructuralMetadataClassProperty* classProperty = view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); REQUIRE( classProperty->type == ExtensionExtStructuralMetadataClassProperty::Type::STRING); @@ -3085,11 +3315,13 @@ TEST_CASE("Test StructuralMetadata callback for string property") { REQUIRE(classProperty->count == std::nullopt); REQUIRE(!classProperty->array); + uint32_t invokedCallbackCount = 0; view.getPropertyView( "TestClassProperty", - [&expected]( + [&expected, &invokedCallbackCount]( const std::string& /*propertyName*/, auto propertyValue) mutable { + invokedCallbackCount++; REQUIRE(propertyValue.status() == MetadataPropertyViewStatus::Valid); REQUIRE(propertyValue.size() > 0); @@ -3106,6 +3338,8 @@ TEST_CASE("Test StructuralMetadata callback for string property") { "type for TestClassProperty."); } }); + + REQUIRE(invokedCallbackCount == 1); } TEST_CASE("Test StructuralMetadata callback for scalar array") { @@ -3156,8 +3390,12 @@ TEST_CASE("Test StructuralMetadata callback for scalar array") { static_cast(model.bufferViews.size() - 1); MetadataPropertyTableView view(model, propertyTable); + REQUIRE(view.status() == MetadataPropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + const ExtensionExtStructuralMetadataClassProperty* classProperty = view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); REQUIRE( classProperty->type == ExtensionExtStructuralMetadataClassProperty::Type::SCALAR); @@ -3167,9 +3405,13 @@ TEST_CASE("Test StructuralMetadata callback for scalar array") { REQUIRE(classProperty->array); REQUIRE(classProperty->count == 3); + uint32_t invokedCallbackCount = 0; view.getPropertyView( "TestClassProperty", - [&values](const std::string& /*propertyName*/, auto propertyValue) { + [&values, &invokedCallbackCount]( + const std::string& /*propertyName*/, + auto propertyValue) { + invokedCallbackCount++; REQUIRE(propertyValue.status() == MetadataPropertyViewStatus::Valid); REQUIRE(propertyValue.size() > 0); @@ -3187,6 +3429,8 @@ TEST_CASE("Test StructuralMetadata callback for scalar array") { "type for TestClassProperty."); } }); + + REQUIRE(invokedCallbackCount == 1); } TEST_CASE("Test StructuralMetadata callback for vecN array") { @@ -3243,8 +3487,12 @@ TEST_CASE("Test StructuralMetadata callback for vecN array") { static_cast(model.bufferViews.size() - 1); MetadataPropertyTableView view(model, propertyTable); + REQUIRE(view.status() == MetadataPropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + const ExtensionExtStructuralMetadataClassProperty* classProperty = view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); REQUIRE( classProperty->type == ExtensionExtStructuralMetadataClassProperty::Type::VEC3); @@ -3254,9 +3502,13 @@ TEST_CASE("Test StructuralMetadata callback for vecN array") { REQUIRE(classProperty->array); REQUIRE(classProperty->count == 2); + uint32_t invokedCallbackCount = 0; view.getPropertyView( "TestClassProperty", - [&values](const std::string& /*propertyName*/, auto propertyValue) { + [&values, &invokedCallbackCount]( + const std::string& /*propertyName*/, + auto propertyValue) { + invokedCallbackCount++; REQUIRE(propertyValue.status() == MetadataPropertyViewStatus::Valid); REQUIRE(propertyValue.size() > 0); @@ -3274,6 +3526,8 @@ TEST_CASE("Test StructuralMetadata callback for vecN array") { "type for TestClassProperty."); } }); + + REQUIRE(invokedCallbackCount == 1); } TEST_CASE("Test StructuralMetadata callback for matN array") { @@ -3344,8 +3598,12 @@ TEST_CASE("Test StructuralMetadata callback for matN array") { static_cast(model.bufferViews.size() - 1); MetadataPropertyTableView view(model, propertyTable); + REQUIRE(view.status() == MetadataPropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + const ExtensionExtStructuralMetadataClassProperty* classProperty = view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); REQUIRE( classProperty->type == ExtensionExtStructuralMetadataClassProperty::Type::MAT2); @@ -3355,9 +3613,13 @@ TEST_CASE("Test StructuralMetadata callback for matN array") { REQUIRE(classProperty->array); REQUIRE(classProperty->count == 2); + uint32_t invokedCallbackCount = 0; view.getPropertyView( "TestClassProperty", - [&values](const std::string& /*propertyName*/, auto propertyValue) { + [&values, &invokedCallbackCount]( + const std::string& /*propertyName*/, + auto propertyValue) { + invokedCallbackCount++; REQUIRE(propertyValue.status() == MetadataPropertyViewStatus::Valid); REQUIRE(propertyValue.size() > 0); @@ -3376,6 +3638,8 @@ TEST_CASE("Test StructuralMetadata callback for matN array") { "type for TestClassProperty."); } }); + + REQUIRE(invokedCallbackCount == 1); } TEST_CASE("Test StructuralMetadata callback for boolean array") { @@ -3446,17 +3710,25 @@ TEST_CASE("Test StructuralMetadata callback for boolean array") { static_cast(model.bufferViews.size() - 1); MetadataPropertyTableView view(model, propertyTable); + REQUIRE(view.status() == MetadataPropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + const ExtensionExtStructuralMetadataClassProperty* classProperty = view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); REQUIRE( classProperty->type == ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN); REQUIRE(classProperty->array); REQUIRE(classProperty->count == 3); + uint32_t invokedCallbackCount = 0; view.getPropertyView( "TestClassProperty", - [&expected](const std::string& /*propertyName*/, auto propertyValue) { + [&expected, &invokedCallbackCount]( + const std::string& /*propertyName*/, + auto propertyValue) { + invokedCallbackCount++; REQUIRE(propertyValue.status() == MetadataPropertyViewStatus::Valid); REQUIRE(propertyValue.size() > 0); @@ -3474,6 +3746,8 @@ TEST_CASE("Test StructuralMetadata callback for boolean array") { "type for TestClassProperty."); } }); + + REQUIRE(invokedCallbackCount == 1); } TEST_CASE("Test StructuralMetadata callback for array of strings") { @@ -3559,25 +3833,32 @@ TEST_CASE("Test StructuralMetadata callback for array of strings") { static_cast(offsetBufferViewIndex); MetadataPropertyTableView view(model, propertyTable); + REQUIRE(view.status() == MetadataPropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + const ExtensionExtStructuralMetadataClassProperty* classProperty = view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); REQUIRE( classProperty->type == ExtensionExtStructuralMetadataClassProperty::Type::STRING); REQUIRE(classProperty->array); REQUIRE(classProperty->count == 2); + uint32_t invokedCallbackCount = 0; view.getPropertyView( "TestClassProperty", - [](const std::string& /*propertyName*/, auto propertyValue) { + [&invokedCallbackCount]( + const std::string& /*propertyName*/, + auto propertyValue) { + invokedCallbackCount++; REQUIRE(propertyValue.status() == MetadataPropertyViewStatus::Valid); + REQUIRE(propertyValue.size() == 3); if constexpr (std::is_same_v< MetadataPropertyView< MetadataArrayView>, decltype(propertyValue)>) { - REQUIRE(propertyValue.size() == 3); - MetadataArrayView v0 = propertyValue.get(0); REQUIRE(v0.size() == 2); REQUIRE(v0[0] == "What's up"); @@ -3600,4 +3881,6 @@ TEST_CASE("Test StructuralMetadata callback for array of strings") { "type for TestClassProperty."); } }); + + REQUIRE(invokedCallbackCount == 1); } From 7f2ea4994013d0ca0ecd1c512a7fe5bcc0ac1c82 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Wed, 24 May 2023 12:42:07 -0400 Subject: [PATCH 042/421] Minor whitespace nitpicks --- CesiumGltf/src/StructuralMetadataPropertyTableView.cpp | 1 - CesiumGltf/test/TestStructuralMetadataPropertyTableView.cpp | 1 - 2 files changed, 2 deletions(-) diff --git a/CesiumGltf/src/StructuralMetadataPropertyTableView.cpp b/CesiumGltf/src/StructuralMetadataPropertyTableView.cpp index d47d48a0c..6e0dfb421 100644 --- a/CesiumGltf/src/StructuralMetadataPropertyTableView.cpp +++ b/CesiumGltf/src/StructuralMetadataPropertyTableView.cpp @@ -115,7 +115,6 @@ MetadataPropertyTableView::MetadataPropertyTableView( _status() { const ExtensionModelExtStructuralMetadata* pMetadata = model.getExtension(); - if (!pMetadata) { _status = MetadataPropertyTableViewStatus::ErrorNoStructuralMetadataExtension; diff --git a/CesiumGltf/test/TestStructuralMetadataPropertyTableView.cpp b/CesiumGltf/test/TestStructuralMetadataPropertyTableView.cpp index 4fb419302..95d148e1a 100644 --- a/CesiumGltf/test/TestStructuralMetadataPropertyTableView.cpp +++ b/CesiumGltf/test/TestStructuralMetadataPropertyTableView.cpp @@ -2990,7 +2990,6 @@ TEST_CASE("Test StructuralMetadata callback for vecN property") { REQUIRE(!classProperty->array); uint32_t invokedCallbackCount = 0; - view.getPropertyView( "TestClassProperty", [&values, &invokedCallbackCount]( From 8ad3f06a96aef72716d58b8de755fcf5155dcc91 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Wed, 24 May 2023 14:54:40 -0400 Subject: [PATCH 043/421] Convert batch table to EXT_structural_metadata instead --- .../src/B3dmToGltfConverter.cpp | 2 +- .../BatchTableToGltfStructuralMetadata.cpp | 1479 +++++++++-------- 2 files changed, 816 insertions(+), 665 deletions(-) diff --git a/Cesium3DTilesSelection/src/B3dmToGltfConverter.cpp b/Cesium3DTilesSelection/src/B3dmToGltfConverter.cpp index dc2689d38..8ee72dc22 100644 --- a/Cesium3DTilesSelection/src/B3dmToGltfConverter.cpp +++ b/Cesium3DTilesSelection/src/B3dmToGltfConverter.cpp @@ -219,7 +219,7 @@ void convertB3dmMetadataToGltfFeatureMetadata( return; } - // upgrade batch table to glTF feature metadata and append the result + // upgrade batch table to glTF structural metadata and append the result result.errors.merge(BatchTableToGltfStructuralMetadata::convertFromB3dm( featureTableJson, batchTableJson, diff --git a/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp b/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp index 98ca77a24..aeb71a6d1 100644 --- a/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp +++ b/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp @@ -4,44 +4,230 @@ #include "Cesium3DTilesSelection/spdlog-cesium.h" #include -#include +#include #include -#include -#include +#include +#include #include #include #include +#include #include #include #include using namespace CesiumGltf; +using namespace CesiumGltf::StructuralMetadata; using namespace Cesium3DTilesSelection::CesiumImpl; namespace Cesium3DTilesSelection { namespace { +/** + * Indicates how a JSON value can be interpreted. Does not correspond one-to-one + * with types / component types in EXT_structural_metadata. + */ struct MaskedType { - bool isInt8 = true; - bool isUint8 = true; - bool isInt16 = true; - bool isUint16 = true; - bool isInt32 = true; - bool isUint32 = true; - bool isInt64 = true; - bool isUint64 = true; - bool isFloat32 = true; - bool isFloat64 = true; - bool isBool = true; - bool isArray = true; + bool isInt8; + bool isUint8; + bool isInt16; + bool isUint16; + bool isInt32; + bool isUint32; + bool isInt64; + bool isUint64; + bool isFloat32; + bool isFloat64; + bool isBool; + + MaskedType() : MaskedType(true){}; + + MaskedType(bool defaultValue) + : isInt8(defaultValue), + isUint8(defaultValue), + isInt16(defaultValue), + isUint16(defaultValue), + isInt32(defaultValue), + isUint32(defaultValue), + isInt64(defaultValue), + isUint64(defaultValue), + isFloat32(defaultValue), + isFloat64(defaultValue), + isBool(defaultValue) {} + + /** + * Merges another MaskedType into this one. + */ + void operator&=(const MaskedType& source) { + isInt8 &= source.isInt8; + isUint8 &= source.isUint8; + isInt16 &= source.isInt16; + isUint16 &= source.isUint16; + isInt32 &= source.isInt32; + isUint32 &= source.isUint32; + isInt64 &= source.isInt64; + isUint64 &= source.isUint64; + isFloat32 &= source.isFloat32; + isFloat64 &= source.isFloat64; + isBool &= source.isBool; + } +}; + +/** + * Indicates how the elements of an array JSON value can be interpreted. Does + * not correspond one-to-one with types / component types in + * EXT_structural_metadata. + * + * To avoid complications while parsing, this implementation disallows array + * elements that are also arrays. The nested arrays will be treated as strings. + */ +struct MaskedArrayType { + MaskedType elementType; + uint32_t minArrayCount; + uint32_t maxArrayCount; + + MaskedArrayType() : MaskedArrayType(true){}; + + MaskedArrayType(bool defaultValue) + : elementType(defaultValue), + minArrayCount(std::numeric_limits::max()), + maxArrayCount(std::numeric_limits::min()){}; + + MaskedArrayType( + MaskedType elementType, + uint32_t minArrayCount, + uint32_t maxArrayCount) + : elementType(elementType), + minArrayCount(minArrayCount), + maxArrayCount(maxArrayCount) {} + + /** + * Merges another MaskedArrayType into this one. + */ + void operator&=(const MaskedArrayType& source) { + elementType &= source.elementType; + minArrayCount = glm::min(minArrayCount, source.minArrayCount); + maxArrayCount = glm::max(maxArrayCount, source.maxArrayCount); + } }; +/** + * Indicates a batch table property's compatibility with C++ types. + */ struct CompatibleTypes { - MaskedType type; - std::optional componentType; - std::optional minComponentCount; - std::optional maxComponentCount; + // std::monostate represents "complete" compatibility, in that nothing has + // been determined to be incompatible yet. Once something is either a + // MaskedType or MaskedArrayType, they are considered incompatible with the + // other type. + std::variant maskedType; + + CompatibleTypes() : maskedType(){}; + + CompatibleTypes(const MaskedType& maskedType) : maskedType(maskedType){}; + CompatibleTypes(const MaskedArrayType& maskedArrayType) + : maskedType(maskedArrayType){}; + + /** + * Whether this is only compatible with arrays. + */ + bool isArray() const { return std::get_if(&maskedType); } + + /** + * Marks as incompatible with every type. Fully-incompatible types will be + * treated as strings. + */ + void makeIncompatible() { + MaskedType incompatibleMaskedType(false); + maskedType = incompatibleMaskedType; + } + + /** + * Merges a MaskedType into this CompatibleTypes. + */ + void operator&=(const MaskedType& otherMaskedType) { + if (isArray()) { + makeIncompatible(); + return; + } + + MaskedType* pMaskedType = std::get_if(&maskedType); + if (pMaskedType) { + *pMaskedType &= otherMaskedType; + } else { + maskedType = otherMaskedType; + } + } + + /** + * Merges a MaskedArrayType into this CompatibleTypes. + */ + void operator&=(const MaskedArrayType& maskedArrayType) { + if (!isArray()) { + makeIncompatible(); + return; + } + + MaskedArrayType* pMaskedArrayType = + std::get_if(&maskedType); + if (pMaskedArrayType) { + *pMaskedArrayType &= maskedArrayType; + } else { + maskedType = maskedArrayType; + } + } + + /** + * Merges another CompatibleTypes into this one. + */ + void operator&=(const CompatibleTypes& otherTypes) { + if (otherTypes.isArray()) { + const MaskedArrayType& otherMaskedType = + std::get(otherTypes.maskedType); + operator&=(otherMaskedType); + } else { + const MaskedType& otherMaskedType = + std::get(otherTypes.maskedType); + operator&=(otherMaskedType); + } + } + + /** + * Derives MaskedType info from this CompatibleTypes. If this CompatibleTypes + * is only compatible with arrays, this will return an incompatible + * MaskedType. + */ + MaskedType toMaskedType() const { + if (isArray()) { + return MaskedType(false); + } + + const MaskedType* pMaskedType = std::get_if(&maskedType); + if (pMaskedType) { + return *pMaskedType; + } + + // If maskedType == std::monostate, then this CompatibleTypes is considered + // compatible with everything. + return MaskedType(true); + } + + /** + * Derives MaskedArrayType info from this CompatibleTypes. If this + * CompatibleTypes is not compatible with arrays, this will return an + * incompatible MaskedArrayType. + */ + MaskedArrayType toMaskedArrayType() const { + if (isArray()) { + return std::get(maskedType); + } + + // If maskedType is a MaskedType, it is incompatible. Otherwise, if + // maskedType == std::monostate, then this CompatibleTypes is considered + // compatible with everything. + const MaskedType* pMaskedType = std::get_if(&maskedType); + return MaskedArrayType(pMaskedType == nullptr); + } }; struct BinaryProperty { @@ -50,21 +236,71 @@ struct BinaryProperty { int64_t byteLength; }; -struct GltfFeatureTableType { - std::string typeName; - size_t typeSize; +struct GltfPropertyTableType { + std::string type; + size_t componentCount; }; -const std::map - batchTableComponentTypeToGltfType = { - {"BYTE", GltfFeatureTableType{"INT8", sizeof(int8_t)}}, - {"UNSIGNED_BYTE", GltfFeatureTableType{"UINT8", sizeof(uint8_t)}}, - {"SHORT", GltfFeatureTableType{"INT16", sizeof(int16_t)}}, - {"UNSIGNED_SHORT", GltfFeatureTableType{"UINT16", sizeof(uint16_t)}}, - {"INT", GltfFeatureTableType{"INT32", sizeof(int32_t)}}, - {"UNSIGNED_INT", GltfFeatureTableType{"UINT32", sizeof(uint32_t)}}, - {"FLOAT", GltfFeatureTableType{"FLOAT32", sizeof(float)}}, - {"DOUBLE", GltfFeatureTableType{"FLOAT64", sizeof(double)}}, +const std::map batchTableTypeToGltfType = { + {"SCALAR", + GltfPropertyTableType{ + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, + 1}}, + {"VEC2", + GltfPropertyTableType{ + ExtensionExtStructuralMetadataClassProperty::Type::VEC2, + 2}}, + {"VEC3", + GltfPropertyTableType{ + ExtensionExtStructuralMetadataClassProperty::Type::VEC3, + 3}}, + {"VEC4", + GltfPropertyTableType{ + ExtensionExtStructuralMetadataClassProperty::Type::VEC4, + 4}}, +}; + +struct GltfPropertyTableComponentType { + std::string componentType; + size_t componentTypeSize; +}; + +const std::map + batchTableComponentTypeToGltfComponentType = { + {"BYTE", + GltfPropertyTableComponentType{ + ExtensionExtStructuralMetadataClassProperty::ComponentType::INT8, + sizeof(int8_t)}}, + {"UNSIGNED_BYTE", + GltfPropertyTableComponentType{ + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT8, + sizeof(uint8_t)}}, + {"SHORT", + GltfPropertyTableComponentType{ + ExtensionExtStructuralMetadataClassProperty::ComponentType::INT16, + sizeof(int16_t)}}, + {"UNSIGNED_SHORT", + GltfPropertyTableComponentType{ + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT16, + sizeof(uint16_t)}}, + {"INT", + GltfPropertyTableComponentType{ + ExtensionExtStructuralMetadataClassProperty::ComponentType::INT32, + sizeof(int32_t)}}, + {"UNSIGNED_INT", + GltfPropertyTableComponentType{ + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT32, + sizeof(uint32_t)}}, + {"FLOAT", + GltfPropertyTableComponentType{ + ExtensionExtStructuralMetadataClassProperty::ComponentType:: + FLOAT32, + sizeof(float)}}, + {"DOUBLE", + GltfPropertyTableComponentType{ + ExtensionExtStructuralMetadataClassProperty::ComponentType:: + FLOAT64, + sizeof(double)}}, }; int64_t roundUp(int64_t num, int64_t multiple) noexcept { @@ -149,132 +385,135 @@ class ArrayOfPropertyValues { const rapidjson::Value& _propertyValues; }; +CompatibleTypes findCompatibleTypesForBoolean() { + MaskedType type; + // Don't allow conversion of bools to numeric 0 or 1. + type.isInt8 = type.isUint8 = false; + type.isInt16 = type.isUint16 = false; + type.isInt32 = type.isUint32 = false; + type.isInt64 = type.isUint64 = false; + type.isFloat32 = false; + type.isFloat64 = false; + type.isBool &= true; + + return CompatibleTypes(type); +} + +template +CompatibleTypes findCompatibleTypesForNumber(const TValueIter& it) { + MaskedType type; + type.isBool = false; + + if (it->IsInt64()) { + const int64_t value = it->GetInt64(); + type.isInt8 = isInRangeForSignedInteger(value); + type.isUint8 = isInRangeForSignedInteger(value); + type.isInt16 = isInRangeForSignedInteger(value); + type.isUint16 = isInRangeForSignedInteger(value); + type.isInt32 = isInRangeForSignedInteger(value); + type.isUint32 = isInRangeForSignedInteger(value); + type.isInt64 = true; + type.isUint64 = value >= 0; + type.isFloat32 = it->IsLosslessFloat(); + type.isFloat64 = it->IsLosslessDouble(); + } else if (it->IsUint64()) { + // Only uint64_t can represent a value that fits in a uint64_t but not in + // an int64_t. + type.isInt8 = type.isUint8 = false; + type.isInt16 = type.isUint16 = false; + type.isInt32 = type.isUint32 = false; + type.isInt64 = false; + type.isUint64 = true; + type.isFloat32 = false; + type.isFloat64 = false; + } else if (it->IsLosslessFloat()) { + type.isInt8 = type.isUint8 = false; + type.isInt16 = type.isUint16 = false; + type.isInt32 = type.isUint32 = false; + type.isInt64 = type.isUint64 = false; + type.isFloat32 = true; + type.isFloat64 = true; + } else if (it->IsDouble()) { + type.isInt8 = type.isUint8 = false; + type.isInt16 = type.isUint16 = false; + type.isInt32 = type.isUint32 = false; + type.isInt64 = type.isUint64 = false; + type.isFloat32 = false; + type.isFloat64 = true; + } + + return CompatibleTypes(type); +} + +template +CompatibleTypes findCompatibleTypesForArray(const TValueIter& it) { + // Iterate over all of the elements in the array and determine their + // compatible type. + CompatibleTypes arrayElementCompatibleTypes = + findCompatibleTypes(ArrayOfPropertyValues(*it)); + + if (arrayElementCompatibleTypes.isArray()) { + // Ignore complications with arrays of arrays. The elements will be treated + // like strings. + arrayElementCompatibleTypes.makeIncompatible(); + assert(!arrayElementCompatibleTypes.isArray()); + } + + MaskedType elementType = arrayElementCompatibleTypes.toMaskedType(); + MaskedArrayType arrayType(elementType, it->Size(), it->Size()); + + return CompatibleTypes(arrayType); +} + template CompatibleTypes findCompatibleTypes(const TValueGetter& propertyValue) { - MaskedType type; - std::optional componentType; - std::optional minComponentCount; - std::optional maxComponentCount; + CompatibleTypes compatibleTypes; for (auto it = propertyValue.begin(); it != propertyValue.end(); ++it) { if (it->IsBool()) { - // Should we allow conversion of bools to numeric 0 or 1? Nah. - type.isInt8 = type.isUint8 = false; - type.isInt16 = type.isUint16 = false; - type.isInt32 = type.isUint32 = false; - type.isInt64 = type.isUint64 = false; - type.isFloat32 = false; - type.isFloat64 = false; - type.isBool &= true; - type.isArray = false; - } else if (it->IsInt64()) { - const int64_t value = it->GetInt64(); - type.isInt8 &= isInRangeForSignedInteger(value); - type.isUint8 &= isInRangeForSignedInteger(value); - type.isInt16 &= isInRangeForSignedInteger(value); - type.isUint16 &= isInRangeForSignedInteger(value); - type.isInt32 &= isInRangeForSignedInteger(value); - type.isUint32 &= isInRangeForSignedInteger(value); - type.isInt64 &= true; - type.isUint64 &= value >= 0; - type.isFloat32 &= it->IsLosslessFloat(); - type.isFloat64 &= it->IsLosslessDouble(); - type.isBool = false; - type.isArray = false; - } else if (it->IsUint64()) { - // Only uint64_t can represent a value that fits in a uint64_t but not in - // an int64_t. - type.isInt8 = type.isUint8 = false; - type.isInt16 = type.isUint16 = false; - type.isInt32 = type.isUint32 = false; - type.isInt64 = false; - type.isUint64 &= true; - type.isFloat32 = false; - type.isFloat64 = false; - type.isBool = false; - type.isArray = false; - } else if (it->IsLosslessFloat()) { - type.isInt8 = type.isUint8 = false; - type.isInt16 = type.isUint16 = false; - type.isInt32 = type.isUint32 = false; - type.isInt64 = type.isUint64 = false; - type.isFloat32 &= true; - type.isFloat64 &= true; - type.isBool = false; - type.isArray = false; - } else if (it->IsDouble()) { - type.isInt8 = type.isUint8 = false; - type.isInt16 = type.isUint16 = false; - type.isInt32 = type.isUint32 = false; - type.isInt64 = type.isUint64 = false; - type.isFloat32 = false; - type.isFloat64 &= true; - type.isBool = false; - type.isArray = false; + compatibleTypes &= findCompatibleTypesForBoolean(); + } else if (it->IsNumber()) { + compatibleTypes &= findCompatibleTypesForNumber(it); } else if (it->IsArray()) { - type.isInt8 = type.isUint8 = false; - type.isInt16 = type.isUint16 = false; - type.isInt32 = type.isUint32 = false; - type.isInt64 = type.isUint64 = false; - type.isFloat32 = false; - type.isFloat64 = false; - type.isBool = false; - type.isArray &= true; - CompatibleTypes currentComponentType = - findCompatibleTypes(ArrayOfPropertyValues(*it)); - if (!componentType) { - componentType = currentComponentType.type; - } else { - componentType->isInt8 &= currentComponentType.type.isInt8; - componentType->isUint8 &= currentComponentType.type.isUint8; - componentType->isInt16 &= currentComponentType.type.isInt16; - componentType->isUint16 &= currentComponentType.type.isUint16; - componentType->isInt32 &= currentComponentType.type.isInt32; - componentType->isUint32 &= currentComponentType.type.isUint32; - componentType->isInt64 &= currentComponentType.type.isInt64; - componentType->isUint64 &= currentComponentType.type.isUint64; - componentType->isFloat32 &= currentComponentType.type.isFloat32; - componentType->isFloat64 &= currentComponentType.type.isFloat64; - componentType->isBool &= currentComponentType.type.isBool; - componentType->isArray &= currentComponentType.type.isArray; - } - - maxComponentCount = maxComponentCount - ? glm::max(*maxComponentCount, it->Size()) - : it->Size(); - minComponentCount = minComponentCount - ? glm::min(*minComponentCount, it->Size()) - : it->Size(); + compatibleTypes &= findCompatibleTypesForArray(it); } else { // A string, null, or something else. - type.isInt8 = type.isUint8 = false; - type.isInt16 = type.isUint16 = false; - type.isInt32 = type.isUint32 = false; - type.isInt64 = type.isUint64 = false; - type.isFloat32 = false; - type.isFloat64 = false; - type.isBool = false; - type.isArray = false; + compatibleTypes.makeIncompatible(); } } - return {type, componentType, minComponentCount, maxComponentCount}; + return compatibleTypes; +} + +int32_t addBufferToGltf(Model& gltf, std::vector&& buffer) { + const size_t gltfBufferIndex = gltf.buffers.size(); + Buffer& gltfBuffer = gltf.buffers.emplace_back(); + gltfBuffer.byteLength = static_cast(buffer.size()); + gltfBuffer.cesium.data = std::move(buffer); + + const size_t bufferViewIndex = gltf.bufferViews.size(); + BufferView& bufferView = gltf.bufferViews.emplace_back(); + bufferView.buffer = static_cast(gltfBufferIndex); + bufferView.byteOffset = 0; + bufferView.byteLength = gltfBuffer.byteLength; + + return static_cast(bufferViewIndex); } template void updateExtensionWithJsonStringProperty( Model& gltf, - ClassProperty& classProperty, - const FeatureTable& featureTable, - FeatureTableProperty& featureTableProperty, + ExtensionExtStructuralMetadataClassProperty& classProperty, + const ExtensionExtStructuralMetadataPropertyTable& propertyTable, + ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty, const TValueGetter& propertyValue) { rapidjson::StringBuffer rapidjsonStrBuffer; std::vector rapidjsonOffsets; - rapidjsonOffsets.reserve(static_cast(featureTable.count + 1)); + rapidjsonOffsets.reserve(static_cast(propertyTable.count + 1)); rapidjsonOffsets.emplace_back(0); auto it = propertyValue.begin(); - for (int64_t i = 0; i < featureTable.count; ++i) { + for (int64_t i = 0; i < propertyTable.count; ++i) { if (it == propertyValue.end()) { rapidjsonOffsets.emplace_back(rapidjsonStrBuffer.GetLength()); continue; @@ -284,9 +523,9 @@ void updateExtensionWithJsonStringProperty( rapidjson::Writer writer(rapidjsonStrBuffer); it->Accept(writer); } else { - // Because serialized string json will add double quotations in the buffer - // which is not needed by us, we will manually add the string to the - // buffer + // Because serialized string json will add double quotations in the + // buffer which is not needed by us, we will manually add the string to + // the buffer const auto& rapidjsonStr = it->GetString(); rapidjsonStrBuffer.Reserve(it->GetStringLength()); for (rapidjson::SizeType j = 0; j < it->GetStringLength(); ++j) { @@ -307,28 +546,36 @@ void updateExtensionWithJsonStringProperty( rapidjsonOffsets, buffer, offsetBuffer); - featureTableProperty.offsetType = "UINT8"; + propertyTableProperty.stringOffsetType = + ExtensionExtStructuralMetadataPropertyTableProperty::StringOffsetType:: + UINT8; } else if (isInRangeForUnsignedInteger(totalSize)) { copyStringBuffer( rapidjsonStrBuffer, rapidjsonOffsets, buffer, offsetBuffer); - featureTableProperty.offsetType = "UINT16"; + propertyTableProperty.stringOffsetType = + ExtensionExtStructuralMetadataPropertyTableProperty::StringOffsetType:: + UINT16; } else if (isInRangeForUnsignedInteger(totalSize)) { copyStringBuffer( rapidjsonStrBuffer, rapidjsonOffsets, buffer, offsetBuffer); - featureTableProperty.offsetType = "UINT32"; + propertyTableProperty.stringOffsetType = + ExtensionExtStructuralMetadataPropertyTableProperty::StringOffsetType:: + UINT32; } else { copyStringBuffer( rapidjsonStrBuffer, rapidjsonOffsets, buffer, offsetBuffer); - featureTableProperty.offsetType = "UINT64"; + propertyTableProperty.stringOffsetType = + ExtensionExtStructuralMetadataPropertyTableProperty::StringOffsetType:: + UINT64; } Buffer& gltfBuffer = gltf.buffers.emplace_back(); @@ -354,85 +601,70 @@ void updateExtensionWithJsonStringProperty( const int32_t offsetBufferViewIdx = static_cast(gltf.bufferViews.size() - 1); - classProperty.type = "STRING"; + classProperty.type = + ExtensionExtStructuralMetadataClassProperty::Type::STRING; - featureTableProperty.bufferView = valueBufferViewIdx; - featureTableProperty.stringOffsetBufferView = offsetBufferViewIdx; + propertyTableProperty.values = valueBufferViewIdx; + propertyTableProperty.stringOffsets = offsetBufferViewIdx; } template -void updateExtensionWithJsonNumericProperty( +void updateExtensionWithJsonScalarProperty( Model& gltf, - ClassProperty& classProperty, - const FeatureTable& featureTable, - FeatureTableProperty& featureTableProperty, + ExtensionExtStructuralMetadataClassProperty& classProperty, + const ExtensionExtStructuralMetadataPropertyTable& propertyTable, + ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty, const TValueGetter& propertyValue, - const std::string& typeName) { - assert(propertyValue.size() >= featureTable.count); + const std::string& componentTypeName) { + assert(propertyValue.size() >= propertyTable.count); - classProperty.type = typeName; + classProperty.type = + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR; + classProperty.componentType = componentTypeName; // Create a new buffer for this property. - const size_t bufferIndex = gltf.buffers.size(); - Buffer& buffer = gltf.buffers.emplace_back(); - buffer.byteLength = - static_cast(sizeof(T) * static_cast(featureTable.count)); - buffer.cesium.data.resize(static_cast(buffer.byteLength)); - - const size_t bufferViewIndex = gltf.bufferViews.size(); - BufferView& bufferView = gltf.bufferViews.emplace_back(); - bufferView.buffer = int32_t(bufferIndex); - bufferView.byteOffset = 0; - bufferView.byteLength = buffer.byteLength; - - featureTableProperty.bufferView = int32_t(bufferViewIndex); + std::vector buffer; + const size_t byteLength = + sizeof(T) * static_cast(propertyTable.count); + buffer.resize(byteLength); - T* p = reinterpret_cast(buffer.cesium.data.data()); + T* p = reinterpret_cast(buffer.data()); auto it = propertyValue.begin(); - for (int64_t i = 0; i < featureTable.count; ++i) { + for (int64_t i = 0; i < propertyTable.count; ++i) { *p = static_cast(it->template Get()); ++p; ++it; } + + propertyTableProperty.values = addBufferToGltf(gltf, std::move(buffer)); } template -void updateExtensionWithJsonBoolProperty( +void updateExtensionWithJsonBooleanProperty( Model& gltf, - ClassProperty& classProperty, - const FeatureTable& featureTable, - FeatureTableProperty& featureTableProperty, + ExtensionExtStructuralMetadataClassProperty& classProperty, + const ExtensionExtStructuralMetadataPropertyTable& propertyTable, + ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty, const TValueGetter& propertyValue) { - assert(propertyValue.size() >= featureTable.count); + assert(propertyValue.size() >= propertyTable.count); - std::vector data(static_cast( - glm::ceil(static_cast(featureTable.count) / 8.0))); + std::vector buffer(static_cast( + glm::ceil(static_cast(propertyTable.count) / 8.0))); auto it = propertyValue.begin(); for (rapidjson::SizeType i = 0; - i < static_cast(featureTable.count); + i < static_cast(propertyTable.count); ++i) { const bool value = it->GetBool(); const size_t byteIndex = i / 8; const size_t bitIndex = i % 8; - data[byteIndex] = - static_cast(value << bitIndex) | data[byteIndex]; + buffer[byteIndex] = + static_cast(value << bitIndex) | buffer[byteIndex]; ++it; } - const size_t bufferIndex = gltf.buffers.size(); - Buffer& buffer = gltf.buffers.emplace_back(); - buffer.byteLength = static_cast(data.size()); - buffer.cesium.data = std::move(data); - - BufferView& bufferView = gltf.bufferViews.emplace_back(); - bufferView.buffer = static_cast(bufferIndex); - bufferView.byteOffset = 0; - bufferView.byteLength = buffer.byteLength; - - featureTableProperty.bufferView = - static_cast(gltf.bufferViews.size() - 1); - - classProperty.type = "BOOLEAN"; + classProperty.type = + ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN; + propertyTableProperty.values = addBufferToGltf(gltf, std::move(buffer)); } template < @@ -440,20 +672,20 @@ template < typename ValueType, typename OffsetType, typename TValueGetter> -void copyNumericDynamicArrayBuffers( +void copyVariableLengthScalarArraysToBuffers( std::vector& valueBuffer, std::vector& offsetBuffer, size_t numOfElements, - const FeatureTable& featureTable, + const ExtensionExtStructuralMetadataPropertyTable& propertyTable, const TValueGetter& propertyValue) { valueBuffer.resize(sizeof(ValueType) * numOfElements); offsetBuffer.resize( - sizeof(OffsetType) * static_cast(featureTable.count + 1)); + sizeof(OffsetType) * static_cast(propertyTable.count + 1)); ValueType* value = reinterpret_cast(valueBuffer.data()); OffsetType* offsetValue = reinterpret_cast(offsetBuffer.data()); OffsetType prevOffset = 0; auto it = propertyValue.begin(); - for (int64_t i = 0; i < featureTable.count; ++i) { + for (int64_t i = 0; i < propertyTable.count; ++i) { const auto& jsonArrayMember = *it; *offsetValue = prevOffset; ++offsetValue; @@ -472,23 +704,31 @@ void copyNumericDynamicArrayBuffers( } template -void updateNumericArrayProperty( +void updateScalarArrayProperty( Model& gltf, - ClassProperty& classProperty, - FeatureTableProperty& featureTableProperty, - const FeatureTable& featureTable, - const CompatibleTypes& compatibleTypes, + ExtensionExtStructuralMetadataClassProperty& classProperty, + ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty, + const ExtensionExtStructuralMetadataPropertyTable& propertyTable, + const MaskedArrayType& arrayType, const TValueGetter& propertyValue) { - assert(propertyValue.size() >= featureTable.count); - - // check if it's a fixed array - if (compatibleTypes.minComponentCount == compatibleTypes.maxComponentCount) { - const size_t numOfValues = static_cast(featureTable.count) * - *compatibleTypes.minComponentCount; + assert(propertyValue.size() >= propertyTable.count); + + classProperty.type = + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR; + classProperty.componentType = + convertPropertyComponentTypeToString(static_cast( + TypeToPropertyType::component)); + classProperty.array = true; + + // Handle fixed-length arrays. + if (arrayType.minArrayCount == arrayType.maxArrayCount) { + const size_t arrayCount = static_cast(arrayType.minArrayCount); + const size_t numOfValues = + static_cast(propertyTable.count) * arrayCount; std::vector valueBuffer(sizeof(ValueType) * numOfValues); ValueType* value = reinterpret_cast(valueBuffer.data()); auto it = propertyValue.begin(); - for (int64_t i = 0; i < featureTable.count; ++i) { + for (int64_t i = 0; i < propertyTable.count; ++i) { const auto& jsonArrayMember = *it; for (const auto& valueJson : jsonArrayMember.GetArray()) { *value = static_cast(valueJson.template Get()); @@ -497,121 +737,82 @@ void updateNumericArrayProperty( ++it; } - Buffer& gltfValueBuffer = gltf.buffers.emplace_back(); - gltfValueBuffer.byteLength = static_cast(valueBuffer.size()); - gltfValueBuffer.cesium.data = std::move(valueBuffer); - - BufferView& gltfValueBufferView = gltf.bufferViews.emplace_back(); - gltfValueBufferView.buffer = static_cast(gltf.buffers.size() - 1); - gltfValueBufferView.byteOffset = 0; - gltfValueBufferView.byteLength = - static_cast(gltfValueBuffer.cesium.data.size()); - - classProperty.type = "ARRAY"; - classProperty.componentType = convertPropertyTypeToString( - static_cast(TypeToPropertyType::value)); - classProperty.componentCount = *compatibleTypes.minComponentCount; - - featureTableProperty.bufferView = - static_cast(gltf.bufferViews.size() - 1); + classProperty.count = arrayCount; + propertyTableProperty.values = + addBufferToGltf(gltf, std::move(valueBuffer)); return; } - // total size of value buffer - size_t numOfElements = 0; + // Handle variable-length arrays. + // Compute total size of the value buffer. + size_t totalNumElements = 0; auto it = propertyValue.begin(); - for (int64_t i = 0; i < featureTable.count; ++i) { + for (int64_t i = 0; i < propertyTable.count; ++i) { const auto& jsonArrayMember = *it; - numOfElements += jsonArrayMember.Size(); + totalNumElements += jsonArrayMember.Size(); ++it; } - PropertyType offsetType = PropertyType::None; + PropertyComponentType offsetType = PropertyComponentType::None; std::vector valueBuffer; std::vector offsetBuffer; - const uint64_t maxOffsetValue = numOfElements * sizeof(ValueType); + const uint64_t maxOffsetValue = totalNumElements * sizeof(ValueType); if (isInRangeForUnsignedInteger(maxOffsetValue)) { - copyNumericDynamicArrayBuffers( + copyVariableLengthScalarArraysToBuffers( valueBuffer, offsetBuffer, - numOfElements, - featureTable, + totalNumElements, + propertyTable, propertyValue); - offsetType = PropertyType::Uint8; + offsetType = PropertyComponentType::Uint8; } else if (isInRangeForUnsignedInteger(maxOffsetValue)) { - copyNumericDynamicArrayBuffers( + copyVariableLengthScalarArraysToBuffers( valueBuffer, offsetBuffer, - numOfElements, - featureTable, + totalNumElements, + propertyTable, propertyValue); - offsetType = PropertyType::Uint16; + offsetType = PropertyComponentType::Uint16; } else if (isInRangeForUnsignedInteger(maxOffsetValue)) { - copyNumericDynamicArrayBuffers( + copyVariableLengthScalarArraysToBuffers( valueBuffer, offsetBuffer, - numOfElements, - featureTable, + totalNumElements, + propertyTable, propertyValue); - offsetType = PropertyType::Uint32; + offsetType = PropertyComponentType::Uint32; } else if (isInRangeForUnsignedInteger(maxOffsetValue)) { - copyNumericDynamicArrayBuffers( + copyVariableLengthScalarArraysToBuffers( valueBuffer, offsetBuffer, - numOfElements, - featureTable, + totalNumElements, + propertyTable, propertyValue); - offsetType = PropertyType::Uint64; + offsetType = PropertyComponentType::Uint64; } - Buffer& gltfValueBuffer = gltf.buffers.emplace_back(); - gltfValueBuffer.byteLength = static_cast(valueBuffer.size()); - gltfValueBuffer.cesium.data = std::move(valueBuffer); - - BufferView& gltfValueBufferView = gltf.bufferViews.emplace_back(); - gltfValueBufferView.buffer = static_cast(gltf.buffers.size() - 1); - gltfValueBufferView.byteOffset = 0; - gltfValueBufferView.byteLength = - static_cast(gltfValueBuffer.cesium.data.size()); - const int32_t valueBufferIdx = - static_cast(gltf.bufferViews.size() - 1); - - Buffer& gltfOffsetBuffer = gltf.buffers.emplace_back(); - gltfOffsetBuffer.byteLength = static_cast(offsetBuffer.size()); - gltfOffsetBuffer.cesium.data = std::move(offsetBuffer); - - BufferView& gltfOffsetBufferView = gltf.bufferViews.emplace_back(); - gltfOffsetBufferView.buffer = static_cast(gltf.buffers.size() - 1); - gltfOffsetBufferView.byteOffset = 0; - gltfOffsetBufferView.byteLength = - static_cast(gltfOffsetBuffer.cesium.data.size()); - const int32_t offsetBufferIdx = - static_cast(gltf.bufferViews.size() - 1); - - classProperty.type = "ARRAY"; - classProperty.componentType = convertPropertyTypeToString( - static_cast(TypeToPropertyType::value)); - - featureTableProperty.bufferView = valueBufferIdx; - featureTableProperty.arrayOffsetBufferView = offsetBufferIdx; - featureTableProperty.offsetType = convertPropertyTypeToString(offsetType); + propertyTableProperty.values = addBufferToGltf(gltf, std::move(valueBuffer)); + propertyTableProperty.arrayOffsets = + addBufferToGltf(gltf, std::move(offsetBuffer)); + propertyTableProperty.arrayOffsetType = + convertPropertyComponentTypeToString(offsetType); } template -void copyStringArrayBuffers( +void copyStringsToBuffers( std::vector& valueBuffer, std::vector& offsetBuffer, size_t totalByteLength, size_t numOfString, - const FeatureTable& featureTable, + const ExtensionExtStructuralMetadataPropertyTable& propertyTable, const TValueGetter& propertyValue) { valueBuffer.resize(totalByteLength); offsetBuffer.resize((numOfString + 1) * sizeof(OffsetType)); OffsetType offset = 0; size_t offsetIndex = 0; auto it = propertyValue.begin(); - for (int64_t i = 0; i < featureTable.count; ++i) { + for (int64_t i = 0; i < propertyTable.count; ++i) { const auto& arrayMember = *it; for (const auto& str : arrayMember.GetArray()) { OffsetType byteLength = static_cast( @@ -634,16 +835,16 @@ void copyStringArrayBuffers( } template -void copyArrayOffsetBufferForStringArrayProperty( +void copyArrayOffsetsForStringArraysToBuffer( std::vector& offsetBuffer, - const FeatureTable& featureTable, + const ExtensionExtStructuralMetadataPropertyTable& propertyTable, const TValueGetter& propertyValue) { OffsetType prevOffset = 0; offsetBuffer.resize( - static_cast(featureTable.count + 1) * sizeof(OffsetType)); + static_cast(propertyTable.count + 1) * sizeof(OffsetType)); OffsetType* offset = reinterpret_cast(offsetBuffer.data()); auto it = propertyValue.begin(); - for (int64_t i = 0; i < featureTable.count; ++i) { + for (int64_t i = 0; i < propertyTable.count; ++i) { const auto& arrayMember = *it; *offset = prevOffset; prevOffset = static_cast( @@ -658,175 +859,137 @@ void copyArrayOffsetBufferForStringArrayProperty( template void updateStringArrayProperty( Model& gltf, - ClassProperty& classProperty, - FeatureTableProperty& featureTableProperty, - const FeatureTable& featureTable, - const CompatibleTypes& compatibleTypes, + ExtensionExtStructuralMetadataClassProperty& classProperty, + ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty, + const ExtensionExtStructuralMetadataPropertyTable& propertyTable, + const MaskedArrayType& arrayType, const TValueGetter& propertyValue) { - assert(propertyValue.size() >= featureTable.count); + assert(propertyValue.size() >= propertyTable.count); - size_t numOfString = 0; - size_t totalChars = 0; + size_t stringCount = 0; + size_t totalCharCount = 0; auto it = propertyValue.begin(); - for (int64_t i = 0; i < featureTable.count; ++i) { + for (int64_t i = 0; i < propertyTable.count; ++i) { const auto& arrayMember = *it; - numOfString += arrayMember.Size(); + stringCount += arrayMember.Size(); for (const auto& str : arrayMember.GetArray()) { - totalChars += str.GetStringLength(); + totalCharCount += str.GetStringLength(); } ++it; } - const uint64_t totalByteLength = totalChars * sizeof(rapidjson::Value::Ch); + const uint64_t totalByteLength = + totalCharCount * sizeof(rapidjson::Value::Ch); std::vector valueBuffer; - std::vector offsetBuffer; - PropertyType offsetType = PropertyType::None; + std::vector stringOffsetBuffer; + PropertyComponentType stringOffsetType = PropertyComponentType::None; if (isInRangeForUnsignedInteger(totalByteLength)) { - copyStringArrayBuffers( + copyStringsToBuffers( valueBuffer, - offsetBuffer, + stringOffsetBuffer, totalByteLength, - numOfString, - featureTable, + stringCount, + propertyTable, propertyValue); - offsetType = PropertyType::Uint8; + stringOffsetType = PropertyComponentType::Uint8; } else if (isInRangeForUnsignedInteger(totalByteLength)) { - copyStringArrayBuffers( + copyStringsToBuffers( valueBuffer, - offsetBuffer, + stringOffsetBuffer, totalByteLength, - numOfString, - featureTable, + stringCount, + propertyTable, propertyValue); - offsetType = PropertyType::Uint16; + stringOffsetType = PropertyComponentType::Uint16; } else if (isInRangeForUnsignedInteger(totalByteLength)) { - copyStringArrayBuffers( + copyStringsToBuffers( valueBuffer, - offsetBuffer, + stringOffsetBuffer, totalByteLength, - numOfString, - featureTable, + stringCount, + propertyTable, propertyValue); - offsetType = PropertyType::Uint32; + stringOffsetType = PropertyComponentType::Uint32; } else if (isInRangeForUnsignedInteger(totalByteLength)) { - copyStringArrayBuffers( + copyStringsToBuffers( valueBuffer, - offsetBuffer, + stringOffsetBuffer, totalByteLength, - numOfString, - featureTable, + stringCount, + propertyTable, propertyValue); - offsetType = PropertyType::Uint64; + stringOffsetType = PropertyComponentType::Uint64; } - // create gltf value buffer view - Buffer& gltfValueBuffer = gltf.buffers.emplace_back(); - gltfValueBuffer.byteLength = static_cast(valueBuffer.size()); - gltfValueBuffer.cesium.data = std::move(valueBuffer); - - BufferView& gltfValueBufferView = gltf.bufferViews.emplace_back(); - gltfValueBufferView.buffer = - static_cast(gltf.buffers.size() - 1); - gltfValueBufferView.byteOffset = 0; - gltfValueBufferView.byteLength = gltfValueBuffer.byteLength; - const int32_t valueBufferViewIdx = - static_cast(gltf.bufferViews.size() - 1); - - // create gltf string offset buffer view - Buffer& gltfOffsetBuffer = gltf.buffers.emplace_back(); - gltfOffsetBuffer.byteLength = static_cast(offsetBuffer.size()); - gltfOffsetBuffer.cesium.data = std::move(offsetBuffer); - - BufferView& gltfOffsetBufferView = gltf.bufferViews.emplace_back(); - gltfOffsetBufferView.buffer = static_cast(gltf.buffers.size() - 1); - gltfOffsetBufferView.byteOffset = 0; - gltfOffsetBufferView.byteLength = gltfOffsetBuffer.byteLength; - const int32_t offsetBufferViewIdx = - static_cast(gltf.bufferViews.size() - 1); - - // fixed array of string - if (compatibleTypes.minComponentCount == compatibleTypes.maxComponentCount) { - classProperty.type = "ARRAY"; - classProperty.componentCount = compatibleTypes.minComponentCount; - classProperty.componentType = "STRING"; - - featureTableProperty.bufferView = valueBufferViewIdx; - featureTableProperty.stringOffsetBufferView = offsetBufferViewIdx; - featureTableProperty.offsetType = convertPropertyTypeToString(offsetType); + classProperty.type = + ExtensionExtStructuralMetadataClassProperty::Type::STRING; + classProperty.array = true; + propertyTableProperty.stringOffsetType = + convertPropertyComponentTypeToString(stringOffsetType); + propertyTableProperty.values = addBufferToGltf(gltf, std::move(valueBuffer)); + propertyTableProperty.stringOffsets = + addBufferToGltf(gltf, std::move(stringOffsetBuffer)); + + // Handle fixed-length arrays + if (arrayType.minArrayCount == arrayType.maxArrayCount) { + classProperty.count = arrayType.minArrayCount; return; } - // dynamic array of string needs array offset buffer + // Handle variable-length arrays std::vector arrayOffsetBuffer; - switch (offsetType) { - case PropertyType::Uint8: - copyArrayOffsetBufferForStringArrayProperty( + switch (stringOffsetType) { + case PropertyComponentType::Uint8: + copyArrayOffsetsForStringArraysToBuffer( arrayOffsetBuffer, - featureTable, + propertyTable, propertyValue); break; - case PropertyType::Uint16: - copyArrayOffsetBufferForStringArrayProperty( + case PropertyComponentType::Uint16: + copyArrayOffsetsForStringArraysToBuffer( arrayOffsetBuffer, - featureTable, + propertyTable, propertyValue); break; - case PropertyType::Uint32: - copyArrayOffsetBufferForStringArrayProperty( + case PropertyComponentType::Uint32: + copyArrayOffsetsForStringArraysToBuffer( arrayOffsetBuffer, - featureTable, + propertyTable, propertyValue); break; - case PropertyType::Uint64: - copyArrayOffsetBufferForStringArrayProperty( + case PropertyComponentType::Uint64: + copyArrayOffsetsForStringArraysToBuffer( arrayOffsetBuffer, - featureTable, + propertyTable, propertyValue); break; default: break; } - // create gltf array offset buffer view - Buffer& gltfArrayOffsetBuffer = gltf.buffers.emplace_back(); - gltfArrayOffsetBuffer.byteLength = - static_cast(arrayOffsetBuffer.size()); - gltfArrayOffsetBuffer.cesium.data = std::move(arrayOffsetBuffer); - - BufferView& gltfArrayOffsetBufferView = gltf.bufferViews.emplace_back(); - gltfArrayOffsetBufferView.buffer = - static_cast(gltf.buffers.size() - 1); - gltfArrayOffsetBufferView.byteOffset = 0; - gltfArrayOffsetBufferView.byteLength = gltfArrayOffsetBuffer.byteLength; - const int32_t arrayOffsetBufferViewIdx = - static_cast(gltf.bufferViews.size() - 1); - - classProperty.type = "ARRAY"; - classProperty.componentType = "STRING"; - - featureTableProperty.bufferView = valueBufferViewIdx; - featureTableProperty.arrayOffsetBufferView = arrayOffsetBufferViewIdx; - featureTableProperty.stringOffsetBufferView = offsetBufferViewIdx; - featureTableProperty.offsetType = convertPropertyTypeToString(offsetType); + propertyTableProperty.arrayOffsets = + addBufferToGltf(gltf, std::move(arrayOffsetBuffer)); + propertyTableProperty.arrayOffsetType = + convertPropertyComponentTypeToString(stringOffsetType); } template -void copyBooleanArrayBuffers( +void copyVariableLengthBooleanArraysBuffers( std::vector& valueBuffer, std::vector& offsetBuffer, size_t numOfElements, - const FeatureTable& featureTable, + const ExtensionExtStructuralMetadataPropertyTable& propertyTable, const TValueGetter& propertyValue) { size_t currentIndex = 0; const size_t totalByteLength = static_cast(glm::ceil(static_cast(numOfElements) / 8.0)); valueBuffer.resize(totalByteLength); offsetBuffer.resize( - static_cast(featureTable.count + 1) * sizeof(OffsetType)); + static_cast(propertyTable.count + 1) * sizeof(OffsetType)); OffsetType* offset = reinterpret_cast(offsetBuffer.data()); OffsetType prevOffset = 0; auto it = propertyValue.begin(); - for (int64_t i = 0; i < featureTable.count; ++i) { + for (int64_t i = 0; i < propertyTable.count; ++i) { const auto& arrayMember = *it; *offset = prevOffset; @@ -850,23 +1013,27 @@ void copyBooleanArrayBuffers( template void updateBooleanArrayProperty( Model& gltf, - ClassProperty& classProperty, - FeatureTableProperty& featureTableProperty, - const FeatureTable& featureTable, - const CompatibleTypes& compatibleTypes, + ExtensionExtStructuralMetadataClassProperty& classProperty, + ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty, + const ExtensionExtStructuralMetadataPropertyTable& propertyTable, + const MaskedArrayType& arrayType, const TValueGetter& propertyValue) { - assert(propertyValue.size() >= featureTable.count); + assert(propertyValue.size() >= propertyTable.count); + + classProperty.type = "BOOLEAN"; + classProperty.array = true; - // fixed array of boolean - if (compatibleTypes.minComponentCount == compatibleTypes.maxComponentCount) { - const size_t numOfElements = static_cast( - featureTable.count * compatibleTypes.minComponentCount.value()); + // Fixed-length array of booleans + if (arrayType.minArrayCount == arrayType.maxArrayCount) { + const size_t arrayCount = static_cast(arrayType.minArrayCount); + const size_t numOfElements = + static_cast(propertyTable.count) * arrayCount; const size_t totalByteLength = static_cast( glm::ceil(static_cast(numOfElements) / 8.0)); std::vector valueBuffer(totalByteLength); size_t currentIndex = 0; auto it = propertyValue.begin(); - for (int64_t i = 0; i < featureTable.count; ++i) { + for (int64_t i = 0; i < propertyTable.count; ++i) { const auto& arrayMember = *it; for (const auto& data : arrayMember.GetArray()) { const bool value = data.GetBool(); @@ -879,29 +1046,17 @@ void updateBooleanArrayProperty( ++it; } - Buffer& gltfValueBuffer = gltf.buffers.emplace_back(); - gltfValueBuffer.byteLength = static_cast(valueBuffer.size()); - gltfValueBuffer.cesium.data = std::move(valueBuffer); - - BufferView& gltfValueBufferView = gltf.bufferViews.emplace_back(); - gltfValueBufferView.buffer = static_cast(gltf.buffers.size() - 1); - gltfValueBufferView.byteOffset = 0; - gltfValueBufferView.byteLength = gltfValueBuffer.byteLength; - - classProperty.type = "ARRAY"; - classProperty.componentCount = compatibleTypes.minComponentCount; - classProperty.componentType = "BOOLEAN"; - - featureTableProperty.bufferView = - static_cast(gltf.bufferViews.size() - 1); + propertyTableProperty.values = + addBufferToGltf(gltf, std::move(valueBuffer)); + classProperty.count = static_cast(arrayCount); return; } - // dynamic array of boolean + // Variable-length array of booleans size_t numOfElements = 0; auto it = propertyValue.begin(); - for (int64_t i = 0; i < featureTable.count; ++i) { + for (int64_t i = 0; i < propertyTable.count; ++i) { const auto& arrayMember = *it; numOfElements += arrayMember.Size(); ++it; @@ -909,197 +1064,175 @@ void updateBooleanArrayProperty( std::vector valueBuffer; std::vector offsetBuffer; - PropertyType offsetType = PropertyType::None; + PropertyComponentType offsetType = PropertyComponentType::None; if (isInRangeForUnsignedInteger(numOfElements)) { - copyBooleanArrayBuffers( + copyVariableLengthBooleanArraysBuffers( valueBuffer, offsetBuffer, numOfElements, - featureTable, + propertyTable, propertyValue); - offsetType = PropertyType::Uint8; + offsetType = PropertyComponentType::Uint8; } else if (isInRangeForUnsignedInteger(numOfElements)) { - copyBooleanArrayBuffers( + copyVariableLengthBooleanArraysBuffers( valueBuffer, offsetBuffer, numOfElements, - featureTable, + propertyTable, propertyValue); - offsetType = PropertyType::Uint16; + offsetType = PropertyComponentType::Uint16; } else if (isInRangeForUnsignedInteger(numOfElements)) { - copyBooleanArrayBuffers( + copyVariableLengthBooleanArraysBuffers( valueBuffer, offsetBuffer, numOfElements, - featureTable, + propertyTable, propertyValue); - offsetType = PropertyType::Uint32; + offsetType = PropertyComponentType::Uint32; } else { - copyBooleanArrayBuffers( + copyVariableLengthBooleanArraysBuffers( valueBuffer, offsetBuffer, numOfElements, - featureTable, + propertyTable, propertyValue); - offsetType = PropertyType::Uint64; + offsetType = PropertyComponentType::Uint64; } - Buffer& gltfValueBuffer = gltf.buffers.emplace_back(); - gltfValueBuffer.byteLength = static_cast(valueBuffer.size()); - gltfValueBuffer.cesium.data = std::move(valueBuffer); - - BufferView& gltfValueBufferView = gltf.bufferViews.emplace_back(); - gltfValueBufferView.buffer = static_cast(gltf.buffers.size() - 1); - gltfValueBufferView.byteOffset = 0; - gltfValueBufferView.byteLength = - static_cast(gltfValueBuffer.cesium.data.size()); - const int32_t valueBufferIdx = - static_cast(gltf.bufferViews.size() - 1); - - Buffer& gltfOffsetBuffer = gltf.buffers.emplace_back(); - gltfOffsetBuffer.byteLength = static_cast(offsetBuffer.size()); - gltfOffsetBuffer.cesium.data = std::move(offsetBuffer); - - BufferView& gltfOffsetBufferView = gltf.bufferViews.emplace_back(); - gltfOffsetBufferView.buffer = static_cast(gltf.buffers.size() - 1); - gltfOffsetBufferView.byteOffset = 0; - gltfOffsetBufferView.byteLength = - static_cast(gltfOffsetBuffer.cesium.data.size()); - const int32_t offsetBufferIdx = - static_cast(gltf.bufferViews.size() - 1); - - classProperty.type = "ARRAY"; - classProperty.componentType = "BOOLEAN"; - - featureTableProperty.bufferView = valueBufferIdx; - featureTableProperty.arrayOffsetBufferView = offsetBufferIdx; - featureTableProperty.offsetType = convertPropertyTypeToString(offsetType); + propertyTableProperty.values = addBufferToGltf(gltf, std::move(valueBuffer)); + propertyTableProperty.arrayOffsets = + addBufferToGltf(gltf, std::move(offsetBuffer)); + propertyTableProperty.arrayOffsetType = + convertPropertyComponentTypeToString(offsetType); } template void updateExtensionWithArrayProperty( Model& gltf, - ClassProperty& classProperty, - const FeatureTable& featureTable, - FeatureTableProperty& featureTableProperty, - const CompatibleTypes& compatibleTypes, + ExtensionExtStructuralMetadataClassProperty& classProperty, + const ExtensionExtStructuralMetadataPropertyTable& propertyTable, + ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty, + const MaskedArrayType& arrayType, const TValueGetter& propertyValue) { - assert(propertyValue.size() >= featureTable.count); + assert(propertyValue.size() >= propertyTable.count); - if (compatibleTypes.componentType->isBool) { + const MaskedType& elementType = arrayType.elementType; + if (elementType.isBool) { updateBooleanArrayProperty( gltf, classProperty, - featureTableProperty, - featureTable, - compatibleTypes, + propertyTableProperty, + propertyTable, + arrayType, propertyValue); - } else if (compatibleTypes.componentType->isInt8) { - updateNumericArrayProperty( + } else if (elementType.isInt8) { + updateScalarArrayProperty( gltf, classProperty, - featureTableProperty, - featureTable, - compatibleTypes, + propertyTableProperty, + propertyTable, + arrayType, propertyValue); - } else if (compatibleTypes.componentType->isUint8) { - updateNumericArrayProperty( + } else if (elementType.isUint8) { + updateScalarArrayProperty( gltf, classProperty, - featureTableProperty, - featureTable, - compatibleTypes, + propertyTableProperty, + propertyTable, + arrayType, propertyValue); - } else if (compatibleTypes.componentType->isInt16) { - updateNumericArrayProperty( + } else if (elementType.isInt16) { + updateScalarArrayProperty( gltf, classProperty, - featureTableProperty, - featureTable, - compatibleTypes, + propertyTableProperty, + propertyTable, + arrayType, propertyValue); - } else if (compatibleTypes.componentType->isUint16) { - updateNumericArrayProperty( + } else if (elementType.isUint16) { + updateScalarArrayProperty( gltf, classProperty, - featureTableProperty, - featureTable, - compatibleTypes, + propertyTableProperty, + propertyTable, + arrayType, propertyValue); - } else if (compatibleTypes.componentType->isInt32) { - updateNumericArrayProperty( + } else if (elementType.isInt32) { + updateScalarArrayProperty( gltf, classProperty, - featureTableProperty, - featureTable, - compatibleTypes, + propertyTableProperty, + propertyTable, + arrayType, propertyValue); - } else if (compatibleTypes.componentType->isUint32) { - updateNumericArrayProperty( + } else if (elementType.isUint32) { + updateScalarArrayProperty( gltf, classProperty, - featureTableProperty, - featureTable, - compatibleTypes, + propertyTableProperty, + propertyTable, + arrayType, propertyValue); - } else if (compatibleTypes.componentType->isInt64) { - updateNumericArrayProperty( + } else if (elementType.isInt64) { + updateScalarArrayProperty( gltf, classProperty, - featureTableProperty, - featureTable, - compatibleTypes, + propertyTableProperty, + propertyTable, + arrayType, propertyValue); - } else if (compatibleTypes.componentType->isUint64) { - updateNumericArrayProperty( + } else if (elementType.isUint64) { + updateScalarArrayProperty( gltf, classProperty, - featureTableProperty, - featureTable, - compatibleTypes, + propertyTableProperty, + propertyTable, + arrayType, propertyValue); - } else if (compatibleTypes.componentType->isFloat32) { - updateNumericArrayProperty( + } else if (elementType.isFloat32) { + updateScalarArrayProperty( gltf, classProperty, - featureTableProperty, - featureTable, - compatibleTypes, + propertyTableProperty, + propertyTable, + arrayType, propertyValue); - } else if (compatibleTypes.componentType->isFloat64) { - updateNumericArrayProperty( + } else if (elementType.isFloat64) { + updateScalarArrayProperty( gltf, classProperty, - featureTableProperty, - featureTable, - compatibleTypes, + propertyTableProperty, + propertyTable, + arrayType, propertyValue); } else { updateStringArrayProperty( gltf, classProperty, - featureTableProperty, - featureTable, - compatibleTypes, + propertyTableProperty, + propertyTable, + arrayType, propertyValue); } } +// Updates the extension with a property defined as an array of values in the +// batch table JSON. template void updateExtensionWithJsonProperty( Model& gltf, - ClassProperty& classProperty, - const FeatureTable& featureTable, - FeatureTableProperty& featureTableProperty, + ExtensionExtStructuralMetadataClassProperty& classProperty, + const ExtensionExtStructuralMetadataPropertyTable& propertyTable, + ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty, const TValueGetter& propertyValue) { - if (propertyValue.size() == 0 || propertyValue.size() < featureTable.count) { + if (propertyValue.size() == 0 || propertyValue.size() < propertyTable.count) { // No property to infer the type from, so assume string. updateExtensionWithJsonStringProperty( gltf, classProperty, - featureTable, - featureTableProperty, + propertyTable, + propertyTableProperty, propertyValue); return; } @@ -1107,107 +1240,112 @@ void updateExtensionWithJsonProperty( // Figure out which types we can use for this data. // Use the smallest type we can, and prefer signed to unsigned. const CompatibleTypes compatibleTypes = findCompatibleTypes(propertyValue); - if (compatibleTypes.type.isBool) { - updateExtensionWithJsonBoolProperty( + if (compatibleTypes.isArray()) { + MaskedArrayType arrayType = compatibleTypes.toMaskedArrayType(); + updateExtensionWithArrayProperty( gltf, classProperty, - featureTable, - featureTableProperty, + propertyTable, + propertyTableProperty, + arrayType, propertyValue); - } else if (compatibleTypes.type.isInt8) { - updateExtensionWithJsonNumericProperty( + return; + } + + MaskedType type = compatibleTypes.toMaskedType(); + if (type.isBool) { + updateExtensionWithJsonBooleanProperty( gltf, classProperty, - featureTable, - featureTableProperty, - propertyValue, - "INT8"); - } else if (compatibleTypes.type.isUint8) { - updateExtensionWithJsonNumericProperty( + propertyTable, + propertyTableProperty, + propertyValue); + } else if (type.isInt8) { + updateExtensionWithJsonScalarProperty( gltf, classProperty, - featureTable, - featureTableProperty, + propertyTable, + propertyTableProperty, propertyValue, - "UINT8"); - } else if (compatibleTypes.type.isInt16) { - updateExtensionWithJsonNumericProperty( + ExtensionExtStructuralMetadataClassProperty::ComponentType::INT8); + } else if (type.isUint8) { + updateExtensionWithJsonScalarProperty( gltf, classProperty, - featureTable, - featureTableProperty, + propertyTable, + propertyTableProperty, propertyValue, - "INT16"); - } else if (compatibleTypes.type.isUint16) { - updateExtensionWithJsonNumericProperty( + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT8); + } else if (type.isInt16) { + updateExtensionWithJsonScalarProperty( gltf, classProperty, - featureTable, - featureTableProperty, + propertyTable, + propertyTableProperty, propertyValue, - "UINT16"); - } else if (compatibleTypes.type.isInt32) { - updateExtensionWithJsonNumericProperty( + ExtensionExtStructuralMetadataClassProperty::ComponentType::INT16); + } else if (type.isUint16) { + updateExtensionWithJsonScalarProperty( gltf, classProperty, - featureTable, - featureTableProperty, + propertyTable, + propertyTableProperty, propertyValue, - "INT32"); - } else if (compatibleTypes.type.isUint32) { - updateExtensionWithJsonNumericProperty( + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT16); + } else if (type.isInt32) { + updateExtensionWithJsonScalarProperty( gltf, classProperty, - featureTable, - featureTableProperty, + propertyTable, + propertyTableProperty, propertyValue, - "UINT32"); - } else if (compatibleTypes.type.isInt64) { - updateExtensionWithJsonNumericProperty( + ExtensionExtStructuralMetadataClassProperty::ComponentType::INT32); + } else if (type.isUint32) { + updateExtensionWithJsonScalarProperty( gltf, classProperty, - featureTable, - featureTableProperty, + propertyTable, + propertyTableProperty, propertyValue, - "INT64"); - } else if (compatibleTypes.type.isUint64) { - updateExtensionWithJsonNumericProperty( + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT32); + } else if (type.isInt64) { + updateExtensionWithJsonScalarProperty( gltf, classProperty, - featureTable, - featureTableProperty, + propertyTable, + propertyTableProperty, propertyValue, - "UINT64"); - } else if (compatibleTypes.type.isFloat32) { - updateExtensionWithJsonNumericProperty( + ExtensionExtStructuralMetadataClassProperty::ComponentType::INT64); + } else if (type.isUint64) { + updateExtensionWithJsonScalarProperty( gltf, classProperty, - featureTable, - featureTableProperty, + propertyTable, + propertyTableProperty, propertyValue, - "FLOAT32"); - } else if (compatibleTypes.type.isFloat64) { - updateExtensionWithJsonNumericProperty( + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT64); + } else if (type.isFloat32) { + updateExtensionWithJsonScalarProperty( gltf, classProperty, - featureTable, - featureTableProperty, + propertyTable, + propertyTableProperty, propertyValue, - "FLOAT64"); - } else if (compatibleTypes.type.isArray) { - updateExtensionWithArrayProperty( + ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT32); + } else if (type.isFloat64) { + updateExtensionWithJsonScalarProperty( gltf, classProperty, - featureTable, - featureTableProperty, - compatibleTypes, - propertyValue); + propertyTable, + propertyTableProperty, + propertyValue, + ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT64); } else { updateExtensionWithJsonStringProperty( gltf, classProperty, - featureTable, - featureTableProperty, + propertyTable, + propertyTableProperty, propertyValue); } } @@ -1217,12 +1355,12 @@ void updateExtensionWithBinaryProperty( int32_t gltfBufferIndex, int64_t gltfBufferOffset, BinaryProperty& binaryProperty, - ClassProperty& classProperty, - FeatureTableProperty& featureTableProperty, - ErrorList& result, - const FeatureTable& featureTable, + ExtensionExtStructuralMetadataClassProperty& classProperty, + ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty, + const ExtensionExtStructuralMetadataPropertyTable& propertyTable, const std::string& propertyName, - const rapidjson::Value& propertyValue) { + const rapidjson::Value& propertyValue, + ErrorList& result) { assert( gltfBufferIndex >= 0 && "gltfBufferIndex is negative. Need to allocate buffer before " @@ -1232,7 +1370,7 @@ void updateExtensionWithBinaryProperty( if (byteOffsetIt == propertyValue.MemberEnd() || !byteOffsetIt->value.IsInt64()) { result.emplaceWarning(fmt::format( - "Skip converting {}. The binary property doesn't have required " + "Skip converting {}. The binary property doesn't have a valid " "byteOffset.", propertyName)); return; @@ -1242,7 +1380,7 @@ void updateExtensionWithBinaryProperty( if (componentTypeIt == propertyValue.MemberEnd() || !componentTypeIt->value.IsString()) { result.emplaceWarning(fmt::format( - "Skip converting {}. The binary property doesn't have required " + "Skip converting {}. The binary property doesn't have a valid " "componentType.", propertyName)); return; @@ -1251,53 +1389,52 @@ void updateExtensionWithBinaryProperty( const auto& typeIt = propertyValue.FindMember("type"); if (typeIt == propertyValue.MemberEnd() || !typeIt->value.IsString()) { result.emplaceWarning(fmt::format( - "Skip convert {}. The binary property doesn't have required type.", + "Skip converting {}. The binary property doesn't have a valid type.", propertyName)); return; } - // convert class property + // Convert batch table property to glTF property table property const int64_t byteOffset = byteOffsetIt->value.GetInt64(); const std::string& componentType = componentTypeIt->value.GetString(); const std::string& type = typeIt->value.GetString(); - auto convertedTypeIt = batchTableComponentTypeToGltfType.find(componentType); - if (convertedTypeIt == batchTableComponentTypeToGltfType.end()) { + auto convertedTypeIt = batchTableTypeToGltfType.find(type); + if (convertedTypeIt == batchTableTypeToGltfType.end()) { + result.emplaceWarning(fmt::format( + "Skip converting {}. The binary property doesn't have a valid type.", + propertyName)); return; } - const GltfFeatureTableType& gltfType = convertedTypeIt->second; - - size_t componentCount = 1; - if (type == "SCALAR") { - classProperty.type = gltfType.typeName; - } else if (type == "VEC2") { - classProperty.type = "ARRAY"; - classProperty.componentCount = 2; - classProperty.componentType = gltfType.typeName; - componentCount = 2; - } else if (type == "VEC3") { - classProperty.type = "ARRAY"; - classProperty.componentCount = 3; - classProperty.componentType = gltfType.typeName; - componentCount = 3; - } else if (type == "VEC4") { - classProperty.type = "ARRAY"; - classProperty.componentCount = 4; - classProperty.componentType = gltfType.typeName; - componentCount = 4; - } else { + auto convertedComponentTypeIt = + batchTableComponentTypeToGltfComponentType.find(componentType); + if (convertedComponentTypeIt == + batchTableComponentTypeToGltfComponentType.end()) { + result.emplaceWarning(fmt::format( + "Skip converting {}. The binary property doesn't have a valid " + "componentType.", + propertyName)); return; } - // convert feature table - const size_t typeSize = gltfType.typeSize; + const GltfPropertyTableType& gltfType = convertedTypeIt->second; + const GltfPropertyTableComponentType& gltfComponentType = + convertedComponentTypeIt->second; + + classProperty.type = gltfType.type; + classProperty.componentType = gltfComponentType.componentType; + + // Convert to a buffer view + const size_t componentCount = gltfType.componentCount; + const size_t componentTypeSize = gltfComponentType.componentTypeSize; auto& bufferView = gltf.bufferViews.emplace_back(); bufferView.buffer = gltfBufferIndex; bufferView.byteOffset = gltfBufferOffset; bufferView.byteLength = static_cast( - typeSize * componentCount * static_cast(featureTable.count)); + componentTypeSize * componentCount * + static_cast(propertyTable.count)); - featureTableProperty.bufferView = + propertyTableProperty.values = static_cast(gltf.bufferViews.size() - 1); binaryProperty.batchTableByteOffset = byteOffset; @@ -1307,8 +1444,8 @@ void updateExtensionWithBinaryProperty( void updateExtensionWithBatchTableHierarchy( Model& gltf, - Class& classDefinition, - FeatureTable& featureTable, + ExtensionExtStructuralMetadataClass& classDefinition, + ExtensionExtStructuralMetadataPropertyTable& propertyTable, ErrorList& result, const rapidjson::Value& batchTableHierarchy) { // EXT_feature_metadata can't support hierarchy, so we need to flatten it. @@ -1329,7 +1466,8 @@ void updateExtensionWithBatchTableHierarchy( for (const auto& element : parentCountsIt->value.GetArray()) { if (!element.IsInt64() || element.GetInt64() != 1LL) { result.emplaceWarning( - "3DTILES_batch_table_hierarchy with a \"parentCounts\" property is " + "3DTILES_batch_table_hierarchy with a \"parentCounts\" property " + "is " "not currently supported. All instances must have at most one " "parent."); return; @@ -1350,7 +1488,8 @@ void updateExtensionWithBatchTableHierarchy( if (propertyIt->value.IsObject()) { result.emplaceWarning(fmt::format( "Property {} uses binary values. Only JSON-based " - "3DTILES_batch_table_hierarchy properties are currently supported.", + "3DTILES_batch_table_hierarchy properties are currently " + "supported.", propertyIt->name.GetString())); } else { properties.insert(propertyIt->name.GetString()); @@ -1360,15 +1499,20 @@ void updateExtensionWithBatchTableHierarchy( BatchTableHierarchyPropertyValues batchTableHierarchyValues( batchTableHierarchy, - featureTable.count); + propertyTable.count); for (const std::string& name : properties) { - ClassProperty& classProperty = - classDefinition.properties.emplace(name, ClassProperty()).first->second; + ExtensionExtStructuralMetadataClassProperty& classProperty = + classDefinition.properties + .emplace(name, ExtensionExtStructuralMetadataClassProperty()) + .first->second; classProperty.name = name; - FeatureTableProperty& featureTableProperty = - featureTable.properties.emplace(name, FeatureTableProperty()) + ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + propertyTable.properties + .emplace( + name, + ExtensionExtStructuralMetadataPropertyTableProperty()) .first->second; batchTableHierarchyValues.setProperty(name); @@ -1376,8 +1520,8 @@ void updateExtensionWithBatchTableHierarchy( updateExtensionWithJsonProperty( gltf, classProperty, - featureTable, - featureTableProperty, + propertyTable, + propertyTableProperty, batchTableHierarchyValues); } } @@ -1388,8 +1532,8 @@ void convertBatchTableToGltfStructuralMetadataExtension( CesiumGltf::Model& gltf, const int64_t featureCount, ErrorList& result) { - // Add the binary part of the batch table - if any - to the glTF as a buffer. - // We will reallign this buffer later on + // Add the binary part of the batch table - if any - to the glTF as a + // buffer. We will reallign this buffer later on int32_t gltfBufferIndex = -1; int64_t gltfBufferOffset = -1; std::vector binaryProperties; @@ -1399,17 +1543,20 @@ void convertBatchTableToGltfStructuralMetadataExtension( gltf.buffers.emplace_back(); } - ExtensionModelExtFeatureMetadata& modelExtension = - gltf.addExtension(); - Schema& schema = modelExtension.schema.emplace(); - Class& classDefinition = - schema.classes.emplace("default", Class()).first->second; + ExtensionModelExtStructuralMetadata& modelExtension = + gltf.addExtension(); + ExtensionExtStructuralMetadataSchema& schema = + modelExtension.schema.emplace(); + schema.id = "default"; // Required by the spec. - FeatureTable& featureTable = - modelExtension.featureTables.emplace("default", FeatureTable()) + ExtensionExtStructuralMetadataClass& classDefinition = + schema.classes.emplace("default", ExtensionExtStructuralMetadataClass()) .first->second; - featureTable.count = featureCount; - featureTable.classProperty = "default"; + + ExtensionExtStructuralMetadataPropertyTable& propertyTable = + modelExtension.propertyTables.emplace_back(); + propertyTable.count = featureCount; + propertyTable.classProperty = "default"; // Convert each regular property in the batch table for (auto propertyIt = batchTableJson.MemberBegin(); @@ -1422,20 +1569,25 @@ void convertBatchTableToGltfStructuralMetadataExtension( continue; } - ClassProperty& classProperty = - classDefinition.properties.emplace(name, ClassProperty()).first->second; + ExtensionExtStructuralMetadataClassProperty& classProperty = + classDefinition.properties + .emplace(name, ExtensionExtStructuralMetadataClassProperty()) + .first->second; classProperty.name = name; - FeatureTableProperty& featureTableProperty = - featureTable.properties.emplace(name, FeatureTableProperty()) + ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + propertyTable.properties + .emplace( + name, + ExtensionExtStructuralMetadataPropertyTableProperty()) .first->second; const rapidjson::Value& propertyValue = propertyIt->value; if (propertyValue.IsArray()) { updateExtensionWithJsonProperty( gltf, classProperty, - featureTable, - featureTableProperty, + propertyTable, + propertyTableProperty, ArrayOfPropertyValues(propertyValue)); } else { BinaryProperty& binaryProperty = binaryProperties.emplace_back(); @@ -1445,11 +1597,11 @@ void convertBatchTableToGltfStructuralMetadataExtension( gltfBufferOffset, binaryProperty, classProperty, - featureTableProperty, - result, - featureTable, + propertyTableProperty, + propertyTable, name, - propertyValue); + propertyValue, + result); gltfBufferOffset += roundUp(binaryProperty.byteLength, 8); } } @@ -1463,13 +1615,13 @@ void convertBatchTableToGltfStructuralMetadataExtension( updateExtensionWithBatchTableHierarchy( gltf, classDefinition, - featureTable, + propertyTable, result, bthIt->value); } } - // re-arrange binary property buffer + // Re-arrange binary property buffer if (!batchTableBinaryData.empty()) { Buffer& buffer = gltf.buffers[static_cast(gltfBufferIndex)]; buffer.byteLength = gltfBufferOffset; @@ -1521,7 +1673,7 @@ ErrorList BatchTableToGltfStructuralMetadata::convertFromB3dm( batchLength, result); - // Create an EXT_feature_metadata extension for each primitive with a _BATCHID + // Create an EXT_mesh_features extension for each primitive with a _BATCHID // attribute. for (Mesh& mesh : gltf.meshes) { for (MeshPrimitive& primitive : mesh.primitives) { @@ -1535,7 +1687,6 @@ ErrorList BatchTableToGltfStructuralMetadata::convertFromB3dm( primitive.attributes["_FEATURE_ID_0"] = batchIDIt->second; primitive.attributes.erase("_BATCHID"); - // Create an EXT_mesh_features extension with a feature ID attribute. ExtensionExtMeshFeatures& extension = primitive.addExtension(); From 219da88a0af4599271a69a74e363be33581c94b8 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Thu, 25 May 2023 10:22:13 -0400 Subject: [PATCH 044/421] Use pointers in MetadataPropertyTableView --- .../StructuralMetadataPropertyTableView.h | 27 +++++++++--------- .../StructuralMetadataPropertyTableView.cpp | 28 +++++++++---------- 2 files changed, 28 insertions(+), 27 deletions(-) diff --git a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTableView.h b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTableView.h index 6d8b0cca7..7fcd8fc38 100644 --- a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTableView.h +++ b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTableView.h @@ -83,7 +83,7 @@ class MetadataPropertyTableView { */ int64_t size() const noexcept { return _status == MetadataPropertyTableViewStatus::Valid - ? _propertyTable.count + ? _pPropertyTable->count : 0; } @@ -973,8 +973,8 @@ class MetadataPropertyTableView { const std::string& propertyName, const ExtensionExtStructuralMetadataClassProperty& classProperty) const { auto propertyTablePropertyIter = - _propertyTable.properties.find(propertyName); - if (propertyTablePropertyIter == _propertyTable.properties.end()) { + _pPropertyTable->properties.find(propertyName); + if (propertyTablePropertyIter == _pPropertyTable->properties.end()) { return createInvalidPropertyView( MetadataPropertyViewStatus::ErrorPropertyDoesNotExist); } @@ -1043,9 +1043,9 @@ class MetadataPropertyTableView { size_t maxRequiredBytes = 0; if (IsMetadataBoolean::value) { maxRequiredBytes = static_cast( - glm::ceil(static_cast(_propertyTable.count) / 8.0)); + glm::ceil(static_cast(_pPropertyTable->count) / 8.0)); } else { - maxRequiredBytes = _propertyTable.count * sizeof(T); + maxRequiredBytes = _pPropertyTable->count * sizeof(T); } if (values.size() < maxRequiredBytes) { @@ -1057,7 +1057,7 @@ class MetadataPropertyTableView { return MetadataPropertyView( MetadataPropertyViewStatus::Valid, values, - _propertyTable.count, + _pPropertyTable->count, classProperty.normalized); } @@ -1118,11 +1118,12 @@ class MetadataPropertyTableView { size_t maxRequiredBytes = 0; if constexpr (IsMetadataBoolean::value) { maxRequiredBytes = static_cast(glm::ceil( - static_cast(_propertyTable.count * fixedLengthArrayCount) / + static_cast( + _pPropertyTable->count * fixedLengthArrayCount) / 8.0)); } else { maxRequiredBytes = static_cast( - _propertyTable.count * fixedLengthArrayCount * sizeof(T)); + _pPropertyTable->count * fixedLengthArrayCount * sizeof(T)); } if (values.size() < maxRequiredBytes) { @@ -1139,7 +1140,7 @@ class MetadataPropertyTableView { PropertyComponentType::None, PropertyComponentType::None, static_cast(fixedLengthArrayCount), - static_cast(_propertyTable.count), + static_cast(_pPropertyTable->count), classProperty.normalized); } @@ -1158,7 +1159,7 @@ class MetadataPropertyTableView { propertyTableProperty.arrayOffsets, arrayOffsetType, values.size(), - static_cast(_propertyTable.count), + static_cast(_pPropertyTable->count), checkBitsSize, arrayOffsets); if (status != MetadataPropertyViewStatus::Valid) { @@ -1173,7 +1174,7 @@ class MetadataPropertyTableView { arrayOffsetType, PropertyComponentType::None, 0, - static_cast(_propertyTable.count), + static_cast(_pPropertyTable->count), classProperty.normalized); } @@ -1212,8 +1213,8 @@ class MetadataPropertyTableView { false); } - const Model& _model; - const ExtensionExtStructuralMetadataPropertyTable& _propertyTable; + const Model* _pModel; + const ExtensionExtStructuralMetadataPropertyTable* _pPropertyTable; const ExtensionExtStructuralMetadataClass* _pClass; MetadataPropertyTableViewStatus _status; }; diff --git a/CesiumGltf/src/StructuralMetadataPropertyTableView.cpp b/CesiumGltf/src/StructuralMetadataPropertyTableView.cpp index 6e0dfb421..66f47c2fe 100644 --- a/CesiumGltf/src/StructuralMetadataPropertyTableView.cpp +++ b/CesiumGltf/src/StructuralMetadataPropertyTableView.cpp @@ -109,8 +109,8 @@ static MetadataPropertyViewStatus checkStringAndArrayOffsetsBuffers( MetadataPropertyTableView::MetadataPropertyTableView( const Model& model, const ExtensionExtStructuralMetadataPropertyTable& propertyTable) - : _model{model}, - _propertyTable{propertyTable}, + : _pModel{&model}, + _pPropertyTable{&propertyTable}, _pClass{nullptr}, _status() { const ExtensionModelExtStructuralMetadata* pMetadata = @@ -128,7 +128,7 @@ MetadataPropertyTableView::MetadataPropertyTableView( return; } - auto classIter = schema->classes.find(_propertyTable.classProperty); + auto classIter = schema->classes.find(_pPropertyTable->classProperty); if (classIter != schema->classes.end()) { _pClass = &classIter->second; } @@ -160,12 +160,12 @@ MetadataPropertyViewStatus MetadataPropertyTableView::getBufferSafe( buffer = {}; const BufferView* pBufferView = - _model.getSafe(&_model.bufferViews, bufferViewIdx); + _pModel->getSafe(&_pModel->bufferViews, bufferViewIdx); if (!pBufferView) { return MetadataPropertyViewStatus::ErrorInvalidValueBufferView; } - const Buffer* pBuffer = _model.getSafe(&_model.buffers, pBufferView->buffer); + const Buffer* pBuffer = _pModel->getSafe(&_pModel->buffers, pBufferView->buffer); if (!pBuffer) { return MetadataPropertyViewStatus::ErrorInvalidValueBuffer; } @@ -318,7 +318,7 @@ MetadataPropertyTableView::getStringPropertyValues( propertyTableProperty.stringOffsets, offsetType, values.size(), - static_cast(_propertyTable.count), + static_cast(_pPropertyTable->count), stringOffsets); if (status != MetadataPropertyViewStatus::Valid) { return createInvalidPropertyView(status); @@ -332,7 +332,7 @@ MetadataPropertyTableView::getStringPropertyValues( PropertyComponentType::None, offsetType, 0, - _propertyTable.count, + _pPropertyTable->count, classProperty.normalized); } @@ -392,7 +392,7 @@ MetadataPropertyTableView::getStringArrayPropertyValues( propertyTableProperty.stringOffsets, stringOffsetType, values.size(), - static_cast(_propertyTable.count * fixedLengthArrayCount), + static_cast(_pPropertyTable->count * fixedLengthArrayCount), stringOffsets); if (status != MetadataPropertyViewStatus::Valid) { return createInvalidPropertyView>( @@ -407,7 +407,7 @@ MetadataPropertyTableView::getStringArrayPropertyValues( PropertyComponentType::None, stringOffsetType, fixedLengthArrayCount, - _propertyTable.count, + _pPropertyTable->count, classProperty.normalized); } @@ -447,7 +447,7 @@ MetadataPropertyTableView::getStringArrayPropertyValues( stringOffsets, values.size(), stringOffsetType, - static_cast(_propertyTable.count)); + static_cast(_pPropertyTable->count)); break; case PropertyComponentType::Uint16: status = checkStringAndArrayOffsetsBuffers( @@ -455,7 +455,7 @@ MetadataPropertyTableView::getStringArrayPropertyValues( stringOffsets, values.size(), stringOffsetType, - static_cast(_propertyTable.count)); + static_cast(_pPropertyTable->count)); break; case PropertyComponentType::Uint32: status = checkStringAndArrayOffsetsBuffers( @@ -463,7 +463,7 @@ MetadataPropertyTableView::getStringArrayPropertyValues( stringOffsets, values.size(), stringOffsetType, - static_cast(_propertyTable.count)); + static_cast(_pPropertyTable->count)); break; case PropertyComponentType::Uint64: status = checkStringAndArrayOffsetsBuffers( @@ -471,7 +471,7 @@ MetadataPropertyTableView::getStringArrayPropertyValues( stringOffsets, values.size(), stringOffsetType, - static_cast(_propertyTable.count)); + static_cast(_pPropertyTable->count)); break; default: status = MetadataPropertyViewStatus::ErrorInvalidArrayOffsetType; @@ -491,7 +491,7 @@ MetadataPropertyTableView::getStringArrayPropertyValues( arrayOffsetType, stringOffsetType, 0, - _propertyTable.count, + _pPropertyTable->count, classProperty.normalized); } From d8658e000af0c370a6ca0a6e93e1463da018e6d5 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Thu, 25 May 2023 11:49:29 -0400 Subject: [PATCH 045/421] Rewrite tests for EXT_structural_metadata --- .../BatchTableToGltfStructuralMetadata.cpp | 12 +- .../test/TestPntsToGltfConverter.cpp | 20 +- ...radeBatchTableToExtStructuralMetadata.cpp} | 1454 ++++++++++------- 3 files changed, 906 insertions(+), 580 deletions(-) rename Cesium3DTilesSelection/test/{TestUpgradeBatchTableToExtFeatureMetadata.cpp => TestUpgradeBatchTableToExtStructuralMetadata.cpp} (52%) diff --git a/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp b/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp index aeb71a6d1..bd49e97d5 100644 --- a/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp +++ b/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp @@ -163,15 +163,15 @@ struct CompatibleTypes { * Merges a MaskedArrayType into this CompatibleTypes. */ void operator&=(const MaskedArrayType& maskedArrayType) { - if (!isArray()) { - makeIncompatible(); + if (isArray()) { + MaskedArrayType& arrayType = std::get(maskedType); + arrayType &= maskedArrayType; return; } - MaskedArrayType* pMaskedArrayType = - std::get_if(&maskedType); - if (pMaskedArrayType) { - *pMaskedArrayType &= maskedArrayType; + MaskedType* pMaskedType = std::get_if(&maskedType); + if (pMaskedType) { + makeIncompatible(); } else { maskedType = maskedArrayType; } diff --git a/Cesium3DTilesSelection/test/TestPntsToGltfConverter.cpp b/Cesium3DTilesSelection/test/TestPntsToGltfConverter.cpp index 41da45719..8560ba20c 100644 --- a/Cesium3DTilesSelection/test/TestPntsToGltfConverter.cpp +++ b/Cesium3DTilesSelection/test/TestPntsToGltfConverter.cpp @@ -5,7 +5,7 @@ #include #include #include -#include +#include #include #include @@ -654,7 +654,7 @@ getUniqueBufferIds(const std::vector& bufferViews) { } TEST_CASE( - "Converts point cloud with batch IDs to glTF with EXT_feature_metadata") { + "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; @@ -665,8 +665,8 @@ TEST_CASE( Model& gltf = *result.model; // The correctness of the model extension is thoroughly tested in - // TestUpgradeBatchTableToExtFeatureMetadata - CHECK(gltf.hasExtension()); + // TestUpgradeBatchTableToExtStructuralMetadata + CHECK(gltf.hasExtension()); CHECK(gltf.nodes.size() == 1); REQUIRE(gltf.meshes.size() == 1); @@ -744,8 +744,8 @@ TEST_CASE("Converts point cloud with per-point properties to glTF with " Model& gltf = *result.model; // The correctness of the model extension is thoroughly tested in - // TestUpgradeBatchTableToExtFeatureMetadata - CHECK(gltf.hasExtension()); + // TestUpgradeBatchTableToExtStructuralMetadata + CHECK(gltf.hasExtension()); CHECK(gltf.nodes.size() == 1); REQUIRE(gltf.meshes.size() == 1); @@ -803,8 +803,8 @@ TEST_CASE("Converts point cloud with Draco compression to glTF") { CHECK(gltf.hasExtension()); // The correctness of the model extension is thoroughly tested in - // TestUpgradeBatchTableToExtFeatureMetadata - CHECK(gltf.hasExtension()); + // TestUpgradeBatchTableToExtStructuralMetadata + CHECK(gltf.hasExtension()); CHECK(gltf.nodes.size() == 1); REQUIRE(gltf.meshes.size() == 1); @@ -948,7 +948,7 @@ TEST_CASE("Converts point cloud with partial Draco compression to glTF") { Model& gltf = *result.model; CHECK(gltf.hasExtension()); - CHECK(gltf.hasExtension()); + CHECK(gltf.hasExtension()); CHECK(gltf.nodes.size() == 1); REQUIRE(gltf.meshes.size() == 1); @@ -1087,7 +1087,7 @@ TEST_CASE("Converts batched point cloud with Draco compression to glTF") { // The correctness of the model extension is thoroughly tested in // TestUpgradeBatchTableToExtFeatureMetadata - CHECK(gltf.hasExtension()); + CHECK(gltf.hasExtension()); CHECK(gltf.nodes.size() == 1); REQUIRE(gltf.meshes.size() == 1); diff --git a/Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtFeatureMetadata.cpp b/Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp similarity index 52% rename from Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtFeatureMetadata.cpp rename to Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp index 34dfc9267..651af2c5d 100644 --- a/Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtFeatureMetadata.cpp +++ b/Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp @@ -4,9 +4,10 @@ #include #include #include -#include -#include -#include +#include +#include +#include +#include #include #include @@ -17,36 +18,49 @@ #include using namespace CesiumGltf; +using namespace CesiumGltf::StructuralMetadata; using namespace Cesium3DTilesSelection; +using namespace CesiumUtility; template -static void checkScalarProperty( +static void checkNonArrayProperty( const Model& model, - const FeatureTable& featureTable, - const Class& metaClass, + const ExtensionExtStructuralMetadataPropertyTable& propertyTable, + const ExtensionExtStructuralMetadataClass& metaClass, const std::string& propertyName, - const std::string& expectedPropertyType, + const std::string& expectedType, + const std::optional& expectedComponentType, const std::vector& expected, size_t expectedTotalInstances) { - const ClassProperty& property = metaClass.properties.at(propertyName); - REQUIRE(property.type == expectedPropertyType); - REQUIRE(property.componentType == std::nullopt); - REQUIRE(property.componentCount == std::nullopt); - - MetadataFeatureTableView view(&model, &featureTable); - std::optional> propertyView = + const ExtensionExtStructuralMetadataClassProperty& property = + metaClass.properties.at(propertyName); + REQUIRE(property.type == expectedType); + REQUIRE(property.componentType == expectedComponentType); + REQUIRE(!property.array); + REQUIRE(property.count == std::nullopt); + + MetadataPropertyTableView view(model, propertyTable); + REQUIRE(view.status() == MetadataPropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + + MetadataPropertyView propertyView = view.getPropertyView(propertyName); - REQUIRE(propertyView != std::nullopt); - REQUIRE(propertyView->size() == featureTable.count); - REQUIRE(propertyView->size() == static_cast(expectedTotalInstances)); - for (int64_t i = 0; i < propertyView->size(); ++i) { - if constexpr ( + REQUIRE(propertyView.status() == MetadataPropertyViewStatus::Valid); + REQUIRE(propertyView.size() == propertyTable.count); + REQUIRE(propertyView.size() == static_cast(expectedTotalInstances)); + for (int64_t i = 0; i < propertyView.size(); ++i) { + if constexpr (std::is_same_v) { + REQUIRE(Math::equalsEpsilon( + static_cast(propertyView.get(i)), + static_cast(expected[static_cast(i)]), + Math::Epsilon6)); + } else if constexpr ( std::is_same_v || std::is_same_v) { - REQUIRE(propertyView->get(i) == Approx(expected[static_cast(i)])); + REQUIRE(propertyView.get(i) == Approx(expected[static_cast(i)])); } else { REQUIRE( - static_cast(propertyView->get(i)) == + static_cast(propertyView.get(i)) == expected[static_cast(i)]); } } @@ -55,52 +69,54 @@ static void checkScalarProperty( template static void checkArrayProperty( const Model& model, - const FeatureTable& featureTable, - const Class& metaClass, + const ExtensionExtStructuralMetadataPropertyTable& propertyTable, + const ExtensionExtStructuralMetadataClass& metaClass, const std::string& propertyName, - int64_t expectedComponentCount, - const std::string& expectedComponentType, + int64_t expectedCount, + const std::string& expectedType, + const std::optional& expectedComponentType, const std::vector>& expected, size_t expectedTotalInstances) { - const ClassProperty& property = metaClass.properties.at(propertyName); - REQUIRE(property.type == "ARRAY"); - REQUIRE(property.componentType.has_value()); - REQUIRE(*property.componentType == expectedComponentType); - if (expectedComponentCount > 0) { - REQUIRE( - property.componentCount.value() == - static_cast(expectedComponentCount)); - } - - MetadataFeatureTableView view(&model, &featureTable); - std::optional>> - propertyView = view.getPropertyView>( - propertyName); - REQUIRE(propertyView->size() == featureTable.count); - REQUIRE(propertyView->size() == static_cast(expectedTotalInstances)); + const ExtensionExtStructuralMetadataClassProperty& property = + metaClass.properties.at(propertyName); + REQUIRE(property.type == expectedType); + REQUIRE(property.componentType == expectedComponentType); + REQUIRE(property.array); + REQUIRE(property.count.value_or(0) == expectedCount); + + MetadataPropertyTableView view(model, propertyTable); + REQUIRE(view.status() == MetadataPropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + + MetadataPropertyView> propertyView = + view.getPropertyView>(propertyName); + REQUIRE(propertyView.status() == MetadataPropertyViewStatus::Valid); + REQUIRE(propertyView.size() == propertyTable.count); + REQUIRE(propertyView.size() == static_cast(expectedTotalInstances)); for (size_t i = 0; i < expectedTotalInstances; ++i) { - MetadataArrayView val = - propertyView->get(static_cast(i)); - if (expectedComponentCount > 0) { - REQUIRE(val.size() == expectedComponentCount); + MetadataArrayView value = + propertyView.get(static_cast(i)); + if (expectedCount > 0) { + REQUIRE(value.size() == expectedCount); } for (size_t j = 0; j < expected[i].size(); ++j) { if constexpr ( std::is_same_v || std::is_same_v) { - REQUIRE(val[static_cast(j)] == Approx(expected[i][j])); + REQUIRE(value[static_cast(j)] == Approx(expected[i][j])); } else { - REQUIRE(val[static_cast(j)] == expected[i][j]); + REQUIRE(value[static_cast(j)] == expected[i][j]); } } } } template -static void createTestForScalarJson( +static void createTestForNonArrayJson( const std::vector& expected, - const std::string& expectedPropertyType, + const std::string& expectedType, + const std::optional& expectedComponentType, size_t totalInstances) { Model model; @@ -132,7 +148,7 @@ static void createTestForScalarJson( } batchTableJson.AddMember( - "scalarProp", + "scalarProperty", scalarProperty, batchTableJson.GetAllocator()); @@ -142,28 +158,37 @@ static void createTestForScalarJson( gsl::span(), model); - ExtensionModelExtFeatureMetadata* metadata = - model.getExtension(); - REQUIRE(metadata != nullptr); + const ExtensionModelExtStructuralMetadata* pMetadata = + model.getExtension(); + REQUIRE(pMetadata); - std::optional schema = metadata->schema; - REQUIRE(schema != std::nullopt); + const std::optional schema = + pMetadata->schema; + REQUIRE(schema); - const std::unordered_map& classes = schema->classes; + const std::unordered_map& + classes = schema->classes; REQUIRE(classes.size() == 1); - const Class& defaultClass = classes.at("default"); - const std::unordered_map& properties = + const ExtensionExtStructuralMetadataClass& defaultClass = + classes.at("default"); + const std::unordered_map< + std::string, + ExtensionExtStructuralMetadataClassProperty>& properties = defaultClass.properties; REQUIRE(properties.size() == 1); - const FeatureTable& featureTable = metadata->featureTables["default"]; - checkScalarProperty( + REQUIRE(pMetadata->propertyTables.size() == 1); + + const ExtensionExtStructuralMetadataPropertyTable& propertyTable = + pMetadata->propertyTables[0]; + checkNonArrayProperty( model, - featureTable, + propertyTable, defaultClass, - "scalarProp", - expectedPropertyType, + "scalarProperty", + expectedType, + expectedComponentType, expected, totalInstances); } @@ -171,8 +196,9 @@ static void createTestForScalarJson( template static void createTestForArrayJson( const std::vector>& expected, - const std::string& expectedComponentType, - int64_t componentCount, + const std::string& expectedType, + const std::optional& expectedComponentType, + int64_t arrayCount, size_t totalInstances) { Model model; @@ -208,7 +234,7 @@ static void createTestForArrayJson( } batchTableJson.AddMember( - "fixedArrayProp", + "fixedLengthArrayProperty", fixedArrayProperties, batchTableJson.GetAllocator()); @@ -218,28 +244,30 @@ static void createTestForArrayJson( gsl::span(), model); - ExtensionModelExtFeatureMetadata* metadata = - model.getExtension(); - REQUIRE(metadata != nullptr); + const ExtensionModelExtStructuralMetadata* pMetadata = + model.getExtension(); + REQUIRE(pMetadata); - std::optional schema = metadata->schema; - REQUIRE(schema != std::nullopt); + const std::optional& schema = + pMetadata->schema; + REQUIRE(schema); + REQUIRE(schema->classes.find("default") != schema->classes.end()); - const std::unordered_map& classes = schema->classes; - REQUIRE(classes.size() == 1); + const ExtensionExtStructuralMetadataClass& defaultClass = + schema->classes.at("default"); + REQUIRE(defaultClass.properties.size() == 1); - const Class& defaultClass = classes.at("default"); - const std::unordered_map& properties = - defaultClass.properties; - REQUIRE(properties.size() == 1); + REQUIRE(pMetadata->propertyTables.size() == 1); + const ExtensionExtStructuralMetadataPropertyTable& propertyTable = + pMetadata->propertyTables[0]; - const FeatureTable& featureTable = metadata->featureTables["default"]; checkArrayProperty( model, - featureTable, + propertyTable, defaultClass, - "fixedArrayProp", - componentCount, + "fixedLengthArrayProperty", + arrayCount, + expectedType, expectedComponentType, expected, totalInstances); @@ -247,21 +275,21 @@ static void createTestForArrayJson( std::set getUniqueBufferViewIds( const std::vector& accessors, - FeatureTable& featureTable) { + const ExtensionExtStructuralMetadataPropertyTable& propertyTable) { std::set result; for (auto it = accessors.begin(); it != accessors.end(); it++) { result.insert(it->bufferView); } - auto& properties = featureTable.properties; + auto& properties = propertyTable.properties; for (auto it = properties.begin(); it != properties.end(); it++) { auto& property = it->second; - result.insert(property.bufferView); - if (property.arrayOffsetBufferView >= 0) { - result.insert(property.arrayOffsetBufferView); + result.insert(property.values); + if (property.arrayOffsets >= 0) { + result.insert(property.arrayOffsets); } - if (property.stringOffsetBufferView >= 0) { - result.insert(property.stringOffsetBufferView); + if (property.stringOffsets >= 0) { + result.insert(property.stringOffsets); } } @@ -278,8 +306,8 @@ TEST_CASE("Converts JSON B3DM batch table to EXT_feature_metadata") { Model& gltf = *result.model; - ExtensionModelExtFeatureMetadata* pExtension = - gltf.getExtension(); + ExtensionModelExtStructuralMetadata* pExtension = + gltf.getExtension(); REQUIRE(pExtension); // Check the schema @@ -289,7 +317,7 @@ TEST_CASE("Converts JSON B3DM batch table to EXT_feature_metadata") { auto firstClassIt = pExtension->schema->classes.begin(); CHECK(firstClassIt->first == "default"); - CesiumGltf::Class& defaultClass = firstClassIt->second; + ExtensionExtStructuralMetadataClass& defaultClass = firstClassIt->second; REQUIRE(defaultClass.properties.size() == 4); auto idIt = defaultClass.properties.find("id"); @@ -301,50 +329,68 @@ TEST_CASE("Converts JSON B3DM batch table to EXT_feature_metadata") { auto heightIt = defaultClass.properties.find("Height"); REQUIRE(heightIt != defaultClass.properties.end()); - CHECK(idIt->second.type == "INT8"); - CHECK(longitudeIt->second.type == "FLOAT64"); - CHECK(latitudeIt->second.type == "FLOAT64"); - CHECK(heightIt->second.type == "FLOAT64"); - - // Check the feature table - auto firstFeatureTableIt = pExtension->featureTables.begin(); - REQUIRE(firstFeatureTableIt != pExtension->featureTables.end()); - - FeatureTable& featureTable = firstFeatureTableIt->second; - CHECK(featureTable.classProperty == "default"); - REQUIRE(featureTable.properties.size() == 4); - - auto idIt2 = featureTable.properties.find("id"); - REQUIRE(idIt2 != featureTable.properties.end()); - auto longitudeIt2 = featureTable.properties.find("Longitude"); - REQUIRE(longitudeIt2 != featureTable.properties.end()); - auto latitudeIt2 = featureTable.properties.find("Latitude"); - REQUIRE(latitudeIt2 != featureTable.properties.end()); - auto heightIt2 = featureTable.properties.find("Height"); - REQUIRE(heightIt2 != featureTable.properties.end()); - - CHECK(idIt2->second.bufferView >= 0); CHECK( - idIt2->second.bufferView < static_cast(gltf.bufferViews.size())); - CHECK(longitudeIt2->second.bufferView >= 0); + idIt->second.type == + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR); CHECK( - longitudeIt2->second.bufferView < - static_cast(gltf.bufferViews.size())); - CHECK(latitudeIt2->second.bufferView >= 0); + longitudeIt->second.type == + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR); CHECK( - latitudeIt2->second.bufferView < - static_cast(gltf.bufferViews.size())); - CHECK(heightIt2->second.bufferView >= 0); + latitudeIt->second.type == + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR); + CHECK( + heightIt->second.type == + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR); + CHECK( - heightIt2->second.bufferView < + idIt->second.componentType == + ExtensionExtStructuralMetadataClassProperty::ComponentType::INT8); + CHECK( + longitudeIt->second.componentType == + ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT64); + CHECK( + latitudeIt->second.componentType == + ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT64); + CHECK( + heightIt->second.componentType == + ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT64); + + // Check the property table + REQUIRE(pExtension->propertyTables.size() == 1); + ExtensionExtStructuralMetadataPropertyTable& propertyTable = + pExtension->propertyTables[0]; + CHECK(propertyTable.classProperty == "default"); + REQUIRE(propertyTable.properties.size() == 4); + + auto idIt2 = propertyTable.properties.find("id"); + REQUIRE(idIt2 != propertyTable.properties.end()); + auto longitudeIt2 = propertyTable.properties.find("Longitude"); + REQUIRE(longitudeIt2 != propertyTable.properties.end()); + auto latitudeIt2 = propertyTable.properties.find("Latitude"); + REQUIRE(latitudeIt2 != propertyTable.properties.end()); + auto heightIt2 = propertyTable.properties.find("Height"); + REQUIRE(heightIt2 != propertyTable.properties.end()); + + REQUIRE(idIt2->second.values >= 0); + REQUIRE(idIt2->second.values < static_cast(gltf.bufferViews.size())); + REQUIRE(longitudeIt2->second.values >= 0); + REQUIRE( + longitudeIt2->second.values < + static_cast(gltf.bufferViews.size())); + REQUIRE(latitudeIt2->second.values >= 0); + REQUIRE( + latitudeIt2->second.values < static_cast(gltf.bufferViews.size())); + REQUIRE(heightIt2->second.values >= 0); + REQUIRE( + heightIt2->second.values < static_cast(gltf.bufferViews.size())); // Make sure all property bufferViews are unique std::set bufferViews{ - idIt2->second.bufferView, - longitudeIt2->second.bufferView, - latitudeIt2->second.bufferView, - heightIt2->second.bufferView}; + idIt2->second.values, + longitudeIt2->second.values, + latitudeIt2->second.values, + heightIt2->second.values}; CHECK(bufferViews.size() == 4); // Check the mesh primitives @@ -359,6 +405,8 @@ TEST_CASE("Converts JSON B3DM batch table to EXT_feature_metadata") { CHECK( primitive.attributes.find("_FEATURE_ID_1") == primitive.attributes.end()); + CHECK( + primitive.attributes.find("_BATCH_ID") == primitive.attributes.end()); ExtensionExtMeshFeatures* pPrimitiveExtension = primitive.getExtension(); @@ -375,12 +423,13 @@ TEST_CASE("Converts JSON B3DM batch table to EXT_feature_metadata") { // Check metadata values { std::vector expected = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; - checkScalarProperty( - *result.model, - featureTable, + checkNonArrayProperty( + gltf, + propertyTable, defaultClass, "id", - "INT8", + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, + ExtensionExtStructuralMetadataClassProperty::ComponentType::INT8, expected, expected.size()); } @@ -397,12 +446,13 @@ TEST_CASE("Converts JSON B3DM batch table to EXT_feature_metadata") { 13.513022359460592, 13.74609257467091, 10.145220385864377}; - checkScalarProperty( + checkNonArrayProperty( *result.model, - featureTable, + propertyTable, defaultClass, "Height", - "FLOAT64", + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, + ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT64, expected, expected.size()); } @@ -419,12 +469,13 @@ TEST_CASE("Converts JSON B3DM batch table to EXT_feature_metadata") { -1.3196718857616954, -1.3196471198757775, -1.319644104024109}; - checkScalarProperty( - *result.model, - featureTable, + checkNonArrayProperty( + gltf, + propertyTable, defaultClass, "Longitude", - "FLOAT64", + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, + ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT64, expected, expected.size()); } @@ -441,18 +492,19 @@ TEST_CASE("Converts JSON B3DM batch table to EXT_feature_metadata") { 0.6988670019309562, 0.6988523191715889, 0.6988697375823105}; - checkScalarProperty( - *result.model, - featureTable, + checkNonArrayProperty( + gltf, + propertyTable, defaultClass, "Latitude", - "FLOAT64", + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, + ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT64, expected, expected.size()); } } -TEST_CASE("Convert binary B3DM batch table to EXT_feature_metadata") { +TEST_CASE("Convert binary B3DM batch table to EXT_structural_metadata") { std::filesystem::path testFilePath = Cesium3DTilesSelection_TEST_DATA_DIR; testFilePath = testFilePath / "BatchTables" / "batchedWithBatchTableBinary.b3dm"; @@ -460,33 +512,69 @@ TEST_CASE("Convert binary B3DM batch table to EXT_feature_metadata") { GltfConverterResult result = ConvertTileToGltf::fromB3dm(testFilePath); REQUIRE(!result.errors); - REQUIRE(result.model != std::nullopt); + REQUIRE(result.model); + + const Model& model = *result.model; - ExtensionModelExtFeatureMetadata* metadata = - result.model->getExtension(); - REQUIRE(metadata != nullptr); + const ExtensionModelExtStructuralMetadata* metadata = + model.getExtension(); + REQUIRE(metadata); - std::optional schema = metadata->schema; - REQUIRE(schema != std::nullopt); + std::optional schema = metadata->schema; + REQUIRE(schema); - const std::unordered_map& classes = schema->classes; + const std::unordered_map& + classes = schema->classes; REQUIRE(classes.size() == 1); - const Class& defaultClass = classes.at("default"); - const std::unordered_map& properties = + const ExtensionExtStructuralMetadataClass& defaultClass = + classes.at("default"); + const std::unordered_map< + std::string, + ExtensionExtStructuralMetadataClassProperty>& properties = defaultClass.properties; REQUIRE(properties.size() == 6); - const FeatureTable& featureTable = metadata->featureTables["default"]; + REQUIRE(metadata->propertyTables.size() == 1); + const ExtensionExtStructuralMetadataPropertyTable& propertyTable = + metadata->propertyTables[0]; + + // Check that batch IDs were converted to EXT_mesh_features + CHECK(!model.meshes.empty()); + + for (const Mesh& mesh : model.meshes) { + CHECK(!mesh.primitives.empty()); + for (const MeshPrimitive& primitive : mesh.primitives) { + CHECK( + primitive.attributes.find("_FEATURE_ID_0") != + primitive.attributes.end()); + CHECK( + primitive.attributes.find("_FEATURE_ID_1") == + primitive.attributes.end()); + CHECK( + primitive.attributes.find("_BATCH_ID") == primitive.attributes.end()); + + const ExtensionExtMeshFeatures* pPrimitiveExtension = + primitive.getExtension(); + REQUIRE(pPrimitiveExtension); + REQUIRE(pPrimitiveExtension->featureIds.size() == 1); + const ExtensionExtMeshFeaturesFeatureId& featureId = + pPrimitiveExtension->featureIds[0]; + CHECK(featureId.featureCount == 10); + CHECK(featureId.attribute == 0); + CHECK(featureId.propertyTable == 0); + } + } { std::vector expected = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; - checkScalarProperty( - *result.model, - featureTable, + checkNonArrayProperty( + model, + propertyTable, defaultClass, "id", - "INT8", + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, + ExtensionExtStructuralMetadataClassProperty::ComponentType::INT8, expected, expected.size()); } @@ -503,12 +591,13 @@ TEST_CASE("Convert binary B3DM batch table to EXT_feature_metadata") { 12.224825594574213, 12.546202838420868, 7.632075032219291}; - checkScalarProperty( - *result.model, - featureTable, + checkNonArrayProperty( + model, + propertyTable, defaultClass, "Height", - "FLOAT64", + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, + ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT64, expected, expected.size()); } @@ -525,12 +614,13 @@ TEST_CASE("Convert binary B3DM batch table to EXT_feature_metadata") { -1.3196940116311096, -1.319683648959897, -1.3196959060375169}; - checkScalarProperty( - *result.model, - featureTable, + checkNonArrayProperty( + model, + propertyTable, defaultClass, "Longitude", - "FLOAT64", + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, + ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT64, expected, expected.size()); } @@ -547,31 +637,33 @@ TEST_CASE("Convert binary B3DM batch table to EXT_feature_metadata") { 0.6988590050687061, 0.6988690935212543, 0.6988854945986224}; - checkScalarProperty( - *result.model, - featureTable, + checkNonArrayProperty( + model, + propertyTable, defaultClass, "Latitude", - "FLOAT64", + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, + ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT64, expected, expected.size()); } { std::vector expected(10, 255); - checkScalarProperty( - *result.model, - featureTable, + checkNonArrayProperty( + model, + propertyTable, defaultClass, "code", - "UINT8", + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT8, expected, expected.size()); } { // clang-format off - std::vector> expected{ + std::vector expected{ {-1.31968, 0.698874, 6.155801922082901}, {-1.3196832683949145, 0.6988615321420496, 13.410263679921627}, {-1.3196637662080655, 0.6988736012180136, 6.1022464875131845}, @@ -584,29 +676,29 @@ TEST_CASE("Convert binary B3DM batch table to EXT_feature_metadata") { {-1.3196959060375169, 0.6988854945986224, 7.632075032219291} }; // clang-format on - checkArrayProperty( - *result.model, - featureTable, + checkNonArrayProperty( + model, + propertyTable, defaultClass, "cartographic", - 3, - "FLOAT64", + ExtensionExtStructuralMetadataClassProperty::Type::VEC3, + ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT64, expected, expected.size()); } } -TEST_CASE("Converts batched PNTS batch table to EXT_feature_metadata") { +TEST_CASE("Converts batched PNTS batch table to EXT_structural_metadata") { std::filesystem::path testFilePath = Cesium3DTilesSelection_TEST_DATA_DIR; testFilePath = testFilePath / "PointCloud" / "pointCloudBatched.pnts"; GltfConverterResult result = ConvertTileToGltf::fromPnts(testFilePath); REQUIRE(result.model); - Model& gltf = *result.model; + const Model& gltf = *result.model; - ExtensionModelExtFeatureMetadata* pExtension = - gltf.getExtension(); + const ExtensionModelExtStructuralMetadata* pExtension = + gltf.getExtension(); REQUIRE(pExtension); // Check the schema @@ -616,7 +708,8 @@ TEST_CASE("Converts batched PNTS batch table to EXT_feature_metadata") { auto firstClassIt = pExtension->schema->classes.begin(); CHECK(firstClassIt->first == "default"); - CesiumGltf::Class& defaultClass = firstClassIt->second; + const ExtensionExtStructuralMetadataClass& defaultClass = + firstClassIt->second; REQUIRE(defaultClass.properties.size() == 3); { @@ -627,61 +720,68 @@ TEST_CASE("Converts batched PNTS batch table to EXT_feature_metadata") { auto idIt = defaultClass.properties.find("id"); REQUIRE(idIt != defaultClass.properties.end()); - CHECK(nameIt->second.type == "STRING"); - CHECK(dimensionsIt->second.type == "ARRAY"); - REQUIRE(dimensionsIt->second.componentType); - CHECK(dimensionsIt->second.componentType.value() == "FLOAT32"); - CHECK(idIt->second.type == "UINT32"); + CHECK( + nameIt->second.type == + ExtensionExtStructuralMetadataClassProperty::Type::STRING); + CHECK( + dimensionsIt->second.type == + ExtensionExtStructuralMetadataClassProperty::Type::VEC3); + CHECK( + dimensionsIt->second.componentType == + ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT32); + CHECK( + idIt->second.type == + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR); + CHECK( + idIt->second.componentType == + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT32); } - // Check the feature table - auto firstFeatureTableIt = pExtension->featureTables.begin(); - REQUIRE(firstFeatureTableIt != pExtension->featureTables.end()); - - FeatureTable& featureTable = firstFeatureTableIt->second; - CHECK(featureTable.classProperty == "default"); - REQUIRE(featureTable.properties.size() == 3); + // Check the property table + REQUIRE(pExtension->propertyTables.size() == 1); + const ExtensionExtStructuralMetadataPropertyTable& propertyTable = + pExtension->propertyTables[0]; + CHECK(propertyTable.classProperty == "default"); + REQUIRE(propertyTable.properties.size() == 3); { - auto nameIt = featureTable.properties.find("name"); - REQUIRE(nameIt != featureTable.properties.end()); - auto dimensionsIt = featureTable.properties.find("dimensions"); - REQUIRE(dimensionsIt != featureTable.properties.end()); - auto idIt = featureTable.properties.find("id"); - REQUIRE(idIt != featureTable.properties.end()); - - CHECK(nameIt->second.bufferView >= 0); - CHECK( - nameIt->second.bufferView < - static_cast(gltf.bufferViews.size())); - CHECK(dimensionsIt->second.bufferView >= 0); - CHECK( - dimensionsIt->second.bufferView < - static_cast(gltf.bufferViews.size())); - CHECK(idIt->second.bufferView >= 0); - CHECK( - idIt->second.bufferView < + auto nameIt = propertyTable.properties.find("name"); + REQUIRE(nameIt != propertyTable.properties.end()); + auto dimensionsIt = propertyTable.properties.find("dimensions"); + REQUIRE(dimensionsIt != propertyTable.properties.end()); + auto idIt = propertyTable.properties.find("id"); + REQUIRE(idIt != propertyTable.properties.end()); + + REQUIRE(nameIt->second.values >= 0); + REQUIRE( + nameIt->second.values < static_cast(gltf.bufferViews.size())); + REQUIRE(dimensionsIt->second.values >= 0); + REQUIRE( + dimensionsIt->second.values < static_cast(gltf.bufferViews.size())); + REQUIRE(idIt->second.values >= 0); + REQUIRE( + idIt->second.values < static_cast(gltf.bufferViews.size())); } std::set bufferViewSet = - getUniqueBufferViewIds(gltf.accessors, featureTable); + getUniqueBufferViewIds(gltf.accessors, propertyTable); CHECK(bufferViewSet.size() == gltf.bufferViews.size()); // Check the mesh primitive REQUIRE(gltf.meshes.size() == 1); - Mesh& mesh = gltf.meshes[0]; + const Mesh& mesh = gltf.meshes[0]; REQUIRE(mesh.primitives.size() == 1); - MeshPrimitive& primitive = mesh.primitives[0]; + const MeshPrimitive& primitive = mesh.primitives[0]; CHECK( primitive.attributes.find("_FEATURE_ID_0") != primitive.attributes.end()); - ExtensionExtMeshFeatures* pPrimitiveExtension = + const ExtensionExtMeshFeatures* pPrimitiveExtension = primitive.getExtension(); REQUIRE(pPrimitiveExtension); REQUIRE(pPrimitiveExtension->featureIds.size() == 1); - ExtensionExtMeshFeaturesFeatureId& featureId = + const ExtensionExtMeshFeaturesFeatureId& featureId = pPrimitiveExtension->featureIds[0]; CHECK(featureId.featureCount == 8); CHECK(featureId.attribute == 0); @@ -698,18 +798,19 @@ TEST_CASE("Converts batched PNTS batch table to EXT_feature_metadata") { "section5", "section6", "section7"}; - checkScalarProperty( - *result.model, - featureTable, + checkNonArrayProperty( + gltf, + propertyTable, defaultClass, "name", - "STRING", + ExtensionExtStructuralMetadataClassProperty::Type::STRING, + std::nullopt, expected, expected.size()); } { - std::vector> expected = { + std::vector expected = { {0.1182744f, 0.7206326f, 0.6399210f}, {0.5820198f, 0.1433532f, 0.5373732f}, {0.9446688f, 0.7586156f, 0.5218483f}, @@ -718,32 +819,32 @@ TEST_CASE("Converts batched PNTS batch table to EXT_feature_metadata") { {0.7369181f, 0.4561503f, 0.2165503f}, {0.5684339f, 0.1352181f, 0.0187897f}, {0.3241409f, 0.6176354f, 0.1496748f}}; - - checkArrayProperty( - *result.model, - featureTable, + checkNonArrayProperty( + gltf, + propertyTable, defaultClass, "dimensions", - 3, - "FLOAT32", + ExtensionExtStructuralMetadataClassProperty::Type::VEC3, + ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT32, expected, expected.size()); } { std::vector expected = {0, 1, 2, 3, 4, 5, 6, 7}; - checkScalarProperty( - *result.model, - featureTable, + checkNonArrayProperty( + gltf, + propertyTable, defaultClass, "id", - "UINT32", + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT32, expected, expected.size()); } } -TEST_CASE("Converts per-point PNTS batch table to EXT_feature_metadata") { +TEST_CASE("Converts per-point PNTS batch table to EXT_structural_metadata") { std::filesystem::path testFilePath = Cesium3DTilesSelection_TEST_DATA_DIR; testFilePath = testFilePath / "PointCloud" / "pointCloudWithPerPointProperties.pnts"; @@ -751,10 +852,10 @@ TEST_CASE("Converts per-point PNTS batch table to EXT_feature_metadata") { GltfConverterResult result = ConvertTileToGltf::fromPnts(testFilePath); REQUIRE(result.model); - Model& gltf = *result.model; + const Model& gltf = *result.model; - ExtensionModelExtFeatureMetadata* pExtension = - gltf.getExtension(); + const ExtensionModelExtStructuralMetadata* pExtension = + gltf.getExtension(); REQUIRE(pExtension); // Check the schema @@ -764,7 +865,8 @@ TEST_CASE("Converts per-point PNTS batch table to EXT_feature_metadata") { auto firstClassIt = pExtension->schema->classes.begin(); CHECK(firstClassIt->first == "default"); - CesiumGltf::Class& defaultClass = firstClassIt->second; + const ExtensionExtStructuralMetadataClass& defaultClass = + firstClassIt->second; REQUIRE(defaultClass.properties.size() == 3); { @@ -775,61 +877,73 @@ TEST_CASE("Converts per-point PNTS batch table to EXT_feature_metadata") { auto idIt = defaultClass.properties.find("id"); REQUIRE(idIt != defaultClass.properties.end()); - CHECK(temperatureIt->second.type == "FLOAT32"); - CHECK(secondaryColorIt->second.type == "ARRAY"); + CHECK( + temperatureIt->second.type == + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR); + CHECK( + temperatureIt->second.componentType == + ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT32); + CHECK( + secondaryColorIt->second.type == + ExtensionExtStructuralMetadataClassProperty::Type::VEC3); REQUIRE(secondaryColorIt->second.componentType); - CHECK(secondaryColorIt->second.componentType.value() == "FLOAT32"); - CHECK(idIt->second.type == "UINT16"); + CHECK( + secondaryColorIt->second.componentType == + ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT32); + CHECK( + idIt->second.type == + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR); + CHECK( + idIt->second.componentType == + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT16); } - // Check the feature table - auto firstFeatureTableIt = pExtension->featureTables.begin(); - REQUIRE(firstFeatureTableIt != pExtension->featureTables.end()); - - FeatureTable& featureTable = firstFeatureTableIt->second; - CHECK(featureTable.classProperty == "default"); - REQUIRE(featureTable.properties.size() == 3); + // Check the property table + REQUIRE(pExtension->propertyTables.size() == 1); + const ExtensionExtStructuralMetadataPropertyTable& propertyTable = + pExtension->propertyTables[0]; + CHECK(propertyTable.classProperty == "default"); + REQUIRE(propertyTable.properties.size() == 3); { - auto temperatureIt = featureTable.properties.find("temperature"); - REQUIRE(temperatureIt != featureTable.properties.end()); - auto secondaryColorIt = featureTable.properties.find("secondaryColor"); - REQUIRE(secondaryColorIt != featureTable.properties.end()); - auto idIt = featureTable.properties.find("id"); - REQUIRE(idIt != featureTable.properties.end()); - - CHECK(temperatureIt->second.bufferView >= 0); - CHECK( - temperatureIt->second.bufferView < - static_cast(gltf.bufferViews.size())); - CHECK(secondaryColorIt->second.bufferView >= 0); - CHECK( - secondaryColorIt->second.bufferView < + auto temperatureIt = propertyTable.properties.find("temperature"); + REQUIRE(temperatureIt != propertyTable.properties.end()); + auto secondaryColorIt = propertyTable.properties.find("secondaryColor"); + REQUIRE(secondaryColorIt != propertyTable.properties.end()); + auto idIt = propertyTable.properties.find("id"); + REQUIRE(idIt != propertyTable.properties.end()); + + REQUIRE(temperatureIt->second.values >= 0); + REQUIRE( + temperatureIt->second.values < static_cast(gltf.bufferViews.size())); - CHECK(idIt->second.bufferView >= 0); - CHECK( - idIt->second.bufferView < + REQUIRE(secondaryColorIt->second.values >= 0); + REQUIRE( + secondaryColorIt->second.values < static_cast(gltf.bufferViews.size())); + REQUIRE(idIt->second.values >= 0); + REQUIRE( + idIt->second.values < static_cast(gltf.bufferViews.size())); } std::set bufferViewSet = - getUniqueBufferViewIds(gltf.accessors, featureTable); + getUniqueBufferViewIds(gltf.accessors, propertyTable); CHECK(bufferViewSet.size() == gltf.bufferViews.size()); // Check the mesh primitive REQUIRE(gltf.meshes.size() == 1); - Mesh& mesh = gltf.meshes[0]; + const Mesh& mesh = gltf.meshes[0]; REQUIRE(mesh.primitives.size() == 1); - MeshPrimitive& primitive = mesh.primitives[0]; + const MeshPrimitive& primitive = mesh.primitives[0]; CHECK( primitive.attributes.find("_FEATURE_ID_0") == primitive.attributes.end()); - ExtensionExtMeshFeatures* pPrimitiveExtension = + const ExtensionExtMeshFeatures* pPrimitiveExtension = primitive.getExtension(); REQUIRE(pPrimitiveExtension); REQUIRE(pPrimitiveExtension->featureIds.size() == 1); - ExtensionExtMeshFeaturesFeatureId& featureId = + const ExtensionExtMeshFeaturesFeatureId& featureId = pPrimitiveExtension->featureIds[0]; CHECK(featureId.featureCount == 8); CHECK(!featureId.attribute); @@ -846,18 +960,19 @@ TEST_CASE("Converts per-point PNTS batch table to EXT_feature_metadata") { 0.3274261f, 0.1337213f, 0.0207673f}; - checkScalarProperty( - *result.model, - featureTable, + checkNonArrayProperty( + gltf, + propertyTable, defaultClass, "temperature", - "FLOAT32", + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, + ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT32, expected, expected.size()); } { - std::vector> expected = { + std::vector expected = { {0.0202183f, 0, 0}, {0.3682415f, 0, 0}, {0.8326198f, 0, 0}, @@ -866,43 +981,43 @@ TEST_CASE("Converts per-point PNTS batch table to EXT_feature_metadata") { {0.1403507f, 0, 0}, {0.8700121f, 0, 0}, {0.8700872f, 0, 0}}; - - checkArrayProperty( - *result.model, - featureTable, + checkNonArrayProperty( + gltf, + propertyTable, defaultClass, "secondaryColor", - 3, - "FLOAT32", + ExtensionExtStructuralMetadataClassProperty::Type::VEC3, + ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT32, expected, expected.size()); } { std::vector expected = {0, 1, 2, 3, 4, 5, 6, 7}; - checkScalarProperty( - *result.model, - featureTable, + checkNonArrayProperty( + gltf, + propertyTable, defaultClass, "id", - "UINT16", + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT16, expected, expected.size()); } } TEST_CASE("Converts Draco per-point PNTS batch table to " - "EXT_feature_metadata") { + "EXT_structural_metadata") { std::filesystem::path testFilePath = Cesium3DTilesSelection_TEST_DATA_DIR; testFilePath = testFilePath / "PointCloud" / "pointCloudDraco.pnts"; GltfConverterResult result = ConvertTileToGltf::fromPnts(testFilePath); REQUIRE(result.model); - Model& gltf = *result.model; + const Model& gltf = *result.model; - ExtensionModelExtFeatureMetadata* pExtension = - gltf.getExtension(); + const ExtensionModelExtStructuralMetadata* pExtension = + gltf.getExtension(); REQUIRE(pExtension); // Check the schema @@ -912,7 +1027,8 @@ TEST_CASE("Converts Draco per-point PNTS batch table to " auto firstClassIt = pExtension->schema->classes.begin(); CHECK(firstClassIt->first == "default"); - CesiumGltf::Class& defaultClass = firstClassIt->second; + const ExtensionExtStructuralMetadataClass& defaultClass = + firstClassIt->second; REQUIRE(defaultClass.properties.size() == 3); { @@ -923,61 +1039,73 @@ TEST_CASE("Converts Draco per-point PNTS batch table to " auto idIt = defaultClass.properties.find("id"); REQUIRE(idIt != defaultClass.properties.end()); - CHECK(temperatureIt->second.type == "FLOAT32"); - CHECK(secondaryColorIt->second.type == "ARRAY"); + CHECK( + temperatureIt->second.type == + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR); + CHECK( + temperatureIt->second.componentType == + ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT32); + CHECK( + secondaryColorIt->second.type == + ExtensionExtStructuralMetadataClassProperty::Type::VEC3); REQUIRE(secondaryColorIt->second.componentType); - CHECK(secondaryColorIt->second.componentType.value() == "FLOAT32"); - CHECK(idIt->second.type == "UINT16"); + CHECK( + secondaryColorIt->second.componentType == + ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT32); + CHECK( + idIt->second.type == + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR); + CHECK( + idIt->second.componentType == + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT16); } - // Check the feature table - auto firstFeatureTableIt = pExtension->featureTables.begin(); - REQUIRE(firstFeatureTableIt != pExtension->featureTables.end()); - - FeatureTable& featureTable = firstFeatureTableIt->second; - CHECK(featureTable.classProperty == "default"); - REQUIRE(featureTable.properties.size() == 3); + // Check the property table + REQUIRE(pExtension->propertyTables.size() == 1); + const ExtensionExtStructuralMetadataPropertyTable& propertyTable = + pExtension->propertyTables[0]; + CHECK(propertyTable.classProperty == "default"); + REQUIRE(propertyTable.properties.size() == 3); { - auto temperatureIt = featureTable.properties.find("temperature"); - REQUIRE(temperatureIt != featureTable.properties.end()); - auto secondaryColorIt = featureTable.properties.find("secondaryColor"); - REQUIRE(secondaryColorIt != featureTable.properties.end()); - auto idIt = featureTable.properties.find("id"); - REQUIRE(idIt != featureTable.properties.end()); - - CHECK(temperatureIt->second.bufferView >= 0); - CHECK( - temperatureIt->second.bufferView < - static_cast(gltf.bufferViews.size())); - CHECK(secondaryColorIt->second.bufferView >= 0); - CHECK( - secondaryColorIt->second.bufferView < + auto temperatureIt = propertyTable.properties.find("temperature"); + REQUIRE(temperatureIt != propertyTable.properties.end()); + auto secondaryColorIt = propertyTable.properties.find("secondaryColor"); + REQUIRE(secondaryColorIt != propertyTable.properties.end()); + auto idIt = propertyTable.properties.find("id"); + REQUIRE(idIt != propertyTable.properties.end()); + + REQUIRE(temperatureIt->second.values >= 0); + REQUIRE( + temperatureIt->second.values < static_cast(gltf.bufferViews.size())); - CHECK(idIt->second.bufferView >= 0); - CHECK( - idIt->second.bufferView < + REQUIRE(secondaryColorIt->second.values >= 0); + REQUIRE( + secondaryColorIt->second.values < static_cast(gltf.bufferViews.size())); + REQUIRE(idIt->second.values >= 0); + REQUIRE( + idIt->second.values < static_cast(gltf.bufferViews.size())); } std::set bufferViewSet = - getUniqueBufferViewIds(gltf.accessors, featureTable); + getUniqueBufferViewIds(gltf.accessors, propertyTable); CHECK(bufferViewSet.size() == gltf.bufferViews.size()); // Check the mesh primitive REQUIRE(gltf.meshes.size() == 1); - Mesh& mesh = gltf.meshes[0]; + const Mesh& mesh = gltf.meshes[0]; REQUIRE(mesh.primitives.size() == 1); - MeshPrimitive& primitive = mesh.primitives[0]; + const MeshPrimitive& primitive = mesh.primitives[0]; CHECK( primitive.attributes.find("_FEATURE_ID_0") == primitive.attributes.end()); - ExtensionExtMeshFeatures* pPrimitiveExtension = + const ExtensionExtMeshFeatures* pPrimitiveExtension = primitive.getExtension(); REQUIRE(pPrimitiveExtension); REQUIRE(pPrimitiveExtension->featureIds.size() == 1); - ExtensionExtMeshFeaturesFeatureId& featureId = + const ExtensionExtMeshFeaturesFeatureId& featureId = pPrimitiveExtension->featureIds[0]; CHECK(featureId.featureCount == 8); CHECK(!featureId.attribute); @@ -994,18 +1122,19 @@ TEST_CASE("Converts Draco per-point PNTS batch table to " 0.3274441f, 0.1337535f, 0.0207673f}; - checkScalarProperty( - *result.model, - featureTable, + checkNonArrayProperty( + gltf, + propertyTable, defaultClass, "temperature", - "FLOAT32", + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, + ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT32, expected, expected.size()); } { - std::vector> expected = { + std::vector expected = { {0.1182744f, 0, 0}, {0.7206645f, 0, 0}, {0.6399421f, 0, 0}, @@ -1014,32 +1143,32 @@ TEST_CASE("Converts Draco per-point PNTS batch table to " {0.5374249f, 0, 0}, {0.9446688f, 0, 0}, {0.7586040f, 0, 0}}; - - checkArrayProperty( - *result.model, - featureTable, + checkNonArrayProperty( + gltf, + propertyTable, defaultClass, "secondaryColor", - 3, - "FLOAT32", + ExtensionExtStructuralMetadataClassProperty::Type::VEC3, + ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT32, expected, expected.size()); } { std::vector expected = {0, 1, 2, 3, 4, 5, 6, 7}; - checkScalarProperty( - *result.model, - featureTable, + checkNonArrayProperty( + gltf, + propertyTable, defaultClass, "id", - "UINT16", + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT16, expected, expected.size()); } } -TEST_CASE("Upgrade json nested json metadata to string") { +TEST_CASE("Upgrade nested JSON metadata to string") { std::filesystem::path testFilePath = Cesium3DTilesSelection_TEST_DATA_DIR; testFilePath = testFilePath / "BatchTables" / "batchedWithStringAndNestedJson.b3dm"; @@ -1047,46 +1176,55 @@ TEST_CASE("Upgrade json nested json metadata to string") { GltfConverterResult result = ConvertTileToGltf::fromB3dm(testFilePath); REQUIRE(!result.errors); - REQUIRE(result.model != std::nullopt); + REQUIRE(result.model); - ExtensionModelExtFeatureMetadata* metadata = - result.model->getExtension(); - REQUIRE(metadata != nullptr); + const Model& model = *result.model; + const ExtensionModelExtStructuralMetadata* pMetadata = + result.model->getExtension(); + REQUIRE(pMetadata); - std::optional schema = metadata->schema; - REQUIRE(schema != std::nullopt); + const std::optional& schema = + pMetadata->schema; + REQUIRE(schema); - const std::unordered_map& classes = schema->classes; + const std::unordered_map& + classes = schema->classes; REQUIRE(classes.size() == 1); - const Class& defaultClass = classes.at("default"); - const std::unordered_map& properties = + const ExtensionExtStructuralMetadataClass& defaultClass = + classes.at("default"); + const std::unordered_map< + std::string, + ExtensionExtStructuralMetadataClassProperty>& properties = defaultClass.properties; REQUIRE(properties.size() == 6); - const FeatureTable& featureTable = metadata->featureTables["default"]; - REQUIRE(featureTable.count == 10); + REQUIRE(pMetadata->propertyTables.size() == 1); + const ExtensionExtStructuralMetadataPropertyTable& propertyTable = + pMetadata->propertyTables[0]; + REQUIRE(propertyTable.count == 10); { std::vector expected; - for (int64_t i = 0; i < featureTable.count; ++i) { + for (int64_t i = 0; i < propertyTable.count; ++i) { std::string v = std::string("{\"name\":\"building") + std::to_string(i) + "\",\"year\":" + std::to_string(i) + "}"; expected.push_back(v); } - checkScalarProperty( - *result.model, - featureTable, + checkNonArrayProperty( + model, + propertyTable, defaultClass, "info", - "STRING", + ExtensionExtStructuralMetadataClassProperty::Type::STRING, + std::nullopt, expected, expected.size()); } { std::vector> expected; - for (int64_t i = 0; i < featureTable.count; ++i) { + for (int64_t i = 0; i < propertyTable.count; ++i) { std::vector expectedVal; expectedVal.emplace_back("room" + std::to_string(i) + "_a"); expectedVal.emplace_back("room" + std::to_string(i) + "_b"); @@ -1094,18 +1232,19 @@ TEST_CASE("Upgrade json nested json metadata to string") { expected.emplace_back(std::move(expectedVal)); } checkArrayProperty( - *result.model, - featureTable, + model, + propertyTable, defaultClass, "rooms", 3, - "STRING", + ExtensionExtStructuralMetadataClassProperty::Type::STRING, + std::nullopt, expected, expected.size()); } } -TEST_CASE("Upgrade bool json to boolean binary") { +TEST_CASE("Upgrade JSON booleans to binary") { Model model; rapidjson::Document featureTableJson; @@ -1138,36 +1277,45 @@ TEST_CASE("Upgrade bool json to boolean binary") { gsl::span(), model); - ExtensionModelExtFeatureMetadata* metadata = - model.getExtension(); - REQUIRE(metadata != nullptr); + const ExtensionModelExtStructuralMetadata* pMetadata = + model.getExtension(); + REQUIRE(pMetadata); - std::optional schema = metadata->schema; - REQUIRE(schema != std::nullopt); + const std::optional& schema = + pMetadata->schema; + REQUIRE(schema); - const std::unordered_map& classes = schema->classes; + const std::unordered_map& + classes = schema->classes; REQUIRE(classes.size() == 1); - const Class& defaultClass = classes.at("default"); - const std::unordered_map& properties = + const ExtensionExtStructuralMetadataClass& defaultClass = + classes.at("default"); + const std::unordered_map< + std::string, + ExtensionExtStructuralMetadataClassProperty>& properties = defaultClass.properties; REQUIRE(properties.size() == 1); - const ClassProperty& propertyClass = properties.at("boolProp"); + const ExtensionExtStructuralMetadataClassProperty& propertyClass = + properties.at("boolProp"); REQUIRE(propertyClass.type == "BOOLEAN"); - const FeatureTable& featureTable = metadata->featureTables["default"]; - checkScalarProperty( + REQUIRE(pMetadata->propertyTables.size() == 1); + const ExtensionExtStructuralMetadataPropertyTable& propertyTable = + pMetadata->propertyTables[0]; + checkNonArrayProperty( model, - featureTable, + propertyTable, defaultClass, "boolProp", - "BOOLEAN", + ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN, + std::nullopt, expected, expected.size()); } -TEST_CASE("Upgrade fixed json number array") { +TEST_CASE("Upgrade fixed-length JSON arrays") { SECTION("int8_t") { // clang-format off std::vector> expected { @@ -1178,8 +1326,12 @@ TEST_CASE("Upgrade fixed json number array") { {11, 22, 3, 5}}; // clang-format on - std::string expectedComponentType = "INT8"; - createTestForArrayJson(expected, expectedComponentType, 4, expected.size()); + createTestForArrayJson( + expected, + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, + ExtensionExtStructuralMetadataClassProperty::ComponentType::INT8, + 4, + expected.size()); } SECTION("uint8_t") { @@ -1192,13 +1344,17 @@ TEST_CASE("Upgrade fixed json number array") { {119, 112, 156, 5, 35}}; // clang-format on - std::string expectedComponentType = "UINT8"; - createTestForArrayJson(expected, expectedComponentType, 5, expected.size()); + createTestForArrayJson( + expected, + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT8, + 5, + expected.size()); } SECTION("int16_t") { // clang-format off - std::vector> expected { + std::vector> expected { {0, 1, 4, 4445}, {12, 50, -12, -1}, {123, 10, 3333, 3}, @@ -1206,13 +1362,17 @@ TEST_CASE("Upgrade fixed json number array") { {11, 22, 3, 50}}; // clang-format on - std::string expectedComponentType = "INT16"; - createTestForArrayJson(expected, expectedComponentType, 4, expected.size()); + createTestForArrayJson( + expected, + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, + ExtensionExtStructuralMetadataClassProperty::ComponentType::INT16, + 4, + expected.size()); } SECTION("uint16_t") { // clang-format off - std::vector> expected { + std::vector> expected { {0, 1, 4, 65000}, {12, 50, 12, 1}, {123, 10, 33330, 3}, @@ -1220,13 +1380,17 @@ TEST_CASE("Upgrade fixed json number array") { {11, 22, 3, 50000}}; // clang-format on - std::string expectedComponentType = "UINT16"; - createTestForArrayJson(expected, expectedComponentType, 4, expected.size()); + createTestForArrayJson( + expected, + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT16, + 4, + expected.size()); } SECTION("int32_t") { // clang-format off - std::vector> expected { + std::vector> expected { {0, 1, 4, 1}, {1244, -500000, 1222, 544662}, {123, -10, 122, 334}, @@ -1234,13 +1398,17 @@ TEST_CASE("Upgrade fixed json number array") { {11, 22, 3, 2147483647}}; // clang-format on - std::string expectedComponentType = "INT32"; - createTestForArrayJson(expected, expectedComponentType, 4, expected.size()); + createTestForArrayJson( + expected, + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, + ExtensionExtStructuralMetadataClassProperty::ComponentType::INT32, + 4, + expected.size()); } SECTION("uint32_t") { // clang-format off - std::vector> expected { + std::vector> expected { {0, 1, 4, 1}, {1244, 12200000, 1222, 544662}, {123, 10, 122, 334}, @@ -1248,15 +1416,19 @@ TEST_CASE("Upgrade fixed json number array") { {11, 22, 3, (uint32_t)4294967295}}; // clang-format on - std::string expectedComponentType = "UINT32"; - createTestForArrayJson(expected, expectedComponentType, 4, expected.size()); + createTestForArrayJson( + expected, + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT32, + 4, + expected.size()); } SECTION("int64_t") { - // though the max positive number is only uint32_t. However, due to negative - // number, it is upgraded to int64_t + // The max positive number only requires uint32_t, but due to + // the negative number, it is upgraded to int64_t. // clang-format off - std::vector> expected { + std::vector> expected { {0, 1, 4, 1}, {1244, -922, 1222, 54}, {123, 10, 122, 334}, @@ -1264,13 +1436,17 @@ TEST_CASE("Upgrade fixed json number array") { {11, 22, 3, 3147483647}}; // clang-format on - std::string expectedComponentType = "INT64"; - createTestForArrayJson(expected, expectedComponentType, 4, expected.size()); + createTestForArrayJson( + expected, + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, + ExtensionExtStructuralMetadataClassProperty::ComponentType::INT64, + 4, + expected.size()); } SECTION("uint64_t") { // clang-format off - std::vector> expected { + std::vector> expected { {0, 1, 4, 1}, {1244, 13223302036854775807u, 1222, 544662}, {123, 10, 122, 334}, @@ -1278,13 +1454,35 @@ TEST_CASE("Upgrade fixed json number array") { {11, 22, 3, 13223302036854775807u}}; // clang-format on - std::string expectedComponentType = "UINT64"; - createTestForArrayJson(expected, expectedComponentType, 4, expected.size()); + createTestForArrayJson( + expected, + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT64, + 4, + expected.size()); + } + + SECTION("float") { + // clang-format off + std::vector> expected { + {0.122f, 1.1233f, 4.113f, 1.11f}, + {1.244f, 122.3f, 1.222f, 544.66f}, + {12.003f, 1.21f, 2.123f, 33.12f}, + {1.333f, 4.232f, 1.422f, 9.4f}, + {1.1221f, 2.2f, 3.0f, 122.31f}}; + // clang-format on + + createTestForArrayJson( + expected, + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, + ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT32, + 4, + expected.size()); } SECTION("double") { // clang-format off - std::vector> expected { + std::vector> expected { {0.122, 1.1233, 4.113, 1.11}, {1.244, 122.3, 1.222, 544.66}, {12.003, 1.21, 2.123, 33.12}, @@ -1292,13 +1490,17 @@ TEST_CASE("Upgrade fixed json number array") { {1.1221, 2.2, 3.0, 122.31}}; // clang-format on - std::string expectedComponentType = "FLOAT64"; - createTestForArrayJson(expected, expectedComponentType, 4, expected.size()); + createTestForArrayJson( + expected, + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, + ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT64, + 4, + expected.size()); } SECTION("string") { // clang-format off - std::vector> expected{ + std::vector> expected{ {"Test0", "Test1", "Test2", "Test4"}, {"Test5", "Test6", "Test7", "Test8"}, {"Test9", "Test10", "Test11", "Test12"}, @@ -1308,14 +1510,15 @@ TEST_CASE("Upgrade fixed json number array") { createTestForArrayJson( expected, - "STRING", + ExtensionExtStructuralMetadataClassProperty::Type::STRING, + std::nullopt, 4, expected.size()); } SECTION("Boolean") { // clang-format off - std::vector> expected{ + std::vector> expected{ {true, true, false, true, false, true}, {true, false, true, false, true, true}, {false, true, true, false, false, true}, @@ -1323,11 +1526,16 @@ TEST_CASE("Upgrade fixed json number array") { }; // clang-format on - createTestForArrayJson(expected, "BOOLEAN", 6, expected.size()); + createTestForArrayJson( + expected, + ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN, + std::nullopt, + 6, + expected.size()); } } -TEST_CASE("Upgrade dynamic json number array") { +TEST_CASE("Upgrade variable-length JSON arrays") { SECTION("int8_t") { // clang-format off std::vector> expected { @@ -1338,8 +1546,12 @@ TEST_CASE("Upgrade dynamic json number array") { {11, 22, 3, 5, 33, 12, -122}}; // clang-format on - std::string expectedComponentType = "INT8"; - createTestForArrayJson(expected, expectedComponentType, 0, expected.size()); + createTestForArrayJson( + expected, + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, + ExtensionExtStructuralMetadataClassProperty::ComponentType::INT8, + 0, + expected.size()); } SECTION("uint8_t") { @@ -1352,8 +1564,12 @@ TEST_CASE("Upgrade dynamic json number array") { {119, 112, 156, 5, 35, 244, 122}}; // clang-format on - std::string expectedComponentType = "UINT8"; - createTestForArrayJson(expected, expectedComponentType, 0, expected.size()); + createTestForArrayJson( + expected, + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT8, + 0, + expected.size()); } SECTION("int16_t") { @@ -1366,8 +1582,12 @@ TEST_CASE("Upgrade dynamic json number array") { {11, 22, 3, 50, 455, 122, 3333, 5555, 12233}}; // clang-format on - std::string expectedComponentType = "INT16"; - createTestForArrayJson(expected, expectedComponentType, 0, expected.size()); + createTestForArrayJson( + expected, + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, + ExtensionExtStructuralMetadataClassProperty::ComponentType::INT16, + 0, + expected.size()); } SECTION("uint16_t") { @@ -1380,8 +1600,12 @@ TEST_CASE("Upgrade dynamic json number array") { {11, 22, 3, 50000, 333}}; // clang-format on - std::string expectedComponentType = "UINT16"; - createTestForArrayJson(expected, expectedComponentType, 0, expected.size()); + createTestForArrayJson( + expected, + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT16, + 0, + expected.size()); } SECTION("int32_t") { @@ -1394,8 +1618,12 @@ TEST_CASE("Upgrade dynamic json number array") { {11, 22, 3, 2147483647, 12233}}; // clang-format on - std::string expectedComponentType = "INT32"; - createTestForArrayJson(expected, expectedComponentType, 0, expected.size()); + createTestForArrayJson( + expected, + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, + ExtensionExtStructuralMetadataClassProperty::ComponentType::INT32, + 0, + expected.size()); } SECTION("uint32_t") { @@ -1408,8 +1636,12 @@ TEST_CASE("Upgrade dynamic json number array") { {11, 22, 3, (uint32_t)4294967295}}; // clang-format on - std::string expectedComponentType = "UINT32"; - createTestForArrayJson(expected, expectedComponentType, 0, expected.size()); + createTestForArrayJson( + expected, + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT32, + 0, + expected.size()); } SECTION("int64_t") { @@ -1422,8 +1654,12 @@ TEST_CASE("Upgrade dynamic json number array") { {11, 22, 3, 9223372036854775807, 12333}}; // clang-format on - std::string expectedComponentType = "INT64"; - createTestForArrayJson(expected, expectedComponentType, 0, expected.size()); + createTestForArrayJson( + expected, + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, + ExtensionExtStructuralMetadataClassProperty::ComponentType::INT64, + 0, + expected.size()); } SECTION("uint64_t") { @@ -1436,8 +1672,30 @@ TEST_CASE("Upgrade dynamic json number array") { {11, 22, 3, 13223302036854775807u, 32323}}; // clang-format on - std::string expectedComponentType = "UINT64"; - createTestForArrayJson(expected, expectedComponentType, 0, expected.size()); + createTestForArrayJson( + expected, + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT64, + 0, + expected.size()); + } + + SECTION("float") { + // clang-format off + std::vector> expected { + {0.122f, 1.1233f}, + {1.244f, 122.3f, 1.222f, 544.66f, 323.122f}, + {12.003f, 1.21f, 2.123f, 33.12f, 122.2f}, + {1.333f}, + {1.1221f, 2.2f}}; + // clang-format on + + createTestForArrayJson( + expected, + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, + ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT32, + 0, + expected.size()); } SECTION("double") { @@ -1450,8 +1708,12 @@ TEST_CASE("Upgrade dynamic json number array") { {1.1221, 2.2}}; // clang-format on - std::string expectedComponentType = "FLOAT64"; - createTestForArrayJson(expected, expectedComponentType, 0, expected.size()); + createTestForArrayJson( + expected, + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, + ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT64, + 0, + expected.size()); } SECTION("string") { @@ -1466,7 +1728,8 @@ TEST_CASE("Upgrade dynamic json number array") { createTestForArrayJson( expected, - "STRING", + ExtensionExtStructuralMetadataClassProperty::Type::STRING, + std::nullopt, 0, expected.size()); } @@ -1482,54 +1745,76 @@ TEST_CASE("Upgrade dynamic json number array") { }; // clang-format on - createTestForArrayJson(expected, "BOOLEAN", 0, expected.size()); + createTestForArrayJson( + expected, + ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN, + std::nullopt, + 0, + expected.size()); } } -TEST_CASE("Upgrade scalar json") { +TEST_CASE("Upgrade JSON values") { SECTION("Uint32") { + // Even though the values are typed uint32, they are small enough to be + // stored as int8s. Signed types are preferred over unsigned. std::vector expected{32, 45, 21, 65, 78}; - createTestForScalarJson( + createTestForNonArrayJson( expected, - "INT8", + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, + ExtensionExtStructuralMetadataClassProperty::ComponentType::INT8, expected.size()); } SECTION("Boolean") { std::vector expected{true, false, true, false, true, true, false}; - createTestForScalarJson(expected, "BOOLEAN", expected.size()); + createTestForNonArrayJson( + expected, + ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN, + std::nullopt, + expected.size()); } SECTION("String") { std::vector expected{"Test 0", "Test 1", "Test 2", "Test 3"}; - createTestForScalarJson( + createTestForNonArrayJson( expected, - "STRING", + ExtensionExtStructuralMetadataClassProperty::Type::STRING, + std::nullopt, expected.size()); } } -TEST_CASE("Cannot write pass batch length table") { - SECTION("Numeric") { +TEST_CASE("Cannot write past batch table length") { + SECTION("Uint32") { std::vector expected{32, 45, 21, 65, 78, 20, 33, 12}; - createTestForScalarJson(expected, "INT8", 4); + createTestForNonArrayJson( + expected, + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, + ExtensionExtStructuralMetadataClassProperty::ComponentType::INT8, + 4); } SECTION("Boolean") { std::vector expected{true, false, true, false, true, true, false}; - createTestForScalarJson(expected, "BOOLEAN", 4); + createTestForNonArrayJson( + expected, + ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN, + std::nullopt, + 4); } SECTION("String") { std::vector expected{"Test 0", "Test 1", "Test 2", "Test 3", "Test 4"}; - createTestForScalarJson( + createTestForNonArrayJson( expected, - "STRING", + ExtensionExtStructuralMetadataClassProperty::Type::STRING, + std::nullopt, 3); } - SECTION("Fixed number array") { + SECTION("Fixed-length scalar array") { // clang-format off std::vector> expected { {0, 1, 4, 1}, @@ -1539,11 +1824,15 @@ TEST_CASE("Cannot write pass batch length table") { {11, 22, 3, 13223302036854775807u}}; // clang-format on - std::string expectedComponentType = "UINT64"; - createTestForArrayJson(expected, expectedComponentType, 4, 2); + createTestForArrayJson( + expected, + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT64, + 4, + 2); } - SECTION("Fixed boolean array") { + SECTION("Fixed-length boolean array") { // clang-format off std::vector> expected{ {true, true, false}, @@ -1553,10 +1842,15 @@ TEST_CASE("Cannot write pass batch length table") { }; // clang-format on - createTestForArrayJson(expected, "BOOLEAN", 3, 2); + createTestForArrayJson( + expected, + ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN, + std::nullopt, + 3, + 2); } - SECTION("Fixed string array") { + SECTION("Fixed-length string array") { // clang-format off std::vector> expected{ {"Test0", "Test1", "Test2", "Test4"}, @@ -1568,59 +1862,70 @@ TEST_CASE("Cannot write pass batch length table") { createTestForArrayJson( expected, - "STRING", + ExtensionExtStructuralMetadataClassProperty::Type::STRING, + std::nullopt, 4, 2); } - SECTION("Dynamic number array") { + SECTION("Variable-length number array") { // clang-format off - std::vector> expected { - {0, 1}, - {1244, -500000, 1222, 544662}, - {123, -10}, - {13}, - {11, 22, 3, 2147483647, 12233}}; + std::vector> expected { + {0, 1}, + {1244, -500000, 1222, 544662}, + {123, -10}, + {13}, + {11, 22, 3, 2147483647, 12233}}; // clang-format on - std::string expectedComponentType = "INT32"; - createTestForArrayJson(expected, expectedComponentType, 0, 3); + createTestForArrayJson( + expected, + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, + ExtensionExtStructuralMetadataClassProperty::ComponentType::INT32, + 0, + 3); } - SECTION("Dynamic boolean array") { + SECTION("Variable-length boolean array") { // clang-format off - std::vector> expected{ - {true, true, false, true, false, false, true}, - {true, false}, - {false, true, true, false}, - {false, true, true}, - {true, true, false, false} - }; + std::vector> expected{ + {true, true, false, true, false, false, true}, + {true, false}, + {false, true, true, false}, + {false, true, true}, + {true, true, false, false} + }; // clang-format on - createTestForArrayJson(expected, "BOOLEAN", 0, 2); + createTestForArrayJson( + expected, + ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN, + std::nullopt, + 0, + 2); } - SECTION("Dynamic string array") { + SECTION("Variable-length string array") { // clang-format off - std::vector> expected{ - {"This is Test", "Another Test"}, - {"Good morning", "How you doing?", "The book in the freezer", "Batman beats superman", ""}, - {"Test9", "Test10", "", "Test12", ""}, - {"Test13", ""}, - }; + std::vector> expected{ + {"This is Test", "Another Test"}, + {"Good morning", "How you doing?", "The book in the freezer", "Batman beats superman", ""}, + {"Test9", "Test10", "", "Test12", ""}, + {"Test13", ""}, + }; // clang-format on createTestForArrayJson( expected, - "STRING", + ExtensionExtStructuralMetadataClassProperty::Type::STRING, + std::nullopt, 0, 2); } } -TEST_CASE("Converts Feature Classes 3DTILES_batch_table_hierarchy example to " - "EXT_feature_metadata") { +TEST_CASE("Converts \"Feature Classes\" 3DTILES_batch_table_hierarchy example " + "to EXT_structural_metadata") { Model gltf; std::string featureTableJson = R"( @@ -1680,8 +1985,8 @@ TEST_CASE("Converts Feature Classes 3DTILES_batch_table_hierarchy example to " gsl::span(), gltf); - ExtensionModelExtFeatureMetadata* pExtension = - gltf.getExtension(); + ExtensionModelExtStructuralMetadata* pExtension = + gltf.getExtension(); REQUIRE(pExtension); // Check the schema @@ -1691,44 +1996,50 @@ TEST_CASE("Converts Feature Classes 3DTILES_batch_table_hierarchy example to " auto firstClassIt = pExtension->schema->classes.begin(); CHECK(firstClassIt->first == "default"); - CesiumGltf::Class& defaultClass = firstClassIt->second; + ExtensionExtStructuralMetadataClass& defaultClass = firstClassIt->second; REQUIRE(defaultClass.properties.size() == 6); - // Check the feature table - auto firstFeatureTableIt = pExtension->featureTables.begin(); - REQUIRE(firstFeatureTableIt != pExtension->featureTables.end()); - - FeatureTable& featureTable = firstFeatureTableIt->second; - CHECK(featureTable.classProperty == "default"); - REQUIRE(featureTable.properties.size() == 6); + // Check the property table + REQUIRE(pExtension->propertyTables.size() == 1); + ExtensionExtStructuralMetadataPropertyTable& propertyTable = + pExtension->propertyTables[0]; + CHECK(propertyTable.classProperty == "default"); + REQUIRE(propertyTable.properties.size() == 6); - // Even though some of these properties are numeric, they become STRING - // because not every feature has every property, and only STRING can - // represent null. + // Even though some of these properties are scalars, they become strings + // because not every feature has every property, and only strings can + // represent "null". struct Expected { std::string name; std::string type; + std::optional componentType; std::vector values; }; std::vector expectedProperties{ {"lampStrength", - "STRING", + ExtensionExtStructuralMetadataClassProperty::Type::STRING, + std::nullopt, {"10", "5", "7", "null", "null", "null", "null", "null"}}, {"lampColor", - "STRING", + ExtensionExtStructuralMetadataClassProperty::Type::STRING, + std::nullopt, {"yellow", "white", "white", "null", "null", "null", "null", "null"}}, {"carType", - "STRING", + ExtensionExtStructuralMetadataClassProperty::Type::STRING, + std::nullopt, {"null", "null", "null", "truck", "bus", "sedan", "null", "null"}}, {"carColor", - "STRING", + ExtensionExtStructuralMetadataClassProperty::Type::STRING, + std::nullopt, {"null", "null", "null", "green", "blue", "red", "null", "null"}}, {"treeHeight", - "STRING", + ExtensionExtStructuralMetadataClassProperty::Type::STRING, + std::nullopt, {"null", "null", "null", "null", "null", "null", "10", "15"}}, {"treeAge", - "STRING", + ExtensionExtStructuralMetadataClassProperty::Type::STRING, + std::nullopt, {"null", "null", "null", "null", "null", "null", "5", "8"}}}; for (const auto& expected : expectedProperties) { @@ -1736,19 +2047,21 @@ TEST_CASE("Converts Feature Classes 3DTILES_batch_table_hierarchy example to " REQUIRE(it != defaultClass.properties.end()); CHECK(it->second.type == expected.type); - checkScalarProperty( + checkNonArrayProperty( gltf, - featureTable, + propertyTable, defaultClass, expected.name, expected.type, + expected.componentType, expected.values, expected.values.size()); } } -TEST_CASE("Converts Feature Hierarchy 3DTILES_batch_table_hierarchy example to " - "EXT_feature_metadata") { +TEST_CASE( + "Converts \"Feature Hierarchy\" 3DTILES_batch_table_hierarchy example to " + "EXT_structural_metadata") { Model gltf; std::string featureTableJson = R"( @@ -1768,17 +2081,17 @@ TEST_CASE("Converts Feature Hierarchy 3DTILES_batch_table_hierarchy example to " "name" : "Wall", "length" : 6, "instances" : { - "wall_color" : ["blue", "pink", "green", "lime", "black", "brown"], - "wall_windows" : [2, 4, 4, 2, 0, 3] + "wall_color" : ["blue", "pink", "green", "lime", "black", + "brown"], "wall_windows" : [2, 4, 4, 2, 0, 3] } }, { "name" : "Building", "length" : 3, "instances" : { - "building_name" : ["building_0", "building_1", "building_2"], - "building_id" : [0, 1, 2], - "building_address" : ["10 Main St", "12 Main St", "14 Main St"] + "building_name" : ["building_0", "building_1", + "building_2"], "building_id" : [0, 1, 2], "building_address" + : ["10 Main St", "12 Main St", "14 Main St"] } }, { @@ -1810,8 +2123,8 @@ TEST_CASE("Converts Feature Hierarchy 3DTILES_batch_table_hierarchy example to " gsl::span(), gltf); - ExtensionModelExtFeatureMetadata* pExtension = - gltf.getExtension(); + ExtensionModelExtStructuralMetadata* pExtension = + gltf.getExtension(); REQUIRE(pExtension); // Check the schema @@ -1821,29 +2134,28 @@ TEST_CASE("Converts Feature Hierarchy 3DTILES_batch_table_hierarchy example to " auto firstClassIt = pExtension->schema->classes.begin(); CHECK(firstClassIt->first == "default"); - CesiumGltf::Class& defaultClass = firstClassIt->second; + ExtensionExtStructuralMetadataClass& defaultClass = firstClassIt->second; REQUIRE(defaultClass.properties.size() == 7); - // Check the feature table - auto firstFeatureTableIt = pExtension->featureTables.begin(); - REQUIRE(firstFeatureTableIt != pExtension->featureTables.end()); - - FeatureTable& featureTable = firstFeatureTableIt->second; - CHECK(featureTable.classProperty == "default"); - REQUIRE(featureTable.properties.size() == 7); + // Check the property table + REQUIRE(pExtension->propertyTables.size() == 1); + ExtensionExtStructuralMetadataPropertyTable& propertyTable = + pExtension->propertyTables[0]; + CHECK(propertyTable.classProperty == "default"); + REQUIRE(propertyTable.properties.size() == 7); struct ExpectedString { std::string name; - std::string type; std::vector values; + std::string type() const { + return ExtensionExtStructuralMetadataClassProperty::Type::STRING; + } + std::optional componentType() const { return std::nullopt; } }; std::vector expectedStringProperties{ - {"wall_color", - "STRING", - {"blue", "pink", "green", "lime", "black", "brown"}}, + {"wall_color", {"blue", "pink", "green", "lime", "black", "brown"}}, {"building_name", - "STRING", {"building_0", "building_0", "building_1", @@ -1851,7 +2163,6 @@ TEST_CASE("Converts Feature Hierarchy 3DTILES_batch_table_hierarchy example to " "building_2", "building_2"}}, {"building_address", - "STRING", {"10 Main St", "10 Main St", "12 Main St", @@ -1859,62 +2170,73 @@ TEST_CASE("Converts Feature Hierarchy 3DTILES_batch_table_hierarchy example to " "14 Main St", "14 Main St"}}, {"block_district", - "STRING", {"central", "central", "central", "central", "central", "central"}}}; for (const auto& expected : expectedStringProperties) { auto it = defaultClass.properties.find(expected.name); REQUIRE(it != defaultClass.properties.end()); - CHECK(it->second.type == expected.type); + CHECK(it->second.type == expected.type()); + CHECK(it->second.componentType == expected.componentType()); - checkScalarProperty( + checkNonArrayProperty( gltf, - featureTable, + propertyTable, defaultClass, expected.name, - expected.type, + expected.type(), + expected.componentType(), expected.values, expected.values.size()); } struct ExpectedInt8Properties { std::string name; - std::string type; std::vector values; + std::string type() const { + return ExtensionExtStructuralMetadataClassProperty::Type::SCALAR; + } + std::optional componentType() const { + return ExtensionExtStructuralMetadataClassProperty::ComponentType::INT8; + } }; std::vector expectedInt8Properties{ - {"wall_windows", "INT8", {2, 4, 4, 2, 0, 3}}, - {"building_id", "INT8", {0, 0, 1, 1, 2, 2}}, + {"wall_windows", {2, 4, 4, 2, 0, 3}}, + {"building_id", {0, 0, 1, 1, 2, 2}}, }; for (const auto& expected : expectedInt8Properties) { auto it = defaultClass.properties.find(expected.name); REQUIRE(it != defaultClass.properties.end()); - CHECK(it->second.type == expected.type); + CHECK(it->second.type == expected.type()); + CHECK(it->second.componentType == expected.componentType()); - checkScalarProperty( + checkNonArrayProperty( gltf, - featureTable, + propertyTable, defaultClass, expected.name, - expected.type, + expected.type(), + expected.componentType(), expected.values, expected.values.size()); } struct ExpectedDoubleArrayProperties { std::string name; - std::string type; - std::string componentType; - int64_t componentCount; + int64_t count; std::vector> values; + std::string type() const { + return ExtensionExtStructuralMetadataClassProperty::Type::SCALAR; + } + std::optional componentType() const { + return ExtensionExtStructuralMetadataClassProperty::ComponentType:: + FLOAT64; + } }; std::vector expectedDoubleArrayProperties{ {"block_lat_long", - "ARRAY", - "FLOAT64", 2, {{0.12, 0.543}, {0.12, 0.543}, @@ -1926,23 +2248,26 @@ TEST_CASE("Converts Feature Hierarchy 3DTILES_batch_table_hierarchy example to " for (const auto& expected : expectedDoubleArrayProperties) { auto it = defaultClass.properties.find(expected.name); REQUIRE(it != defaultClass.properties.end()); - CHECK(it->second.type == expected.type); - CHECK(it->second.componentType == expected.componentType); + CHECK(it->second.type == expected.type()); + CHECK(it->second.componentType == expected.componentType()); + CHECK(it->second.array); + CHECK(it->second.count == expected.count); checkArrayProperty( gltf, - featureTable, + propertyTable, defaultClass, expected.name, - expected.componentCount, - expected.componentType, + expected.count, + expected.type(), + expected.componentType(), expected.values, expected.values.size()); } } -TEST_CASE( - "3DTILES_batch_table_hierarchy with parentCounts is ok if all are 1") { +TEST_CASE("3DTILES_batch_table_hierarchy with parentCounts is okay if all " + "values are 1") { Model gltf; std::string featureTableJson = R"( @@ -2010,8 +2335,8 @@ TEST_CASE( REQUIRE(logMessages.size() == 0); // There should actually be metadata properties as normal. - ExtensionModelExtFeatureMetadata* pExtension = - gltf.getExtension(); + const ExtensionModelExtStructuralMetadata* pExtension = + gltf.getExtension(); REQUIRE(pExtension); // Check the schema @@ -2021,20 +2346,20 @@ TEST_CASE( auto firstClassIt = pExtension->schema->classes.begin(); CHECK(firstClassIt->first == "default"); - CesiumGltf::Class& defaultClass = firstClassIt->second; + const ExtensionExtStructuralMetadataClass& defaultClass = + firstClassIt->second; REQUIRE(defaultClass.properties.size() == 3); - // Check the feature table - auto firstFeatureTableIt = pExtension->featureTables.begin(); - REQUIRE(firstFeatureTableIt != pExtension->featureTables.end()); - - FeatureTable& featureTable = firstFeatureTableIt->second; - CHECK(featureTable.classProperty == "default"); - REQUIRE(featureTable.properties.size() == 3); + // Check the property table + REQUIRE(pExtension->propertyTables.size() == 1); + const ExtensionExtStructuralMetadataPropertyTable& propertyTable = + pExtension->propertyTables[0]; + CHECK(propertyTable.classProperty == "default"); + REQUIRE(propertyTable.properties.size() == 3); } -TEST_CASE( - "3DTILES_batch_table_hierarchy with parentCounts != 1 is not supported") { +TEST_CASE("3DTILES_batch_table_hierarchy with parentCounts values != 1 is " + "unsupported") { Model gltf; std::string featureTableJson = R"( @@ -2101,8 +2426,8 @@ TEST_CASE( REQUIRE(logMessages.size() == 1); CHECK(logMessages[0].find("parentCounts") != std::string::npos); - ExtensionModelExtFeatureMetadata* pExtension = - gltf.getExtension(); + const ExtensionModelExtStructuralMetadata* pExtension = + gltf.getExtension(); REQUIRE(pExtension); // Check the schema @@ -2112,14 +2437,15 @@ TEST_CASE( auto firstClassIt = pExtension->schema->classes.begin(); CHECK(firstClassIt->first == "default"); - CesiumGltf::Class& defaultClass = firstClassIt->second; + const ExtensionExtStructuralMetadataClass& defaultClass = + firstClassIt->second; REQUIRE(defaultClass.properties.size() == 0); - // Check the feature table - auto firstFeatureTableIt = pExtension->featureTables.begin(); - REQUIRE(firstFeatureTableIt != pExtension->featureTables.end()); + // Check the property table + REQUIRE(pExtension->propertyTables.size() == 1); + const ExtensionExtStructuralMetadataPropertyTable& propertyTable = + pExtension->propertyTables[0]; - FeatureTable& featureTable = firstFeatureTableIt->second; - CHECK(featureTable.classProperty == "default"); - REQUIRE(featureTable.properties.size() == 0); + CHECK(propertyTable.classProperty == "default"); + REQUIRE(propertyTable.properties.size() == 0); } From dec58d660e445563bf1b050a5b62794cdaa4cbcb Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Thu, 25 May 2023 11:55:24 -0400 Subject: [PATCH 046/421] Rewrite names / comments for EXT_structural_metadata --- Cesium3DTilesSelection/src/B3dmToGltfConverter.cpp | 4 ++-- .../src/BatchTableToGltfStructuralMetadata.cpp | 11 ++++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Cesium3DTilesSelection/src/B3dmToGltfConverter.cpp b/Cesium3DTilesSelection/src/B3dmToGltfConverter.cpp index 8ee72dc22..c4c9bf5c7 100644 --- a/Cesium3DTilesSelection/src/B3dmToGltfConverter.cpp +++ b/Cesium3DTilesSelection/src/B3dmToGltfConverter.cpp @@ -176,7 +176,7 @@ rapidjson::Document parseFeatureTableJsonData( return document; } -void convertB3dmMetadataToGltfFeatureMetadata( +void convertB3dmMetadataToGltfStructuralMetadata( const gsl::span& b3dmBinary, const B3dmHeader& header, uint32_t headerLength, @@ -246,7 +246,7 @@ GltfConverterResult B3dmToGltfConverter::convert( return result; } - convertB3dmMetadataToGltfFeatureMetadata( + convertB3dmMetadataToGltfStructuralMetadata( b3dmBinary, header, headerLength, diff --git a/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp b/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp index bd49e97d5..23a5fc98d 100644 --- a/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp +++ b/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp @@ -1448,10 +1448,11 @@ void updateExtensionWithBatchTableHierarchy( ExtensionExtStructuralMetadataPropertyTable& propertyTable, ErrorList& result, const rapidjson::Value& batchTableHierarchy) { - // EXT_feature_metadata can't support hierarchy, so we need to flatten it. + // EXT_structural_metadata can't support hierarchy, so we need to flatten it. // It also can't support multiple classes with a single set of feature IDs. - // So essentially every property of every class gets added to the one class - // definition. + // (Feature IDs can only specify one property table, which only supports one class.) + // So essentially every property of every class gets added to + // the one class definition. auto classesIt = batchTableHierarchy.FindMember("classes"); if (classesIt == batchTableHierarchy.MemberEnd()) { result.emplaceWarning( @@ -1649,7 +1650,7 @@ ErrorList BatchTableToGltfStructuralMetadata::convertFromB3dm( ErrorList result; - // Parse the b3dm batch table and convert it to the EXT_feature_metadata + // Parse the b3dm batch table and convert it to the EXT_structural_metadata // extension. // If the feature table is missing the BATCH_LENGTH semantic, ignore the batch @@ -1716,7 +1717,7 @@ ErrorList BatchTableToGltfStructuralMetadata::convertFromPnts( ErrorList result; - // Parse the pnts batch table and convert it to the EXT_feature_metadata + // Parse the pnts batch table and convert it to the EXT_structural_metadata // extension. const auto pointsLengthIt = featureTableJson.FindMember("POINTS_LENGTH"); From 0284c86f30023cd394bb67482795c6fe81c262c1 Mon Sep 17 00:00:00 2001 From: Joseph Kaile Date: Thu, 25 May 2023 13:07:26 -0400 Subject: [PATCH 047/421] use decorator pattern for gunip asset accessor --- .../include/CesiumAsync/GunzipAssetAccessor.h | 44 ++++++++ CesiumAsync/src/CachingAssetAccessor.cpp | 65 ------------ CesiumAsync/src/GunzipAssetAccessor.cpp | 100 ++++++++++++++++++ 3 files changed, 144 insertions(+), 65 deletions(-) create mode 100644 CesiumAsync/include/CesiumAsync/GunzipAssetAccessor.h create mode 100644 CesiumAsync/src/GunzipAssetAccessor.cpp diff --git a/CesiumAsync/include/CesiumAsync/GunzipAssetAccessor.h b/CesiumAsync/include/CesiumAsync/GunzipAssetAccessor.h new file mode 100644 index 000000000..945ac4635 --- /dev/null +++ b/CesiumAsync/include/CesiumAsync/GunzipAssetAccessor.h @@ -0,0 +1,44 @@ +#pragma once + +#include "IAssetAccessor.h" +#include "IAssetRequest.h" + +namespace CesiumAsync { +class AsyncSystem; + +/** + * @brief A decorator for an {@link IAssetAccessor} that automatically unzips + * gzipped asset responses from the underlying Asset Accessor. + */ +class GunzipAssetAccessor : public IAssetAccessor { +public: + /** + * @brief Constructs a new instance. + * + * @param pAssetAccessor The underlying {@link IAssetAccessor} used to + * retrieve assets that may or may not be zipped. + */ + GunzipAssetAccessor(const std::shared_ptr& pAssetAccessor); + + virtual ~GunzipAssetAccessor() noexcept override; + + /** @copydoc IAssetAccessor::get */ + virtual Future> + get(const AsyncSystem& asyncSystem, + const std::string& url, + const std::vector& headers) override; + + virtual Future> request( + const AsyncSystem& asyncSystem, + const std::string& verb, + const std::string& url, + const std::vector& headers, + const gsl::span& contentPayload) override; + + /** @copydoc IAssetAccessor::tick */ + virtual void tick() noexcept override; + +private: + std::shared_ptr _pAssetAccessor; +}; +} // namespace CesiumAsync diff --git a/CesiumAsync/src/CachingAssetAccessor.cpp b/CesiumAsync/src/CachingAssetAccessor.cpp index e4dbffa0c..c3bd25135 100644 --- a/CesiumAsync/src/CachingAssetAccessor.cpp +++ b/CesiumAsync/src/CachingAssetAccessor.cpp @@ -3,7 +3,6 @@ #include "CesiumAsync/AsyncSystem.h" #include "CesiumAsync/CacheItem.h" #include "CesiumAsync/IAssetResponse.h" -#include "CesiumUtility/Gunzip.h" #include "InternalTimegm.h" #include "ResponseCacheControl.h" @@ -72,63 +71,6 @@ class CacheAssetRequest : public IAssetRequest { CacheAssetResponse _response; }; -class GunzippedAssetResponse : public IAssetResponse { -public: - GunzippedAssetResponse(const IAssetResponse* pOther) noexcept - : _pAssetResponse{pOther} { - _dataValid = CesiumUtility::gunzip( - this->_pAssetResponse->data(), - this->_gunzippedData); - } - - virtual uint16_t statusCode() const noexcept override { - return this->_pAssetResponse->statusCode(); - } - - virtual std::string contentType() const override { - return this->_pAssetResponse->contentType(); - } - - virtual const HttpHeaders& headers() const noexcept override { - return this->_pAssetResponse->headers(); - } - - virtual gsl::span data() const noexcept override { - return _dataValid ? gsl::span(_gunzippedData.data(), _gunzippedData.size()) - : _pAssetResponse->data(); - } - -private: - const IAssetResponse* _pAssetResponse; - std::vector _gunzippedData; - bool _dataValid; -}; - -class GunzippedAssetRequest : public IAssetRequest { -public: - GunzippedAssetRequest(std::shared_ptr& pOther) - : _pAssetRequest(pOther), _AssetResponse(pOther->response()){}; - virtual const std::string& method() const noexcept override { - return this->_pAssetRequest->method(); - } - - virtual const std::string& url() const noexcept override { - return this->_pAssetRequest->url(); - } - - virtual const HttpHeaders& headers() const noexcept override { - return this->_pAssetRequest->headers(); - } - - virtual const IAssetResponse* response() const noexcept override { - return &this->_AssetResponse; - } - -private: - std::shared_ptr _pAssetRequest; - GunzippedAssetResponse _AssetResponse; -}; - static std::time_t convertHttpDateToTime(const std::string& httpDate); static bool shouldRevalidateCache(const CacheItem& cacheItem); @@ -207,13 +149,6 @@ Future> CachingAssetAccessor::get( return std::move(pCompletedRequest); } - if (CesiumUtility::isGzip(pResponse->data())) { - pCompletedRequest = - std::make_shared( - GunzippedAssetRequest(pCompletedRequest)); - pResponse = pCompletedRequest->response(); - } - const std::optional cacheControl = ResponseCacheControl::parseFromResponseHeaders( pResponse->headers()); diff --git a/CesiumAsync/src/GunzipAssetAccessor.cpp b/CesiumAsync/src/GunzipAssetAccessor.cpp new file mode 100644 index 000000000..6e78debde --- /dev/null +++ b/CesiumAsync/src/GunzipAssetAccessor.cpp @@ -0,0 +1,100 @@ +#include "CesiumAsync/GunzipAssetAccessor.h" + +#include "CesiumAsync/AsyncSystem.h" +#include "CesiumAsync/IAssetResponse.h" +#include "CesiumUtility/Gunzip.h" + +namespace CesiumAsync { +class GunzippedAssetResponse : public IAssetResponse { +public: + GunzippedAssetResponse(const IAssetResponse* pOther) noexcept + : _pAssetResponse{pOther} { + _dataValid = CesiumUtility::gunzip( + this->_pAssetResponse->data(), + this->_gunzippedData); + } + + virtual uint16_t statusCode() const noexcept override { + return this->_pAssetResponse->statusCode(); + } + + virtual std::string contentType() const override { + return this->_pAssetResponse->contentType(); + } + + virtual const HttpHeaders& headers() const noexcept override { + return this->_pAssetResponse->headers(); + } + + virtual gsl::span data() const noexcept override { + return _dataValid ? gsl::span(_gunzippedData.data(), _gunzippedData.size()) + : _pAssetResponse->data(); + } + +private: + const IAssetResponse* _pAssetResponse; + std::vector _gunzippedData; + bool _dataValid; +}; + +class GunzippedAssetRequest : public IAssetRequest { +public: + GunzippedAssetRequest(std::shared_ptr& pOther) + : _pAssetRequest(pOther), _AssetResponse(pOther->response()){}; + virtual const std::string& method() const noexcept override { + return this->_pAssetRequest->method(); + } + + virtual const std::string& url() const noexcept override { + return this->_pAssetRequest->url(); + } + + virtual const HttpHeaders& headers() const noexcept override { + return this->_pAssetRequest->headers(); + } + + virtual const IAssetResponse* response() const noexcept override { + return &this->_AssetResponse; + } + +private: + std::shared_ptr _pAssetRequest; + GunzippedAssetResponse _AssetResponse; +}; + +GunzipAssetAccessor::GunzipAssetAccessor( + const std::shared_ptr& pAssetAccessor) + : _pAssetAccessor(pAssetAccessor) {} + +GunzipAssetAccessor::~GunzipAssetAccessor() noexcept {} + +Future> GunzipAssetAccessor::get( + const AsyncSystem& asyncSystem, + const std::string& url, + const std::vector& headers) { + return this->_pAssetAccessor->get(asyncSystem, url, headers) + .thenImmediately([](std::shared_ptr&& pCompletedRequest) { + const IAssetResponse* pResponse = pCompletedRequest->response(); + if (!pResponse) { + return std::move(pCompletedRequest); + } + if (CesiumUtility::isGzip(pResponse->data())) { + pCompletedRequest = std::make_shared( + GunzippedAssetRequest(pCompletedRequest)); + } + return std::move(pCompletedRequest); + }); +} + +Future> GunzipAssetAccessor::request( + const AsyncSystem& asyncSystem, + const std::string& verb, + const std::string& url, + const std::vector& headers, + const gsl::span& contentPayload) { + return this->_pAssetAccessor + ->request(asyncSystem, verb, url, headers, contentPayload); +} + +void GunzipAssetAccessor::tick() noexcept { _pAssetAccessor->tick(); } +} // namespace CesiumAsync From d7f9c98c330b5c83456c132dec807dfc21a34866 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Thu, 25 May 2023 13:13:35 -0400 Subject: [PATCH 048/421] Initial self-review --- .../BatchTableToGltfStructuralMetadata.cpp | 205 ++++++++---------- .../src/PntsToGltfConverter.cpp | 1 - .../test/TestPntsToGltfConverter.cpp | 2 +- 3 files changed, 89 insertions(+), 119 deletions(-) diff --git a/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp b/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp index 23a5fc98d..815983282 100644 --- a/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp +++ b/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp @@ -95,12 +95,12 @@ struct MaskedArrayType { maxArrayCount(std::numeric_limits::min()){}; MaskedArrayType( - MaskedType elementType, - uint32_t minArrayCount, - uint32_t maxArrayCount) - : elementType(elementType), - minArrayCount(minArrayCount), - maxArrayCount(maxArrayCount) {} + MaskedType inElementType, + uint32_t inMinArrayCount, + uint32_t inMaxArrayCount) + : elementType(inElementType), + minArrayCount(inMinArrayCount), + maxArrayCount(inMaxArrayCount) {} /** * Merges another MaskedArrayType into this one. @@ -120,76 +120,101 @@ struct CompatibleTypes { // been determined to be incompatible yet. Once something is either a // MaskedType or MaskedArrayType, they are considered incompatible with the // other type. - std::variant maskedType; +private: + std::variant type; - CompatibleTypes() : maskedType(){}; +public: + CompatibleTypes() : type(){}; - CompatibleTypes(const MaskedType& maskedType) : maskedType(maskedType){}; + CompatibleTypes(const MaskedType& maskedType) : type(maskedType){}; CompatibleTypes(const MaskedArrayType& maskedArrayType) - : maskedType(maskedArrayType){}; + : type(maskedArrayType){}; /** - * Whether this is only compatible with arrays. + * Whether this is compatible with array types. */ - bool isArray() const { return std::get_if(&maskedType); } + bool supportsArray() const { + return !std::holds_alternative(type); + } /** * Marks as incompatible with every type. Fully-incompatible types will be * treated as strings. */ - void makeIncompatible() { - MaskedType incompatibleMaskedType(false); - maskedType = incompatibleMaskedType; + void makeIncompatible() { type = MaskedType(false); } + + /** + * Marks as incompatible with array types. + */ + void makeIncompatibleWithArray() { + if (std::holds_alternative(type)) { + return; + } + + if (std::holds_alternative(type)) { + makeIncompatible(); + return; + } + + // If std::monostate, retain potential compatibility with non-array types + type = MaskedType(true); } /** * Merges a MaskedType into this CompatibleTypes. */ - void operator&=(const MaskedType& otherMaskedType) { - if (isArray()) { - makeIncompatible(); + void operator&=(const MaskedType& inMaskedType) { + if (std::holds_alternative(type)) { + MaskedType& maskedType = std::get(type); + maskedType &= inMaskedType; return; } - MaskedType* pMaskedType = std::get_if(&maskedType); - if (pMaskedType) { - *pMaskedType &= otherMaskedType; - } else { - maskedType = otherMaskedType; + if (std::holds_alternative(type)) { + makeIncompatible(); + return; } + + type = inMaskedType; } /** * Merges a MaskedArrayType into this CompatibleTypes. */ - void operator&=(const MaskedArrayType& maskedArrayType) { - if (isArray()) { - MaskedArrayType& arrayType = std::get(maskedType); - arrayType &= maskedArrayType; + void operator&=(const MaskedArrayType& inArrayType) { + if (std::holds_alternative(type)) { + MaskedArrayType& arrayType = std::get(type); + arrayType &= inArrayType; return; } - MaskedType* pMaskedType = std::get_if(&maskedType); - if (pMaskedType) { + if (std::holds_alternative(type)) { makeIncompatible(); - } else { - maskedType = maskedArrayType; + return; } + + type = inArrayType; } /** * Merges another CompatibleTypes into this one. */ - void operator&=(const CompatibleTypes& otherTypes) { - if (otherTypes.isArray()) { - const MaskedArrayType& otherMaskedType = - std::get(otherTypes.maskedType); - operator&=(otherMaskedType); - } else { - const MaskedType& otherMaskedType = - std::get(otherTypes.maskedType); - operator&=(otherMaskedType); + void operator&=(const CompatibleTypes& inCompatibleTypes) { + if (std::holds_alternative(inCompatibleTypes.type)) { + // The other CompatibleTypes is compatible with everything, so it does not + // change this one. + return; + } + + if (std::holds_alternative(inCompatibleTypes.type)) { + const MaskedArrayType& arrayType = + std::get(inCompatibleTypes.type); + operator&=(arrayType); + return; } + + const MaskedType& maskedType = std::get(inCompatibleTypes.type); + operator&=(maskedType); } /** @@ -198,18 +223,12 @@ struct CompatibleTypes { * MaskedType. */ MaskedType toMaskedType() const { - if (isArray()) { - return MaskedType(false); + if (std::holds_alternative(type)) { + return std::get(type); } - const MaskedType* pMaskedType = std::get_if(&maskedType); - if (pMaskedType) { - return *pMaskedType; - } - - // If maskedType == std::monostate, then this CompatibleTypes is considered - // compatible with everything. - return MaskedType(true); + bool isArray = std::holds_alternative(type); + return MaskedType(!isArray); } /** @@ -218,15 +237,12 @@ struct CompatibleTypes { * incompatible MaskedArrayType. */ MaskedArrayType toMaskedArrayType() const { - if (isArray()) { - return std::get(maskedType); + if (std::holds_alternative(type)) { + return std::get(type); } - // If maskedType is a MaskedType, it is incompatible. Otherwise, if - // maskedType == std::monostate, then this CompatibleTypes is considered - // compatible with everything. - const MaskedType* pMaskedType = std::get_if(&maskedType); - return MaskedArrayType(pMaskedType == nullptr); + bool isNonArray = std::holds_alternative(type); + return MaskedArrayType(!isNonArray); } }; @@ -386,23 +402,16 @@ class ArrayOfPropertyValues { }; CompatibleTypes findCompatibleTypesForBoolean() { - MaskedType type; // Don't allow conversion of bools to numeric 0 or 1. - type.isInt8 = type.isUint8 = false; - type.isInt16 = type.isUint16 = false; - type.isInt32 = type.isUint32 = false; - type.isInt64 = type.isUint64 = false; - type.isFloat32 = false; - type.isFloat64 = false; - type.isBool &= true; + MaskedType type(false); + type.isBool = true; return CompatibleTypes(type); } template CompatibleTypes findCompatibleTypesForNumber(const TValueIter& it) { - MaskedType type; - type.isBool = false; + MaskedType type(false); if (it->IsInt64()) { const int64_t value = it->GetInt64(); @@ -419,26 +428,11 @@ CompatibleTypes findCompatibleTypesForNumber(const TValueIter& it) { } else if (it->IsUint64()) { // Only uint64_t can represent a value that fits in a uint64_t but not in // an int64_t. - type.isInt8 = type.isUint8 = false; - type.isInt16 = type.isUint16 = false; - type.isInt32 = type.isUint32 = false; - type.isInt64 = false; type.isUint64 = true; - type.isFloat32 = false; - type.isFloat64 = false; } else if (it->IsLosslessFloat()) { - type.isInt8 = type.isUint8 = false; - type.isInt16 = type.isUint16 = false; - type.isInt32 = type.isUint32 = false; - type.isInt64 = type.isUint64 = false; type.isFloat32 = true; type.isFloat64 = true; } else if (it->IsDouble()) { - type.isInt8 = type.isUint8 = false; - type.isInt16 = type.isUint16 = false; - type.isInt32 = type.isUint32 = false; - type.isInt64 = type.isUint64 = false; - type.isFloat32 = false; type.isFloat64 = true; } @@ -450,13 +444,13 @@ CompatibleTypes findCompatibleTypesForArray(const TValueIter& it) { // Iterate over all of the elements in the array and determine their // compatible type. CompatibleTypes arrayElementCompatibleTypes = - findCompatibleTypes(ArrayOfPropertyValues(*it)); + findCompatibleTypes(ArrayOfPropertyValues(*it)); - if (arrayElementCompatibleTypes.isArray()) { + if (arrayElementCompatibleTypes.supportsArray()) { // Ignore complications with arrays of arrays. The elements will be treated // like strings. - arrayElementCompatibleTypes.makeIncompatible(); - assert(!arrayElementCompatibleTypes.isArray()); + arrayElementCompatibleTypes.makeIncompatibleWithArray(); + assert(!arrayElementCompatibleTypes.supportsArray()); } MaskedType elementType = arrayElementCompatibleTypes.toMaskedType(); @@ -578,34 +572,12 @@ void updateExtensionWithJsonStringProperty( UINT64; } - Buffer& gltfBuffer = gltf.buffers.emplace_back(); - gltfBuffer.byteLength = static_cast(buffer.size()); - gltfBuffer.cesium.data = std::move(buffer); - - BufferView& gltfBufferView = gltf.bufferViews.emplace_back(); - gltfBufferView.buffer = static_cast(gltf.buffers.size() - 1); - gltfBufferView.byteOffset = 0; - gltfBufferView.byteLength = static_cast(totalSize); - const int32_t valueBufferViewIdx = - static_cast(gltf.bufferViews.size() - 1); - - Buffer& gltfOffsetBuffer = gltf.buffers.emplace_back(); - gltfOffsetBuffer.byteLength = static_cast(offsetBuffer.size()); - gltfOffsetBuffer.cesium.data = std::move(offsetBuffer); - - BufferView& gltfOffsetBufferView = gltf.bufferViews.emplace_back(); - gltfOffsetBufferView.buffer = static_cast(gltf.buffers.size() - 1); - gltfOffsetBufferView.byteOffset = 0; - gltfOffsetBufferView.byteLength = - static_cast(gltfOffsetBuffer.cesium.data.size()); - const int32_t offsetBufferViewIdx = - static_cast(gltf.bufferViews.size() - 1); - classProperty.type = ExtensionExtStructuralMetadataClassProperty::Type::STRING; - propertyTableProperty.values = valueBufferViewIdx; - propertyTableProperty.stringOffsets = offsetBufferViewIdx; + propertyTableProperty.values = addBufferToGltf(gltf, std::move(buffer)); + propertyTableProperty.stringOffsets = + addBufferToGltf(gltf, std::move(offsetBuffer)); } template @@ -623,10 +595,9 @@ void updateExtensionWithJsonScalarProperty( classProperty.componentType = componentTypeName; // Create a new buffer for this property. - std::vector buffer; const size_t byteLength = sizeof(T) * static_cast(propertyTable.count); - buffer.resize(byteLength); + std::vector buffer(byteLength); T* p = reinterpret_cast(buffer.data()); auto it = propertyValue.begin(); @@ -1240,7 +1211,7 @@ void updateExtensionWithJsonProperty( // Figure out which types we can use for this data. // Use the smallest type we can, and prefer signed to unsigned. const CompatibleTypes compatibleTypes = findCompatibleTypes(propertyValue); - if (compatibleTypes.isArray()) { + if (compatibleTypes.supportsArray()) { MaskedArrayType arrayType = compatibleTypes.toMaskedArrayType(); updateExtensionWithArrayProperty( gltf, @@ -1450,9 +1421,9 @@ void updateExtensionWithBatchTableHierarchy( const rapidjson::Value& batchTableHierarchy) { // EXT_structural_metadata can't support hierarchy, so we need to flatten it. // It also can't support multiple classes with a single set of feature IDs. - // (Feature IDs can only specify one property table, which only supports one class.) - // So essentially every property of every class gets added to - // the one class definition. + // (Feature IDs can only specify one property table, which only supports one + // class.) So essentially every property of every class gets added to the one + // class definition. auto classesIt = batchTableHierarchy.FindMember("classes"); if (classesIt == batchTableHierarchy.MemberEnd()) { result.emplaceWarning( diff --git a/Cesium3DTilesSelection/src/PntsToGltfConverter.cpp b/Cesium3DTilesSelection/src/PntsToGltfConverter.cpp index ca49693b2..d732fc7da 100644 --- a/Cesium3DTilesSelection/src/PntsToGltfConverter.cpp +++ b/Cesium3DTilesSelection/src/PntsToGltfConverter.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include diff --git a/Cesium3DTilesSelection/test/TestPntsToGltfConverter.cpp b/Cesium3DTilesSelection/test/TestPntsToGltfConverter.cpp index 8560ba20c..f647fc64f 100644 --- a/Cesium3DTilesSelection/test/TestPntsToGltfConverter.cpp +++ b/Cesium3DTilesSelection/test/TestPntsToGltfConverter.cpp @@ -1086,7 +1086,7 @@ TEST_CASE("Converts batched point cloud with Draco compression to glTF") { Model& gltf = *result.model; // The correctness of the model extension is thoroughly tested in - // TestUpgradeBatchTableToExtFeatureMetadata + // TestUpgradeBatchTableToExtStructuralMetadata CHECK(gltf.hasExtension()); CHECK(gltf.nodes.size() == 1); From f2bee8531ec307849e21748054ea052debda0b77 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Thu, 25 May 2023 14:11:49 -0400 Subject: [PATCH 049/421] Try to resolve CI errors --- .../BatchTableToGltfStructuralMetadata.cpp | 100 +++++++----------- .../test/TestPntsToGltfConverter.cpp | 4 +- 2 files changed, 40 insertions(+), 64 deletions(-) diff --git a/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp b/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp index 815983282..f05ce2bd3 100644 --- a/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp +++ b/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp @@ -131,10 +131,10 @@ struct CompatibleTypes { : type(maskedArrayType){}; /** - * Whether this is compatible with array types. + * Whether this is exclusively compatible with array types. */ - bool supportsArray() const { - return !std::holds_alternative(type); + bool isExclusivelyArray() const { + return std::holds_alternative(type); } /** @@ -143,23 +143,6 @@ struct CompatibleTypes { */ void makeIncompatible() { type = MaskedType(false); } - /** - * Marks as incompatible with array types. - */ - void makeIncompatibleWithArray() { - if (std::holds_alternative(type)) { - return; - } - - if (std::holds_alternative(type)) { - makeIncompatible(); - return; - } - - // If std::monostate, retain potential compatibility with non-array types - type = MaskedType(true); - } - /** * Merges a MaskedType into this CompatibleTypes. */ @@ -439,20 +422,16 @@ CompatibleTypes findCompatibleTypesForNumber(const TValueIter& it) { return CompatibleTypes(type); } -template +template CompatibleTypes findCompatibleTypesForArray(const TValueIter& it) { // Iterate over all of the elements in the array and determine their // compatible type. CompatibleTypes arrayElementCompatibleTypes = - findCompatibleTypes(ArrayOfPropertyValues(*it)); - - if (arrayElementCompatibleTypes.supportsArray()) { - // Ignore complications with arrays of arrays. The elements will be treated - // like strings. - arrayElementCompatibleTypes.makeIncompatibleWithArray(); - assert(!arrayElementCompatibleTypes.supportsArray()); - } + findCompatibleTypes(ArrayOfPropertyValues(*it)); + // If the elements inside the array are also arrays, this will return a + // completely incompatible MaskedType, which means the elements will be + // treated like strings. MaskedType elementType = arrayElementCompatibleTypes.toMaskedType(); MaskedArrayType arrayType(elementType, it->Size(), it->Size()); @@ -468,7 +447,7 @@ CompatibleTypes findCompatibleTypes(const TValueGetter& propertyValue) { } else if (it->IsNumber()) { compatibleTypes &= findCompatibleTypesForNumber(it); } else if (it->IsArray()) { - compatibleTypes &= findCompatibleTypesForArray(it); + compatibleTypes &= findCompatibleTypesForArray(it); } else { // A string, null, or something else. compatibleTypes.makeIncompatible(); @@ -601,10 +580,8 @@ void updateExtensionWithJsonScalarProperty( T* p = reinterpret_cast(buffer.data()); auto it = propertyValue.begin(); - for (int64_t i = 0; i < propertyTable.count; ++i) { + for (int64_t i = 0; i < propertyTable.count; ++i, ++p, ++it) { *p = static_cast(it->template Get()); - ++p; - ++it; } propertyTableProperty.values = addBufferToGltf(gltf, std::move(buffer)); @@ -881,7 +858,7 @@ void updateStringArrayProperty( propertyTable, propertyValue); stringOffsetType = PropertyComponentType::Uint32; - } else if (isInRangeForUnsignedInteger(totalByteLength)) { + } else { copyStringsToBuffers( valueBuffer, stringOffsetBuffer, @@ -907,45 +884,46 @@ void updateStringArrayProperty( return; } - // Handle variable-length arrays + // Handle variable-length arrays. + // For string arrays, arrayOffsets indexes into the stringOffsets buffer, + // the size of which is the number of stringElements + 1. This determines the + // component type of the array offsets. std::vector arrayOffsetBuffer; - switch (stringOffsetType) { - case PropertyComponentType::Uint8: + PropertyComponentType arrayOffsetType = PropertyComponentType::None; + if (isInRangeForUnsignedInteger(stringCount + 1)) { copyArrayOffsetsForStringArraysToBuffer( arrayOffsetBuffer, propertyTable, propertyValue); - break; - case PropertyComponentType::Uint16: + arrayOffsetType = PropertyComponentType::Uint8; + } else if (isInRangeForUnsignedInteger(stringCount + 1)) { copyArrayOffsetsForStringArraysToBuffer( arrayOffsetBuffer, propertyTable, propertyValue); - break; - case PropertyComponentType::Uint32: + arrayOffsetType = PropertyComponentType::Uint16; + } else if (isInRangeForUnsignedInteger(stringCount + 1)) { copyArrayOffsetsForStringArraysToBuffer( arrayOffsetBuffer, propertyTable, propertyValue); - break; - case PropertyComponentType::Uint64: + arrayOffsetType = PropertyComponentType::Uint32; + } else { copyArrayOffsetsForStringArraysToBuffer( arrayOffsetBuffer, propertyTable, propertyValue); - break; - default: - break; + arrayOffsetType = PropertyComponentType::Uint64; } propertyTableProperty.arrayOffsets = addBufferToGltf(gltf, std::move(arrayOffsetBuffer)); propertyTableProperty.arrayOffsetType = - convertPropertyComponentTypeToString(stringOffsetType); + convertPropertyComponentTypeToString(arrayOffsetType); } template -void copyVariableLengthBooleanArraysBuffers( +void copyVariableLengthBooleanArraysToBuffers( std::vector& valueBuffer, std::vector& offsetBuffer, size_t numOfElements, @@ -1036,24 +1014,24 @@ void updateBooleanArrayProperty( std::vector valueBuffer; std::vector offsetBuffer; PropertyComponentType offsetType = PropertyComponentType::None; - if (isInRangeForUnsignedInteger(numOfElements)) { - copyVariableLengthBooleanArraysBuffers( + if (isInRangeForUnsignedInteger(numOfElements + 1)) { + copyVariableLengthBooleanArraysToBuffers( valueBuffer, offsetBuffer, numOfElements, propertyTable, propertyValue); offsetType = PropertyComponentType::Uint8; - } else if (isInRangeForUnsignedInteger(numOfElements)) { - copyVariableLengthBooleanArraysBuffers( + } else if (isInRangeForUnsignedInteger(numOfElements + 1)) { + copyVariableLengthBooleanArraysToBuffers( valueBuffer, offsetBuffer, numOfElements, propertyTable, propertyValue); offsetType = PropertyComponentType::Uint16; - } else if (isInRangeForUnsignedInteger(numOfElements)) { - copyVariableLengthBooleanArraysBuffers( + } else if (isInRangeForUnsignedInteger(numOfElements + 1)) { + copyVariableLengthBooleanArraysToBuffers( valueBuffer, offsetBuffer, numOfElements, @@ -1061,7 +1039,7 @@ void updateBooleanArrayProperty( propertyValue); offsetType = PropertyComponentType::Uint32; } else { - copyVariableLengthBooleanArraysBuffers( + copyVariableLengthBooleanArraysToBuffers( valueBuffer, offsetBuffer, numOfElements, @@ -1211,7 +1189,7 @@ void updateExtensionWithJsonProperty( // Figure out which types we can use for this data. // Use the smallest type we can, and prefer signed to unsigned. const CompatibleTypes compatibleTypes = findCompatibleTypes(propertyValue); - if (compatibleTypes.supportsArray()) { + if (compatibleTypes.isExclusivelyArray()) { MaskedArrayType arrayType = compatibleTypes.toMaskedArrayType(); updateExtensionWithArrayProperty( gltf, @@ -1420,10 +1398,9 @@ void updateExtensionWithBatchTableHierarchy( ErrorList& result, const rapidjson::Value& batchTableHierarchy) { // EXT_structural_metadata can't support hierarchy, so we need to flatten it. - // It also can't support multiple classes with a single set of feature IDs. - // (Feature IDs can only specify one property table, which only supports one - // class.) So essentially every property of every class gets added to the one - // class definition. + // It also can't support multiple classes with a single set of feature IDs, + // because IDs can only specify one property table. So essentially every + // property of every class gets added to the one class definition. auto classesIt = batchTableHierarchy.FindMember("classes"); if (classesIt == batchTableHierarchy.MemberEnd()) { result.emplaceWarning( @@ -1439,8 +1416,7 @@ void updateExtensionWithBatchTableHierarchy( if (!element.IsInt64() || element.GetInt64() != 1LL) { result.emplaceWarning( "3DTILES_batch_table_hierarchy with a \"parentCounts\" property " - "is " - "not currently supported. All instances must have at most one " + "is not currently supported. All instances must have at most one " "parent."); return; } diff --git a/Cesium3DTilesSelection/test/TestPntsToGltfConverter.cpp b/Cesium3DTilesSelection/test/TestPntsToGltfConverter.cpp index f647fc64f..ca7bdca5a 100644 --- a/Cesium3DTilesSelection/test/TestPntsToGltfConverter.cpp +++ b/Cesium3DTilesSelection/test/TestPntsToGltfConverter.cpp @@ -653,8 +653,8 @@ getUniqueBufferIds(const std::vector& bufferViews) { return result; } -TEST_CASE( - "Converts point cloud with batch IDs to glTF with EXT_structural_metadata") { +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; From b5fb7302e34b1d5b7a2159e4a57403925acdf64c Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Thu, 25 May 2023 14:43:43 -0400 Subject: [PATCH 050/421] Fold helper functions into findCompatibleTypes, fix formatting --- .../BatchTableToGltfStructuralMetadata.cpp | 49 ++++++++----------- .../StructuralMetadataPropertyTableView.cpp | 3 +- 2 files changed, 22 insertions(+), 30 deletions(-) diff --git a/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp b/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp index f05ce2bd3..1262edbc8 100644 --- a/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp +++ b/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp @@ -384,16 +384,8 @@ class ArrayOfPropertyValues { const rapidjson::Value& _propertyValues; }; -CompatibleTypes findCompatibleTypesForBoolean() { - // Don't allow conversion of bools to numeric 0 or 1. - MaskedType type(false); - type.isBool = true; - - return CompatibleTypes(type); -} - template -CompatibleTypes findCompatibleTypesForNumber(const TValueIter& it) { +MaskedType getCompatibleTypesForNumber(const TValueIter& it) { MaskedType type(false); if (it->IsInt64()) { @@ -419,23 +411,7 @@ CompatibleTypes findCompatibleTypesForNumber(const TValueIter& it) { type.isFloat64 = true; } - return CompatibleTypes(type); -} - -template -CompatibleTypes findCompatibleTypesForArray(const TValueIter& it) { - // Iterate over all of the elements in the array and determine their - // compatible type. - CompatibleTypes arrayElementCompatibleTypes = - findCompatibleTypes(ArrayOfPropertyValues(*it)); - - // If the elements inside the array are also arrays, this will return a - // completely incompatible MaskedType, which means the elements will be - // treated like strings. - MaskedType elementType = arrayElementCompatibleTypes.toMaskedType(); - MaskedArrayType arrayType(elementType, it->Size(), it->Size()); - - return CompatibleTypes(arrayType); + return type; } template @@ -443,11 +419,26 @@ CompatibleTypes findCompatibleTypes(const TValueGetter& propertyValue) { CompatibleTypes compatibleTypes; for (auto it = propertyValue.begin(); it != propertyValue.end(); ++it) { if (it->IsBool()) { - compatibleTypes &= findCompatibleTypesForBoolean(); + // Don't allow booleans to be converted to numeric 0 or 1. + MaskedType booleanType(false); + booleanType.isBool = true; + + compatibleTypes &= booleanType; } else if (it->IsNumber()) { - compatibleTypes &= findCompatibleTypesForNumber(it); + compatibleTypes &= getCompatibleTypesForNumber(it); } else if (it->IsArray()) { - compatibleTypes &= findCompatibleTypesForArray(it); + // Iterate over all of the elements in the array + // and determine their compatible type. + CompatibleTypes arrayElementCompatibleTypes = + findCompatibleTypes(ArrayOfPropertyValues(*it)); + + // If the elements inside the array are also arrays, this will return a + // completely incompatible MaskedType, which means the elements will be + // treated like strings. + MaskedType elementType = arrayElementCompatibleTypes.toMaskedType(); + MaskedArrayType arrayType(elementType, it->Size(), it->Size()); + + compatibleTypes &= arrayType; } else { // A string, null, or something else. compatibleTypes.makeIncompatible(); diff --git a/CesiumGltf/src/StructuralMetadataPropertyTableView.cpp b/CesiumGltf/src/StructuralMetadataPropertyTableView.cpp index 66f47c2fe..bf6bdfd18 100644 --- a/CesiumGltf/src/StructuralMetadataPropertyTableView.cpp +++ b/CesiumGltf/src/StructuralMetadataPropertyTableView.cpp @@ -165,7 +165,8 @@ MetadataPropertyViewStatus MetadataPropertyTableView::getBufferSafe( return MetadataPropertyViewStatus::ErrorInvalidValueBufferView; } - const Buffer* pBuffer = _pModel->getSafe(&_pModel->buffers, pBufferView->buffer); + const Buffer* pBuffer = + _pModel->getSafe(&_pModel->buffers, pBufferView->buffer); if (!pBuffer) { return MetadataPropertyViewStatus::ErrorInvalidValueBuffer; } From 8bfb032c0640d8c29e823b5ee098e864fe99766f Mon Sep 17 00:00:00 2001 From: Joseph Kaile Date: Thu, 25 May 2023 15:06:54 -0400 Subject: [PATCH 051/421] actually emit warnings when there are no errors --- .../src/RasterOverlayCollection.cpp | 6 ++-- .../src/TilesetContentManager.cpp | 32 ++++++++++--------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/Cesium3DTilesSelection/src/RasterOverlayCollection.cpp b/Cesium3DTilesSelection/src/RasterOverlayCollection.cpp index 1a13929d9..5a9ae7405 100644 --- a/Cesium3DTilesSelection/src/RasterOverlayCollection.cpp +++ b/Cesium3DTilesSelection/src/RasterOverlayCollection.cpp @@ -124,14 +124,12 @@ void RasterOverlayCollection::add( // Report error creating the tile provider. const RasterOverlayLoadFailureDetails& failureDetails = result.error(); + SPDLOG_LOGGER_ERROR(pLogger, failureDetails.message); if (pOverlay->getOptions().loadErrorCallback) { pOverlay->getOptions().loadErrorCallback(failureDetails); - } else { - SPDLOG_LOGGER_ERROR(pLogger, failureDetails.message); } + // CESIUM_TRACE_END_IN_TRACK("createTileProvider"); } - - // CESIUM_TRACE_END_IN_TRACK("createTileProvider"); }); } diff --git a/Cesium3DTilesSelection/src/TilesetContentManager.cpp b/Cesium3DTilesSelection/src/TilesetContentManager.cpp index bba7efa28..5d4a065ed 100644 --- a/Cesium3DTilesSelection/src/TilesetContentManager.cpp +++ b/Cesium3DTilesSelection/src/TilesetContentManager.cpp @@ -1418,22 +1418,24 @@ void TilesetContentManager::propagateTilesetContentLoaderResult( const std::function& loadErrorCallback, TilesetContentLoaderResult&& result) { - if (result.errors) { - if (loadErrorCallback) { - loadErrorCallback(TilesetLoadFailureDetails{ - nullptr, - type, - result.statusCode, - CesiumUtility::joinToString(result.errors.errors, "\n- ")}); - } - result.errors.logError( - this->_externals.pLogger, - "Errors when loading tileset"); - result.errors.logWarning( - this->_externals.pLogger, - "Warnings when loading tileset"); - } else { + result.errors.logError( + this->_externals.pLogger, + "Errors when loading tileset"); + + result.errors.logWarning( + this->_externals.pLogger, + "Warnings when loading tileset"); + + if (loadErrorCallback) { + loadErrorCallback(TilesetLoadFailureDetails{ + nullptr, + type, + result.statusCode, + CesiumUtility::joinToString(result.errors.errors, "\n- ")}); + } + + if (!result.errors) { this->_tilesetCredits.reserve( this->_tilesetCredits.size() + result.credits.size()); for (const auto& creditResult : result.credits) { From aa7b16307439fb64d141ee8b3f9502aa73a63314 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Fri, 26 May 2023 21:32:31 +1000 Subject: [PATCH 052/421] Move commented-out code back where it belongs. --- Cesium3DTilesSelection/src/RasterOverlayCollection.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cesium3DTilesSelection/src/RasterOverlayCollection.cpp b/Cesium3DTilesSelection/src/RasterOverlayCollection.cpp index 5a9ae7405..261ff35b5 100644 --- a/Cesium3DTilesSelection/src/RasterOverlayCollection.cpp +++ b/Cesium3DTilesSelection/src/RasterOverlayCollection.cpp @@ -128,8 +128,8 @@ void RasterOverlayCollection::add( if (pOverlay->getOptions().loadErrorCallback) { pOverlay->getOptions().loadErrorCallback(failureDetails); } - // CESIUM_TRACE_END_IN_TRACK("createTileProvider"); } + // CESIUM_TRACE_END_IN_TRACK("createTileProvider"); }); } From 619faea5b7e50315009a6adb58e2a3ad26679916 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Tue, 30 May 2023 10:27:21 -0400 Subject: [PATCH 053/421] Add PropertyTextureView and rename PropertyTable classes --- ...gradeBatchTableToExtStructuralMetadata.cpp | 18 +- ...cturalMetadataPropertyTablePropertyView.h} | 78 +- .../StructuralMetadataPropertyTableView.h | 171 ++-- ...turalMetadataPropertyTexturePropertyView.h | 245 ++++++ .../StructuralMetadataPropertyTextureView.h | 124 +++ .../StructuralMetadataPropertyTableView.cpp | 172 ++-- ...ralMetadataPropertyTexturePropertyView.cpp | 102 +++ .../StructuralMetadataPropertyTextureView.cpp | 84 ++ ...uralMetadataPropertyTablePropertyView.cpp} | 38 +- ...estStructuralMetadataPropertyTableView.cpp | 808 +++++++++--------- ...tStructuralMetadataPropertyTextureView.cpp | 169 ++++ 11 files changed, 1388 insertions(+), 621 deletions(-) rename CesiumGltf/include/CesiumGltf/{StructuralMetadataPropertyView.h => StructuralMetadataPropertyTablePropertyView.h} (87%) create mode 100644 CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTexturePropertyView.h create mode 100644 CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTextureView.h create mode 100644 CesiumGltf/src/StructuralMetadataPropertyTexturePropertyView.cpp create mode 100644 CesiumGltf/src/StructuralMetadataPropertyTextureView.cpp rename CesiumGltf/test/{TestStructuralMetadataPropertyView.cpp => TestStructuralMetadataPropertyTablePropertyView.cpp} (96%) create mode 100644 CesiumGltf/test/TestStructuralMetadataPropertyTextureView.cpp diff --git a/Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp b/Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp index 651af2c5d..f6d232138 100644 --- a/Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp +++ b/Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include #include @@ -39,13 +39,13 @@ static void checkNonArrayProperty( REQUIRE(!property.array); REQUIRE(property.count == std::nullopt); - MetadataPropertyTableView view(model, propertyTable); - REQUIRE(view.status() == MetadataPropertyTableViewStatus::Valid); + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); REQUIRE(view.size() == propertyTable.count); - MetadataPropertyView propertyView = + PropertyTablePropertyView propertyView = view.getPropertyView(propertyName); - REQUIRE(propertyView.status() == MetadataPropertyViewStatus::Valid); + REQUIRE(propertyView.status() == PropertyTablePropertyViewStatus::Valid); REQUIRE(propertyView.size() == propertyTable.count); REQUIRE(propertyView.size() == static_cast(expectedTotalInstances)); for (int64_t i = 0; i < propertyView.size(); ++i) { @@ -84,13 +84,13 @@ static void checkArrayProperty( REQUIRE(property.array); REQUIRE(property.count.value_or(0) == expectedCount); - MetadataPropertyTableView view(model, propertyTable); - REQUIRE(view.status() == MetadataPropertyTableViewStatus::Valid); + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); REQUIRE(view.size() == propertyTable.count); - MetadataPropertyView> propertyView = + PropertyTablePropertyView> propertyView = view.getPropertyView>(propertyName); - REQUIRE(propertyView.status() == MetadataPropertyViewStatus::Valid); + REQUIRE(propertyView.status() == PropertyTablePropertyViewStatus::Valid); REQUIRE(propertyView.size() == propertyTable.count); REQUIRE(propertyView.size() == static_cast(expectedTotalInstances)); for (size_t i = 0; i < expectedTotalInstances; ++i) { diff --git a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyView.h b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTablePropertyView.h similarity index 87% rename from CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyView.h rename to CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTablePropertyView.h index 4ad34057b..320beb349 100644 --- a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyView.h +++ b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTablePropertyView.h @@ -15,15 +15,15 @@ namespace CesiumGltf { namespace StructuralMetadata { /** - * @brief Indicates the status of a property view. + * @brief Indicates the status of a property table property view. * - * The {@link MetadataPropertyView} constructor always completes successfully. + * The {@link PropertyTablePropertyView} constructor always completes successfully. * However, it may not always reflect the actual content of the * {@link ExtensionExtStructuralMetadataPropertyTableProperty}, but instead - * indicate that its {@link MetadataPropertyView::size} is 0. This enumeration + * indicate that its {@link PropertyTablePropertyView::size} is 0. This enumeration * provides the reason. */ -enum class MetadataPropertyViewStatus { +enum class PropertyTablePropertyViewStatus { /** * @brief This property view is valid and ready to use. */ @@ -36,7 +36,8 @@ enum class MetadataPropertyViewStatus { ErrorInvalidPropertyTable, /** - * @brief This property view does not exist in the + * @brief This property view is trying to view a property that does not exist + * in the * {@link ExtensionExtStructuralMetadataPropertyTable}. */ ErrorPropertyDoesNotExist, @@ -172,15 +173,15 @@ enum class MetadataPropertyViewStatus { * the scalar types, bool, std::string_view, or MetadataArrayView with T as * one of the aforementioned types. */ -template class MetadataPropertyView { +template class PropertyTablePropertyView { public: /** * @brief Constructs a new instance with a non-existent property. */ - MetadataPropertyView() - : _status{MetadataPropertyViewStatus::ErrorPropertyDoesNotExist}, + PropertyTablePropertyView() + : _status{PropertyTablePropertyViewStatus::ErrorPropertyDoesNotExist}, _values{}, - _fixedLengthArrayCount{}, + _arrayCount{}, _size{}, _normalized{} {} @@ -191,8 +192,8 @@ template class MetadataPropertyView { * @param size The number of elements in the property table specified by {@link ExtensionExtStructuralMetadataPropertyTable::count} * @param normalized Whether this property has a normalized integer type. */ - MetadataPropertyView( - MetadataPropertyViewStatus status, + PropertyTablePropertyView( + PropertyTablePropertyViewStatus status, gsl::span values, int64_t size, bool normalized) noexcept @@ -204,7 +205,7 @@ template class MetadataPropertyView { _stringOffsets{}, _stringOffsetType{PropertyComponentType::None}, _stringOffsetTypeSize{0}, - _fixedLengthArrayCount{0}, + _arrayCount{0}, _size{size}, _normalized{normalized} {} @@ -216,18 +217,18 @@ template class MetadataPropertyView { * @param stringOffsets The raw buffer specified by {@link ExtensionExtStructuralMetadataPropertyTableProperty::stringOffsets} * @param offsetType The offset type of arrayOffsets specified by {@link ExtensionExtStructuralMetadataPropertyTableProperty::arrayOffsetType} * @param offsetType The offset type of stringOffsets specified by {@link ExtensionExtStructuralMetadataPropertyTableProperty::stringOffsetType} - * @param fixedLengthArrayCount The number of elements in each array value specified by {@link ExtensionExtStructuralMetadataClassProperty::count} + * @param arrayCount The number of elements in each array value specified by {@link ExtensionExtStructuralMetadataClassProperty::count} * @param size The number of elements in the property table specified by {@link ExtensionExtStructuralMetadataPropertyTable::count} * @param normalized Whether this property has a normalized integer type. */ - MetadataPropertyView( - MetadataPropertyViewStatus status, + PropertyTablePropertyView( + PropertyTablePropertyViewStatus status, gsl::span values, gsl::span arrayOffsets, gsl::span stringOffsets, StructuralMetadata::PropertyComponentType arrayOffsetType, StructuralMetadata::PropertyComponentType stringOffsetType, - int64_t fixedLengthArrayCount, + int64_t arrayCount, int64_t size, bool normalized) noexcept : _status{status}, @@ -238,19 +239,19 @@ template class MetadataPropertyView { _stringOffsets{stringOffsets}, _stringOffsetType{stringOffsetType}, _stringOffsetTypeSize{getOffsetTypeSize(stringOffsetType)}, - _fixedLengthArrayCount{fixedLengthArrayCount}, + _arrayCount{arrayCount}, _size{size}, _normalized{normalized} {} /** - * @brief Gets the status of this property view. + * @brief Gets the status of this property table property view. * * Indicates whether the view accurately reflects the property's data, or * whether an error occurred. * * @return The status of this property view. */ - MetadataPropertyViewStatus status() const noexcept { return _status; } + PropertyTablePropertyViewStatus status() const noexcept { return _status; } /** * @brief Get the value of an element of the {@link ExtensionExtStructuralMetadataPropertyTable}. @@ -259,7 +260,7 @@ template class MetadataPropertyView { */ ElementType get(int64_t index) const noexcept { assert( - _status == MetadataPropertyViewStatus::Valid && + _status == PropertyTablePropertyViewStatus::Valid && "Check the status() first to make sure view is valid"); assert( size() > 0 && @@ -294,14 +295,14 @@ template class MetadataPropertyView { } /** - * @brief Get the number of elements in this MetadataPropertyView. If the view - * is valid, this returns + * @brief Get the number of elements in this + * PropertyTablePropertyView. If the view is valid, this returns * {@link ExtensionExtStructuralMetadataPropertyTable::count}. Otherwise, this returns 0. * - * @return The number of elements in this MetadataPropertyView. + * @return The number of elements in this PropertyTablePropertyView. */ int64_t size() const noexcept { - return status() == MetadataPropertyViewStatus::Valid ? _size : 0; + return status() == PropertyTablePropertyViewStatus::Valid ? _size : 0; } /** @@ -310,9 +311,7 @@ template class MetadataPropertyView { * * @return The count of this property. */ - int64_t getFixedLengthArrayCount() const noexcept { - return _fixedLengthArrayCount; - } + int64_t getArrayCount() const noexcept { return _arrayCount; } /** * @brief Whether this property has a normalized integer type. @@ -348,8 +347,8 @@ template class MetadataPropertyView { template MetadataArrayView getNumericArrayValues(int64_t index) const noexcept { // Handle fixed-length arrays - if (_fixedLengthArrayCount > 0) { - size_t arraySize = _fixedLengthArrayCount * sizeof(T); + if (_arrayCount > 0) { + size_t arraySize = _arrayCount * sizeof(T); const gsl::span values( _values.data() + index * arraySize, arraySize); @@ -370,9 +369,9 @@ template class MetadataPropertyView { MetadataArrayView getStringArrayValues(int64_t index) const noexcept { // Handle fixed-length arrays - if (_fixedLengthArrayCount > 0) { + if (_arrayCount > 0) { // Copy the corresponding string offsets to pass to the MetadataArrayView. - const size_t arraySize = _fixedLengthArrayCount * _stringOffsetTypeSize; + const size_t arraySize = _arrayCount * _stringOffsetTypeSize; const gsl::span stringOffsetValues( _stringOffsets.data() + index * arraySize, arraySize + _stringOffsetTypeSize); @@ -380,7 +379,7 @@ template class MetadataPropertyView { _values, stringOffsetValues, _stringOffsetType, - _fixedLengthArrayCount); + _arrayCount); } // Handle variable-length arrays @@ -401,16 +400,13 @@ template class MetadataPropertyView { MetadataArrayView getBooleanArrayValues(int64_t index) const noexcept { // Handle fixed-length arrays - if (_fixedLengthArrayCount > 0) { - const size_t offsetBits = _fixedLengthArrayCount * index; - const size_t nextOffsetBits = _fixedLengthArrayCount * (index + 1); + if (_arrayCount > 0) { + const size_t offsetBits = _arrayCount * index; + const size_t nextOffsetBits = _arrayCount * (index + 1); const gsl::span buffer( _values.data() + offsetBits / 8, (nextOffsetBits / 8 - offsetBits / 8 + 1)); - return MetadataArrayView( - buffer, - offsetBits % 8, - _fixedLengthArrayCount); + return MetadataArrayView(buffer, offsetBits % 8, _arrayCount); } // Handle variable-length arrays @@ -440,7 +436,7 @@ template class MetadataPropertyView { } } - MetadataPropertyViewStatus _status; + PropertyTablePropertyViewStatus _status; gsl::span _values; gsl::span _arrayOffsets; @@ -451,7 +447,7 @@ template class MetadataPropertyView { PropertyComponentType _stringOffsetType; int64_t _stringOffsetTypeSize; - int64_t _fixedLengthArrayCount; + int64_t _arrayCount; int64_t _size; bool _normalized; }; diff --git a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTableView.h b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTableView.h index 7fcd8fc38..e3c55db2d 100644 --- a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTableView.h +++ b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTableView.h @@ -2,8 +2,8 @@ #include "CesiumGltf/ExtensionModelExtStructuralMetadata.h" #include "CesiumGltf/Model.h" +#include "CesiumGltf/StructuralMetadataPropertyTablePropertyView.h" #include "CesiumGltf/StructuralMetadataPropertyType.h" -#include "CesiumGltf/StructuralMetadataPropertyView.h" #include @@ -15,12 +15,12 @@ namespace StructuralMetadata { /** * @brief Indicates the status of a property table view. * - * The {@link MetadataPropertyTableView} constructor always completes successfully. + * The {@link PropertyTableView} constructor always completes successfully. * However, it may not always reflect the actual content of the * {@link ExtensionExtStructuralMetadataPropertyTable}, but instead indicate that its - * {@link MetadataPropertyTableView::size} is 0. This enumeration provides the reason. + * {@link PropertyTableView::size} is 0. This enumeration provides the reason. */ -enum class MetadataPropertyTableViewStatus { +enum class PropertyTableViewStatus { /** * @brief This property table view is valid and ready to use. */ @@ -30,37 +30,38 @@ enum class MetadataPropertyTableViewStatus { * @brief The property table view's model does not contain an * EXT_structural_metadata extension. */ - ErrorNoStructuralMetadataExtension, + ErrorMissingMetadataExtension, /** * @brief The property table view's model does not have a schema in its * EXT_structural_metadata extension. */ - ErrorNoSchema, + ErrorMissingSchema, /** - * @brief The class of the property table does not exist in the schema. + * @brief The property table's specified class could not be found in the + * extension. */ - ErrorPropertyTableClassDoesNotExist + ErrorClassNotFound }; /** * @brief Utility to retrieve the data of * {@link ExtensionExtStructuralMetadataPropertyTable}. * - * This should be used to get a {@link MetadataPropertyView} of a property in the property table. - * It will validate the EXT_structural_metadata format and ensure {@link MetadataPropertyView} + * This should be used to get a {@link PropertyTablePropertyView} of a property in the property table. + * It will validate the EXT_structural_metadata format and ensure {@link PropertyTablePropertyView} * does not access out of bounds. */ -class MetadataPropertyTableView { +class PropertyTableView { public: /** - * @brief Creates an instance of MetadataPropertyTableView. + * @brief Creates an instance of PropertyTableView. * @param model The Gltf Model that contains property table data. * @param propertyTable The {@link ExtensionExtStructuralMetadataPropertyTable} * from which the view will retrieve data. */ - MetadataPropertyTableView( + PropertyTableView( const Model& model, const ExtensionExtStructuralMetadataPropertyTable& propertyTable); @@ -72,19 +73,18 @@ class MetadataPropertyTableView { * * @return The status of this property table view. */ - MetadataPropertyTableViewStatus status() const noexcept { return _status; } + PropertyTableViewStatus status() const noexcept { return _status; } /** - * @brief Get the number of elements in this MetadataPropertyTableView. If the + * @brief Get the number of elements in this PropertyTableView. If the * view is valid, this returns * {@link ExtensionExtStructuralMetadataPropertyTable::count}. Otherwise, this returns 0. * - * @return The number of elements in this MetadataPropertyTableView. + * @return The number of elements in this PropertyTableView. */ int64_t size() const noexcept { - return _status == MetadataPropertyTableViewStatus::Valid - ? _pPropertyTable->count - : 0; + return _status == PropertyTableViewStatus::Valid ? _pPropertyTable->count + : 0; } /** @@ -92,18 +92,18 @@ class MetadataPropertyTableView { * describes the type information of the property with the specified name. * @param propertyName The name of the property to retrieve the class for. * @return A pointer to the {@link ExtensionExtStructuralMetadataClassProperty}. - * Return nullptr if the MetadataPropertyTableView is invalid or if no class + * Return nullptr if the PropertyTableView is invalid or if no class * property was found. */ const ExtensionExtStructuralMetadataClassProperty* getClassProperty(const std::string& propertyName) const; /** - * @brief Gets a {@link MetadataPropertyView} that views the data of a property stored + * @brief Gets a {@link PropertyTablePropertyView} that views the data of a property stored * in the {@link ExtensionExtStructuralMetadataPropertyTable}. * * This method will validate the EXT_structural_metadata format to ensure - * {@link MetadataPropertyView} retrieves the correct data. T must be one of the + * {@link PropertyTablePropertyView} retrieves the correct data. T must be one of the * following: a scalar (uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t, * uint64_t, int64_t, float, double), a glm vecN composed of one of the scalar * types, a glm matN composed of one of the scalar types, bool, @@ -111,15 +111,15 @@ class MetadataPropertyTableView { * aforementioned types. * * @param propertyName The name of the property to retrieve data from - * @return A {@link MetadataPropertyView} of the property. If no valid property is + * @return A {@link PropertyTablePropertyView} of the property. If no valid property is * found, the property view will be invalid. */ template - MetadataPropertyView + PropertyTablePropertyView getPropertyView(const std::string& propertyName) const { if (this->size() <= 0) { return createInvalidPropertyView( - StructuralMetadata::MetadataPropertyViewStatus:: + StructuralMetadata::PropertyTablePropertyViewStatus:: ErrorInvalidPropertyTable); } @@ -127,7 +127,7 @@ class MetadataPropertyTableView { getClassProperty(propertyName); if (!pClassProperty) { return createInvalidPropertyView( - StructuralMetadata::MetadataPropertyViewStatus:: + StructuralMetadata::PropertyTablePropertyViewStatus:: ErrorPropertyDoesNotExist); } @@ -135,23 +135,23 @@ class MetadataPropertyTableView { } /** - * @brief Gets a {@link MetadataPropertyView} through a callback that accepts a - * property name and a {@link MetadataPropertyView} that views the data + * @brief Gets a {@link PropertyTablePropertyView} through a callback that accepts a + * property name and a {@link PropertyTablePropertyView} that views the data * of the property with the specified name. * * This method will validate the EXT_structural_metadata format to ensure - * {@link MetadataPropertyView} retrieves the correct data. T must be one of the + * {@link PropertyTablePropertyView} retrieves the correct data. T must be one of the * following: a scalar (uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t, * uint64_t, int64_t, float, double), a glm vecN composed of one of the scalar * types, a glm matN composed of one of the scalar types, bool, * std::string_view, or {@link MetadataArrayView} with T as one of the * aforementioned types. If the property is invalid, an empty - * {@link MetadataPropertyView} with an error status will be passed to the + * {@link PropertyTablePropertyView} with an error status will be passed to the * callback. Otherwise, a valid property view will be passed to the callback. * * @param propertyName The name of the property to retrieve data from * @tparam callback A callback function that accepts a property name and a - * {@link MetadataPropertyView} + * {@link PropertyTablePropertyView} */ template void @@ -160,7 +160,7 @@ class MetadataPropertyTableView { callback( propertyName, createInvalidPropertyView( - StructuralMetadata::MetadataPropertyViewStatus:: + StructuralMetadata::PropertyTablePropertyViewStatus:: ErrorInvalidPropertyTable)); return; } @@ -171,7 +171,7 @@ class MetadataPropertyTableView { callback( propertyName, createInvalidPropertyView( - MetadataPropertyViewStatus::ErrorPropertyDoesNotExist)); + PropertyTablePropertyViewStatus::ErrorPropertyDoesNotExist)); return; } @@ -221,30 +221,30 @@ class MetadataPropertyTableView { callback( propertyName, createInvalidPropertyView( - MetadataPropertyViewStatus::ErrorTypeMismatch)); + PropertyTablePropertyViewStatus::ErrorTypeMismatch)); } } /** * @brief Iterates over each property in the * {@link ExtensionExtStructuralMetadataPropertyTable} with a callback that accepts a - * property name and a {@link MetadataPropertyView} to view the data + * property name and a {@link PropertyTablePropertyView} to view the data * stored in the {@link ExtensionExtStructuralMetadataPropertyTableProperty}. * * This method will validate the EXT_structural_metadata format to ensure - * {@link MetadataPropertyView} retrieves the correct data. T must be one of the + * {@link PropertyTablePropertyView} retrieves the correct data. T must be one of the * following: a scalar (uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t, * uint64_t, int64_t, float, double), a glm vecN composed of one of the scalar * types, a glm matN composed of one of the scalar types, bool, * std::string_view, or {@link MetadataArrayView} with T as one of the * aforementioned types. If the property is invalid, an empty - * {@link MetadataPropertyView} with an error status code will be passed to the + * {@link PropertyTablePropertyView} with an error status code will be passed to the * callback. Otherwise, a valid property view will be passed to * the callback. * * @param propertyName The name of the property to retrieve data from * @tparam callback A callback function that accepts property name and - * {@link MetadataPropertyView} + * {@link PropertyTablePropertyView} */ template void forEachProperty(Callback&& callback) const { for (const auto& property : this->_pClass->properties) { @@ -350,7 +350,7 @@ class MetadataPropertyTableView { callback( propertyName, createInvalidPropertyView( - MetadataPropertyViewStatus::ErrorComponentTypeMismatch)); + PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch)); break; } } @@ -436,7 +436,7 @@ class MetadataPropertyTableView { callback( propertyName, createInvalidPropertyView( - MetadataPropertyViewStatus::ErrorComponentTypeMismatch)); + PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch)); break; } } @@ -475,7 +475,7 @@ class MetadataPropertyTableView { callback( propertyName, createInvalidPropertyView( - MetadataPropertyViewStatus::ErrorTypeMismatch)); + PropertyTablePropertyViewStatus::ErrorTypeMismatch)); break; } } @@ -561,7 +561,7 @@ class MetadataPropertyTableView { callback( propertyName, createInvalidPropertyView( - MetadataPropertyViewStatus::ErrorComponentTypeMismatch)); + PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch)); break; } } @@ -600,7 +600,7 @@ class MetadataPropertyTableView { callback( propertyName, createInvalidPropertyView( - MetadataPropertyViewStatus::ErrorTypeMismatch)); + PropertyTablePropertyViewStatus::ErrorTypeMismatch)); break; } } @@ -649,7 +649,7 @@ class MetadataPropertyTableView { callback( propertyName, createInvalidPropertyView( - MetadataPropertyViewStatus::ErrorTypeMismatch)); + PropertyTablePropertyViewStatus::ErrorTypeMismatch)); } } @@ -733,7 +733,7 @@ class MetadataPropertyTableView { callback( propertyName, createInvalidPropertyView( - MetadataPropertyViewStatus::ErrorComponentTypeMismatch)); + PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch)); break; } } @@ -772,7 +772,7 @@ class MetadataPropertyTableView { callback( propertyName, createInvalidPropertyView( - MetadataPropertyViewStatus::ErrorTypeMismatch)); + PropertyTablePropertyViewStatus::ErrorTypeMismatch)); break; } } @@ -858,7 +858,7 @@ class MetadataPropertyTableView { callback( propertyName, createInvalidPropertyView( - MetadataPropertyViewStatus::ErrorComponentTypeMismatch)); + PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch)); break; } } @@ -897,7 +897,7 @@ class MetadataPropertyTableView { callback( propertyName, createInvalidPropertyView( - MetadataPropertyViewStatus::ErrorTypeMismatch)); + PropertyTablePropertyViewStatus::ErrorTypeMismatch)); break; } } @@ -963,20 +963,20 @@ class MetadataPropertyTableView { callback( propertyName, createInvalidPropertyView( - MetadataPropertyViewStatus::ErrorComponentTypeMismatch)); + PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch)); break; } } template - MetadataPropertyView getPropertyViewImpl( + PropertyTablePropertyView getPropertyViewImpl( const std::string& propertyName, const ExtensionExtStructuralMetadataClassProperty& classProperty) const { auto propertyTablePropertyIter = _pPropertyTable->properties.find(propertyName); if (propertyTablePropertyIter == _pPropertyTable->properties.end()) { return createInvalidPropertyView( - MetadataPropertyViewStatus::ErrorPropertyDoesNotExist); + PropertyTablePropertyViewStatus::ErrorPropertyDoesNotExist); } const ExtensionExtStructuralMetadataPropertyTableProperty& @@ -1006,37 +1006,37 @@ class MetadataPropertyTableView { } template - MetadataPropertyView getNumericOrBooleanPropertyValues( + PropertyTablePropertyView getNumericOrBooleanPropertyValues( const ExtensionExtStructuralMetadataClassProperty& classProperty, const ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty) const { if (classProperty.array) { return createInvalidPropertyView( - MetadataPropertyViewStatus::ErrorArrayTypeMismatch); + PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); } const PropertyType type = convertStringToPropertyType(classProperty.type); if (TypeToPropertyType::value != type) { return createInvalidPropertyView( - MetadataPropertyViewStatus::ErrorTypeMismatch); + PropertyTablePropertyViewStatus::ErrorTypeMismatch); } const PropertyComponentType componentType = convertStringToPropertyComponentType( classProperty.componentType.value_or("")); if (TypeToPropertyType::component != componentType) { return createInvalidPropertyView( - MetadataPropertyViewStatus::ErrorComponentTypeMismatch); + PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); } gsl::span values; const auto status = getBufferSafe(propertyTableProperty.values, values); - if (status != MetadataPropertyViewStatus::Valid) { + if (status != PropertyTablePropertyViewStatus::Valid) { return createInvalidPropertyView(status); } if (values.size() % sizeof(T) != 0) { return createInvalidPropertyView( - StructuralMetadata::MetadataPropertyViewStatus:: + StructuralMetadata::PropertyTablePropertyViewStatus:: ErrorBufferViewSizeNotDivisibleByTypeSize); } @@ -1050,36 +1050,37 @@ class MetadataPropertyTableView { if (values.size() < maxRequiredBytes) { return createInvalidPropertyView( - MetadataPropertyViewStatus:: + PropertyTablePropertyViewStatus:: ErrorBufferViewSizeDoesNotMatchPropertyTableCount); } - return MetadataPropertyView( - MetadataPropertyViewStatus::Valid, + return PropertyTablePropertyView( + PropertyTablePropertyViewStatus::Valid, values, _pPropertyTable->count, classProperty.normalized); } - MetadataPropertyView getStringPropertyValues( + PropertyTablePropertyView getStringPropertyValues( const ExtensionExtStructuralMetadataClassProperty& classProperty, const ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty) const; template - MetadataPropertyView> getPrimitiveArrayPropertyValues( + PropertyTablePropertyView> + getPrimitiveArrayPropertyValues( const ExtensionExtStructuralMetadataClassProperty& classProperty, const ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty) const { if (!classProperty.array) { return createInvalidPropertyView>( - MetadataPropertyViewStatus::ErrorArrayTypeMismatch); + PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); } const PropertyType type = convertStringToPropertyType(classProperty.type); if (TypeToPropertyType::value != type) { return createInvalidPropertyView>( - MetadataPropertyViewStatus::ErrorTypeMismatch); + PropertyTablePropertyViewStatus::ErrorTypeMismatch); } const PropertyComponentType componentType = @@ -1087,30 +1088,32 @@ class MetadataPropertyTableView { classProperty.componentType.value_or("")); if (TypeToPropertyType::component != componentType) { return createInvalidPropertyView>( - MetadataPropertyViewStatus::ErrorComponentTypeMismatch); + PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); } gsl::span values; auto status = getBufferSafe(propertyTableProperty.values, values); - if (status != MetadataPropertyViewStatus::Valid) { + if (status != PropertyTablePropertyViewStatus::Valid) { return createInvalidPropertyView>(status); } if (values.size() % sizeof(T) != 0) { return createInvalidPropertyView>( - MetadataPropertyViewStatus:: + PropertyTablePropertyViewStatus:: ErrorBufferViewSizeNotDivisibleByTypeSize); } const int64_t fixedLengthArrayCount = classProperty.count.value_or(0); if (fixedLengthArrayCount > 0 && propertyTableProperty.arrayOffsets >= 0) { return createInvalidPropertyView>( - MetadataPropertyViewStatus::ErrorArrayCountAndOffsetBufferCoexist); + PropertyTablePropertyViewStatus:: + ErrorArrayCountAndOffsetBufferCoexist); } if (fixedLengthArrayCount <= 0 && propertyTableProperty.arrayOffsets < 0) { return createInvalidPropertyView>( - MetadataPropertyViewStatus::ErrorArrayCountAndOffsetBufferDontExist); + PropertyTablePropertyViewStatus:: + ErrorArrayCountAndOffsetBufferDontExist); } // Handle fixed-length arrays @@ -1128,12 +1131,12 @@ class MetadataPropertyTableView { if (values.size() < maxRequiredBytes) { return createInvalidPropertyView>( - MetadataPropertyViewStatus:: + PropertyTablePropertyViewStatus:: ErrorBufferViewSizeDoesNotMatchPropertyTableCount); } - return MetadataPropertyView>( - MetadataPropertyViewStatus::Valid, + return PropertyTablePropertyView>( + PropertyTablePropertyViewStatus::Valid, values, gsl::span(), gsl::span(), @@ -1150,7 +1153,7 @@ class MetadataPropertyTableView { propertyTableProperty.arrayOffsetType); if (arrayOffsetType == PropertyComponentType::None) { return createInvalidPropertyView>( - MetadataPropertyViewStatus::ErrorInvalidArrayOffsetType); + PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); } constexpr bool checkBitsSize = IsMetadataBoolean::value; @@ -1162,12 +1165,12 @@ class MetadataPropertyTableView { static_cast(_pPropertyTable->count), checkBitsSize, arrayOffsets); - if (status != MetadataPropertyViewStatus::Valid) { + if (status != PropertyTablePropertyViewStatus::Valid) { return createInvalidPropertyView>(status); } - return MetadataPropertyView>( - MetadataPropertyViewStatus::Valid, + return PropertyTablePropertyView>( + PropertyTablePropertyViewStatus::Valid, values, arrayOffsets, gsl::span(), @@ -1178,17 +1181,17 @@ class MetadataPropertyTableView { classProperty.normalized); } - MetadataPropertyView> + PropertyTablePropertyView> getStringArrayPropertyValues( const ExtensionExtStructuralMetadataClassProperty& classProperty, const ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty) const; - MetadataPropertyViewStatus getBufferSafe( + PropertyTablePropertyViewStatus getBufferSafe( int32_t bufferView, gsl::span& buffer) const noexcept; - MetadataPropertyViewStatus getArrayOffsetsBufferSafe( + PropertyTablePropertyViewStatus getArrayOffsetsBufferSafe( int32_t arrayOffsetsBufferView, PropertyComponentType arrayOffsetType, size_t valuesBufferSize, @@ -1196,7 +1199,7 @@ class MetadataPropertyTableView { bool checkBitsSize, gsl::span& arrayOffsetsBuffer) const noexcept; - MetadataPropertyViewStatus getStringOffsetsBufferSafe( + PropertyTablePropertyViewStatus getStringOffsetsBufferSafe( int32_t stringOffsetsBufferView, PropertyComponentType stringOffsetType, size_t valuesBufferSize, @@ -1204,9 +1207,9 @@ class MetadataPropertyTableView { gsl::span& stringOffsetsBuffer) const noexcept; template - static MetadataPropertyView - createInvalidPropertyView(MetadataPropertyViewStatus invalidStatus) noexcept { - return MetadataPropertyView( + static PropertyTablePropertyView createInvalidPropertyView( + PropertyTablePropertyViewStatus invalidStatus) noexcept { + return PropertyTablePropertyView( invalidStatus, gsl::span(), 0, @@ -1216,7 +1219,7 @@ class MetadataPropertyTableView { const Model* _pModel; const ExtensionExtStructuralMetadataPropertyTable* _pPropertyTable; const ExtensionExtStructuralMetadataClass* _pClass; - MetadataPropertyTableViewStatus _status; + PropertyTableViewStatus _status; }; } // namespace StructuralMetadata diff --git a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTexturePropertyView.h b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTexturePropertyView.h new file mode 100644 index 000000000..7cd609a13 --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTexturePropertyView.h @@ -0,0 +1,245 @@ +#pragma once + +#include "CesiumGltf/ExtensionExtStructuralMetadataClassProperty.h" +#include "CesiumGltf/ExtensionExtStructuralMetadataPropertyTexture.h" +#include "CesiumGltf/ExtensionModelExtStructuralMetadata.h" +#include "CesiumGltf/Image.h" +#include "CesiumGltf/ImageCesium.h" +#include "CesiumGltf/Model.h" +#include "CesiumGltf/Sampler.h" +#include "CesiumGltf/Texture.h" + +#include +#include +#include + +namespace CesiumGltf { +namespace StructuralMetadata { +/** + * @brief Indicates the status of a property texture property view. + * + * The {@link PropertyTexturePropertyView} constructor always completes + * successfully. However it may not always reflect the actual content of the + * corresponding property texture property. This enumeration provides the + * reason. + */ +enum class PropertyTexturePropertyViewStatus { + /** + * @brief This view is valid and ready to use. + */ + Valid, + + /** + * @brief This view has not been initialized. + */ + ErrorUninitialized, + + /** + * @brief This property texture property has a texture index that does not + * exist in the glTF. + */ + ErrorInvalidTexture, + + /** + * @brief This property texture property has a texture sampler index that does + * not exist in the glTF. + */ + ErrorInvalidTextureSampler, + + /** + * @brief This property texture property has an image index that does not + * exist in the glTF. + */ + ErrorInvalidImage, + + /** + * @brief This property texture property points to an empty image. + */ + ErrorEmptyImage, + + /** + * @brief The channels of this property texture property are invalid. + * Channels must be in the range 0-3, with a minimum of one channel and a + * maximum of four. + */ + ErrorInvalidChannels +}; + +/** + * @brief The supported component types that can exist in property id textures. + */ +enum class PropertyTexturePropertyComponentType { + Uint8 + // TODO: add more types. Currently this is the only one outputted by stb, + // so change stb call to output more of the original types. +}; + +/** + * @brief The property texture property value for a pixel. This will contain + * four channels of the specified type. + * + * Only the first n components will be valid, where n is the number of channels + * in this property texture property. + * + * @tparam T The component type, must correspond to a valid + * {@link PropertyTexturePropertyComponentType}. + */ +template struct PropertyTexturePropertyValue { + T components[4]; +}; + +/** + * @brief A view of the data specified by a + * {@link ExtensionExtStructuralMetadataPropertyTextureProperty}. + * + * Provides utilities to sample the property texture property using texture + * coordinates. + */ +class PropertyTexturePropertyView { +public: + /** + * @brief Construct an uninitialized, invalid view. + */ + PropertyTexturePropertyView() noexcept; + + /** + * @brief Construct a view of the data specified by the given property texture + * property. Assumes the model has already been validated by + * PropertyTextureView. + * + * @param model The glTF in which to look for the data specified by the + * property texture property. + * @param classProperty The property description. + * @param propertyTextureProperty The property texture property + */ + PropertyTexturePropertyView( + const Model& model, + const ExtensionExtStructuralMetadataClassProperty& classProperty, + const ExtensionExtStructuralMetadataPropertyTextureProperty& + propertyTextureProperty) noexcept; + + /** + * @brief Gets the unswizzled property for the given texture coordinates. + * + * Will return -1s when the status is not Valid or when the templated + * component type doesn't match the image's channel byte-size. + * + * @tparam T The component type to use when interpreting the channels of the + * property's pixel value. + * @param u The u-component of the texture coordinates. Must be within + * [0.0, 1.0]. + * @param v The v-component of the texture coordinates. Must be within + * [0.0, 1.0]. + * @return The property at the nearest pixel to the texture coordinates. + */ + template + PropertyTexturePropertyValue + getProperty(double u, double v) const noexcept { + PropertyTexturePropertyValue property; + property.components[0] = -1; + property.components[1] = -1; + property.components[2] = -1; + property.components[3] = -1; + + if (this->_status != PropertyTexturePropertyViewStatus::Valid || + sizeof(T) != this->_pImage->bytesPerChannel) { + return property; + } + + // TODO: actually use the sampler?? + int64_t x = std::clamp( + std::llround(u * this->_pImage->width), + 0LL, + (long long)this->_pImage->width); + int64_t y = std::clamp( + std::llround(v * this->_pImage->height), + 0LL, + (long long)this->_pImage->height); + + int64_t pixelOffset = this->_pImage->bytesPerChannel * + this->_pImage->channels * + (y * this->_pImage->width + x); + const T* pRedChannel = reinterpret_cast( + this->_pImage->pixelData.data() + pixelOffset); + + for (size_t i = 0; i < this->_channels.size(); i++) { + const size_t channel = static_cast(this->_channels[i]); + property.components[channel] = *(pRedChannel + channel); + } + + return property; + } + + /** + * @brief Get the status of this view. + * + * If invalid, it will not be safe to sample from this view. + */ + PropertyTexturePropertyViewStatus status() const noexcept { + return this->_status; + } + + /** + * @brief Get the component type for this property. + */ + PropertyTexturePropertyComponentType getPropertyType() const noexcept { + return this->_type; + } + + /** + * @brief Get the count for this property. + * + * This is also how many channels a pixel value for this property will use. + */ + int64_t getCount() const noexcept { return this->_count; } + + /** + * @brief Get the texture coordinate set index for this property. + */ + int64_t getTextureCoordinateSetIndex() const noexcept { + return this->_textureCoordinateSetIndex; + } + + /** + * @brief Whether the component type for this property should be normalized. + */ + bool isNormalized() const noexcept { return this->_normalized; } + + /** + * @brief Get the image containing this property's data. + * + * This will be nullptr if the property texture property view runs into + * problems during construction. + */ + const ImageCesium* getImage() const noexcept { return this->_pImage; } + + /** + * @brief Gets the channels of this property texture property. + */ + const std::vector& getChannels() const noexcept { + return this->_channels; + } + + /** + * @brief Gets this property's channels as a swizzle string. + */ + const std::string& getSwizzle() const noexcept { return this->_swizzle; } + +private: + PropertyTexturePropertyViewStatus _status; + const ExtensionExtStructuralMetadataClassProperty* _pClassProperty; + const ExtensionExtStructuralMetadataPropertyTextureProperty* + _pPropertyTextureProperty; + + const Sampler* _pSampler; + const ImageCesium* _pImage; + int64_t _textureCoordinateSetIndex; + std::vector _channels; + std::string _swizzle; + PropertyTexturePropertyComponentType _type; + int64_t _count; + bool _normalized; +}; + +} // namespace StructuralMetadata +} // namespace CesiumGltf diff --git a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTextureView.h b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTextureView.h new file mode 100644 index 000000000..9d053357e --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTextureView.h @@ -0,0 +1,124 @@ +#pragma once + +#include "CesiumGltf/ExtensionExtStructuralMetadataClass.h" +#include "CesiumGltf/ExtensionExtStructuralMetadataClassProperty.h" +#include "CesiumGltf/ExtensionExtStructuralMetadataPropertyTexture.h" +#include "CesiumGltf/ExtensionModelExtStructuralMetadata.h" +#include "CesiumGltf/StructuralMetadataPropertyTexturePropertyView.h" +#include "CesiumGltf/Texture.h" +#include "CesiumGltf/TextureAccessor.h" +#include "Image.h" +#include "ImageCesium.h" +#include "Model.h" + +namespace CesiumGltf { +namespace StructuralMetadata { + +/** + * @brief Indicates the status of a property texture view. + * + * The {@link PropertyTextureView} constructor always completes successfully. + * However it may not always reflect the actual content of the + * {@link ExtensionExtStructuralMetadataPropertyTexture}. This enumeration provides the reason. + */ +enum class PropertyTextureViewStatus { + /** + * @brief This property texture view is valid and ready to use. + */ + Valid, + + /** + * @brief This property texture view is not initialized. + */ + ErrorUninitialized, + + /** + * @brief The glTF is missing the EXT_structural_metadata extension. + */ + ErrorMissingMetadataExtension, + + /** + * @brief The glTF EXT_structural_metadata extension doesn't contain a schema. + */ + ErrorMissingSchema, + + /** + * @brief The property texture's specified class could not be found in the + * extension. + */ + ErrorClassNotFound, + + /** + * @brief A property name specified in the property texture could not be found + * in the class. + */ + ErrorClassPropertyNotFound, + + /** + * @brief A property view for one of this property texture's properties failed + * to initialize successfully. Look for the invalid property view's status to + * find why it failed. + */ + ErrorInvalidPropertyView +}; + +/** + * @brief A view on the {@link ExtensionExtStructuralMetadataPropertyTexture}. + * + * Provides access to views on the property texture properties. + */ +class PropertyTextureView { +public: + /** + * @brief Construct an uninitialized, invalid property texture view. + */ + PropertyTextureView() noexcept; + + /** + * @brief Construct a view for the property texture. + * + * @param model The glTF in which to look for the property texture's data. + * @param propertyTexture The property texture to create a view for. + */ + PropertyTextureView( + const Model& model, + const ExtensionExtStructuralMetadataPropertyTexture& + propertyTexture) noexcept; + + /** + * @brief Gets the status of this property texture view. + * + * Indicates whether the view accurately reflects the property texture's data, + * or whether an error occurred. + */ + PropertyTextureViewStatus status() const noexcept { return this->_status; } + + /** + * @brief Finds the {@link ExtensionExtStructuralMetadataClassProperty} that + * describes the type information of the property with the specified name. + * @param propertyName The name of the property to retrieve the class for. + * @return A pointer to the {@link ExtensionExtStructuralMetadataClassProperty}. + * Return nullptr if the PropertyTextureView is invalid or if no class + * property was found. + */ + const ExtensionExtStructuralMetadataClassProperty* + getClassProperty(const std::string& propertyName) const; + + /** + * @brief Get the views for this property texture's properties. + */ + const std::unordered_map& + getProperties() const noexcept { + return this->_propertyViews; + } + +private: + const Model* _pModel; + const ExtensionExtStructuralMetadataPropertyTexture* _pPropertyTexture; + const ExtensionExtStructuralMetadataClass* _pClass; + std::unordered_map _propertyViews; + PropertyTextureViewStatus _status; +}; + +} // namespace StructuralMetadata +} // namespace CesiumGltf diff --git a/CesiumGltf/src/StructuralMetadataPropertyTableView.cpp b/CesiumGltf/src/StructuralMetadataPropertyTableView.cpp index bf6bdfd18..c1012e1e5 100644 --- a/CesiumGltf/src/StructuralMetadataPropertyTableView.cpp +++ b/CesiumGltf/src/StructuralMetadataPropertyTableView.cpp @@ -4,21 +4,21 @@ namespace CesiumGltf { namespace StructuralMetadata { template -static MetadataPropertyViewStatus checkOffsetsBuffer( +static PropertyTablePropertyViewStatus checkOffsetsBuffer( const gsl::span& offsetBuffer, size_t valueBufferSize, size_t instanceCount, bool checkBitSize, - MetadataPropertyViewStatus offsetsNotSortedError, - MetadataPropertyViewStatus offsetOutOfBoundsError) noexcept { + PropertyTablePropertyViewStatus offsetsNotSortedError, + PropertyTablePropertyViewStatus offsetOutOfBoundsError) noexcept { if (offsetBuffer.size() % sizeof(T) != 0) { - return MetadataPropertyViewStatus:: + return PropertyTablePropertyViewStatus:: ErrorBufferViewSizeNotDivisibleByTypeSize; } const size_t size = offsetBuffer.size() / sizeof(T); if (size != instanceCount + 1) { - return MetadataPropertyViewStatus:: + return PropertyTablePropertyViewStatus:: ErrorBufferViewSizeDoesNotMatchPropertyTableCount; } @@ -34,21 +34,21 @@ static MetadataPropertyViewStatus checkOffsetsBuffer( if (checkBitSize) { if (offsetValues.back() / 8 <= valueBufferSize) { - return MetadataPropertyViewStatus::Valid; + return PropertyTablePropertyViewStatus::Valid; } return offsetOutOfBoundsError; } if (offsetValues.back() <= valueBufferSize) { - return MetadataPropertyViewStatus::Valid; + return PropertyTablePropertyViewStatus::Valid; } return offsetOutOfBoundsError; } template -static MetadataPropertyViewStatus checkStringAndArrayOffsetsBuffers( +static PropertyTablePropertyViewStatus checkStringAndArrayOffsetsBuffers( const gsl::span& arrayOffsets, const gsl::span& stringOffsets, size_t valueBufferSize, @@ -59,10 +59,10 @@ static MetadataPropertyViewStatus checkStringAndArrayOffsetsBuffers( stringOffsets.size(), propertyTableCount, false, - MetadataPropertyViewStatus::ErrorArrayOffsetsNotSorted, - MetadataPropertyViewStatus::ErrorArrayOffsetOutOfBounds); + PropertyTablePropertyViewStatus::ErrorArrayOffsetsNotSorted, + PropertyTablePropertyViewStatus::ErrorArrayOffsetOutOfBounds); - if (status != MetadataPropertyViewStatus::Valid) { + if (status != PropertyTablePropertyViewStatus::Valid) { return status; } @@ -75,38 +75,38 @@ static MetadataPropertyViewStatus checkStringAndArrayOffsetsBuffers( valueBufferSize, pValue[propertyTableCount] / sizeof(T), false, - MetadataPropertyViewStatus::ErrorStringOffsetsNotSorted, - MetadataPropertyViewStatus::ErrorStringOffsetOutOfBounds); + PropertyTablePropertyViewStatus::ErrorStringOffsetsNotSorted, + PropertyTablePropertyViewStatus::ErrorStringOffsetOutOfBounds); case PropertyComponentType::Uint16: return checkOffsetsBuffer( stringOffsets, valueBufferSize, pValue[propertyTableCount] / sizeof(T), false, - MetadataPropertyViewStatus::ErrorStringOffsetsNotSorted, - MetadataPropertyViewStatus::ErrorStringOffsetOutOfBounds); + PropertyTablePropertyViewStatus::ErrorStringOffsetsNotSorted, + PropertyTablePropertyViewStatus::ErrorStringOffsetOutOfBounds); case PropertyComponentType::Uint32: return checkOffsetsBuffer( stringOffsets, valueBufferSize, pValue[propertyTableCount] / sizeof(T), false, - MetadataPropertyViewStatus::ErrorStringOffsetsNotSorted, - MetadataPropertyViewStatus::ErrorStringOffsetOutOfBounds); + PropertyTablePropertyViewStatus::ErrorStringOffsetsNotSorted, + PropertyTablePropertyViewStatus::ErrorStringOffsetOutOfBounds); case PropertyComponentType::Uint64: return checkOffsetsBuffer( stringOffsets, valueBufferSize, pValue[propertyTableCount] / sizeof(T), false, - MetadataPropertyViewStatus::ErrorStringOffsetsNotSorted, - MetadataPropertyViewStatus::ErrorStringOffsetOutOfBounds); + PropertyTablePropertyViewStatus::ErrorStringOffsetsNotSorted, + PropertyTablePropertyViewStatus::ErrorStringOffsetOutOfBounds); default: - return MetadataPropertyViewStatus::ErrorInvalidStringOffsetType; + return PropertyTablePropertyViewStatus::ErrorInvalidStringOffsetType; } } -MetadataPropertyTableView::MetadataPropertyTableView( +PropertyTableView::PropertyTableView( const Model& model, const ExtensionExtStructuralMetadataPropertyTable& propertyTable) : _pModel{&model}, @@ -116,15 +116,14 @@ MetadataPropertyTableView::MetadataPropertyTableView( const ExtensionModelExtStructuralMetadata* pMetadata = model.getExtension(); if (!pMetadata) { - _status = - MetadataPropertyTableViewStatus::ErrorNoStructuralMetadataExtension; + _status = PropertyTableViewStatus::ErrorMissingMetadataExtension; return; } const std::optional& schema = pMetadata->schema; if (!schema) { - _status = MetadataPropertyTableViewStatus::ErrorNoSchema; + _status = PropertyTableViewStatus::ErrorMissingSchema; return; } @@ -134,15 +133,14 @@ MetadataPropertyTableView::MetadataPropertyTableView( } if (!_pClass) { - _status = - MetadataPropertyTableViewStatus::ErrorPropertyTableClassDoesNotExist; + _status = PropertyTableViewStatus::ErrorClassNotFound; } } const ExtensionExtStructuralMetadataClassProperty* -MetadataPropertyTableView::getClassProperty( +PropertyTableView::getClassProperty( const std::string& propertyName) const { - if (_status != MetadataPropertyTableViewStatus::Valid) { + if (_status != PropertyTableViewStatus::Valid) { return nullptr; } @@ -154,7 +152,7 @@ MetadataPropertyTableView::getClassProperty( return &propertyIter->second; } -MetadataPropertyViewStatus MetadataPropertyTableView::getBufferSafe( +PropertyTablePropertyViewStatus PropertyTableView::getBufferSafe( int32_t bufferViewIdx, gsl::span& buffer) const noexcept { buffer = {}; @@ -162,36 +160,36 @@ MetadataPropertyViewStatus MetadataPropertyTableView::getBufferSafe( const BufferView* pBufferView = _pModel->getSafe(&_pModel->bufferViews, bufferViewIdx); if (!pBufferView) { - return MetadataPropertyViewStatus::ErrorInvalidValueBufferView; + return PropertyTablePropertyViewStatus::ErrorInvalidValueBufferView; } const Buffer* pBuffer = _pModel->getSafe(&_pModel->buffers, pBufferView->buffer); if (!pBuffer) { - return MetadataPropertyViewStatus::ErrorInvalidValueBuffer; + return PropertyTablePropertyViewStatus::ErrorInvalidValueBuffer; } if (pBufferView->byteOffset + pBufferView->byteLength > static_cast(pBuffer->cesium.data.size())) { - return MetadataPropertyViewStatus::ErrorBufferViewOutOfBounds; + return PropertyTablePropertyViewStatus::ErrorBufferViewOutOfBounds; } buffer = gsl::span( pBuffer->cesium.data.data() + pBufferView->byteOffset, static_cast(pBufferView->byteLength)); - return MetadataPropertyViewStatus::Valid; + return PropertyTablePropertyViewStatus::Valid; } -MetadataPropertyViewStatus MetadataPropertyTableView::getArrayOffsetsBufferSafe( +PropertyTablePropertyViewStatus PropertyTableView::getArrayOffsetsBufferSafe( int32_t arrayOffsetsBufferView, PropertyComponentType arrayOffsetType, size_t valueBufferSize, size_t propertyTableCount, bool checkBitsSize, gsl::span& arrayOffsetsBuffer) const noexcept { - const MetadataPropertyViewStatus bufferStatus = + const PropertyTablePropertyViewStatus bufferStatus = getBufferSafe(arrayOffsetsBufferView, arrayOffsetsBuffer); - if (bufferStatus != MetadataPropertyViewStatus::Valid) { + if (bufferStatus != PropertyTablePropertyViewStatus::Valid) { return bufferStatus; } @@ -202,47 +200,47 @@ MetadataPropertyViewStatus MetadataPropertyTableView::getArrayOffsetsBufferSafe( valueBufferSize, propertyTableCount, checkBitsSize, - MetadataPropertyViewStatus::ErrorArrayOffsetsNotSorted, - MetadataPropertyViewStatus::ErrorArrayOffsetOutOfBounds); + PropertyTablePropertyViewStatus::ErrorArrayOffsetsNotSorted, + PropertyTablePropertyViewStatus::ErrorArrayOffsetOutOfBounds); case PropertyComponentType::Uint16: return checkOffsetsBuffer( arrayOffsetsBuffer, valueBufferSize, propertyTableCount, checkBitsSize, - MetadataPropertyViewStatus::ErrorArrayOffsetsNotSorted, - MetadataPropertyViewStatus::ErrorArrayOffsetOutOfBounds); + PropertyTablePropertyViewStatus::ErrorArrayOffsetsNotSorted, + PropertyTablePropertyViewStatus::ErrorArrayOffsetOutOfBounds); case PropertyComponentType::Uint32: return checkOffsetsBuffer( arrayOffsetsBuffer, valueBufferSize, propertyTableCount, checkBitsSize, - MetadataPropertyViewStatus::ErrorArrayOffsetsNotSorted, - MetadataPropertyViewStatus::ErrorArrayOffsetOutOfBounds); + PropertyTablePropertyViewStatus::ErrorArrayOffsetsNotSorted, + PropertyTablePropertyViewStatus::ErrorArrayOffsetOutOfBounds); case PropertyComponentType::Uint64: return checkOffsetsBuffer( arrayOffsetsBuffer, valueBufferSize, propertyTableCount, checkBitsSize, - MetadataPropertyViewStatus::ErrorArrayOffsetsNotSorted, - MetadataPropertyViewStatus::ErrorArrayOffsetOutOfBounds); + PropertyTablePropertyViewStatus::ErrorArrayOffsetsNotSorted, + PropertyTablePropertyViewStatus::ErrorArrayOffsetOutOfBounds); default: - return MetadataPropertyViewStatus::ErrorInvalidArrayOffsetType; + return PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType; } } -MetadataPropertyViewStatus -MetadataPropertyTableView::getStringOffsetsBufferSafe( +PropertyTablePropertyViewStatus +PropertyTableView::getStringOffsetsBufferSafe( int32_t stringOffsetsBufferView, PropertyComponentType stringOffsetType, size_t valueBufferSize, size_t propertyTableCount, gsl::span& stringOffsetsBuffer) const noexcept { - const MetadataPropertyViewStatus bufferStatus = + const PropertyTablePropertyViewStatus bufferStatus = getBufferSafe(stringOffsetsBufferView, stringOffsetsBuffer); - if (bufferStatus != MetadataPropertyViewStatus::Valid) { + if (bufferStatus != PropertyTablePropertyViewStatus::Valid) { return bufferStatus; } @@ -253,56 +251,56 @@ MetadataPropertyTableView::getStringOffsetsBufferSafe( valueBufferSize, propertyTableCount, false, - MetadataPropertyViewStatus::ErrorStringOffsetsNotSorted, - MetadataPropertyViewStatus::ErrorStringOffsetOutOfBounds); + PropertyTablePropertyViewStatus::ErrorStringOffsetsNotSorted, + PropertyTablePropertyViewStatus::ErrorStringOffsetOutOfBounds); case PropertyComponentType::Uint16: return checkOffsetsBuffer( stringOffsetsBuffer, valueBufferSize, propertyTableCount, false, - MetadataPropertyViewStatus::ErrorStringOffsetsNotSorted, - MetadataPropertyViewStatus::ErrorStringOffsetOutOfBounds); + PropertyTablePropertyViewStatus::ErrorStringOffsetsNotSorted, + PropertyTablePropertyViewStatus::ErrorStringOffsetOutOfBounds); case PropertyComponentType::Uint32: return checkOffsetsBuffer( stringOffsetsBuffer, valueBufferSize, propertyTableCount, false, - MetadataPropertyViewStatus::ErrorStringOffsetsNotSorted, - MetadataPropertyViewStatus::ErrorStringOffsetOutOfBounds); + PropertyTablePropertyViewStatus::ErrorStringOffsetsNotSorted, + PropertyTablePropertyViewStatus::ErrorStringOffsetOutOfBounds); case PropertyComponentType::Uint64: return checkOffsetsBuffer( stringOffsetsBuffer, valueBufferSize, propertyTableCount, false, - MetadataPropertyViewStatus::ErrorStringOffsetsNotSorted, - MetadataPropertyViewStatus::ErrorStringOffsetOutOfBounds); + PropertyTablePropertyViewStatus::ErrorStringOffsetsNotSorted, + PropertyTablePropertyViewStatus::ErrorStringOffsetOutOfBounds); default: - return MetadataPropertyViewStatus::ErrorInvalidStringOffsetType; + return PropertyTablePropertyViewStatus::ErrorInvalidStringOffsetType; } } -MetadataPropertyView -MetadataPropertyTableView::getStringPropertyValues( +PropertyTablePropertyView +PropertyTableView::getStringPropertyValues( const ExtensionExtStructuralMetadataClassProperty& classProperty, const ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty) const { if (classProperty.array) { return createInvalidPropertyView( - MetadataPropertyViewStatus::ErrorArrayTypeMismatch); + PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); } if (classProperty.type != ExtensionExtStructuralMetadataClassProperty::Type::STRING) { return createInvalidPropertyView( - MetadataPropertyViewStatus::ErrorTypeMismatch); + PropertyTablePropertyViewStatus::ErrorTypeMismatch); } gsl::span values; auto status = getBufferSafe(propertyTableProperty.values, values); - if (status != MetadataPropertyViewStatus::Valid) { + if (status != PropertyTablePropertyViewStatus::Valid) { return createInvalidPropertyView(status); } @@ -311,7 +309,7 @@ MetadataPropertyTableView::getStringPropertyValues( propertyTableProperty.stringOffsetType); if (offsetType == PropertyComponentType::None) { return createInvalidPropertyView( - MetadataPropertyViewStatus::ErrorInvalidStringOffsetType); + PropertyTablePropertyViewStatus::ErrorInvalidStringOffsetType); } gsl::span stringOffsets; @@ -321,12 +319,12 @@ MetadataPropertyTableView::getStringPropertyValues( values.size(), static_cast(_pPropertyTable->count), stringOffsets); - if (status != MetadataPropertyViewStatus::Valid) { + if (status != PropertyTablePropertyViewStatus::Valid) { return createInvalidPropertyView(status); } - return MetadataPropertyView( - MetadataPropertyViewStatus::Valid, + return PropertyTablePropertyView( + PropertyTablePropertyViewStatus::Valid, values, gsl::span(), stringOffsets, @@ -337,25 +335,25 @@ MetadataPropertyTableView::getStringPropertyValues( classProperty.normalized); } -MetadataPropertyView> -MetadataPropertyTableView::getStringArrayPropertyValues( +PropertyTablePropertyView> +PropertyTableView::getStringArrayPropertyValues( const ExtensionExtStructuralMetadataClassProperty& classProperty, const ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty) const { if (!classProperty.array) { return createInvalidPropertyView>( - MetadataPropertyViewStatus::ErrorArrayTypeMismatch); + PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); } if (classProperty.type != ExtensionExtStructuralMetadataClassProperty::Type::STRING) { return createInvalidPropertyView>( - MetadataPropertyViewStatus::ErrorTypeMismatch); + PropertyTablePropertyViewStatus::ErrorTypeMismatch); } gsl::span values; auto status = getBufferSafe(propertyTableProperty.values, values); - if (status != MetadataPropertyViewStatus::Valid) { + if (status != PropertyTablePropertyViewStatus::Valid) { return createInvalidPropertyView>( status); } @@ -364,12 +362,12 @@ MetadataPropertyTableView::getStringArrayPropertyValues( const int64_t fixedLengthArrayCount = classProperty.count.value_or(0); if (fixedLengthArrayCount > 0 && propertyTableProperty.arrayOffsets >= 0) { return createInvalidPropertyView>( - MetadataPropertyViewStatus::ErrorArrayCountAndOffsetBufferCoexist); + PropertyTablePropertyViewStatus::ErrorArrayCountAndOffsetBufferCoexist); } if (fixedLengthArrayCount <= 0 && propertyTableProperty.arrayOffsets < 0) { return createInvalidPropertyView>( - MetadataPropertyViewStatus::ErrorArrayCountAndOffsetBufferDontExist); + PropertyTablePropertyViewStatus::ErrorArrayCountAndOffsetBufferDontExist); } // Get string offset type @@ -378,12 +376,12 @@ MetadataPropertyTableView::getStringArrayPropertyValues( propertyTableProperty.stringOffsetType); if (stringOffsetType == PropertyComponentType::None) { return createInvalidPropertyView>( - MetadataPropertyViewStatus::ErrorInvalidStringOffsetType); + PropertyTablePropertyViewStatus::ErrorInvalidStringOffsetType); } if (propertyTableProperty.stringOffsets < 0) { return createInvalidPropertyView>( - MetadataPropertyViewStatus::ErrorInvalidStringOffsetBufferView); + PropertyTablePropertyViewStatus::ErrorInvalidStringOffsetBufferView); } // Handle fixed-length arrays @@ -395,13 +393,13 @@ MetadataPropertyTableView::getStringArrayPropertyValues( values.size(), static_cast(_pPropertyTable->count * fixedLengthArrayCount), stringOffsets); - if (status != MetadataPropertyViewStatus::Valid) { + if (status != PropertyTablePropertyViewStatus::Valid) { return createInvalidPropertyView>( status); } - return MetadataPropertyView>( - MetadataPropertyViewStatus::Valid, + return PropertyTablePropertyView>( + PropertyTablePropertyViewStatus::Valid, values, gsl::span(), stringOffsets, @@ -418,25 +416,25 @@ MetadataPropertyTableView::getStringArrayPropertyValues( propertyTableProperty.arrayOffsetType); if (arrayOffsetType == PropertyComponentType::None) { return createInvalidPropertyView>( - MetadataPropertyViewStatus::ErrorInvalidArrayOffsetType); + PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); } if (propertyTableProperty.arrayOffsets < 0) { return createInvalidPropertyView>( - MetadataPropertyViewStatus::ErrorInvalidArrayOffsetBufferView); + PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetBufferView); } // Handle variable-length arrays gsl::span stringOffsets; status = getBufferSafe(propertyTableProperty.stringOffsets, stringOffsets); - if (status != MetadataPropertyViewStatus::Valid) { + if (status != PropertyTablePropertyViewStatus::Valid) { return createInvalidPropertyView>( status); } gsl::span arrayOffsets; status = getBufferSafe(propertyTableProperty.arrayOffsets, arrayOffsets); - if (status != MetadataPropertyViewStatus::Valid) { + if (status != PropertyTablePropertyViewStatus::Valid) { return createInvalidPropertyView>( status); } @@ -475,17 +473,17 @@ MetadataPropertyTableView::getStringArrayPropertyValues( static_cast(_pPropertyTable->count)); break; default: - status = MetadataPropertyViewStatus::ErrorInvalidArrayOffsetType; + status = PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType; break; } - if (status != MetadataPropertyViewStatus::Valid) { + if (status != PropertyTablePropertyViewStatus::Valid) { return createInvalidPropertyView>( status); } - return MetadataPropertyView>( - MetadataPropertyViewStatus::Valid, + return PropertyTablePropertyView>( + PropertyTablePropertyViewStatus::Valid, values, arrayOffsets, stringOffsets, diff --git a/CesiumGltf/src/StructuralMetadataPropertyTexturePropertyView.cpp b/CesiumGltf/src/StructuralMetadataPropertyTexturePropertyView.cpp new file mode 100644 index 000000000..316341124 --- /dev/null +++ b/CesiumGltf/src/StructuralMetadataPropertyTexturePropertyView.cpp @@ -0,0 +1,102 @@ + +#include "CesiumGltf/StructuralMetadataPropertyTexturePropertyView.h" + +namespace CesiumGltf { +namespace StructuralMetadata { + +PropertyTexturePropertyView::PropertyTexturePropertyView() noexcept + : _status(PropertyTexturePropertyViewStatus::ErrorUninitialized), + _pClassProperty(nullptr), + _pPropertyTextureProperty(nullptr), + _pSampler(nullptr), + _pImage(nullptr), + _textureCoordinateSetIndex(-1), + _channels(), + _swizzle(""), + _type(PropertyTexturePropertyComponentType::Uint8), + _count(0), + _normalized(false) {} + +PropertyTexturePropertyView::PropertyTexturePropertyView( + const Model& model, + const ExtensionExtStructuralMetadataClassProperty& classProperty, + const ExtensionExtStructuralMetadataPropertyTextureProperty& + propertyTextureProperty) noexcept + : _status(PropertyTexturePropertyViewStatus::ErrorUninitialized), + _pClassProperty(&classProperty), + _pPropertyTextureProperty(&propertyTextureProperty), + _pSampler(nullptr), + _pImage(nullptr), + _textureCoordinateSetIndex(propertyTextureProperty.texCoord), + _channels(), + _swizzle(""), + _type(PropertyTexturePropertyComponentType::Uint8), + _count(0), + _normalized(false) { + const int64_t index = _pPropertyTextureProperty->index; + if (index < 0 || static_cast(index) >= model.textures.size()) { + this->_status = PropertyTexturePropertyViewStatus::ErrorInvalidTexture; + return; + } + + const Texture& texture = model.textures[static_cast(index)]; + if (texture.sampler < 0 || + static_cast(texture.sampler) >= model.samplers.size()) { + this->_status = + PropertyTexturePropertyViewStatus::ErrorInvalidTextureSampler; + return; + } + + this->_pSampler = &model.samplers[static_cast(texture.sampler)]; + + if (texture.source < 0 || + static_cast(texture.source) >= model.images.size()) { + this->_status = PropertyTexturePropertyViewStatus::ErrorInvalidImage; + return; + } + + this->_pImage = &model.images[static_cast(texture.source)].cesium; + if (this->_pImage->width < 1 || this->_pImage->height < 1) { + this->_status = PropertyTexturePropertyViewStatus::ErrorEmptyImage; + return; + } + + // TODO: support more types + // this->_type = ... + + this->_count = + this->_pClassProperty->count ? *this->_pClassProperty->count : 1; + this->_normalized = this->_pClassProperty->normalized; + + const std::vector& channels = _pPropertyTextureProperty->channels; + if (channels.size() == 0 || channels.size() > 4 || + channels.size() > static_cast(this->_pImage->channels) || + channels.size() != static_cast(this->_count)) { + this->_status = PropertyTexturePropertyViewStatus::ErrorInvalidChannels; + return; + } + + for (size_t i = 0; i < channels.size(); ++i) { + switch (channels[i]) { + case 0: + this->_swizzle += "r"; + break; + case 1: + this->_swizzle += "g"; + break; + case 2: + this->_swizzle += "b"; + break; + case 3: + this->_swizzle += "a"; + break; + default: + this->_status = PropertyTexturePropertyViewStatus::ErrorInvalidChannels; + return; + } + } + + this->_status = PropertyTexturePropertyViewStatus::Valid; +} +} // namespace StructuralMetadata +} // namespace CesiumGltf diff --git a/CesiumGltf/src/StructuralMetadataPropertyTextureView.cpp b/CesiumGltf/src/StructuralMetadataPropertyTextureView.cpp new file mode 100644 index 000000000..507aa9e0c --- /dev/null +++ b/CesiumGltf/src/StructuralMetadataPropertyTextureView.cpp @@ -0,0 +1,84 @@ +#include "CesiumGltf/StructuralMetadataPropertyTextureView.h" + +namespace CesiumGltf { +namespace StructuralMetadata { +PropertyTextureView::PropertyTextureView() noexcept + : _pModel(nullptr), + _pPropertyTexture(nullptr), + _pClass(nullptr), + _propertyViews(), + _status(PropertyTextureViewStatus::ErrorUninitialized) {} + +PropertyTextureView::PropertyTextureView( + const Model& model, + const ExtensionExtStructuralMetadataPropertyTexture& propertyTexture) noexcept + : _pModel(&model), + _pPropertyTexture(&propertyTexture), + _pClass(nullptr), + _propertyViews(), + _status(PropertyTextureViewStatus::ErrorUninitialized) { + + const ExtensionModelExtStructuralMetadata* pMetadata = + model.getExtension(); + + if (!pMetadata) { + this->_status = PropertyTextureViewStatus::ErrorMissingMetadataExtension; + return; + } + + if (!pMetadata->schema) { + this->_status = PropertyTextureViewStatus::ErrorMissingSchema; + return; + } + + const auto& classIt = + pMetadata->schema->classes.find(propertyTexture.classProperty); + if (classIt == pMetadata->schema->classes.end()) { + this->_status = PropertyTextureViewStatus::ErrorClassNotFound; + return; + } + + this->_pClass = &classIt->second; + + this->_propertyViews.reserve(propertyTexture.properties.size()); + for (const auto& property : propertyTexture.properties) { + auto classPropertyIt = this->_pClass->properties.find(property.first); + + if (classPropertyIt == this->_pClass->properties.end()) { + this->_status = PropertyTextureViewStatus::ErrorClassPropertyNotFound; + return; + } + + this->_propertyViews[property.first] = PropertyTexturePropertyView( + model, + classPropertyIt->second, + property.second); + } + + for (const auto& propertyView : this->_propertyViews) { + if (propertyView.second.status() != + PropertyTexturePropertyViewStatus::Valid) { + this->_status = PropertyTextureViewStatus::ErrorInvalidPropertyView; + return; + } + } + + this->_status = PropertyTextureViewStatus::Valid; +} + +const ExtensionExtStructuralMetadataClassProperty* +PropertyTextureView::getClassProperty(const std::string& propertyName) const { + if (_status != PropertyTextureViewStatus::Valid) { + return nullptr; + } + + auto propertyIter = _pClass->properties.find(propertyName); + if (propertyIter == _pClass->properties.end()) { + return nullptr; + } + + return &propertyIter->second; +} + +} // namespace StructuralMetadata +} // namespace CesiumGltf diff --git a/CesiumGltf/test/TestStructuralMetadataPropertyView.cpp b/CesiumGltf/test/TestStructuralMetadataPropertyTablePropertyView.cpp similarity index 96% rename from CesiumGltf/test/TestStructuralMetadataPropertyView.cpp rename to CesiumGltf/test/TestStructuralMetadataPropertyTablePropertyView.cpp index 4c8103b08..d77e0a2a6 100644 --- a/CesiumGltf/test/TestStructuralMetadataPropertyView.cpp +++ b/CesiumGltf/test/TestStructuralMetadataPropertyTablePropertyView.cpp @@ -1,4 +1,4 @@ -#include "CesiumGltf/StructuralMetadataPropertyView.h" +#include "CesiumGltf/StructuralMetadataPropertyTablePropertyView.h" #include #include @@ -16,8 +16,8 @@ template static void checkNumeric(const std::vector& expected) { data.resize(expected.size() * sizeof(T)); std::memcpy(data.data(), expected.data(), data.size()); - MetadataPropertyView property( - MetadataPropertyViewStatus::Valid, + PropertyTablePropertyView property( + PropertyTablePropertyViewStatus::Valid, gsl::span(data.data(), data.size()), static_cast(expected.size()), false); @@ -46,8 +46,8 @@ static void checkVariableLengthArray( offsets.data(), offsets.size() * sizeof(OffsetType)); - MetadataPropertyView> property( - MetadataPropertyViewStatus::Valid, + PropertyTablePropertyView> property( + PropertyTablePropertyViewStatus::Valid, gsl::span(buffer.data(), buffer.size()), gsl::span(offsetBuffer.data(), offsetBuffer.size()), gsl::span(), @@ -78,8 +78,8 @@ static void checkFixedLengthArray( buffer.resize(data.size() * sizeof(T)); std::memcpy(buffer.data(), data.data(), data.size() * sizeof(T)); - MetadataPropertyView> property( - MetadataPropertyViewStatus::Valid, + PropertyTablePropertyView> property( + PropertyTablePropertyViewStatus::Valid, gsl::span(buffer.data(), buffer.size()), gsl::span(), gsl::span(), @@ -225,8 +225,8 @@ TEST_CASE("Check StructuralMetadata boolean property") { std::memcpy(data.data(), &val, sizeof(val)); size_t instanceCount = sizeof(unsigned long) * CHAR_BIT; - MetadataPropertyView property( - MetadataPropertyViewStatus::Valid, + PropertyTablePropertyView property( + PropertyTablePropertyViewStatus::Valid, gsl::span(data.data(), data.size()), static_cast(instanceCount), false); @@ -272,8 +272,8 @@ TEST_CASE("Check StructuralMetadata string property") { ¤tOffset, sizeof(uint32_t)); - MetadataPropertyView property( - MetadataPropertyViewStatus::Valid, + PropertyTablePropertyView property( + PropertyTablePropertyViewStatus::Valid, gsl::span(buffer.data(), buffer.size()), gsl::span(), gsl::span(offsetBuffer.data(), offsetBuffer.size()), @@ -831,8 +831,8 @@ TEST_CASE("Check StructuralMetadata fixed-length array of string") { ¤tStringOffset, sizeof(uint32_t)); - MetadataPropertyView> property( - MetadataPropertyViewStatus::Valid, + PropertyTablePropertyView> property( + PropertyTablePropertyViewStatus::Valid, gsl::span(buffer.data(), buffer.size()), gsl::span(), gsl::span(stringOffsets.data(), stringOffsets.size()), @@ -904,8 +904,8 @@ TEST_CASE( ¤tOffset, sizeof(uint32_t)); - MetadataPropertyView> property( - MetadataPropertyViewStatus::Valid, + PropertyTablePropertyView> property( + PropertyTablePropertyViewStatus::Valid, gsl::span(buffer.data(), buffer.size()), gsl::span( reinterpret_cast(arrayOffsets.data()), @@ -936,8 +936,8 @@ TEST_CASE("Check StructuralMetadata fixed-length boolean array property") { static_cast(0b11111010), static_cast(0b11100111)}; - MetadataPropertyView> property( - MetadataPropertyViewStatus::Valid, + PropertyTablePropertyView> property( + PropertyTablePropertyViewStatus::Valid, gsl::span(buffer.data(), buffer.size()), gsl::span(), gsl::span(), @@ -988,8 +988,8 @@ TEST_CASE("Check StructuralMetadata variable-length boolean array property") { std::vector offsetBuffer{0, 3, 12, 28}; - MetadataPropertyView> property( - MetadataPropertyViewStatus::Valid, + PropertyTablePropertyView> property( + PropertyTablePropertyViewStatus::Valid, gsl::span(buffer.data(), buffer.size()), gsl::span( reinterpret_cast(offsetBuffer.data()), diff --git a/CesiumGltf/test/TestStructuralMetadataPropertyTableView.cpp b/CesiumGltf/test/TestStructuralMetadataPropertyTableView.cpp index 95d148e1a..952f6d4cd 100644 --- a/CesiumGltf/test/TestStructuralMetadataPropertyTableView.cpp +++ b/CesiumGltf/test/TestStructuralMetadataPropertyTableView.cpp @@ -7,7 +7,8 @@ using namespace CesiumGltf; using namespace CesiumGltf::StructuralMetadata; -TEST_CASE("Test model without EXT_structural_metadata extension") { +TEST_CASE("Test PropertyTableView on model without EXT_structural_metadata " + "extension") { Model model; // Create an erroneously isolated property table. @@ -19,10 +20,9 @@ TEST_CASE("Test model without EXT_structural_metadata extension") { propertyTable.properties["TestClassProperty"]; propertyTableProperty.values = static_cast(0); - MetadataPropertyTableView view(model, propertyTable); + PropertyTableView view(model, propertyTable); REQUIRE( - view.status() == - MetadataPropertyTableViewStatus::ErrorNoStructuralMetadataExtension); + view.status() == PropertyTableViewStatus::ErrorMissingMetadataExtension); REQUIRE(view.size() == 0); const ExtensionExtStructuralMetadataClassProperty* classProperty = @@ -30,7 +30,7 @@ TEST_CASE("Test model without EXT_structural_metadata extension") { REQUIRE(!classProperty); } -TEST_CASE("Test model without metadata schema") { +TEST_CASE("Test PropertyTableView on model without metadata schema") { Model model; ExtensionModelExtStructuralMetadata& metadata = @@ -45,8 +45,8 @@ TEST_CASE("Test model without metadata schema") { propertyTable.properties["TestClassProperty"]; propertyTableProperty.values = static_cast(0); - MetadataPropertyTableView view(model, propertyTable); - REQUIRE(view.status() == MetadataPropertyTableViewStatus::ErrorNoSchema); + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::ErrorMissingSchema); REQUIRE(view.size() == 0); const ExtensionExtStructuralMetadataClassProperty* classProperty = @@ -78,10 +78,8 @@ TEST_CASE("Test property table with nonexistent class") { propertyTable.properties["TestClassProperty"]; propertyTableProperty.values = static_cast(0); - MetadataPropertyTableView view(model, propertyTable); - REQUIRE( - view.status() == - MetadataPropertyTableViewStatus::ErrorPropertyTableClassDoesNotExist); + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::ErrorClassNotFound); REQUIRE(view.size() == 0); const ExtensionExtStructuralMetadataClassProperty* classProperty = @@ -138,8 +136,8 @@ TEST_CASE("Test StructuralMetadata scalar property") { propertyTable.properties["TestClassProperty"]; propertyTableProperty.values = static_cast(valueBufferViewIndex); - MetadataPropertyTableView view(model, propertyTable); - REQUIRE(view.status() == MetadataPropertyTableViewStatus::Valid); + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); REQUIRE(view.size() == propertyTable.count); const ExtensionExtStructuralMetadataClassProperty* classProperty = @@ -155,9 +153,9 @@ TEST_CASE("Test StructuralMetadata scalar property") { REQUIRE(!classProperty->array); SECTION("Access correct type") { - MetadataPropertyView uint32Property = + PropertyTablePropertyView uint32Property = view.getPropertyView("TestClassProperty"); - REQUIRE(uint32Property.status() == MetadataPropertyViewStatus::Valid); + REQUIRE(uint32Property.status() == PropertyTablePropertyViewStatus::Valid); REQUIRE(uint32Property.size() > 0); for (int64_t i = 0; i < uint32Property.size(); ++i) { @@ -166,100 +164,103 @@ TEST_CASE("Test StructuralMetadata scalar property") { } SECTION("Access wrong type") { - MetadataPropertyView uvec3Invalid = + PropertyTablePropertyView uvec3Invalid = view.getPropertyView("TestClassProperty"); REQUIRE( - uvec3Invalid.status() == MetadataPropertyViewStatus::ErrorTypeMismatch); + uvec3Invalid.status() == + PropertyTablePropertyViewStatus::ErrorTypeMismatch); - MetadataPropertyView u32mat3x3Invalid = + PropertyTablePropertyView u32mat3x3Invalid = view.getPropertyView("TestClassProperty"); REQUIRE( u32mat3x3Invalid.status() == - MetadataPropertyViewStatus::ErrorTypeMismatch); + PropertyTablePropertyViewStatus::ErrorTypeMismatch); - MetadataPropertyView boolInvalid = + PropertyTablePropertyView boolInvalid = view.getPropertyView("TestClassProperty"); REQUIRE( - boolInvalid.status() == MetadataPropertyViewStatus::ErrorTypeMismatch); + boolInvalid.status() == + PropertyTablePropertyViewStatus::ErrorTypeMismatch); - MetadataPropertyView stringInvalid = + PropertyTablePropertyView stringInvalid = view.getPropertyView("TestClassProperty"); REQUIRE( stringInvalid.status() == - MetadataPropertyViewStatus::ErrorTypeMismatch); + PropertyTablePropertyViewStatus::ErrorTypeMismatch); } SECTION("Access wrong component type") { - MetadataPropertyView uint8Invalid = + PropertyTablePropertyView uint8Invalid = view.getPropertyView("TestClassProperty"); REQUIRE( uint8Invalid.status() == - MetadataPropertyViewStatus::ErrorComponentTypeMismatch); + PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); - MetadataPropertyView int32Invalid = + PropertyTablePropertyView int32Invalid = view.getPropertyView("TestClassProperty"); REQUIRE( int32Invalid.status() == - MetadataPropertyViewStatus::ErrorComponentTypeMismatch); + PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); - MetadataPropertyView uint64Invalid = + PropertyTablePropertyView uint64Invalid = view.getPropertyView("TestClassProperty"); REQUIRE( uint64Invalid.status() == - MetadataPropertyViewStatus::ErrorComponentTypeMismatch); + PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); } SECTION("Access incorrectly as array") { - MetadataPropertyView> uint32ArrayInvalid = + PropertyTablePropertyView> uint32ArrayInvalid = view.getPropertyView>("TestClassProperty"); REQUIRE( uint32ArrayInvalid.status() == - MetadataPropertyViewStatus::ErrorArrayTypeMismatch); + PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); } SECTION("Wrong buffer index") { model.bufferViews[valueBufferViewIndex].buffer = 2; - MetadataPropertyView uint32Property = + PropertyTablePropertyView uint32Property = view.getPropertyView("TestClassProperty"); REQUIRE( uint32Property.status() == - MetadataPropertyViewStatus::ErrorInvalidValueBuffer); + PropertyTablePropertyViewStatus::ErrorInvalidValueBuffer); } SECTION("Wrong buffer view index") { propertyTableProperty.values = -1; - MetadataPropertyView uint32Property = + PropertyTablePropertyView uint32Property = view.getPropertyView("TestClassProperty"); REQUIRE( uint32Property.status() == - MetadataPropertyViewStatus::ErrorInvalidValueBufferView); + PropertyTablePropertyViewStatus::ErrorInvalidValueBufferView); } SECTION("Buffer view points outside of the real buffer length") { model.buffers[valueBufferIndex].cesium.data.resize(12); - MetadataPropertyView uint32Property = + PropertyTablePropertyView uint32Property = view.getPropertyView("TestClassProperty"); REQUIRE( uint32Property.status() == - MetadataPropertyViewStatus::ErrorBufferViewOutOfBounds); + PropertyTablePropertyViewStatus::ErrorBufferViewOutOfBounds); } SECTION("Buffer view length isn't multiple of sizeof(T)") { model.bufferViews[valueBufferViewIndex].byteLength = 13; - MetadataPropertyView uint32Property = + PropertyTablePropertyView uint32Property = view.getPropertyView("TestClassProperty"); REQUIRE( uint32Property.status() == - MetadataPropertyViewStatus::ErrorBufferViewSizeNotDivisibleByTypeSize); + PropertyTablePropertyViewStatus:: + ErrorBufferViewSizeNotDivisibleByTypeSize); } SECTION("Buffer view length doesn't match with propertyTableCount") { model.bufferViews[valueBufferViewIndex].byteLength = 12; - MetadataPropertyView uint32Property = + PropertyTablePropertyView uint32Property = view.getPropertyView("TestClassProperty"); REQUIRE( uint32Property.status() == - MetadataPropertyViewStatus:: + PropertyTablePropertyViewStatus:: ErrorBufferViewSizeDoesNotMatchPropertyTableCount); } } @@ -317,8 +318,8 @@ TEST_CASE("Test StructuralMetadata vecN property") { propertyTable.properties["TestClassProperty"]; propertyTableProperty.values = static_cast(valueBufferViewIndex); - MetadataPropertyTableView view(model, propertyTable); - REQUIRE(view.status() == MetadataPropertyTableViewStatus::Valid); + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); REQUIRE(view.size() == propertyTable.count); const ExtensionExtStructuralMetadataClassProperty* classProperty = @@ -334,9 +335,9 @@ TEST_CASE("Test StructuralMetadata vecN property") { REQUIRE(!classProperty->array); SECTION("Access correct type") { - MetadataPropertyView ivec3Property = + PropertyTablePropertyView ivec3Property = view.getPropertyView("TestClassProperty"); - REQUIRE(ivec3Property.status() == MetadataPropertyViewStatus::Valid); + REQUIRE(ivec3Property.status() == PropertyTablePropertyViewStatus::Valid); REQUIRE(ivec3Property.size() > 0); for (int64_t i = 0; i < ivec3Property.size(); ++i) { @@ -345,106 +346,110 @@ TEST_CASE("Test StructuralMetadata vecN property") { } SECTION("Access wrong type") { - MetadataPropertyView int32Invalid = + PropertyTablePropertyView int32Invalid = view.getPropertyView("TestClassProperty"); REQUIRE( - int32Invalid.status() == MetadataPropertyViewStatus::ErrorTypeMismatch); + int32Invalid.status() == + PropertyTablePropertyViewStatus::ErrorTypeMismatch); - MetadataPropertyView ivec2Invalid = + PropertyTablePropertyView ivec2Invalid = view.getPropertyView("TestClassProperty"); REQUIRE( - ivec2Invalid.status() == MetadataPropertyViewStatus::ErrorTypeMismatch); + ivec2Invalid.status() == + PropertyTablePropertyViewStatus::ErrorTypeMismatch); - MetadataPropertyView i32mat3x3Invalid = + PropertyTablePropertyView i32mat3x3Invalid = view.getPropertyView("TestClassProperty"); REQUIRE( i32mat3x3Invalid.status() == - MetadataPropertyViewStatus::ErrorTypeMismatch); + PropertyTablePropertyViewStatus::ErrorTypeMismatch); - MetadataPropertyView boolInvalid = + PropertyTablePropertyView boolInvalid = view.getPropertyView("TestClassProperty"); REQUIRE( - boolInvalid.status() == MetadataPropertyViewStatus::ErrorTypeMismatch); + boolInvalid.status() == + PropertyTablePropertyViewStatus::ErrorTypeMismatch); - MetadataPropertyView stringInvalid = + PropertyTablePropertyView stringInvalid = view.getPropertyView("TestClassProperty"); REQUIRE( stringInvalid.status() == - MetadataPropertyViewStatus::ErrorTypeMismatch); + PropertyTablePropertyViewStatus::ErrorTypeMismatch); } SECTION("Access wrong component type") { - MetadataPropertyView u8vec3Invalid = + PropertyTablePropertyView u8vec3Invalid = view.getPropertyView("TestClassProperty"); REQUIRE( u8vec3Invalid.status() == - MetadataPropertyViewStatus::ErrorComponentTypeMismatch); + PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); - MetadataPropertyView i16vec3Invalid = + PropertyTablePropertyView i16vec3Invalid = view.getPropertyView("TestClassProperty"); REQUIRE( i16vec3Invalid.status() == - MetadataPropertyViewStatus::ErrorComponentTypeMismatch); + PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); - MetadataPropertyView vec3Invalid = + PropertyTablePropertyView vec3Invalid = view.getPropertyView("TestClassProperty"); REQUIRE( vec3Invalid.status() == - MetadataPropertyViewStatus::ErrorComponentTypeMismatch); + PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); } SECTION("Access incorrectly as array") { - MetadataPropertyView> ivec3ArrayInvalid = + PropertyTablePropertyView> ivec3ArrayInvalid = view.getPropertyView>( "TestClassProperty"); REQUIRE( ivec3ArrayInvalid.status() == - MetadataPropertyViewStatus::ErrorArrayTypeMismatch); + PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); } SECTION("Wrong buffer index") { model.bufferViews[valueBufferViewIndex].buffer = 2; - MetadataPropertyView ivec3Property = + PropertyTablePropertyView ivec3Property = view.getPropertyView("TestClassProperty"); REQUIRE( ivec3Property.status() == - MetadataPropertyViewStatus::ErrorInvalidValueBuffer); + PropertyTablePropertyViewStatus::ErrorInvalidValueBuffer); } SECTION("Wrong buffer view index") { propertyTableProperty.values = -1; - MetadataPropertyView ivec3Property = + PropertyTablePropertyView ivec3Property = view.getPropertyView("TestClassProperty"); REQUIRE( ivec3Property.status() == - MetadataPropertyViewStatus::ErrorInvalidValueBufferView); + PropertyTablePropertyViewStatus::ErrorInvalidValueBufferView); } SECTION("Buffer view points outside of the real buffer length") { model.buffers[valueBufferIndex].cesium.data.resize(12); - MetadataPropertyView ivec3Property = + PropertyTablePropertyView ivec3Property = view.getPropertyView("TestClassProperty"); REQUIRE( ivec3Property.status() == - MetadataPropertyViewStatus::ErrorBufferViewOutOfBounds); + PropertyTablePropertyViewStatus::ErrorBufferViewOutOfBounds); } SECTION("Buffer view length isn't multiple of sizeof(T)") { model.bufferViews[valueBufferViewIndex].byteLength = 11; - MetadataPropertyView ivec3Property = + PropertyTablePropertyView ivec3Property = view.getPropertyView("TestClassProperty"); REQUIRE( ivec3Property.status() == - MetadataPropertyViewStatus::ErrorBufferViewSizeNotDivisibleByTypeSize); + PropertyTablePropertyViewStatus:: + ErrorBufferViewSizeNotDivisibleByTypeSize); } SECTION("Buffer view length doesn't match with propertyTableCount") { model.bufferViews[valueBufferViewIndex].byteLength = 12; - MetadataPropertyView ivec3Property = + PropertyTablePropertyView ivec3Property = view.getPropertyView("TestClassProperty"); REQUIRE( ivec3Property.status() == - MetadataPropertyViewStatus:: + PropertyTablePropertyViewStatus:: ErrorBufferViewSizeDoesNotMatchPropertyTableCount); } } @@ -512,8 +517,8 @@ TEST_CASE("Test StructuralMetadata matN property") { propertyTable.properties["TestClassProperty"]; propertyTableProperty.values = static_cast(valueBufferViewIndex); - MetadataPropertyTableView view(model, propertyTable); - REQUIRE(view.status() == MetadataPropertyTableViewStatus::Valid); + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); REQUIRE(view.size() == propertyTable.count); const ExtensionExtStructuralMetadataClassProperty* classProperty = @@ -529,9 +534,10 @@ TEST_CASE("Test StructuralMetadata matN property") { REQUIRE(!classProperty->array); SECTION("Access correct type") { - MetadataPropertyView u32mat2x2Property = + PropertyTablePropertyView u32mat2x2Property = view.getPropertyView("TestClassProperty"); - REQUIRE(u32mat2x2Property.status() == MetadataPropertyViewStatus::Valid); + REQUIRE( + u32mat2x2Property.status() == PropertyTablePropertyViewStatus::Valid); REQUIRE(u32mat2x2Property.size() > 0); for (int64_t i = 0; i < u32mat2x2Property.size(); ++i) { @@ -540,110 +546,113 @@ TEST_CASE("Test StructuralMetadata matN property") { } SECTION("Access wrong type") { - MetadataPropertyView uint32Invalid = + PropertyTablePropertyView uint32Invalid = view.getPropertyView("TestClassProperty"); REQUIRE( uint32Invalid.status() == - MetadataPropertyViewStatus::ErrorTypeMismatch); + PropertyTablePropertyViewStatus::ErrorTypeMismatch); - MetadataPropertyView uvec2Invalid = + PropertyTablePropertyView uvec2Invalid = view.getPropertyView("TestClassProperty"); REQUIRE( - uvec2Invalid.status() == MetadataPropertyViewStatus::ErrorTypeMismatch); + uvec2Invalid.status() == + PropertyTablePropertyViewStatus::ErrorTypeMismatch); - MetadataPropertyView u32mat4x4Invalid = + PropertyTablePropertyView u32mat4x4Invalid = view.getPropertyView("TestClassProperty"); REQUIRE( u32mat4x4Invalid.status() == - MetadataPropertyViewStatus::ErrorTypeMismatch); + PropertyTablePropertyViewStatus::ErrorTypeMismatch); - MetadataPropertyView boolInvalid = + PropertyTablePropertyView boolInvalid = view.getPropertyView("TestClassProperty"); REQUIRE( - boolInvalid.status() == MetadataPropertyViewStatus::ErrorTypeMismatch); + boolInvalid.status() == + PropertyTablePropertyViewStatus::ErrorTypeMismatch); - MetadataPropertyView stringInvalid = + PropertyTablePropertyView stringInvalid = view.getPropertyView("TestClassProperty"); REQUIRE( stringInvalid.status() == - MetadataPropertyViewStatus::ErrorTypeMismatch); + PropertyTablePropertyViewStatus::ErrorTypeMismatch); } SECTION("Access wrong component type") { - MetadataPropertyView u8mat2x2Invalid = + PropertyTablePropertyView u8mat2x2Invalid = view.getPropertyView("TestClassProperty"); REQUIRE( u8mat2x2Invalid.status() == - MetadataPropertyViewStatus::ErrorComponentTypeMismatch); + PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); - MetadataPropertyView i32mat2x2Invalid = + PropertyTablePropertyView i32mat2x2Invalid = view.getPropertyView("TestClassProperty"); REQUIRE( i32mat2x2Invalid.status() == - MetadataPropertyViewStatus::ErrorComponentTypeMismatch); + PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); - MetadataPropertyView mat2Invalid = + PropertyTablePropertyView mat2Invalid = view.getPropertyView("TestClassProperty"); REQUIRE( mat2Invalid.status() == - MetadataPropertyViewStatus::ErrorComponentTypeMismatch); + PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); } SECTION("Access incorrectly as array") { - MetadataPropertyView> + PropertyTablePropertyView> u32mat2x2ArrayInvalid = view.getPropertyView>( "TestClassProperty"); REQUIRE( u32mat2x2ArrayInvalid.status() == - MetadataPropertyViewStatus::ErrorArrayTypeMismatch); + PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); } SECTION("Wrong buffer index") { model.bufferViews[valueBufferViewIndex].buffer = 2; - MetadataPropertyView u32mat2x2Property = + PropertyTablePropertyView u32mat2x2Property = view.getPropertyView("TestClassProperty"); REQUIRE( u32mat2x2Property.status() == - MetadataPropertyViewStatus::ErrorInvalidValueBuffer); + PropertyTablePropertyViewStatus::ErrorInvalidValueBuffer); } SECTION("Wrong buffer view index") { propertyTableProperty.values = -1; - MetadataPropertyView u32mat2x2Property = + PropertyTablePropertyView u32mat2x2Property = view.getPropertyView("TestClassProperty"); REQUIRE( u32mat2x2Property.status() == - MetadataPropertyViewStatus::ErrorInvalidValueBufferView); + PropertyTablePropertyViewStatus::ErrorInvalidValueBufferView); } SECTION("Buffer view points outside of the real buffer length") { model.buffers[valueBufferIndex].cesium.data.resize(sizeof(glm::u32mat2x2)); - MetadataPropertyView u32mat2x2Property = + PropertyTablePropertyView u32mat2x2Property = view.getPropertyView("TestClassProperty"); REQUIRE( u32mat2x2Property.status() == - MetadataPropertyViewStatus::ErrorBufferViewOutOfBounds); + PropertyTablePropertyViewStatus::ErrorBufferViewOutOfBounds); } SECTION("Buffer view length isn't multiple of sizeof(T)") { model.bufferViews[valueBufferViewIndex].byteLength = sizeof(glm::u32mat2x2) * 4 - 1; - MetadataPropertyView u32mat2x2Property = + PropertyTablePropertyView u32mat2x2Property = view.getPropertyView("TestClassProperty"); REQUIRE( u32mat2x2Property.status() == - MetadataPropertyViewStatus::ErrorBufferViewSizeNotDivisibleByTypeSize); + PropertyTablePropertyViewStatus:: + ErrorBufferViewSizeNotDivisibleByTypeSize); } SECTION("Buffer view length doesn't match with propertyTableCount") { model.bufferViews[valueBufferViewIndex].byteLength = sizeof(glm::u32mat2x2); - MetadataPropertyView u32mat2x2Property = + PropertyTablePropertyView u32mat2x2Property = view.getPropertyView("TestClassProperty"); REQUIRE( u32mat2x2Property.status() == - MetadataPropertyViewStatus:: + PropertyTablePropertyViewStatus:: ErrorBufferViewSizeDoesNotMatchPropertyTableCount); } } @@ -707,8 +716,8 @@ TEST_CASE("Test StructuralMetadata boolean property") { propertyTableProperty.values = static_cast(model.bufferViews.size() - 1); - MetadataPropertyTableView view(model, propertyTable); - REQUIRE(view.status() == MetadataPropertyTableViewStatus::Valid); + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); REQUIRE(view.size() == propertyTable.count); const ExtensionExtStructuralMetadataClassProperty* classProperty = @@ -722,9 +731,9 @@ TEST_CASE("Test StructuralMetadata boolean property") { REQUIRE(!classProperty->array); SECTION("Access correct type") { - MetadataPropertyView boolProperty = + PropertyTablePropertyView boolProperty = view.getPropertyView("TestClassProperty"); - REQUIRE(boolProperty.status() == MetadataPropertyViewStatus::Valid); + REQUIRE(boolProperty.status() == PropertyTablePropertyViewStatus::Valid); REQUIRE(boolProperty.size() == instanceCount); for (int64_t i = 0; i < boolProperty.size(); ++i) { bool expectedValue = expected[static_cast(i)]; @@ -734,11 +743,11 @@ TEST_CASE("Test StructuralMetadata boolean property") { SECTION("Buffer size doesn't match with propertyTableCount") { propertyTable.count = 66; - MetadataPropertyView boolProperty = + PropertyTablePropertyView boolProperty = view.getPropertyView("TestClassProperty"); REQUIRE( boolProperty.status() == - MetadataPropertyViewStatus:: + PropertyTablePropertyViewStatus:: ErrorBufferViewSizeDoesNotMatchPropertyTableCount); } } @@ -822,8 +831,8 @@ TEST_CASE("Test StructuralMetadata string property") { propertyTableProperty.stringOffsets = static_cast(offsetBufferViewIndex); - MetadataPropertyTableView view(model, propertyTable); - REQUIRE(view.status() == MetadataPropertyTableViewStatus::Valid); + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); REQUIRE(view.size() == propertyTable.count); const ExtensionExtStructuralMetadataClassProperty* classProperty = @@ -837,33 +846,33 @@ TEST_CASE("Test StructuralMetadata string property") { REQUIRE(!classProperty->array); SECTION("Access correct type") { - MetadataPropertyView stringProperty = + PropertyTablePropertyView stringProperty = view.getPropertyView("TestClassProperty"); - REQUIRE(stringProperty.status() == MetadataPropertyViewStatus::Valid); + REQUIRE(stringProperty.status() == PropertyTablePropertyViewStatus::Valid); for (size_t i = 0; i < expected.size(); ++i) { REQUIRE(stringProperty.get(static_cast(i)) == expected[i]); } } SECTION("Wrong array type") { - MetadataPropertyView> + PropertyTablePropertyView> stringArrayInvalid = view.getPropertyView>( "TestClassProperty"); REQUIRE( stringArrayInvalid.status() == - MetadataPropertyViewStatus::ErrorArrayTypeMismatch); + PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); } SECTION("Wrong offset type") { propertyTableProperty.stringOffsetType = ExtensionExtStructuralMetadataPropertyTableProperty::StringOffsetType:: UINT8; - MetadataPropertyView stringProperty = + PropertyTablePropertyView stringProperty = view.getPropertyView("TestClassProperty"); REQUIRE( stringProperty.status() == - MetadataPropertyViewStatus:: + PropertyTablePropertyViewStatus:: ErrorBufferViewSizeDoesNotMatchPropertyTableCount); propertyTableProperty.stringOffsetType = @@ -873,7 +882,7 @@ TEST_CASE("Test StructuralMetadata string property") { view.getPropertyView("TestClassProperty"); REQUIRE( stringProperty.status() == - MetadataPropertyViewStatus:: + PropertyTablePropertyViewStatus:: ErrorBufferViewSizeDoesNotMatchPropertyTableCount); propertyTableProperty.stringOffsetType = "NONSENSE"; @@ -881,7 +890,7 @@ TEST_CASE("Test StructuralMetadata string property") { view.getPropertyView("TestClassProperty"); REQUIRE( stringProperty.status() == - MetadataPropertyViewStatus::ErrorInvalidStringOffsetType); + PropertyTablePropertyViewStatus::ErrorInvalidStringOffsetType); propertyTableProperty.stringOffsetType = ""; propertyTableProperty.arrayOffsetType = @@ -891,7 +900,7 @@ TEST_CASE("Test StructuralMetadata string property") { view.getPropertyView("TestClassProperty"); REQUIRE( stringProperty.status() == - MetadataPropertyViewStatus::ErrorInvalidStringOffsetType); + PropertyTablePropertyViewStatus::ErrorInvalidStringOffsetType); } SECTION("Offset values are not sorted ascending") { @@ -899,11 +908,11 @@ TEST_CASE("Test StructuralMetadata string property") { model.buffers[offsetBufferIndex].cesium.data.data()); offset[2] = static_cast(model.buffers[valueBufferIndex].byteLength + 4); - MetadataPropertyView stringProperty = + PropertyTablePropertyView stringProperty = view.getPropertyView("TestClassProperty"); REQUIRE( stringProperty.status() == - MetadataPropertyViewStatus::ErrorStringOffsetsNotSorted); + PropertyTablePropertyViewStatus::ErrorStringOffsetsNotSorted); } SECTION("Offset value points outside of value buffer") { @@ -911,11 +920,11 @@ TEST_CASE("Test StructuralMetadata string property") { model.buffers[offsetBufferIndex].cesium.data.data()); offset[propertyTable.count] = static_cast(model.buffers[valueBufferIndex].byteLength + 4); - MetadataPropertyView stringProperty = + PropertyTablePropertyView stringProperty = view.getPropertyView("TestClassProperty"); REQUIRE( stringProperty.status() == - MetadataPropertyViewStatus::ErrorStringOffsetOutOfBounds); + PropertyTablePropertyViewStatus::ErrorStringOffsetOutOfBounds); } } @@ -968,8 +977,8 @@ TEST_CASE("Test StructuralMetadata fixed-length scalar array") { propertyTableProperty.values = static_cast(model.bufferViews.size() - 1); - MetadataPropertyTableView view(model, propertyTable); - REQUIRE(view.status() == MetadataPropertyTableViewStatus::Valid); + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); REQUIRE(view.size() == propertyTable.count); const ExtensionExtStructuralMetadataClassProperty* classProperty = @@ -985,9 +994,9 @@ TEST_CASE("Test StructuralMetadata fixed-length scalar array") { REQUIRE(classProperty->count == 3); SECTION("Access the right type") { - MetadataPropertyView> arrayProperty = + PropertyTablePropertyView> arrayProperty = view.getPropertyView>("TestClassProperty"); - REQUIRE(arrayProperty.status() == MetadataPropertyViewStatus::Valid); + REQUIRE(arrayProperty.status() == PropertyTablePropertyViewStatus::Valid); for (int64_t i = 0; i < arrayProperty.size(); ++i) { MetadataArrayView member = arrayProperty.get(i); @@ -998,61 +1007,62 @@ TEST_CASE("Test StructuralMetadata fixed-length scalar array") { } SECTION("Wrong type") { - MetadataPropertyView> boolArrayInvalid = + PropertyTablePropertyView> boolArrayInvalid = view.getPropertyView>("TestClassProperty"); REQUIRE( boolArrayInvalid.status() == - MetadataPropertyViewStatus::ErrorTypeMismatch); + PropertyTablePropertyViewStatus::ErrorTypeMismatch); - MetadataPropertyView> uvec2ArrayInvalid = + PropertyTablePropertyView> uvec2ArrayInvalid = view.getPropertyView>( "TestClassProperty"); REQUIRE( uvec2ArrayInvalid.status() == - MetadataPropertyViewStatus::ErrorTypeMismatch); + PropertyTablePropertyViewStatus::ErrorTypeMismatch); } SECTION("Wrong component type") { - MetadataPropertyView> int32ArrayInvalid = + PropertyTablePropertyView> int32ArrayInvalid = view.getPropertyView>("TestClassProperty"); REQUIRE( int32ArrayInvalid.status() == - MetadataPropertyViewStatus::ErrorComponentTypeMismatch); + PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); } SECTION("Not an array type") { - MetadataPropertyView uint32Invalid = + PropertyTablePropertyView uint32Invalid = view.getPropertyView("TestClassProperty"); REQUIRE( uint32Invalid.status() == - MetadataPropertyViewStatus::ErrorArrayTypeMismatch); + PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); } SECTION("Buffer size is not a multiple of type size") { model.bufferViews[valueBufferViewIndex].byteLength = 13; - MetadataPropertyView> arrayProperty = + PropertyTablePropertyView> arrayProperty = view.getPropertyView>("TestClassProperty"); REQUIRE( arrayProperty.status() == - MetadataPropertyViewStatus::ErrorBufferViewSizeNotDivisibleByTypeSize); + PropertyTablePropertyViewStatus:: + ErrorBufferViewSizeNotDivisibleByTypeSize); } SECTION("Negative component count") { testClassProperty.count = -1; - MetadataPropertyView> arrayProperty = + PropertyTablePropertyView> arrayProperty = view.getPropertyView>("TestClassProperty"); REQUIRE( - arrayProperty.status() == - MetadataPropertyViewStatus::ErrorArrayCountAndOffsetBufferDontExist); + arrayProperty.status() == PropertyTablePropertyViewStatus:: + ErrorArrayCountAndOffsetBufferDontExist); } SECTION("Value buffer doesn't fit into property table count") { testClassProperty.count = 55; - MetadataPropertyView> arrayProperty = + PropertyTablePropertyView> arrayProperty = view.getPropertyView>("TestClassProperty"); REQUIRE( arrayProperty.status() == - MetadataPropertyViewStatus:: + PropertyTablePropertyViewStatus:: ErrorBufferViewSizeDoesNotMatchPropertyTableCount); } } @@ -1142,8 +1152,8 @@ TEST_CASE("Test Structural Metadata variable-length scalar array") { ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: UINT64; - MetadataPropertyTableView view(model, propertyTable); - REQUIRE(view.status() == MetadataPropertyTableViewStatus::Valid); + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); REQUIRE(view.size() == propertyTable.count); const ExtensionExtStructuralMetadataClassProperty* classProperty = @@ -1159,9 +1169,9 @@ TEST_CASE("Test Structural Metadata variable-length scalar array") { REQUIRE(!classProperty->count); SECTION("Access the correct type") { - MetadataPropertyView> property = + PropertyTablePropertyView> property = view.getPropertyView>("TestClassProperty"); - REQUIRE(property.status() == MetadataPropertyViewStatus::Valid); + REQUIRE(property.status() == PropertyTablePropertyViewStatus::Valid); for (size_t i = 0; i < expected.size(); ++i) { MetadataArrayView valueMember = property.get(static_cast(i)); @@ -1175,11 +1185,11 @@ TEST_CASE("Test Structural Metadata variable-length scalar array") { propertyTableProperty.arrayOffsetType = ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: UINT8; - MetadataPropertyView> arrayProperty = + PropertyTablePropertyView> arrayProperty = view.getPropertyView>("TestClassProperty"); REQUIRE( arrayProperty.status() == - MetadataPropertyViewStatus:: + PropertyTablePropertyViewStatus:: ErrorBufferViewSizeDoesNotMatchPropertyTableCount); propertyTableProperty.arrayOffsetType = @@ -1189,7 +1199,7 @@ TEST_CASE("Test Structural Metadata variable-length scalar array") { view.getPropertyView>("TestClassProperty"); REQUIRE( arrayProperty.status() == - MetadataPropertyViewStatus:: + PropertyTablePropertyViewStatus:: ErrorBufferViewSizeDoesNotMatchPropertyTableCount); propertyTableProperty.arrayOffsetType = "NONSENSE"; @@ -1197,7 +1207,7 @@ TEST_CASE("Test Structural Metadata variable-length scalar array") { view.getPropertyView>("TestClassProperty"); REQUIRE( arrayProperty.status() == - MetadataPropertyViewStatus::ErrorInvalidArrayOffsetType); + PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); propertyTableProperty.arrayOffsetType = ""; propertyTableProperty.stringOffsetType = @@ -1207,7 +1217,7 @@ TEST_CASE("Test Structural Metadata variable-length scalar array") { view.getPropertyView>("TestClassProperty"); REQUIRE( arrayProperty.status() == - MetadataPropertyViewStatus::ErrorInvalidArrayOffsetType); + PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); } SECTION("Offset values are not sorted ascending") { @@ -1217,11 +1227,11 @@ TEST_CASE("Test Structural Metadata variable-length scalar array") { uint64_t* offset = reinterpret_cast( model.buffers[offsetBufferIndex].cesium.data.data()); offset[propertyTable.count] = 0; - MetadataPropertyView> arrayProperty = + PropertyTablePropertyView> arrayProperty = view.getPropertyView>("TestClassProperty"); REQUIRE( arrayProperty.status() == - MetadataPropertyViewStatus::ErrorArrayOffsetsNotSorted); + PropertyTablePropertyViewStatus::ErrorArrayOffsetsNotSorted); } SECTION("Offset value points outside of value buffer") { @@ -1229,20 +1239,20 @@ TEST_CASE("Test Structural Metadata variable-length scalar array") { model.buffers[offsetBufferIndex].cesium.data.data()); offset[propertyTable.count] = static_cast(model.buffers[valueBufferIndex].byteLength + 4); - MetadataPropertyView> arrayProperty = + PropertyTablePropertyView> arrayProperty = view.getPropertyView>("TestClassProperty"); REQUIRE( arrayProperty.status() == - MetadataPropertyViewStatus::ErrorArrayOffsetOutOfBounds); + PropertyTablePropertyViewStatus::ErrorArrayOffsetOutOfBounds); } SECTION("Count and offset buffer are both present") { testClassProperty.count = 3; - MetadataPropertyView> property = + PropertyTablePropertyView> property = view.getPropertyView>("TestClassProperty"); REQUIRE( property.status() == - MetadataPropertyViewStatus::ErrorArrayCountAndOffsetBufferCoexist); + PropertyTablePropertyViewStatus::ErrorArrayCountAndOffsetBufferCoexist); } } @@ -1301,8 +1311,8 @@ TEST_CASE("Test StructuralMetadata fixed-length vecN array") { propertyTableProperty.values = static_cast(model.bufferViews.size() - 1); - MetadataPropertyTableView view(model, propertyTable); - REQUIRE(view.status() == MetadataPropertyTableViewStatus::Valid); + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); REQUIRE(view.size() == propertyTable.count); const ExtensionExtStructuralMetadataClassProperty* classProperty = @@ -1318,10 +1328,10 @@ TEST_CASE("Test StructuralMetadata fixed-length vecN array") { REQUIRE(classProperty->count == 2); SECTION("Access the right type") { - MetadataPropertyView> arrayProperty = + PropertyTablePropertyView> arrayProperty = view.getPropertyView>( "TestClassProperty"); - REQUIRE(arrayProperty.status() == MetadataPropertyViewStatus::Valid); + REQUIRE(arrayProperty.status() == PropertyTablePropertyViewStatus::Valid); for (int64_t i = 0; i < arrayProperty.size(); ++i) { MetadataArrayView member = arrayProperty.get(i); @@ -1332,65 +1342,66 @@ TEST_CASE("Test StructuralMetadata fixed-length vecN array") { } SECTION("Wrong type") { - MetadataPropertyView> int32ArrayInvalid = + PropertyTablePropertyView> int32ArrayInvalid = view.getPropertyView>("TestClassProperty"); REQUIRE( int32ArrayInvalid.status() == - MetadataPropertyViewStatus::ErrorTypeMismatch); + PropertyTablePropertyViewStatus::ErrorTypeMismatch); - MetadataPropertyView> ivec2ArrayInvalid = + PropertyTablePropertyView> ivec2ArrayInvalid = view.getPropertyView>( "TestClassProperty"); REQUIRE( ivec2ArrayInvalid.status() == - MetadataPropertyViewStatus::ErrorTypeMismatch); + PropertyTablePropertyViewStatus::ErrorTypeMismatch); } SECTION("Wrong component type") { - MetadataPropertyView> uvec3ArrayInvalid = + PropertyTablePropertyView> uvec3ArrayInvalid = view.getPropertyView>( "TestClassProperty"); REQUIRE( uvec3ArrayInvalid.status() == - MetadataPropertyViewStatus::ErrorComponentTypeMismatch); + PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); } SECTION("Not an array type") { - MetadataPropertyView ivec3Invalid = + PropertyTablePropertyView ivec3Invalid = view.getPropertyView("TestClassProperty"); REQUIRE( ivec3Invalid.status() == - MetadataPropertyViewStatus::ErrorArrayTypeMismatch); + PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); } SECTION("Buffer size is not a multiple of type size") { model.bufferViews[valueBufferViewIndex].byteLength = 13; - MetadataPropertyView> arrayProperty = + PropertyTablePropertyView> arrayProperty = view.getPropertyView>( "TestClassProperty"); REQUIRE( arrayProperty.status() == - MetadataPropertyViewStatus::ErrorBufferViewSizeNotDivisibleByTypeSize); + PropertyTablePropertyViewStatus:: + ErrorBufferViewSizeNotDivisibleByTypeSize); } SECTION("Negative component count") { testClassProperty.count = -1; - MetadataPropertyView> arrayProperty = + PropertyTablePropertyView> arrayProperty = view.getPropertyView>( "TestClassProperty"); REQUIRE( - arrayProperty.status() == - MetadataPropertyViewStatus::ErrorArrayCountAndOffsetBufferDontExist); + arrayProperty.status() == PropertyTablePropertyViewStatus:: + ErrorArrayCountAndOffsetBufferDontExist); } SECTION("Value buffer doesn't fit into property table count") { testClassProperty.count = 55; - MetadataPropertyView> arrayProperty = + PropertyTablePropertyView> arrayProperty = view.getPropertyView>( "TestClassProperty"); REQUIRE( arrayProperty.status() == - MetadataPropertyViewStatus:: + PropertyTablePropertyViewStatus:: ErrorBufferViewSizeDoesNotMatchPropertyTableCount); } } @@ -1482,8 +1493,8 @@ TEST_CASE("Test Structural Metadata variable-length vecN array") { ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: UINT64; - MetadataPropertyTableView view(model, propertyTable); - REQUIRE(view.status() == MetadataPropertyTableViewStatus::Valid); + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); REQUIRE(view.size() == propertyTable.count); const ExtensionExtStructuralMetadataClassProperty* classProperty = @@ -1499,10 +1510,10 @@ TEST_CASE("Test Structural Metadata variable-length vecN array") { REQUIRE(!classProperty->count); SECTION("Access the correct type") { - MetadataPropertyView> property = + PropertyTablePropertyView> property = view.getPropertyView>( "TestClassProperty"); - REQUIRE(property.status() == MetadataPropertyViewStatus::Valid); + REQUIRE(property.status() == PropertyTablePropertyViewStatus::Valid); for (size_t i = 0; i < expected.size(); ++i) { MetadataArrayView valueMember = property.get(static_cast(i)); @@ -1517,12 +1528,12 @@ TEST_CASE("Test Structural Metadata variable-length vecN array") { propertyTableProperty.arrayOffsetType = ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: UINT8; - MetadataPropertyView> arrayProperty = + PropertyTablePropertyView> arrayProperty = view.getPropertyView>( "TestClassProperty"); REQUIRE( arrayProperty.status() == - MetadataPropertyViewStatus:: + PropertyTablePropertyViewStatus:: ErrorBufferViewSizeDoesNotMatchPropertyTableCount); propertyTableProperty.arrayOffsetType = @@ -1532,7 +1543,7 @@ TEST_CASE("Test Structural Metadata variable-length vecN array") { "TestClassProperty"); REQUIRE( arrayProperty.status() == - MetadataPropertyViewStatus:: + PropertyTablePropertyViewStatus:: ErrorBufferViewSizeDoesNotMatchPropertyTableCount); propertyTableProperty.arrayOffsetType = "NONSENSE"; @@ -1540,7 +1551,7 @@ TEST_CASE("Test Structural Metadata variable-length vecN array") { "TestClassProperty"); REQUIRE( arrayProperty.status() == - MetadataPropertyViewStatus::ErrorInvalidArrayOffsetType); + PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); propertyTableProperty.arrayOffsetType = ""; propertyTableProperty.stringOffsetType = @@ -1550,7 +1561,7 @@ TEST_CASE("Test Structural Metadata variable-length vecN array") { "TestClassProperty"); REQUIRE( arrayProperty.status() == - MetadataPropertyViewStatus::ErrorInvalidArrayOffsetType); + PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); } SECTION("Offset values are not sorted ascending") { @@ -1560,12 +1571,12 @@ TEST_CASE("Test Structural Metadata variable-length vecN array") { uint64_t* offset = reinterpret_cast( model.buffers[offsetBufferIndex].cesium.data.data()); offset[propertyTable.count] = 0; - MetadataPropertyView> arrayProperty = + PropertyTablePropertyView> arrayProperty = view.getPropertyView>( "TestClassProperty"); REQUIRE( arrayProperty.status() == - MetadataPropertyViewStatus::ErrorArrayOffsetsNotSorted); + PropertyTablePropertyViewStatus::ErrorArrayOffsetsNotSorted); } SECTION("Offset value points outside of value buffer") { @@ -1573,22 +1584,22 @@ TEST_CASE("Test Structural Metadata variable-length vecN array") { model.buffers[offsetBufferIndex].cesium.data.data()); offset[propertyTable.count] = static_cast(model.buffers[valueBufferIndex].byteLength + 4); - MetadataPropertyView> arrayProperty = + PropertyTablePropertyView> arrayProperty = view.getPropertyView>( "TestClassProperty"); REQUIRE( arrayProperty.status() == - MetadataPropertyViewStatus::ErrorArrayOffsetOutOfBounds); + PropertyTablePropertyViewStatus::ErrorArrayOffsetOutOfBounds); } SECTION("Count and offset buffer are both present") { testClassProperty.count = 3; - MetadataPropertyView> property = + PropertyTablePropertyView> property = view.getPropertyView>( "TestClassProperty"); REQUIRE( property.status() == - MetadataPropertyViewStatus::ErrorArrayCountAndOffsetBufferCoexist); + PropertyTablePropertyViewStatus::ErrorArrayCountAndOffsetBufferCoexist); } } @@ -1661,8 +1672,8 @@ TEST_CASE("Test StructuralMetadata fixed-length matN array") { propertyTableProperty.values = static_cast(model.bufferViews.size() - 1); - MetadataPropertyTableView view(model, propertyTable); - REQUIRE(view.status() == MetadataPropertyTableViewStatus::Valid); + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); REQUIRE(view.size() == propertyTable.count); const ExtensionExtStructuralMetadataClassProperty* classProperty = @@ -1678,10 +1689,10 @@ TEST_CASE("Test StructuralMetadata fixed-length matN array") { REQUIRE(classProperty->count == 2); SECTION("Access the right type") { - MetadataPropertyView> arrayProperty = + PropertyTablePropertyView> arrayProperty = view.getPropertyView>( "TestClassProperty"); - REQUIRE(arrayProperty.status() == MetadataPropertyViewStatus::Valid); + REQUIRE(arrayProperty.status() == PropertyTablePropertyViewStatus::Valid); for (int64_t i = 0; i < arrayProperty.size(); ++i) { MetadataArrayView member = arrayProperty.get(i); @@ -1692,66 +1703,67 @@ TEST_CASE("Test StructuralMetadata fixed-length matN array") { } SECTION("Wrong type") { - MetadataPropertyView> int32ArrayInvalid = + PropertyTablePropertyView> int32ArrayInvalid = view.getPropertyView>("TestClassProperty"); REQUIRE( int32ArrayInvalid.status() == - MetadataPropertyViewStatus::ErrorTypeMismatch); + PropertyTablePropertyViewStatus::ErrorTypeMismatch); - MetadataPropertyView> ivec2ArrayInvalid = + PropertyTablePropertyView> ivec2ArrayInvalid = view.getPropertyView>( "TestClassProperty"); REQUIRE( ivec2ArrayInvalid.status() == - MetadataPropertyViewStatus::ErrorTypeMismatch); + PropertyTablePropertyViewStatus::ErrorTypeMismatch); } SECTION("Wrong component type") { - MetadataPropertyView> + PropertyTablePropertyView> u32mat2x2ArrayInvalid = view.getPropertyView>( "TestClassProperty"); REQUIRE( u32mat2x2ArrayInvalid.status() == - MetadataPropertyViewStatus::ErrorComponentTypeMismatch); + PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); } SECTION("Not an array type") { - MetadataPropertyView ivec3Invalid = + PropertyTablePropertyView ivec3Invalid = view.getPropertyView("TestClassProperty"); REQUIRE( ivec3Invalid.status() == - MetadataPropertyViewStatus::ErrorArrayTypeMismatch); + PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); } SECTION("Buffer size is not a multiple of type size") { model.bufferViews[valueBufferViewIndex].byteLength = 13; - MetadataPropertyView> arrayProperty = + PropertyTablePropertyView> arrayProperty = view.getPropertyView>( "TestClassProperty"); REQUIRE( arrayProperty.status() == - MetadataPropertyViewStatus::ErrorBufferViewSizeNotDivisibleByTypeSize); + PropertyTablePropertyViewStatus:: + ErrorBufferViewSizeNotDivisibleByTypeSize); } SECTION("Negative component count") { testClassProperty.count = -1; - MetadataPropertyView> arrayProperty = + PropertyTablePropertyView> arrayProperty = view.getPropertyView>( "TestClassProperty"); REQUIRE( - arrayProperty.status() == - MetadataPropertyViewStatus::ErrorArrayCountAndOffsetBufferDontExist); + arrayProperty.status() == PropertyTablePropertyViewStatus:: + ErrorArrayCountAndOffsetBufferDontExist); } SECTION("Value buffer doesn't fit into property table count") { testClassProperty.count = 55; - MetadataPropertyView> arrayProperty = + PropertyTablePropertyView> arrayProperty = view.getPropertyView>( "TestClassProperty"); REQUIRE( arrayProperty.status() == - MetadataPropertyViewStatus:: + PropertyTablePropertyViewStatus:: ErrorBufferViewSizeDoesNotMatchPropertyTableCount); } } @@ -1863,8 +1875,8 @@ TEST_CASE("Test Structural Metadata variable-length matN array") { ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: UINT64; - MetadataPropertyTableView view(model, propertyTable); - REQUIRE(view.status() == MetadataPropertyTableViewStatus::Valid); + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); REQUIRE(view.size() == propertyTable.count); const ExtensionExtStructuralMetadataClassProperty* classProperty = @@ -1880,10 +1892,10 @@ TEST_CASE("Test Structural Metadata variable-length matN array") { REQUIRE(!classProperty->count); SECTION("Access the correct type") { - MetadataPropertyView> property = + PropertyTablePropertyView> property = view.getPropertyView>( "TestClassProperty"); - REQUIRE(property.status() == MetadataPropertyViewStatus::Valid); + REQUIRE(property.status() == PropertyTablePropertyViewStatus::Valid); for (size_t i = 0; i < expected.size(); ++i) { MetadataArrayView valueMember = property.get(static_cast(i)); @@ -1898,12 +1910,12 @@ TEST_CASE("Test Structural Metadata variable-length matN array") { propertyTableProperty.arrayOffsetType = ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: UINT8; - MetadataPropertyView> arrayProperty = + PropertyTablePropertyView> arrayProperty = view.getPropertyView>( "TestClassProperty"); REQUIRE( arrayProperty.status() == - MetadataPropertyViewStatus:: + PropertyTablePropertyViewStatus:: ErrorBufferViewSizeDoesNotMatchPropertyTableCount); propertyTableProperty.arrayOffsetType = @@ -1913,7 +1925,7 @@ TEST_CASE("Test Structural Metadata variable-length matN array") { "TestClassProperty"); REQUIRE( arrayProperty.status() == - MetadataPropertyViewStatus:: + PropertyTablePropertyViewStatus:: ErrorBufferViewSizeDoesNotMatchPropertyTableCount); propertyTableProperty.arrayOffsetType = "NONSENSE"; @@ -1921,7 +1933,7 @@ TEST_CASE("Test Structural Metadata variable-length matN array") { "TestClassProperty"); REQUIRE( arrayProperty.status() == - MetadataPropertyViewStatus::ErrorInvalidArrayOffsetType); + PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); propertyTableProperty.arrayOffsetType = ""; propertyTableProperty.stringOffsetType = @@ -1931,7 +1943,7 @@ TEST_CASE("Test Structural Metadata variable-length matN array") { "TestClassProperty"); REQUIRE( arrayProperty.status() == - MetadataPropertyViewStatus::ErrorInvalidArrayOffsetType); + PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); } SECTION("Offset values are not sorted ascending") { @@ -1941,12 +1953,12 @@ TEST_CASE("Test Structural Metadata variable-length matN array") { uint64_t* offset = reinterpret_cast( model.buffers[offsetBufferIndex].cesium.data.data()); offset[propertyTable.count] = 0; - MetadataPropertyView> arrayProperty = + PropertyTablePropertyView> arrayProperty = view.getPropertyView>( "TestClassProperty"); REQUIRE( arrayProperty.status() == - MetadataPropertyViewStatus::ErrorArrayOffsetsNotSorted); + PropertyTablePropertyViewStatus::ErrorArrayOffsetsNotSorted); } SECTION("Offset value points outside of value buffer") { @@ -1954,22 +1966,22 @@ TEST_CASE("Test Structural Metadata variable-length matN array") { model.buffers[offsetBufferIndex].cesium.data.data()); offset[propertyTable.count] = static_cast(model.buffers[valueBufferIndex].byteLength + 4); - MetadataPropertyView> arrayProperty = + PropertyTablePropertyView> arrayProperty = view.getPropertyView>( "TestClassProperty"); REQUIRE( arrayProperty.status() == - MetadataPropertyViewStatus::ErrorArrayOffsetOutOfBounds); + PropertyTablePropertyViewStatus::ErrorArrayOffsetOutOfBounds); } SECTION("Count and offset buffer are both present") { testClassProperty.count = 3; - MetadataPropertyView> property = + PropertyTablePropertyView> property = view.getPropertyView>( "TestClassProperty"); REQUIRE( property.status() == - MetadataPropertyViewStatus::ErrorArrayCountAndOffsetBufferCoexist); + PropertyTablePropertyViewStatus::ErrorArrayCountAndOffsetBufferCoexist); } } @@ -2040,8 +2052,8 @@ TEST_CASE("Test StructuralMetadata fixed-length boolean array") { propertyTableProperty.values = static_cast(model.bufferViews.size() - 1); - MetadataPropertyTableView view(model, propertyTable); - REQUIRE(view.status() == MetadataPropertyTableViewStatus::Valid); + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); REQUIRE(view.size() == propertyTable.count); const ExtensionExtStructuralMetadataClassProperty* classProperty = @@ -2054,9 +2066,10 @@ TEST_CASE("Test StructuralMetadata fixed-length boolean array") { REQUIRE(classProperty->count == 3); SECTION("Access correct type") { - MetadataPropertyView> boolArrayProperty = + PropertyTablePropertyView> boolArrayProperty = view.getPropertyView>("TestClassProperty"); - REQUIRE(boolArrayProperty.status() == MetadataPropertyViewStatus::Valid); + REQUIRE( + boolArrayProperty.status() == PropertyTablePropertyViewStatus::Valid); REQUIRE(boolArrayProperty.size() == propertyTable.count); REQUIRE(boolArrayProperty.size() > 0); for (int64_t i = 0; i < boolArrayProperty.size(); ++i) { @@ -2068,38 +2081,39 @@ TEST_CASE("Test StructuralMetadata fixed-length boolean array") { } SECTION("Wrong type") { - MetadataPropertyView> uint8ArrayInvalid = + PropertyTablePropertyView> uint8ArrayInvalid = view.getPropertyView>("TestClassProperty"); REQUIRE( uint8ArrayInvalid.status() == - MetadataPropertyViewStatus::ErrorTypeMismatch); + PropertyTablePropertyViewStatus::ErrorTypeMismatch); } SECTION("View is not array type") { - MetadataPropertyView boolInvalid = + PropertyTablePropertyView boolInvalid = view.getPropertyView("TestClassProperty"); REQUIRE( boolInvalid.status() == - MetadataPropertyViewStatus::ErrorArrayTypeMismatch); + PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); } SECTION("Value buffer doesn't have enough required bytes") { testClassProperty.count = 11; - MetadataPropertyView> boolArrayProperty = + PropertyTablePropertyView> boolArrayProperty = view.getPropertyView>("TestClassProperty"); REQUIRE( boolArrayProperty.status() == - MetadataPropertyViewStatus:: + PropertyTablePropertyViewStatus:: ErrorBufferViewSizeDoesNotMatchPropertyTableCount); } SECTION("Count is negative") { testClassProperty.count = -1; - MetadataPropertyView> boolArrayProperty = + PropertyTablePropertyView> boolArrayProperty = view.getPropertyView>("TestClassProperty"); REQUIRE( boolArrayProperty.status() == - MetadataPropertyViewStatus::ErrorArrayCountAndOffsetBufferDontExist); + PropertyTablePropertyViewStatus:: + ErrorArrayCountAndOffsetBufferDontExist); } } @@ -2193,8 +2207,8 @@ TEST_CASE("Test StructuralMetadata variable-length boolean array") { ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: UINT64; - MetadataPropertyTableView view(model, propertyTable); - REQUIRE(view.status() == MetadataPropertyTableViewStatus::Valid); + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); REQUIRE(view.size() == propertyTable.count); const ExtensionExtStructuralMetadataClassProperty* classProperty = @@ -2207,9 +2221,10 @@ TEST_CASE("Test StructuralMetadata variable-length boolean array") { REQUIRE(!classProperty->count); SECTION("Access correct type") { - MetadataPropertyView> boolArrayProperty = + PropertyTablePropertyView> boolArrayProperty = view.getPropertyView>("TestClassProperty"); - REQUIRE(boolArrayProperty.status() == MetadataPropertyViewStatus::Valid); + REQUIRE( + boolArrayProperty.status() == PropertyTablePropertyViewStatus::Valid); for (size_t i = 0; i < expected.size(); ++i) { MetadataArrayView arrayMember = boolArrayProperty.get(static_cast(i)); @@ -2224,11 +2239,11 @@ TEST_CASE("Test StructuralMetadata variable-length boolean array") { propertyTableProperty.arrayOffsetType = ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: UINT8; - MetadataPropertyView> arrayProperty = + PropertyTablePropertyView> arrayProperty = view.getPropertyView>("TestClassProperty"); REQUIRE( arrayProperty.status() == - MetadataPropertyViewStatus:: + PropertyTablePropertyViewStatus:: ErrorBufferViewSizeDoesNotMatchPropertyTableCount); propertyTableProperty.arrayOffsetType = @@ -2238,7 +2253,7 @@ TEST_CASE("Test StructuralMetadata variable-length boolean array") { view.getPropertyView>("TestClassProperty"); REQUIRE( arrayProperty.status() == - MetadataPropertyViewStatus:: + PropertyTablePropertyViewStatus:: ErrorBufferViewSizeDoesNotMatchPropertyTableCount); propertyTableProperty.arrayOffsetType = "NONSENSE"; @@ -2246,7 +2261,7 @@ TEST_CASE("Test StructuralMetadata variable-length boolean array") { view.getPropertyView>("TestClassProperty"); REQUIRE( arrayProperty.status() == - MetadataPropertyViewStatus::ErrorInvalidArrayOffsetType); + PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); propertyTableProperty.arrayOffsetType = ""; propertyTableProperty.stringOffsetType = @@ -2256,7 +2271,7 @@ TEST_CASE("Test StructuralMetadata variable-length boolean array") { view.getPropertyView>("TestClassProperty"); REQUIRE( arrayProperty.status() == - MetadataPropertyViewStatus::ErrorInvalidArrayOffsetType); + PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); } SECTION("Offset values are not sorted ascending") { @@ -2266,11 +2281,11 @@ TEST_CASE("Test StructuralMetadata variable-length boolean array") { uint64_t* offset = reinterpret_cast( model.buffers[offsetBufferIndex].cesium.data.data()); offset[propertyTable.count] = 0; - MetadataPropertyView> arrayProperty = + PropertyTablePropertyView> arrayProperty = view.getPropertyView>("TestClassProperty"); REQUIRE( arrayProperty.status() == - MetadataPropertyViewStatus::ErrorArrayOffsetsNotSorted); + PropertyTablePropertyViewStatus::ErrorArrayOffsetsNotSorted); } SECTION("Offset value points outside of value buffer") { @@ -2278,20 +2293,20 @@ TEST_CASE("Test StructuralMetadata variable-length boolean array") { model.buffers[offsetBufferIndex].cesium.data.data()); offset[propertyTable.count] = static_cast( model.buffers[valueBufferIndex].byteLength * 8 + 20); - MetadataPropertyView> arrayProperty = + PropertyTablePropertyView> arrayProperty = view.getPropertyView>("TestClassProperty"); REQUIRE( arrayProperty.status() == - MetadataPropertyViewStatus::ErrorArrayOffsetOutOfBounds); + PropertyTablePropertyViewStatus::ErrorArrayOffsetOutOfBounds); } SECTION("Count and offset buffer both present") { testClassProperty.count = 3; - MetadataPropertyView> boolArrayProperty = + PropertyTablePropertyView> boolArrayProperty = view.getPropertyView>("TestClassProperty"); REQUIRE( boolArrayProperty.status() == - MetadataPropertyViewStatus::ErrorArrayCountAndOffsetBufferCoexist); + PropertyTablePropertyViewStatus::ErrorArrayCountAndOffsetBufferCoexist); } } @@ -2377,8 +2392,8 @@ TEST_CASE("Test StructuralMetadata fixed-length arrays of strings") { propertyTableProperty.stringOffsets = static_cast(offsetBufferViewIndex); - MetadataPropertyTableView view(model, propertyTable); - REQUIRE(view.status() == MetadataPropertyTableViewStatus::Valid); + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); REQUIRE(view.size() == propertyTable.count); const ExtensionExtStructuralMetadataClassProperty* classProperty = @@ -2391,10 +2406,11 @@ TEST_CASE("Test StructuralMetadata fixed-length arrays of strings") { REQUIRE(classProperty->count == 2); SECTION("Access correct type") { - MetadataPropertyView> stringProperty = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE(stringProperty.status() == MetadataPropertyViewStatus::Valid); + PropertyTablePropertyView> + stringProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE(stringProperty.status() == PropertyTablePropertyViewStatus::Valid); REQUIRE(stringProperty.size() == 3); MetadataArrayView v0 = stringProperty.get(0); @@ -2414,31 +2430,33 @@ TEST_CASE("Test StructuralMetadata fixed-length arrays of strings") { } SECTION("Array type mismatch") { - MetadataPropertyView stringProperty = + PropertyTablePropertyView stringProperty = view.getPropertyView("TestClassProperty"); REQUIRE( stringProperty.status() == - MetadataPropertyViewStatus::ErrorArrayTypeMismatch); + PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); } SECTION("Count is negative") { testClassProperty.count = -1; - MetadataPropertyView> stringProperty = - view.getPropertyView>( - "TestClassProperty"); + PropertyTablePropertyView> + stringProperty = + view.getPropertyView>( + "TestClassProperty"); REQUIRE( - stringProperty.status() == - MetadataPropertyViewStatus::ErrorArrayCountAndOffsetBufferDontExist); + stringProperty.status() == PropertyTablePropertyViewStatus:: + ErrorArrayCountAndOffsetBufferDontExist); } SECTION("Offset type is unknown") { propertyTableProperty.stringOffsetType = "NONSENSE"; - MetadataPropertyView> stringProperty = - view.getPropertyView>( - "TestClassProperty"); + PropertyTablePropertyView> + stringProperty = + view.getPropertyView>( + "TestClassProperty"); REQUIRE( stringProperty.status() == - MetadataPropertyViewStatus::ErrorInvalidStringOffsetType); + PropertyTablePropertyViewStatus::ErrorInvalidStringOffsetType); propertyTableProperty.stringOffsetType = ""; propertyTableProperty.arrayOffsetType = @@ -2448,17 +2466,18 @@ TEST_CASE("Test StructuralMetadata fixed-length arrays of strings") { "TestClassProperty"); REQUIRE( stringProperty.status() == - MetadataPropertyViewStatus::ErrorInvalidStringOffsetType); + PropertyTablePropertyViewStatus::ErrorInvalidStringOffsetType); } SECTION("String offsets don't exist") { propertyTableProperty.stringOffsets = -1; - MetadataPropertyView> stringProperty = - view.getPropertyView>( - "TestClassProperty"); + PropertyTablePropertyView> + stringProperty = + view.getPropertyView>( + "TestClassProperty"); REQUIRE( stringProperty.status() == - MetadataPropertyViewStatus::ErrorInvalidStringOffsetBufferView); + PropertyTablePropertyViewStatus::ErrorInvalidStringOffsetBufferView); } } @@ -2582,8 +2601,8 @@ TEST_CASE("Test StructuralMetadata variable-length arrays of strings") { propertyTableProperty.stringOffsets = static_cast(stringOffsetBufferView); - MetadataPropertyTableView view(model, propertyTable); - REQUIRE(view.status() == MetadataPropertyTableViewStatus::Valid); + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); REQUIRE(view.size() == propertyTable.count); const ExtensionExtStructuralMetadataClassProperty* classProperty = @@ -2597,10 +2616,11 @@ TEST_CASE("Test StructuralMetadata variable-length arrays of strings") { REQUIRE(!classProperty->count); SECTION("Access correct type") { - MetadataPropertyView> stringProperty = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE(stringProperty.status() == MetadataPropertyViewStatus::Valid); + PropertyTablePropertyView> + stringProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE(stringProperty.status() == PropertyTablePropertyViewStatus::Valid); for (size_t i = 0; i < expected.size(); ++i) { MetadataArrayView stringArray = stringProperty.get(static_cast(i)); @@ -2614,12 +2634,13 @@ TEST_CASE("Test StructuralMetadata variable-length arrays of strings") { propertyTableProperty.arrayOffsetType = ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: UINT8; - MetadataPropertyView> arrayProperty = - view.getPropertyView>( - "TestClassProperty"); + PropertyTablePropertyView> + arrayProperty = + view.getPropertyView>( + "TestClassProperty"); REQUIRE( arrayProperty.status() == - MetadataPropertyViewStatus:: + PropertyTablePropertyViewStatus:: ErrorBufferViewSizeDoesNotMatchPropertyTableCount); propertyTableProperty.arrayOffsetType = @@ -2629,7 +2650,7 @@ TEST_CASE("Test StructuralMetadata variable-length arrays of strings") { "TestClassProperty"); REQUIRE( arrayProperty.status() == - MetadataPropertyViewStatus:: + PropertyTablePropertyViewStatus:: ErrorBufferViewSizeDoesNotMatchPropertyTableCount); propertyTableProperty.arrayOffsetType = "NONSENSE"; @@ -2637,7 +2658,7 @@ TEST_CASE("Test StructuralMetadata variable-length arrays of strings") { "TestClassProperty"); REQUIRE( arrayProperty.status() == - MetadataPropertyViewStatus::ErrorInvalidArrayOffsetType); + PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); propertyTableProperty.arrayOffsetType = ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: UINT32; @@ -2647,12 +2668,13 @@ TEST_CASE("Test StructuralMetadata variable-length arrays of strings") { propertyTableProperty.stringOffsetType = ExtensionExtStructuralMetadataPropertyTableProperty::StringOffsetType:: UINT8; - MetadataPropertyView> arrayProperty = - view.getPropertyView>( - "TestClassProperty"); + PropertyTablePropertyView> + arrayProperty = + view.getPropertyView>( + "TestClassProperty"); REQUIRE( arrayProperty.status() == - MetadataPropertyViewStatus:: + PropertyTablePropertyViewStatus:: ErrorBufferViewSizeDoesNotMatchPropertyTableCount); propertyTableProperty.stringOffsetType = @@ -2662,7 +2684,7 @@ TEST_CASE("Test StructuralMetadata variable-length arrays of strings") { "TestClassProperty"); REQUIRE( arrayProperty.status() == - MetadataPropertyViewStatus:: + PropertyTablePropertyViewStatus:: ErrorBufferViewSizeDoesNotMatchPropertyTableCount); propertyTableProperty.stringOffsetType = "NONSENSE"; @@ -2670,7 +2692,7 @@ TEST_CASE("Test StructuralMetadata variable-length arrays of strings") { "TestClassProperty"); REQUIRE( arrayProperty.status() == - MetadataPropertyViewStatus::ErrorInvalidStringOffsetType); + PropertyTablePropertyViewStatus::ErrorInvalidStringOffsetType); propertyTableProperty.stringOffsetType = ExtensionExtStructuralMetadataPropertyTableProperty::StringOffsetType:: UINT32; @@ -2680,12 +2702,13 @@ TEST_CASE("Test StructuralMetadata variable-length arrays of strings") { uint32_t* offset = reinterpret_cast( model.buffers[arrayOffsetBuffer].cesium.data.data()); offset[0] = static_cast(1000); - MetadataPropertyView> arrayProperty = - view.getPropertyView>( - "TestClassProperty"); + PropertyTablePropertyView> + arrayProperty = + view.getPropertyView>( + "TestClassProperty"); REQUIRE( arrayProperty.status() == - MetadataPropertyViewStatus::ErrorArrayOffsetsNotSorted); + PropertyTablePropertyViewStatus::ErrorArrayOffsetsNotSorted); offset[0] = 0; } @@ -2693,12 +2716,13 @@ TEST_CASE("Test StructuralMetadata variable-length arrays of strings") { uint32_t* offset = reinterpret_cast( model.buffers[stringOffsetBuffer].cesium.data.data()); offset[0] = static_cast(1000); - MetadataPropertyView> arrayProperty = - view.getPropertyView>( - "TestClassProperty"); + PropertyTablePropertyView> + arrayProperty = + view.getPropertyView>( + "TestClassProperty"); REQUIRE( arrayProperty.status() == - MetadataPropertyViewStatus::ErrorStringOffsetsNotSorted); + PropertyTablePropertyViewStatus::ErrorStringOffsetsNotSorted); offset[0] = 0; } @@ -2707,12 +2731,13 @@ TEST_CASE("Test StructuralMetadata variable-length arrays of strings") { model.buffers[arrayOffsetBuffer].cesium.data.data()); uint32_t previousValue = offset[propertyTable.count]; offset[propertyTable.count] = static_cast(100000); - MetadataPropertyView> arrayProperty = - view.getPropertyView>( - "TestClassProperty"); + PropertyTablePropertyView> + arrayProperty = + view.getPropertyView>( + "TestClassProperty"); REQUIRE( arrayProperty.status() == - MetadataPropertyViewStatus::ErrorArrayOffsetOutOfBounds); + PropertyTablePropertyViewStatus::ErrorArrayOffsetOutOfBounds); offset[propertyTable.count] = previousValue; } @@ -2721,24 +2746,25 @@ TEST_CASE("Test StructuralMetadata variable-length arrays of strings") { model.buffers[stringOffsetBuffer].cesium.data.data()); uint32_t previousValue = offset[6]; offset[6] = static_cast(100000); - MetadataPropertyView> arrayProperty = - view.getPropertyView>( - "TestClassProperty"); + PropertyTablePropertyView> + arrayProperty = + view.getPropertyView>( + "TestClassProperty"); REQUIRE( arrayProperty.status() == - MetadataPropertyViewStatus::ErrorStringOffsetOutOfBounds); + PropertyTablePropertyViewStatus::ErrorStringOffsetOutOfBounds); offset[6] = previousValue; } SECTION("Count and offset buffer both present") { testClassProperty.count = 3; - MetadataPropertyView> + PropertyTablePropertyView> boolArrayProperty = view.getPropertyView>( "TestClassProperty"); REQUIRE( boolArrayProperty.status() == - MetadataPropertyViewStatus::ErrorArrayCountAndOffsetBufferCoexist); + PropertyTablePropertyViewStatus::ErrorArrayCountAndOffsetBufferCoexist); } } @@ -2758,10 +2784,8 @@ TEST_CASE("Test StructuralMetadata callback on invalid property table view") { propertyTable.properties["TestClassProperty"]; propertyTableProperty.values = static_cast(-1); - MetadataPropertyTableView view(model, propertyTable); - REQUIRE( - view.status() == - MetadataPropertyTableViewStatus::ErrorPropertyTableClassDoesNotExist); + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::ErrorClassNotFound); REQUIRE(view.size() == 0); const ExtensionExtStructuralMetadataClassProperty* classProperty = @@ -2777,7 +2801,7 @@ TEST_CASE("Test StructuralMetadata callback on invalid property table view") { invokedCallbackCount++; REQUIRE( propertyValue.status() == - MetadataPropertyViewStatus::ErrorInvalidPropertyTable); + PropertyTablePropertyViewStatus::ErrorInvalidPropertyTable); REQUIRE(propertyValue.size() == 0); }); @@ -2807,8 +2831,8 @@ TEST_CASE("Test StructuralMetadata callback for invalid property") { propertyTable.properties["InvalidProperty"]; propertyTableProperty.values = static_cast(-1); - MetadataPropertyTableView view(model, propertyTable); - REQUIRE(view.status() == MetadataPropertyTableViewStatus::Valid); + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); REQUIRE(view.size() == propertyTable.count); const ExtensionExtStructuralMetadataClassProperty* classProperty = @@ -2823,7 +2847,7 @@ TEST_CASE("Test StructuralMetadata callback for invalid property") { const std::string& /*propertyName*/, auto propertyValue) mutable { invokedCallbackCount++; - REQUIRE(propertyValue.status() != MetadataPropertyViewStatus::Valid); + REQUIRE(propertyValue.status() != PropertyTablePropertyViewStatus::Valid); REQUIRE(propertyValue.size() == 0); }; @@ -2879,8 +2903,8 @@ TEST_CASE("Test StructuralMetadata callback for scalar property") { propertyTable.properties["TestClassProperty"]; propertyTableProperty.values = static_cast(valueBufferViewIndex); - MetadataPropertyTableView view(model, propertyTable); - REQUIRE(view.status() == MetadataPropertyTableViewStatus::Valid); + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); REQUIRE(view.size() == propertyTable.count); const ExtensionExtStructuralMetadataClassProperty* classProperty = @@ -2903,9 +2927,10 @@ TEST_CASE("Test StructuralMetadata callback for scalar property") { auto propertyValue) mutable { invokedCallbackCount++; if constexpr (std::is_same_v< - MetadataPropertyView, + PropertyTablePropertyView, decltype(propertyValue)>) { - REQUIRE(propertyValue.status() == MetadataPropertyViewStatus::Valid); + REQUIRE( + propertyValue.status() == PropertyTablePropertyViewStatus::Valid); REQUIRE(propertyValue.size() > 0); for (int64_t i = 0; i < propertyValue.size(); ++i) { @@ -2914,8 +2939,9 @@ TEST_CASE("Test StructuralMetadata callback for scalar property") { values[static_cast(i)]); } } else { - FAIL("getPropertyView returned MetadataPropertyView of incorrect " - "type for TestClassProperty."); + FAIL( + "getPropertyView returned PropertyTablePropertyView of incorrect " + "type for TestClassProperty."); } }); @@ -2973,8 +2999,8 @@ TEST_CASE("Test StructuralMetadata callback for vecN property") { propertyTable.properties["TestClassProperty"]; propertyTableProperty.values = static_cast(valueBufferViewIndex); - MetadataPropertyTableView view(model, propertyTable); - REQUIRE(view.status() == MetadataPropertyTableViewStatus::Valid); + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); REQUIRE(view.size() == propertyTable.count); const ExtensionExtStructuralMetadataClassProperty* classProperty = @@ -2996,11 +3022,12 @@ TEST_CASE("Test StructuralMetadata callback for vecN property") { const std::string& /*propertyName*/, auto propertyValue) mutable { invokedCallbackCount++; - REQUIRE(propertyValue.status() == MetadataPropertyViewStatus::Valid); + REQUIRE( + propertyValue.status() == PropertyTablePropertyViewStatus::Valid); REQUIRE(propertyValue.size() > 0); if constexpr (std::is_same_v< - MetadataPropertyView, + PropertyTablePropertyView, decltype(propertyValue)>) { for (int64_t i = 0; i < propertyValue.size(); ++i) { REQUIRE( @@ -3008,8 +3035,9 @@ TEST_CASE("Test StructuralMetadata callback for vecN property") { values[static_cast(i)]); } } else { - FAIL("getPropertyView returned MetadataPropertyView of incorrect " - "type for TestClassProperty."); + FAIL( + "getPropertyView returned PropertyTablePropertyView of incorrect " + "type for TestClassProperty."); } }); @@ -3077,8 +3105,8 @@ TEST_CASE("Test StructuralMetadata callback for matN property") { propertyTable.properties["TestClassProperty"]; propertyTableProperty.values = static_cast(valueBufferViewIndex); - MetadataPropertyTableView view(model, propertyTable); - REQUIRE(view.status() == MetadataPropertyTableViewStatus::Valid); + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); REQUIRE(view.size() == propertyTable.count); const ExtensionExtStructuralMetadataClassProperty* classProperty = @@ -3099,11 +3127,12 @@ TEST_CASE("Test StructuralMetadata callback for matN property") { [&values, &invokedCallbackCount]( const std::string& /*propertyName*/, auto propertyValue) mutable { - REQUIRE(propertyValue.status() == MetadataPropertyViewStatus::Valid); + REQUIRE( + propertyValue.status() == PropertyTablePropertyViewStatus::Valid); REQUIRE(propertyValue.size() > 0); if constexpr (std::is_same_v< - MetadataPropertyView, + PropertyTablePropertyView, decltype(propertyValue)>) { for (int64_t i = 0; i < propertyValue.size(); ++i) { REQUIRE( @@ -3111,8 +3140,9 @@ TEST_CASE("Test StructuralMetadata callback for matN property") { values[static_cast(i)]); } } else { - FAIL("getPropertyView returned MetadataPropertyView of incorrect " - "type for TestClassProperty."); + FAIL( + "getPropertyView returned PropertyTablePropertyView of incorrect " + "type for TestClassProperty."); } invokedCallbackCount++; }); @@ -3179,8 +3209,8 @@ TEST_CASE("Test StructuralMetadata callback for boolean property") { propertyTableProperty.values = static_cast(model.bufferViews.size() - 1); - MetadataPropertyTableView view(model, propertyTable); - REQUIRE(view.status() == MetadataPropertyTableViewStatus::Valid); + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); REQUIRE(view.size() == propertyTable.count); const ExtensionExtStructuralMetadataClassProperty* classProperty = @@ -3201,11 +3231,12 @@ TEST_CASE("Test StructuralMetadata callback for boolean property") { auto propertyValue) mutable { invokedCallbackCount++; - REQUIRE(propertyValue.status() == MetadataPropertyViewStatus::Valid); + REQUIRE( + propertyValue.status() == PropertyTablePropertyViewStatus::Valid); REQUIRE(propertyValue.size() > 0); if constexpr (std::is_same_v< - MetadataPropertyView, + PropertyTablePropertyView, decltype(propertyValue)>) { for (int64_t i = 0; i < propertyValue.size(); ++i) { REQUIRE( @@ -3213,8 +3244,9 @@ TEST_CASE("Test StructuralMetadata callback for boolean property") { expected[static_cast(i)]); } } else { - FAIL("getPropertyView returned MetadataPropertyView of incorrect " - "type for TestClassProperty."); + FAIL( + "getPropertyView returned PropertyTablePropertyView of incorrect " + "type for TestClassProperty."); } }); @@ -3300,8 +3332,8 @@ TEST_CASE("Test StructuralMetadata callback for string property") { propertyTableProperty.stringOffsets = static_cast(offsetBufferViewIndex); - MetadataPropertyTableView view(model, propertyTable); - REQUIRE(view.status() == MetadataPropertyTableViewStatus::Valid); + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); REQUIRE(view.size() == propertyTable.count); const ExtensionExtStructuralMetadataClassProperty* classProperty = @@ -3321,11 +3353,12 @@ TEST_CASE("Test StructuralMetadata callback for string property") { const std::string& /*propertyName*/, auto propertyValue) mutable { invokedCallbackCount++; - REQUIRE(propertyValue.status() == MetadataPropertyViewStatus::Valid); + REQUIRE( + propertyValue.status() == PropertyTablePropertyViewStatus::Valid); REQUIRE(propertyValue.size() > 0); if constexpr (std::is_same_v< - MetadataPropertyView, + PropertyTablePropertyView, decltype(propertyValue)>) { for (int64_t i = 0; i < propertyValue.size(); ++i) { REQUIRE( @@ -3333,8 +3366,9 @@ TEST_CASE("Test StructuralMetadata callback for string property") { expected[static_cast(i)]); } } else { - FAIL("getPropertyView returned MetadataPropertyView of incorrect " - "type for TestClassProperty."); + FAIL( + "getPropertyView returned PropertyTablePropertyView of incorrect " + "type for TestClassProperty."); } }); @@ -3388,8 +3422,8 @@ TEST_CASE("Test StructuralMetadata callback for scalar array") { propertyTableProperty.values = static_cast(model.bufferViews.size() - 1); - MetadataPropertyTableView view(model, propertyTable); - REQUIRE(view.status() == MetadataPropertyTableViewStatus::Valid); + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); REQUIRE(view.size() == propertyTable.count); const ExtensionExtStructuralMetadataClassProperty* classProperty = @@ -3411,11 +3445,13 @@ TEST_CASE("Test StructuralMetadata callback for scalar array") { const std::string& /*propertyName*/, auto propertyValue) { invokedCallbackCount++; - REQUIRE(propertyValue.status() == MetadataPropertyViewStatus::Valid); + REQUIRE( + propertyValue.status() == PropertyTablePropertyViewStatus::Valid); REQUIRE(propertyValue.size() > 0); if constexpr (std::is_same_v< - MetadataPropertyView>, + PropertyTablePropertyView< + MetadataArrayView>, decltype(propertyValue)>) { for (int64_t i = 0; i < propertyValue.size(); ++i) { MetadataArrayView member = propertyValue.get(i); @@ -3424,8 +3460,9 @@ TEST_CASE("Test StructuralMetadata callback for scalar array") { } } } else { - FAIL("getPropertyView returned MetadataPropertyView of incorrect " - "type for TestClassProperty."); + FAIL( + "getPropertyView returned PropertyTablePropertyView of incorrect " + "type for TestClassProperty."); } }); @@ -3485,8 +3522,8 @@ TEST_CASE("Test StructuralMetadata callback for vecN array") { propertyTableProperty.values = static_cast(model.bufferViews.size() - 1); - MetadataPropertyTableView view(model, propertyTable); - REQUIRE(view.status() == MetadataPropertyTableViewStatus::Valid); + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); REQUIRE(view.size() == propertyTable.count); const ExtensionExtStructuralMetadataClassProperty* classProperty = @@ -3508,11 +3545,13 @@ TEST_CASE("Test StructuralMetadata callback for vecN array") { const std::string& /*propertyName*/, auto propertyValue) { invokedCallbackCount++; - REQUIRE(propertyValue.status() == MetadataPropertyViewStatus::Valid); + REQUIRE( + propertyValue.status() == PropertyTablePropertyViewStatus::Valid); REQUIRE(propertyValue.size() > 0); if constexpr (std::is_same_v< - MetadataPropertyView>, + PropertyTablePropertyView< + MetadataArrayView>, decltype(propertyValue)>) { for (int64_t i = 0; i < propertyValue.size(); ++i) { MetadataArrayView member = propertyValue.get(i); @@ -3521,8 +3560,9 @@ TEST_CASE("Test StructuralMetadata callback for vecN array") { } } } else { - FAIL("getPropertyView returned MetadataPropertyView of incorrect " - "type for TestClassProperty."); + FAIL( + "getPropertyView returned PropertyTablePropertyView of incorrect " + "type for TestClassProperty."); } }); @@ -3596,8 +3636,8 @@ TEST_CASE("Test StructuralMetadata callback for matN array") { propertyTableProperty.values = static_cast(model.bufferViews.size() - 1); - MetadataPropertyTableView view(model, propertyTable); - REQUIRE(view.status() == MetadataPropertyTableViewStatus::Valid); + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); REQUIRE(view.size() == propertyTable.count); const ExtensionExtStructuralMetadataClassProperty* classProperty = @@ -3619,11 +3659,12 @@ TEST_CASE("Test StructuralMetadata callback for matN array") { const std::string& /*propertyName*/, auto propertyValue) { invokedCallbackCount++; - REQUIRE(propertyValue.status() == MetadataPropertyViewStatus::Valid); + REQUIRE( + propertyValue.status() == PropertyTablePropertyViewStatus::Valid); REQUIRE(propertyValue.size() > 0); if constexpr (std::is_same_v< - MetadataPropertyView< + PropertyTablePropertyView< MetadataArrayView>, decltype(propertyValue)>) { for (int64_t i = 0; i < propertyValue.size(); ++i) { @@ -3633,8 +3674,9 @@ TEST_CASE("Test StructuralMetadata callback for matN array") { } } } else { - FAIL("getPropertyView returned MetadataPropertyView of incorrect " - "type for TestClassProperty."); + FAIL( + "getPropertyView returned PropertyTablePropertyView of incorrect " + "type for TestClassProperty."); } }); @@ -3708,8 +3750,8 @@ TEST_CASE("Test StructuralMetadata callback for boolean array") { propertyTableProperty.values = static_cast(model.bufferViews.size() - 1); - MetadataPropertyTableView view(model, propertyTable); - REQUIRE(view.status() == MetadataPropertyTableViewStatus::Valid); + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); REQUIRE(view.size() == propertyTable.count); const ExtensionExtStructuralMetadataClassProperty* classProperty = @@ -3728,11 +3770,12 @@ TEST_CASE("Test StructuralMetadata callback for boolean array") { const std::string& /*propertyName*/, auto propertyValue) { invokedCallbackCount++; - REQUIRE(propertyValue.status() == MetadataPropertyViewStatus::Valid); + REQUIRE( + propertyValue.status() == PropertyTablePropertyViewStatus::Valid); REQUIRE(propertyValue.size() > 0); if constexpr (std::is_same_v< - MetadataPropertyView>, + PropertyTablePropertyView>, decltype(propertyValue)>) { for (int64_t i = 0; i < propertyValue.size(); ++i) { MetadataArrayView member = propertyValue.get(i); @@ -3741,8 +3784,9 @@ TEST_CASE("Test StructuralMetadata callback for boolean array") { } } } else { - FAIL("getPropertyView returned MetadataPropertyView of incorrect " - "type for TestClassProperty."); + FAIL( + "getPropertyView returned PropertyTablePropertyView of incorrect " + "type for TestClassProperty."); } }); @@ -3831,8 +3875,8 @@ TEST_CASE("Test StructuralMetadata callback for array of strings") { propertyTableProperty.stringOffsets = static_cast(offsetBufferViewIndex); - MetadataPropertyTableView view(model, propertyTable); - REQUIRE(view.status() == MetadataPropertyTableViewStatus::Valid); + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); REQUIRE(view.size() == propertyTable.count); const ExtensionExtStructuralMetadataClassProperty* classProperty = @@ -3851,11 +3895,12 @@ TEST_CASE("Test StructuralMetadata callback for array of strings") { const std::string& /*propertyName*/, auto propertyValue) { invokedCallbackCount++; - REQUIRE(propertyValue.status() == MetadataPropertyViewStatus::Valid); + REQUIRE( + propertyValue.status() == PropertyTablePropertyViewStatus::Valid); REQUIRE(propertyValue.size() == 3); if constexpr (std::is_same_v< - MetadataPropertyView< + PropertyTablePropertyView< MetadataArrayView>, decltype(propertyValue)>) { MetadataArrayView v0 = propertyValue.get(0); @@ -3876,8 +3921,9 @@ TEST_CASE("Test StructuralMetadata callback for array of strings") { REQUIRE(v2[0] == "I love you, meat bags! ❤️"); REQUIRE(v2[1] == "Book in the freezer"); } else { - FAIL("getPropertyView returned MetadataPropertyView of incorrect " - "type for TestClassProperty."); + FAIL( + "getPropertyView returned PropertyTablePropertyView of incorrect " + "type for TestClassProperty."); } }); diff --git a/CesiumGltf/test/TestStructuralMetadataPropertyTextureView.cpp b/CesiumGltf/test/TestStructuralMetadataPropertyTextureView.cpp new file mode 100644 index 000000000..01cfed051 --- /dev/null +++ b/CesiumGltf/test/TestStructuralMetadataPropertyTextureView.cpp @@ -0,0 +1,169 @@ +#include "CesiumGltf/StructuralMetadataPropertyTextureView.h" + +#include +#include + +#include +#include +#include +#include +#include + +using namespace CesiumGltf; +using namespace CesiumGltf::StructuralMetadata; + +TEST_CASE("Test PropertyTextureView on model without EXT_structural_metadata " + "extension") { + Model model; + + // Create an erroneously isolated property texture. + ExtensionExtStructuralMetadataPropertyTexture propertyTexture; + propertyTexture.classProperty = "TestClass"; + + ExtensionExtStructuralMetadataPropertyTextureProperty& + propertyTextureProperty = propertyTexture.properties["TestClassProperty"]; + propertyTextureProperty.index = 0; + propertyTextureProperty.texCoord = 0; + propertyTextureProperty.channels = {0}; + + PropertyTextureView view(model, propertyTexture); + REQUIRE( + view.status() == + PropertyTextureViewStatus::ErrorMissingMetadataExtension); + REQUIRE(view.getProperties().empty()); + + const ExtensionExtStructuralMetadataClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(!classProperty); +} + +TEST_CASE("Test PropertyTextureView on model without metadata schema") { + Model model; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + ExtensionExtStructuralMetadataPropertyTexture& propertyTexture = + metadata.propertyTextures.emplace_back(); + propertyTexture.classProperty = "TestClass"; + + ExtensionExtStructuralMetadataPropertyTextureProperty& + propertyTextureProperty = propertyTexture.properties["TestClassProperty"]; + propertyTextureProperty.index = 0; + propertyTextureProperty.texCoord = 0; + propertyTextureProperty.channels = {0}; + + PropertyTextureView view(model, propertyTexture); + REQUIRE(view.status() == PropertyTextureViewStatus::ErrorMissingSchema); + REQUIRE(view.getProperties().empty()); + + const ExtensionExtStructuralMetadataClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(!classProperty); +} + +TEST_CASE("Test property texture with nonexistent class") { + Model model; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); + ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; + ExtensionExtStructuralMetadataClassProperty& testClassProperty = + testClass.properties["TestClassProperty"]; + testClassProperty.type = + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR; + testClassProperty.componentType = + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT8; + + ExtensionExtStructuralMetadataPropertyTexture& propertyTexture = + metadata.propertyTextures.emplace_back(); + propertyTexture.classProperty = "I Don't Exist"; + + ExtensionExtStructuralMetadataPropertyTextureProperty& + propertyTextureProperty = propertyTexture.properties["TestClassProperty"]; + propertyTextureProperty.index = 0; + propertyTextureProperty.texCoord = 0; + propertyTextureProperty.channels = {0}; + + PropertyTextureView view(model, propertyTexture); + REQUIRE(view.status() == PropertyTextureViewStatus::ErrorClassNotFound); + REQUIRE(view.getProperties().empty()); + + const ExtensionExtStructuralMetadataClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(!classProperty); +} + +TEST_CASE("Test property texture with nonexistent class property") { + Model model; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); + ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; + ExtensionExtStructuralMetadataClassProperty& testClassProperty = + testClass.properties["TestClassProperty"]; + testClassProperty.type = + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR; + testClassProperty.componentType = + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT8; + + ExtensionExtStructuralMetadataPropertyTexture& propertyTexture = + metadata.propertyTextures.emplace_back(); + propertyTexture.classProperty = "TestClass"; + + ExtensionExtStructuralMetadataPropertyTextureProperty& + propertyTextureProperty = propertyTexture.properties["I Don't Exist"]; + propertyTextureProperty.index = 0; + propertyTextureProperty.texCoord = 0; + propertyTextureProperty.channels = {0}; + + PropertyTextureView view(model, propertyTexture); + REQUIRE( + view.status() == PropertyTextureViewStatus::ErrorClassPropertyNotFound); + REQUIRE(view.getProperties().empty()); + + const ExtensionExtStructuralMetadataClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(!classProperty); +} + +TEST_CASE("Test property texture with invalid property") { + Model model; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); + ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; + ExtensionExtStructuralMetadataClassProperty& testClassProperty = + testClass.properties["TestClassProperty"]; + testClassProperty.type = + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR; + testClassProperty.componentType = + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT8; + + ExtensionExtStructuralMetadataPropertyTexture& propertyTexture = + metadata.propertyTextures.emplace_back(); + propertyTexture.classProperty = "TestClass"; + + ExtensionExtStructuralMetadataPropertyTextureProperty& + propertyTextureProperty = propertyTexture.properties["TestClassProperty"]; + propertyTextureProperty.index = -1; + + PropertyTextureView view(model, propertyTexture); + REQUIRE(view.status() == PropertyTextureViewStatus::ErrorInvalidPropertyView); + + auto properties = view.getProperties(); + REQUIRE(properties.size() == 1); + + PropertyTexturePropertyView& propertyView = properties["TestClassProperty"]; + REQUIRE(propertyView.status() != PropertyTexturePropertyViewStatus::Valid); + + const ExtensionExtStructuralMetadataClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(!classProperty); +} From 44b829a081a0ff56cf8a9bd14c3bf3d3d065aa82 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Tue, 30 May 2023 11:11:06 -0400 Subject: [PATCH 054/421] Add PropertyTextureProperty tests, fix formatting --- ...gradeBatchTableToExtStructuralMetadata.cpp | 2 +- ...turalMetadataPropertyTexturePropertyView.h | 16 +- ...ralMetadataPropertyTexturePropertyView.cpp | 10 +- ...ralMetadataPropertyTexturePropertyView.cpp | 290 ++++++++++++++++++ 4 files changed, 309 insertions(+), 9 deletions(-) create mode 100644 CesiumGltf/test/TestStructuralMetadataPropertyTexturePropertyView.cpp diff --git a/Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp b/Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp index f6d232138..712098bff 100644 --- a/Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp +++ b/Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp @@ -5,8 +5,8 @@ #include #include #include -#include #include +#include #include #include diff --git a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTexturePropertyView.h b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTexturePropertyView.h index 7cd609a13..09e427aaf 100644 --- a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTexturePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTexturePropertyView.h @@ -57,6 +57,11 @@ enum class PropertyTexturePropertyViewStatus { */ ErrorEmptyImage, + /** + * @brief This property texture property has a negative TEXCOORD set index. + */ + ErrorInvalidTexCoordSetIndex, + /** * @brief The channels of this property texture property are invalid. * Channels must be in the range 0-3, with a minimum of one channel and a @@ -187,17 +192,16 @@ class PropertyTexturePropertyView { } /** - * @brief Get the count for this property. - * - * This is also how many channels a pixel value for this property will use. + * @brief Get the count for this property. This is equivalent to how many + * channels a pixel value for this property will use. */ int64_t getCount() const noexcept { return this->_count; } /** * @brief Get the texture coordinate set index for this property. */ - int64_t getTextureCoordinateSetIndex() const noexcept { - return this->_textureCoordinateSetIndex; + int64_t getTexCoordSetIndex() const noexcept { + return this->_texCoordSetIndex; } /** @@ -233,7 +237,7 @@ class PropertyTexturePropertyView { const Sampler* _pSampler; const ImageCesium* _pImage; - int64_t _textureCoordinateSetIndex; + int64_t _texCoordSetIndex; std::vector _channels; std::string _swizzle; PropertyTexturePropertyComponentType _type; diff --git a/CesiumGltf/src/StructuralMetadataPropertyTexturePropertyView.cpp b/CesiumGltf/src/StructuralMetadataPropertyTexturePropertyView.cpp index 316341124..d43e5bdfd 100644 --- a/CesiumGltf/src/StructuralMetadataPropertyTexturePropertyView.cpp +++ b/CesiumGltf/src/StructuralMetadataPropertyTexturePropertyView.cpp @@ -10,7 +10,7 @@ PropertyTexturePropertyView::PropertyTexturePropertyView() noexcept _pPropertyTextureProperty(nullptr), _pSampler(nullptr), _pImage(nullptr), - _textureCoordinateSetIndex(-1), + _texCoordSetIndex(-1), _channels(), _swizzle(""), _type(PropertyTexturePropertyComponentType::Uint8), @@ -27,7 +27,7 @@ PropertyTexturePropertyView::PropertyTexturePropertyView( _pPropertyTextureProperty(&propertyTextureProperty), _pSampler(nullptr), _pImage(nullptr), - _textureCoordinateSetIndex(propertyTextureProperty.texCoord), + _texCoordSetIndex(propertyTextureProperty.texCoord), _channels(), _swizzle(""), _type(PropertyTexturePropertyComponentType::Uint8), @@ -61,6 +61,12 @@ PropertyTexturePropertyView::PropertyTexturePropertyView( return; } + if (this->_texCoordSetIndex < 0) { + this->_status = + PropertyTexturePropertyViewStatus::ErrorInvalidTexCoordSetIndex; + return; + } + // TODO: support more types // this->_type = ... diff --git a/CesiumGltf/test/TestStructuralMetadataPropertyTexturePropertyView.cpp b/CesiumGltf/test/TestStructuralMetadataPropertyTexturePropertyView.cpp new file mode 100644 index 000000000..234b68d89 --- /dev/null +++ b/CesiumGltf/test/TestStructuralMetadataPropertyTexturePropertyView.cpp @@ -0,0 +1,290 @@ +#include "CesiumGltf/StructuralMetadataPropertyTexturePropertyView.h" + +#include +#include + +#include +#include +#include +#include +#include + +using namespace CesiumGltf; +using namespace CesiumGltf::StructuralMetadata; + +TEST_CASE( + "Test PropertyTexturePropertyView on property with invalid texture index") { + Model model; + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); + ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; + ExtensionExtStructuralMetadataClassProperty& testClassProperty = + testClass.properties["TestClassProperty"]; + testClassProperty.type = + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR; + testClassProperty.componentType = + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT8; + + ExtensionExtStructuralMetadataPropertyTexture propertyTexture; + propertyTexture.classProperty = "TestClass"; + + ExtensionExtStructuralMetadataPropertyTextureProperty& + propertyTextureProperty = propertyTexture.properties["TestClassProperty"]; + propertyTextureProperty.index = -1; + propertyTextureProperty.texCoord = 0; + propertyTextureProperty.channels = {0}; + + PropertyTexturePropertyView view( + model, + testClassProperty, + propertyTextureProperty); + REQUIRE( + view.status() == PropertyTexturePropertyViewStatus::ErrorInvalidTexture); +} + +TEST_CASE( + "Test PropertyTexturePropertyView on property with invalid sampler index") { + Model model; + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); + ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; + ExtensionExtStructuralMetadataClassProperty& testClassProperty = + testClass.properties["TestClassProperty"]; + testClassProperty.type = + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR; + testClassProperty.componentType = + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT8; + + Image& image = model.images.emplace_back(); + image.cesium.width = 1; + image.cesium.height = 1; + Texture& texture = model.textures.emplace_back(); + texture.sampler = -1; + texture.source = 0; + + ExtensionExtStructuralMetadataPropertyTexture propertyTexture; + propertyTexture.classProperty = "TestClass"; + + ExtensionExtStructuralMetadataPropertyTextureProperty& + propertyTextureProperty = propertyTexture.properties["TestClassProperty"]; + propertyTextureProperty.index = 0; + propertyTextureProperty.texCoord = 0; + propertyTextureProperty.channels = {0}; + + PropertyTexturePropertyView view( + model, + testClassProperty, + propertyTextureProperty); + REQUIRE( + view.status() == + PropertyTexturePropertyViewStatus::ErrorInvalidTextureSampler); +} + +TEST_CASE( + "Test PropertyTexturePropertyView on property with invalid image index") { + Model model; + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); + ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; + ExtensionExtStructuralMetadataClassProperty& testClassProperty = + testClass.properties["TestClassProperty"]; + testClassProperty.type = + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR; + testClassProperty.componentType = + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT8; + + model.samplers.emplace_back(); + + Texture& texture = model.textures.emplace_back(); + texture.sampler = 0; + texture.source = -1; + + ExtensionExtStructuralMetadataPropertyTexture propertyTexture; + propertyTexture.classProperty = "TestClass"; + + ExtensionExtStructuralMetadataPropertyTextureProperty& + propertyTextureProperty = propertyTexture.properties["TestClassProperty"]; + propertyTextureProperty.index = 0; + propertyTextureProperty.texCoord = 0; + propertyTextureProperty.channels = {0}; + + PropertyTexturePropertyView view( + model, + testClassProperty, + propertyTextureProperty); + REQUIRE( + view.status() == PropertyTexturePropertyViewStatus::ErrorInvalidImage); +} + +TEST_CASE("Test PropertyTexturePropertyView on property with empty image") { + Model model; + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); + ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; + ExtensionExtStructuralMetadataClassProperty& testClassProperty = + testClass.properties["TestClassProperty"]; + testClassProperty.type = + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR; + testClassProperty.componentType = + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT8; + + Image& image = model.images.emplace_back(); + image.cesium.width = 0; + image.cesium.height = 0; + + model.samplers.emplace_back(); + + Texture& texture = model.textures.emplace_back(); + texture.sampler = 0; + texture.source = 0; + + ExtensionExtStructuralMetadataPropertyTexture propertyTexture; + propertyTexture.classProperty = "TestClass"; + + ExtensionExtStructuralMetadataPropertyTextureProperty& + propertyTextureProperty = propertyTexture.properties["TestClassProperty"]; + propertyTextureProperty.index = 0; + propertyTextureProperty.texCoord = 0; + propertyTextureProperty.channels = {0}; + + PropertyTexturePropertyView view( + model, + testClassProperty, + propertyTextureProperty); + REQUIRE(view.status() == PropertyTexturePropertyViewStatus::ErrorEmptyImage); +} + +TEST_CASE("Test PropertyTextureView on model with negative texture coordinate " + "set index") { + Model model; + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); + ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; + ExtensionExtStructuralMetadataClassProperty& testClassProperty = + testClass.properties["TestClassProperty"]; + testClassProperty.type = + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR; + testClassProperty.componentType = + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT8; + + Image& image = model.images.emplace_back(); + image.cesium.width = 1; + image.cesium.height = 1; + + model.samplers.emplace_back(); + + Texture& texture = model.textures.emplace_back(); + texture.sampler = 0; + texture.source = 0; + + ExtensionExtStructuralMetadataPropertyTexture propertyTexture; + propertyTexture.classProperty = "TestClass"; + + ExtensionExtStructuralMetadataPropertyTextureProperty& + propertyTextureProperty = propertyTexture.properties["TestClassProperty"]; + propertyTextureProperty.index = 0; + propertyTextureProperty.texCoord = -1; + propertyTextureProperty.channels = {0}; + + PropertyTexturePropertyView view( + model, + testClassProperty, + propertyTextureProperty); + REQUIRE( + view.status() == + PropertyTexturePropertyViewStatus::ErrorInvalidTexCoordSetIndex); +} + +TEST_CASE("Test PropertyTextureView on model with zero channels") { + Model model; + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); + ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; + ExtensionExtStructuralMetadataClassProperty& testClassProperty = + testClass.properties["TestClassProperty"]; + testClassProperty.type = + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR; + testClassProperty.componentType = + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT8; + + Image& image = model.images.emplace_back(); + image.cesium.width = 1; + image.cesium.height = 1; + image.cesium.channels = 1; + + model.samplers.emplace_back(); + + Texture& texture = model.textures.emplace_back(); + texture.sampler = 0; + texture.source = 0; + + ExtensionExtStructuralMetadataPropertyTexture propertyTexture; + propertyTexture.classProperty = "TestClass"; + + ExtensionExtStructuralMetadataPropertyTextureProperty& + propertyTextureProperty = propertyTexture.properties["TestClassProperty"]; + propertyTextureProperty.index = 0; + propertyTextureProperty.texCoord = 0; + propertyTextureProperty.channels = {}; + + PropertyTexturePropertyView view( + model, + testClassProperty, + propertyTextureProperty); + REQUIRE( + view.status() == PropertyTexturePropertyViewStatus::ErrorInvalidChannels); +} + +TEST_CASE("Test PropertyTextureView on model with too many channels") { + Model model; + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); + ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; + ExtensionExtStructuralMetadataClassProperty& testClassProperty = + testClass.properties["TestClassProperty"]; + testClassProperty.type = + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR; + testClassProperty.componentType = + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT8; + + Image& image = model.images.emplace_back(); + image.cesium.width = 1; + image.cesium.height = 1; + image.cesium.channels = 1; + + model.samplers.emplace_back(); + + Texture& texture = model.textures.emplace_back(); + texture.sampler = 0; + texture.source = 0; + + ExtensionExtStructuralMetadataPropertyTexture propertyTexture; + propertyTexture.classProperty = "TestClass"; + + ExtensionExtStructuralMetadataPropertyTextureProperty& + propertyTextureProperty = propertyTexture.properties["TestClassProperty"]; + propertyTextureProperty.index = 0; + propertyTextureProperty.texCoord = 0; + propertyTextureProperty.channels = {0, 1}; + + PropertyTexturePropertyView view( + model, + testClassProperty, + propertyTextureProperty); + REQUIRE( + view.status() == PropertyTexturePropertyViewStatus::ErrorInvalidChannels); +} From db0f1734d163feda66247c2b61576fb549fb3711 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Tue, 30 May 2023 20:26:14 -0400 Subject: [PATCH 055/421] Fix formatting --- .../StructuralMetadataPropertyTexturePropertyView.h | 4 +--- CesiumGltf/src/StructuralMetadataPropertyTableView.cpp | 9 ++++----- .../StructuralMetadataPropertyTexturePropertyView.cpp | 1 - 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTexturePropertyView.h b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTexturePropertyView.h index 09e427aaf..ca2e3eda7 100644 --- a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTexturePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTexturePropertyView.h @@ -89,9 +89,7 @@ enum class PropertyTexturePropertyComponentType { * @tparam T The component type, must correspond to a valid * {@link PropertyTexturePropertyComponentType}. */ -template struct PropertyTexturePropertyValue { - T components[4]; -}; +template struct PropertyTexturePropertyValue { T components[4]; }; /** * @brief A view of the data specified by a diff --git a/CesiumGltf/src/StructuralMetadataPropertyTableView.cpp b/CesiumGltf/src/StructuralMetadataPropertyTableView.cpp index c1012e1e5..b6e58fa87 100644 --- a/CesiumGltf/src/StructuralMetadataPropertyTableView.cpp +++ b/CesiumGltf/src/StructuralMetadataPropertyTableView.cpp @@ -138,8 +138,7 @@ PropertyTableView::PropertyTableView( } const ExtensionExtStructuralMetadataClassProperty* -PropertyTableView::getClassProperty( - const std::string& propertyName) const { +PropertyTableView::getClassProperty(const std::string& propertyName) const { if (_status != PropertyTableViewStatus::Valid) { return nullptr; } @@ -231,8 +230,7 @@ PropertyTablePropertyViewStatus PropertyTableView::getArrayOffsetsBufferSafe( } } -PropertyTablePropertyViewStatus -PropertyTableView::getStringOffsetsBufferSafe( +PropertyTablePropertyViewStatus PropertyTableView::getStringOffsetsBufferSafe( int32_t stringOffsetsBufferView, PropertyComponentType stringOffsetType, size_t valueBufferSize, @@ -367,7 +365,8 @@ PropertyTableView::getStringArrayPropertyValues( if (fixedLengthArrayCount <= 0 && propertyTableProperty.arrayOffsets < 0) { return createInvalidPropertyView>( - PropertyTablePropertyViewStatus::ErrorArrayCountAndOffsetBufferDontExist); + PropertyTablePropertyViewStatus:: + ErrorArrayCountAndOffsetBufferDontExist); } // Get string offset type diff --git a/CesiumGltf/src/StructuralMetadataPropertyTexturePropertyView.cpp b/CesiumGltf/src/StructuralMetadataPropertyTexturePropertyView.cpp index d43e5bdfd..aaebff6d6 100644 --- a/CesiumGltf/src/StructuralMetadataPropertyTexturePropertyView.cpp +++ b/CesiumGltf/src/StructuralMetadataPropertyTexturePropertyView.cpp @@ -3,7 +3,6 @@ namespace CesiumGltf { namespace StructuralMetadata { - PropertyTexturePropertyView::PropertyTexturePropertyView() noexcept : _status(PropertyTexturePropertyViewStatus::ErrorUninitialized), _pClassProperty(nullptr), From 60a528a19b71f7857a81ca70f3f26bf386be281f Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Tue, 30 May 2023 20:28:42 -0400 Subject: [PATCH 056/421] Last formatting fix --- CesiumGltf/src/StructuralMetadataPropertyTextureView.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CesiumGltf/src/StructuralMetadataPropertyTextureView.cpp b/CesiumGltf/src/StructuralMetadataPropertyTextureView.cpp index 507aa9e0c..7ebcdab2a 100644 --- a/CesiumGltf/src/StructuralMetadataPropertyTextureView.cpp +++ b/CesiumGltf/src/StructuralMetadataPropertyTextureView.cpp @@ -11,7 +11,8 @@ PropertyTextureView::PropertyTextureView() noexcept PropertyTextureView::PropertyTextureView( const Model& model, - const ExtensionExtStructuralMetadataPropertyTexture& propertyTexture) noexcept + const ExtensionExtStructuralMetadataPropertyTexture& + propertyTexture) noexcept : _pModel(&model), _pPropertyTexture(&propertyTexture), _pClass(nullptr), From 224f392c31ec4ecf43193dc798c9b22e0bd857ed Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Thu, 1 Jun 2023 11:45:24 +1000 Subject: [PATCH 057/421] Cleanup and tests. --- CesiumAsync/src/GunzipAssetAccessor.cpp | 53 +++-- CesiumAsync/test/MockAssetAccessor.h | 38 ++++ CesiumAsync/test/MockAssetRequest.h | 1 + CesiumAsync/test/MockTaskProcessor.h | 10 + CesiumAsync/test/TestCacheAssetAccessor.cpp | 37 +--- CesiumAsync/test/TestGunzipAssetAccessor.cpp | 195 +++++++++++++++++++ 6 files changed, 282 insertions(+), 52 deletions(-) create mode 100644 CesiumAsync/test/MockAssetAccessor.h create mode 100644 CesiumAsync/test/MockTaskProcessor.h create mode 100644 CesiumAsync/test/TestGunzipAssetAccessor.cpp diff --git a/CesiumAsync/src/GunzipAssetAccessor.cpp b/CesiumAsync/src/GunzipAssetAccessor.cpp index 6e78debde..c5ed21fde 100644 --- a/CesiumAsync/src/GunzipAssetAccessor.cpp +++ b/CesiumAsync/src/GunzipAssetAccessor.cpp @@ -5,11 +5,14 @@ #include "CesiumUtility/Gunzip.h" namespace CesiumAsync { + +namespace { + class GunzippedAssetResponse : public IAssetResponse { public: GunzippedAssetResponse(const IAssetResponse* pOther) noexcept : _pAssetResponse{pOther} { - _dataValid = CesiumUtility::gunzip( + this->_dataValid = CesiumUtility::gunzip( this->_pAssetResponse->data(), this->_gunzippedData); } @@ -27,8 +30,8 @@ class GunzippedAssetResponse : public IAssetResponse { } virtual gsl::span data() const noexcept override { - return _dataValid ? gsl::span(_gunzippedData.data(), _gunzippedData.size()) - : _pAssetResponse->data(); + return this->_dataValid ? this->_gunzippedData + : this->_pAssetResponse->data(); } private: @@ -39,8 +42,9 @@ class GunzippedAssetResponse : public IAssetResponse { class GunzippedAssetRequest : public IAssetRequest { public: - GunzippedAssetRequest(std::shared_ptr& pOther) - : _pAssetRequest(pOther), _AssetResponse(pOther->response()){}; + GunzippedAssetRequest(std::shared_ptr&& pOther) + : _pAssetRequest(std::move(pOther)), + _AssetResponse(_pAssetRequest->response()){}; virtual const std::string& method() const noexcept override { return this->_pAssetRequest->method(); } @@ -62,6 +66,23 @@ class GunzippedAssetRequest : public IAssetRequest { GunzippedAssetResponse _AssetResponse; }; +Future> gunzipIfNeeded( + const AsyncSystem& asyncSystem, + std::shared_ptr&& pCompletedRequest) { + const IAssetResponse* pResponse = pCompletedRequest->response(); + if (pResponse && CesiumUtility::isGzip(pResponse->data())) { + return asyncSystem.runInWorkerThread( + [pCompletedRequest = std::move( + pCompletedRequest)]() mutable -> std::shared_ptr { + return std::make_shared( + std::move(pCompletedRequest)); + }); + } + return asyncSystem.createResolvedFuture(std::move(pCompletedRequest)); +} + +} // namespace + GunzipAssetAccessor::GunzipAssetAccessor( const std::shared_ptr& pAssetAccessor) : _pAssetAccessor(pAssetAccessor) {} @@ -73,17 +94,10 @@ Future> GunzipAssetAccessor::get( const std::string& url, const std::vector& headers) { return this->_pAssetAccessor->get(asyncSystem, url, headers) - .thenImmediately([](std::shared_ptr&& pCompletedRequest) { - const IAssetResponse* pResponse = pCompletedRequest->response(); - if (!pResponse) { - return std::move(pCompletedRequest); - } - if (CesiumUtility::isGzip(pResponse->data())) { - pCompletedRequest = std::make_shared( - GunzippedAssetRequest(pCompletedRequest)); - } - return std::move(pCompletedRequest); - }); + .thenImmediately( + [asyncSystem](std::shared_ptr&& pCompletedRequest) { + return gunzipIfNeeded(asyncSystem, std::move(pCompletedRequest)); + }); } Future> GunzipAssetAccessor::request( @@ -93,8 +107,13 @@ Future> GunzipAssetAccessor::request( const std::vector& headers, const gsl::span& contentPayload) { return this->_pAssetAccessor - ->request(asyncSystem, verb, url, headers, contentPayload); + ->request(asyncSystem, verb, url, headers, contentPayload) + .thenImmediately( + [asyncSystem](std::shared_ptr&& pCompletedRequest) { + return gunzipIfNeeded(asyncSystem, std::move(pCompletedRequest)); + }); } void GunzipAssetAccessor::tick() noexcept { _pAssetAccessor->tick(); } + } // namespace CesiumAsync diff --git a/CesiumAsync/test/MockAssetAccessor.h b/CesiumAsync/test/MockAssetAccessor.h new file mode 100644 index 000000000..175ce7909 --- /dev/null +++ b/CesiumAsync/test/MockAssetAccessor.h @@ -0,0 +1,38 @@ +#pragma once + +#include + +#include +#include +#include + +class MockAssetAccessor : public CesiumAsync::IAssetAccessor { +public: + MockAssetAccessor(const std::shared_ptr& request) + : testRequest{request} {} + + virtual CesiumAsync::Future> + get(const CesiumAsync::AsyncSystem& asyncSystem, + const std::string& /* url */, + const std::vector& /* headers */ + ) override { + return asyncSystem.createResolvedFuture( + std::shared_ptr(testRequest)); + } + + virtual CesiumAsync::Future> + request( + const CesiumAsync::AsyncSystem& asyncSystem, + const std::string& /* verb */, + const std::string& /* url */, + const std::vector& /* headers */, + const gsl::span& /* contentPayload */ + ) override { + return asyncSystem.createResolvedFuture( + std::shared_ptr(testRequest)); + } + + virtual void tick() noexcept override {} + + std::shared_ptr testRequest; +}; diff --git a/CesiumAsync/test/MockAssetRequest.h b/CesiumAsync/test/MockAssetRequest.h index 53b8222fa..50464a427 100644 --- a/CesiumAsync/test/MockAssetRequest.h +++ b/CesiumAsync/test/MockAssetRequest.h @@ -2,6 +2,7 @@ #include "CesiumAsync/HttpHeaders.h" #include "CesiumAsync/IAssetRequest.h" +#include "CesiumAsync/IAssetResponse.h" #include diff --git a/CesiumAsync/test/MockTaskProcessor.h b/CesiumAsync/test/MockTaskProcessor.h new file mode 100644 index 000000000..4550a4542 --- /dev/null +++ b/CesiumAsync/test/MockTaskProcessor.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +#include + +class MockTaskProcessor : public CesiumAsync::ITaskProcessor { +public: + virtual void startTask(std::function f) override { f(); } +}; diff --git a/CesiumAsync/test/TestCacheAssetAccessor.cpp b/CesiumAsync/test/TestCacheAssetAccessor.cpp index dfa0f532e..c6821d7cc 100644 --- a/CesiumAsync/test/TestCacheAssetAccessor.cpp +++ b/CesiumAsync/test/TestCacheAssetAccessor.cpp @@ -2,8 +2,10 @@ #include "CesiumAsync/CachingAssetAccessor.h" #include "CesiumAsync/ICacheDatabase.h" #include "CesiumAsync/ITaskProcessor.h" +#include "MockAssetAccessor.h" #include "MockAssetRequest.h" #include "MockAssetResponse.h" +#include "MockTaskProcessor.h" #include "ResponseCacheControl.h" #include @@ -82,41 +84,6 @@ class MockStoreCacheDatabase : public ICacheDatabase { std::optional cacheItem; }; -class MockAssetAccessor : public IAssetAccessor { -public: - MockAssetAccessor(const std::shared_ptr& request) - : testRequest{request} {} - - virtual CesiumAsync::Future> - get(const AsyncSystem& asyncSystem, - const std::string& /* url */, - const std::vector& /* headers */ - ) override { - return asyncSystem.createResolvedFuture( - std::shared_ptr(testRequest)); - } - - virtual CesiumAsync::Future> request( - const AsyncSystem& asyncSystem, - const std::string& /* verb */, - const std::string& /* url */, - const std::vector& /* headers */, - const gsl::span& /* contentPayload */ - ) override { - return asyncSystem.createResolvedFuture( - std::shared_ptr(testRequest)); - } - - virtual void tick() noexcept override {} - - std::shared_ptr testRequest; -}; - -class MockTaskProcessor : public ITaskProcessor { -public: - virtual void startTask(std::function f) override { f(); } -}; - } // namespace TEST_CASE("Test the condition of caching the request") { diff --git a/CesiumAsync/test/TestGunzipAssetAccessor.cpp b/CesiumAsync/test/TestGunzipAssetAccessor.cpp new file mode 100644 index 000000000..de1d8bab3 --- /dev/null +++ b/CesiumAsync/test/TestGunzipAssetAccessor.cpp @@ -0,0 +1,195 @@ +#include "MockAssetAccessor.h" +#include "MockAssetRequest.h" +#include "MockAssetResponse.h" +#include "MockTaskProcessor.h" + +#include + +#include + +using namespace CesiumAsync; + +namespace { + +std::vector asBytes(const std::vector& ints) { + std::vector bytes(ints.size()); + + for (size_t i = 0; i < ints.size(); ++i) { + CHECK(ints[i] >= 0); + CHECK(ints[i] < 255); + bytes[i] = std::byte(ints[i]); + } + + return bytes; +} + +} // namespace + +TEST_CASE("GunzipAssetAccessor") { + SECTION("passes through responses without gzip header") { + auto pAccessor = std::make_shared( + std::make_shared(std::make_shared( + "GET", + "https://example.com", + HttpHeaders{std::make_pair("Foo", "Bar")}, + std::make_unique( + 200, + "Application/Whatever", + HttpHeaders{std::make_pair("Some-Header", "in the response")}, + asBytes(std::vector{0x01, 0x02, 0x03}))))); + + std::shared_ptr mockTaskProcessor = + std::make_shared(); + AsyncSystem asyncSystem(mockTaskProcessor); + + auto pCompletedRequest = + pAccessor->get(asyncSystem, "https://example.com", {}).wait(); + CHECK(pCompletedRequest->url() == "https://example.com"); + CHECK(pCompletedRequest->method() == "GET"); + CHECK( + pCompletedRequest->headers() == + HttpHeaders{std::make_pair("Foo", "Bar")}); + + const IAssetResponse* pResponse = pCompletedRequest->response(); + REQUIRE(pResponse != nullptr); + CHECK(pResponse->statusCode() == 200); + CHECK(pResponse->contentType() == "Application/Whatever"); + CHECK( + pResponse->headers() == + HttpHeaders{std::make_pair("Some-Header", "in the response")}); + CHECK( + std::vector( + pResponse->data().data(), + pResponse->data().data() + pResponse->data().size()) == + asBytes(std::vector{0x01, 0x02, 0x03})); + } + + SECTION("gunzips a gzipped responses") { + auto pAccessor = std::make_shared( + std::make_shared(std::make_shared( + "GET", + "https://example.com", + HttpHeaders{std::make_pair("Foo", "Bar")}, + std::make_unique( + 200, + "Application/Whatever", + HttpHeaders{std::make_pair("Some-Header", "in the response")}, + asBytes(std::vector{ + 0x1F, 0x8B, 0x08, 0x08, 0x34, 0xEE, 0x77, 0x64, 0x00, 0x03, + 0x6F, 0x6E, 0x65, 0x74, 0x77, 0x6F, 0x74, 0x68, 0x72, 0x65, + 0x65, 0x2E, 0x64, 0x61, 0x74, 0x00, 0x63, 0x64, 0x62, 0x06, + 0x00, 0x1D, 0x80, 0xBC, 0x55, 0x03, 0x00, 0x00, 0x00}))))); + + std::shared_ptr mockTaskProcessor = + std::make_shared(); + AsyncSystem asyncSystem(mockTaskProcessor); + + auto pCompletedRequest = + pAccessor->get(asyncSystem, "https://example.com", {}).wait(); + CHECK(pCompletedRequest->url() == "https://example.com"); + CHECK(pCompletedRequest->method() == "GET"); + CHECK( + pCompletedRequest->headers() == + HttpHeaders{std::make_pair("Foo", "Bar")}); + + const IAssetResponse* pResponse = pCompletedRequest->response(); + REQUIRE(pResponse != nullptr); + CHECK(pResponse->statusCode() == 200); + CHECK(pResponse->contentType() == "Application/Whatever"); + CHECK( + pResponse->headers() == + HttpHeaders{std::make_pair("Some-Header", "in the response")}); + CHECK( + std::vector( + pResponse->data().data(), + pResponse->data().data() + pResponse->data().size()) == + asBytes(std::vector{0x01, 0x02, 0x03})); + } + + SECTION("passes through a response that has a gzip header but can't be " + "gunzipped") { + auto pAccessor = std::make_shared( + std::make_shared(std::make_shared( + "GET", + "https://example.com", + HttpHeaders{std::make_pair("Foo", "Bar")}, + std::make_unique( + 200, + "Application/Whatever", + HttpHeaders{std::make_pair("Some-Header", "in the response")}, + asBytes(std::vector{0x1F, 0x8B, 0x01, 0x02, 0x03}))))); + + std::shared_ptr mockTaskProcessor = + std::make_shared(); + AsyncSystem asyncSystem(mockTaskProcessor); + + auto pCompletedRequest = + pAccessor->get(asyncSystem, "https://example.com", {}).wait(); + CHECK(pCompletedRequest->url() == "https://example.com"); + CHECK(pCompletedRequest->method() == "GET"); + CHECK( + pCompletedRequest->headers() == + HttpHeaders{std::make_pair("Foo", "Bar")}); + + const IAssetResponse* pResponse = pCompletedRequest->response(); + REQUIRE(pResponse != nullptr); + CHECK(pResponse->statusCode() == 200); + CHECK(pResponse->contentType() == "Application/Whatever"); + CHECK( + pResponse->headers() == + HttpHeaders{std::make_pair("Some-Header", "in the response")}); + CHECK( + std::vector( + pResponse->data().data(), + pResponse->data().data() + pResponse->data().size()) == + asBytes(std::vector{0x1F, 0x8B, 0x01, 0x02, 0x03})); + } + + SECTION("works with request method") { + auto pAccessor = std::make_shared( + std::make_shared(std::make_shared( + "GET", + "https://example.com", + HttpHeaders{std::make_pair("Foo", "Bar")}, + std::make_unique( + 200, + "Application/Whatever", + HttpHeaders{std::make_pair("Some-Header", "in the response")}, + asBytes(std::vector{ + 0x1F, 0x8B, 0x08, 0x08, 0x34, 0xEE, 0x77, 0x64, 0x00, 0x03, + 0x6F, 0x6E, 0x65, 0x74, 0x77, 0x6F, 0x74, 0x68, 0x72, 0x65, + 0x65, 0x2E, 0x64, 0x61, 0x74, 0x00, 0x63, 0x64, 0x62, 0x06, + 0x00, 0x1D, 0x80, 0xBC, 0x55, 0x03, 0x00, 0x00, 0x00}))))); + + std::shared_ptr mockTaskProcessor = + std::make_shared(); + AsyncSystem asyncSystem(mockTaskProcessor); + + auto pCompletedRequest = pAccessor + ->request( + asyncSystem, + "GET", + "https://example.com", + {}, + std::vector()) + .wait(); + CHECK(pCompletedRequest->url() == "https://example.com"); + CHECK(pCompletedRequest->method() == "GET"); + CHECK( + pCompletedRequest->headers() == + HttpHeaders{std::make_pair("Foo", "Bar")}); + + const IAssetResponse* pResponse = pCompletedRequest->response(); + REQUIRE(pResponse != nullptr); + CHECK(pResponse->statusCode() == 200); + CHECK(pResponse->contentType() == "Application/Whatever"); + CHECK( + pResponse->headers() == + HttpHeaders{std::make_pair("Some-Header", "in the response")}); + CHECK( + std::vector( + pResponse->data().data(), + pResponse->data().data() + pResponse->data().size()) == + asBytes(std::vector{0x01, 0x02, 0x03})); + } +} From dabc99439f1fa8c7a5844938b588cd00ea73c6f6 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Thu, 1 Jun 2023 11:48:15 +1000 Subject: [PATCH 058/421] Update CHANGES.md. --- CHANGES.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 2e433025e..e7a8841f3 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,11 +6,11 @@ - Added `computeTransformationToAnotherLocal` method to `LocalHorizontalCoordinateSystem`. - Added support for the `KHR_materials_variants` extension to the glTF reader and writer. -- The Caching Asset Accessor will now automatically unzip data if gzipped data is detected. +- Added `GunzipAssetAccessor`. It can decorate another asset accessor in order to automatically gunzip responses (if they're gzipped) even if they're missing the proper `Content-Encoding` header. ##### Fixes :wrench: -- On Tileset Load Failure, warning/error messages will always be logged even if the failure callback is set. +- On Tileset Load Failure, warning/error messages will always be logged even if the failure callback is set. - Fixed a bug that caused meshes to be missing entirely when upsampled from a parent with `UNSIGNED_BYTE` indices. ### v0.24.0 - 2023-05-01 From 4c0f85e897d842e9d8292fba127a2527412573f5 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Thu, 1 Jun 2023 12:08:56 +1000 Subject: [PATCH 059/421] Fix warning on VS2017 and VS2019. --- CesiumAsync/test/TestGunzipAssetAccessor.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CesiumAsync/test/TestGunzipAssetAccessor.cpp b/CesiumAsync/test/TestGunzipAssetAccessor.cpp index de1d8bab3..5250f33b8 100644 --- a/CesiumAsync/test/TestGunzipAssetAccessor.cpp +++ b/CesiumAsync/test/TestGunzipAssetAccessor.cpp @@ -33,7 +33,7 @@ TEST_CASE("GunzipAssetAccessor") { "https://example.com", HttpHeaders{std::make_pair("Foo", "Bar")}, std::make_unique( - 200, + static_cast(200), "Application/Whatever", HttpHeaders{std::make_pair("Some-Header", "in the response")}, asBytes(std::vector{0x01, 0x02, 0x03}))))); @@ -71,7 +71,7 @@ TEST_CASE("GunzipAssetAccessor") { "https://example.com", HttpHeaders{std::make_pair("Foo", "Bar")}, std::make_unique( - 200, + static_cast(200), "Application/Whatever", HttpHeaders{std::make_pair("Some-Header", "in the response")}, asBytes(std::vector{ @@ -114,7 +114,7 @@ TEST_CASE("GunzipAssetAccessor") { "https://example.com", HttpHeaders{std::make_pair("Foo", "Bar")}, std::make_unique( - 200, + static_cast(200), "Application/Whatever", HttpHeaders{std::make_pair("Some-Header", "in the response")}, asBytes(std::vector{0x1F, 0x8B, 0x01, 0x02, 0x03}))))); From 35db73e0cb9831e9546ef393ef6c1620232829f1 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Thu, 1 Jun 2023 12:29:44 +1000 Subject: [PATCH 060/421] Fix another warning. --- CesiumAsync/test/TestGunzipAssetAccessor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CesiumAsync/test/TestGunzipAssetAccessor.cpp b/CesiumAsync/test/TestGunzipAssetAccessor.cpp index 5250f33b8..b0f9bca01 100644 --- a/CesiumAsync/test/TestGunzipAssetAccessor.cpp +++ b/CesiumAsync/test/TestGunzipAssetAccessor.cpp @@ -152,7 +152,7 @@ TEST_CASE("GunzipAssetAccessor") { "https://example.com", HttpHeaders{std::make_pair("Foo", "Bar")}, std::make_unique( - 200, + static_cast(200), "Application/Whatever", HttpHeaders{std::make_pair("Some-Header", "in the response")}, asBytes(std::vector{ From 7cf906432cf1e6e93fe1ce767249ca848bb97868 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Thu, 1 Jun 2023 13:10:23 +1000 Subject: [PATCH 061/421] Bump version to v0.25.0. --- CHANGES.md | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index e7a8841f3..13c2ed0d8 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,6 @@ # Change Log -### ? - ? +### v0.25.0 - 2023-06-01 ##### Additions :tada: diff --git a/package.json b/package.json index 9f791af89..338e37338 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cesium-native", - "version": "0.23.0", + "version": "0.25.0", "description": "Cesium 3D Geospatial for C++", "main": "index.js", "directories": { From 4644086f65e474272630a024cd1610bf635fccea Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Fri, 2 Jun 2023 14:00:54 -0400 Subject: [PATCH 062/421] Create feature ID texture views for EXT_mesh_features --- .../MeshFeaturesFeatureIDTextureView.h | 150 ++++++++++++++++++ ...turalMetadataPropertyTexturePropertyView.h | 11 +- .../src/MeshFeaturesFeatureIDTextureView.cpp | 92 +++++++++++ ...ralMetadataPropertyTexturePropertyView.cpp | 8 +- 4 files changed, 252 insertions(+), 9 deletions(-) create mode 100644 CesiumGltf/include/CesiumGltf/MeshFeaturesFeatureIDTextureView.h create mode 100644 CesiumGltf/src/MeshFeaturesFeatureIDTextureView.cpp diff --git a/CesiumGltf/include/CesiumGltf/MeshFeaturesFeatureIDTextureView.h b/CesiumGltf/include/CesiumGltf/MeshFeaturesFeatureIDTextureView.h new file mode 100644 index 000000000..e97343edf --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/MeshFeaturesFeatureIDTextureView.h @@ -0,0 +1,150 @@ +#pragma once + +#include "CesiumGltf/ExtensionExtMeshFeaturesFeatureIdTexture.h" +#include "CesiumGltf/Texture.h" +#include "CesiumGltf/TextureAccessor.h" +#include "Image.h" +#include "ImageCesium.h" +#include "Model.h" + +#include +#include +#include +#include +#include +#include + +namespace CesiumGltf { +namespace MeshFeatures { + +/** + * @brief The status of a feature ID texture view. + * + * The {@link FeatureIdTextureView} constructor always completes successfully, + * but it may not always reflect the actual content of the + * {@link ExtensionExtMeshFeaturesFeatureIdTexture}. This enumeration provides the reason. + */ +enum class FeatureIdTextureViewStatus { + /** + * @brief This view is valid and ready to use. + */ + Valid, + + /** + * @brief This view has not yet been initialized. + */ + ErrorUninitialized, + + /** + * @brief This feature ID texture has a texture index that doesn't exist in + * the glTF. + */ + ErrorInvalidTexture, + + /** + * @brief This feature ID texture has an image index that doesn't exist in + * the glTF. + */ + ErrorInvalidImage, + + /** + * @brief This feature ID texture has an empty image. + */ + ErrorEmptyImage, + + /** + * @brief The image for this feature ID texture has channels that take up more + * than a byte. The feature ID texture's channels should represent the bytes + * of the actual feature ID. + */ + ErrorInvalidImageChannelSize, + + /** + * @brief This feature ID texture has a negative TEXCOORD set index. + */ + ErrorInvalidTexCoordSetIndex, + + /** + * @brief The channels of this feature ID texture property are invalid. + * Channels must be in the range 0-3, with a minimum of one channel. Although + * more than four channels can be defined for specialized texture + * formats, this view only supports a maximum of four channels. + */ + ErrorInvalidChannels +}; + +/** + * @brief A view on the image data of {@link ExtensionExtMeshFeaturesFeatureIdTexture}. + * + * It provides the ability to sample the feature IDs from the + * {@link ExtensionExtMeshFeaturesFeatureIdTexture} using texture coordinates. + */ +class FeatureIdTextureView { +public: + /** + * @brief Constructs an uninitialized and invalid view. + */ + FeatureIdTextureView() noexcept; + + /** + * @brief Construct a view of the data specified by a + * {@link ExtensionExtMeshFeaturesFeatureIdTexture}. + * + * @param model The glTF in which to look for the feature id texture's data. + * @param featureIDTexture The feature id texture to create a view for. + */ + FeatureIdTextureView( + const Model& model, + const ExtensionExtMeshFeaturesFeatureIdTexture& + featureIdTexture) noexcept; + + /** + * @brief Get the Feature ID for the given texture coordinates. + * + * Will return -1 when the status is not Valid. + * + * @param u The u-component of the texture coordinates. Must be within + * [0.0, 1.0]. + * @param v The v-component of the texture coordinates. Must be within + * [0.0, 1.0]. + * @return The feature ID at the nearest pixel to the texture coordinates. + */ + int64_t getFeatureId(double u, double v) const noexcept; + + /** + * @brief Get the status of this view. + * + * If invalid, it will not be safe to sample feature IDs from this view. + */ + FeatureIdTextureViewStatus status() const { return _status; } + + /** + * @brief Get the actual feature ID texture. + * + * This will be nullptr if the feature ID texture view runs into problems + * during construction. + */ + const ImageCesium* getImage() const { return _pImage; } + + /** + * @brief Get the channels of this feature ID texture. The channels represent + * the bytes of the actual feature ID, in little-endian order. + */ + std::vector getChannels() const { return _channels; } + + /** + * @brief Get the texture coordinate set index for this feature ID + * texture. + */ + int64_t getTexCoordSetIndex() const { return this->_texCoordSetIndex; } + +private: + FeatureIdTextureViewStatus _status; + std::vector _channels; + int64_t _texCoordSetIndex; + + const ImageCesium* _pImage; +}; + +} // namespace MeshFeatures +} // namespace CesiumGltf diff --git a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTexturePropertyView.h b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTexturePropertyView.h index ca2e3eda7..c65140147 100644 --- a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTexturePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTexturePropertyView.h @@ -64,8 +64,9 @@ enum class PropertyTexturePropertyViewStatus { /** * @brief The channels of this property texture property are invalid. - * Channels must be in the range 0-3, with a minimum of one channel and a - * maximum of four. + * Channels must be in the range 0-3, with a minimum of one channel. Although + * more than four channels can be defined for specialized texture + * formats, this view only supports a maximum of four channels. */ ErrorInvalidChannels }; @@ -89,7 +90,9 @@ enum class PropertyTexturePropertyComponentType { * @tparam T The component type, must correspond to a valid * {@link PropertyTexturePropertyComponentType}. */ -template struct PropertyTexturePropertyValue { T components[4]; }; +template struct PropertyTexturePropertyValue { + T components[4]; +}; /** * @brief A view of the data specified by a @@ -230,8 +233,6 @@ class PropertyTexturePropertyView { private: PropertyTexturePropertyViewStatus _status; const ExtensionExtStructuralMetadataClassProperty* _pClassProperty; - const ExtensionExtStructuralMetadataPropertyTextureProperty* - _pPropertyTextureProperty; const Sampler* _pSampler; const ImageCesium* _pImage; diff --git a/CesiumGltf/src/MeshFeaturesFeatureIDTextureView.cpp b/CesiumGltf/src/MeshFeaturesFeatureIDTextureView.cpp new file mode 100644 index 000000000..dd4b0691a --- /dev/null +++ b/CesiumGltf/src/MeshFeaturesFeatureIDTextureView.cpp @@ -0,0 +1,92 @@ +#include "CesiumGltf/MeshFeaturesFeatureIdTextureView.h" + +namespace CesiumGltf { +namespace MeshFeatures { +FeatureIdTextureView::FeatureIdTextureView() noexcept + : _status(FeatureIdTextureViewStatus::ErrorUninitialized), + _texCoordSetIndex(-1), + _channels(), + _pImage(nullptr) {} + +FeatureIdTextureView::FeatureIdTextureView( + const Model& model, + const ExtensionExtMeshFeaturesFeatureIdTexture& featureIdTexture) noexcept + : _status(FeatureIdTextureViewStatus::ErrorUninitialized), + _texCoordSetIndex(featureIdTexture.texCoord), + _channels(featureIdTexture.channels), + _pImage(nullptr) { + int32_t textureIndex = featureIdTexture.index; + if (textureIndex < 0 || + static_cast(textureIndex) >= model.textures.size()) { + this->_status = FeatureIdTextureViewStatus::ErrorInvalidTexture; + return; + } + + const Texture& texture = model.textures[static_cast(textureIndex)]; + if (texture.source < 0 || + static_cast(texture.source) >= model.images.size()) { + this->_status = FeatureIdTextureViewStatus::ErrorInvalidImage; + return; + } + + // Ignore the texture's sampler, we will always use nearest pixel sampling. + + this->_pImage = &model.images[static_cast(texture.source)].cesium; + if (this->_pImage->width < 1 || this->_pImage->height < 1) { + this->_status = FeatureIdTextureViewStatus::ErrorEmptyImage; + return; + } + + // TODO: once compressed texture support is merged, check that the image is + // decompressed here. + + if (this->_pImage->bytesPerChannel > 1) { + this->_status = FeatureIdTextureViewStatus::ErrorInvalidImageChannelSize; + return; + } + + const std::vector& channels = featureIdTexture.channels; + if (channels.size() == 0 || channels.size() > 4 || + channels.size() > static_cast(this->_pImage->channels)) { + this->_status = FeatureIdTextureViewStatus::ErrorInvalidChannels; + return; + } + this->_channels = channels; + + this->_status = FeatureIdTextureViewStatus::Valid; +} + +int64_t FeatureIdTextureView::getFeatureId(double u, double v) const noexcept { + if (this->_status != FeatureIdTextureViewStatus::Valid) { + return -1; + } + + int64_t x = std::clamp( + std::llround(u * this->_pImage->width), + 0LL, + (long long)this->_pImage->width); + int64_t y = std::clamp( + std::llround(v * this->_pImage->height), + 0LL, + (long long)this->_pImage->height); + + int64_t pixelOffset = this->_pImage->bytesPerChannel * + this->_pImage->channels * + (y * this->_pImage->width + x); + + int64_t value = 0; + // As stated in the spec: values from the selected channels are treated as + // unsigned 8 bit integers, and represent the bytes of the actual feature ID, + // in little-endian order. + for (int i = 0, offset = 0; i < this->_channels.size(); i++, offset += 8) { + int64_t channelValue = static_cast( + this->_pImage + ->pixelData[static_cast(pixelOffset + this->_channels[i])]); + value |= channelValue << offset; + } + + return value; +} + +} // namespace MeshFeatures +} // namespace CesiumGltf diff --git a/CesiumGltf/src/StructuralMetadataPropertyTexturePropertyView.cpp b/CesiumGltf/src/StructuralMetadataPropertyTexturePropertyView.cpp index aaebff6d6..409c29166 100644 --- a/CesiumGltf/src/StructuralMetadataPropertyTexturePropertyView.cpp +++ b/CesiumGltf/src/StructuralMetadataPropertyTexturePropertyView.cpp @@ -6,7 +6,6 @@ namespace StructuralMetadata { PropertyTexturePropertyView::PropertyTexturePropertyView() noexcept : _status(PropertyTexturePropertyViewStatus::ErrorUninitialized), _pClassProperty(nullptr), - _pPropertyTextureProperty(nullptr), _pSampler(nullptr), _pImage(nullptr), _texCoordSetIndex(-1), @@ -23,7 +22,6 @@ PropertyTexturePropertyView::PropertyTexturePropertyView( propertyTextureProperty) noexcept : _status(PropertyTexturePropertyViewStatus::ErrorUninitialized), _pClassProperty(&classProperty), - _pPropertyTextureProperty(&propertyTextureProperty), _pSampler(nullptr), _pImage(nullptr), _texCoordSetIndex(propertyTextureProperty.texCoord), @@ -32,7 +30,7 @@ PropertyTexturePropertyView::PropertyTexturePropertyView( _type(PropertyTexturePropertyComponentType::Uint8), _count(0), _normalized(false) { - const int64_t index = _pPropertyTextureProperty->index; + const int64_t index = propertyTextureProperty.index; if (index < 0 || static_cast(index) >= model.textures.size()) { this->_status = PropertyTexturePropertyViewStatus::ErrorInvalidTexture; return; @@ -73,7 +71,7 @@ PropertyTexturePropertyView::PropertyTexturePropertyView( this->_pClassProperty->count ? *this->_pClassProperty->count : 1; this->_normalized = this->_pClassProperty->normalized; - const std::vector& channels = _pPropertyTextureProperty->channels; + const std::vector& channels = propertyTextureProperty.channels; if (channels.size() == 0 || channels.size() > 4 || channels.size() > static_cast(this->_pImage->channels) || channels.size() != static_cast(this->_count)) { @@ -101,6 +99,8 @@ PropertyTexturePropertyView::PropertyTexturePropertyView( } } + this->_channels = channels; + this->_status = PropertyTexturePropertyViewStatus::Valid; } } // namespace StructuralMetadata From 7e0cd8a055a73c4303e602cbdfe9dd68fae5498c Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Fri, 2 Jun 2023 14:23:15 -0400 Subject: [PATCH 063/421] Add tests for feature ID texture view --- .../MeshFeaturesFeatureIDTextureView.h | 2 +- .../src/MeshFeaturesFeatureIDTextureView.cpp | 19 +- .../TestMeshFeaturesFeatureIdTextureView.cpp | 247 ++++++++++++++++++ ...ralMetadataPropertyTexturePropertyView.cpp | 10 +- 4 files changed, 271 insertions(+), 7 deletions(-) create mode 100644 CesiumGltf/test/TestMeshFeaturesFeatureIdTextureView.cpp diff --git a/CesiumGltf/include/CesiumGltf/MeshFeaturesFeatureIDTextureView.h b/CesiumGltf/include/CesiumGltf/MeshFeaturesFeatureIDTextureView.h index e97343edf..0962bc07b 100644 --- a/CesiumGltf/include/CesiumGltf/MeshFeaturesFeatureIDTextureView.h +++ b/CesiumGltf/include/CesiumGltf/MeshFeaturesFeatureIDTextureView.h @@ -57,7 +57,7 @@ enum class FeatureIdTextureViewStatus { * than a byte. The feature ID texture's channels should represent the bytes * of the actual feature ID. */ - ErrorInvalidImageChannelSize, + ErrorInvalidImageBytesPerChannel, /** * @brief This feature ID texture has a negative TEXCOORD set index. diff --git a/CesiumGltf/src/MeshFeaturesFeatureIDTextureView.cpp b/CesiumGltf/src/MeshFeaturesFeatureIDTextureView.cpp index dd4b0691a..c147fdcdb 100644 --- a/CesiumGltf/src/MeshFeaturesFeatureIDTextureView.cpp +++ b/CesiumGltf/src/MeshFeaturesFeatureIDTextureView.cpp @@ -12,7 +12,7 @@ FeatureIdTextureView::FeatureIdTextureView( const Model& model, const ExtensionExtMeshFeaturesFeatureIdTexture& featureIdTexture) noexcept : _status(FeatureIdTextureViewStatus::ErrorUninitialized), - _texCoordSetIndex(featureIdTexture.texCoord), + _texCoordSetIndex(-1), _channels(featureIdTexture.channels), _pImage(nullptr) { int32_t textureIndex = featureIdTexture.index; @@ -41,9 +41,16 @@ FeatureIdTextureView::FeatureIdTextureView( // decompressed here. if (this->_pImage->bytesPerChannel > 1) { - this->_status = FeatureIdTextureViewStatus::ErrorInvalidImageChannelSize; + this->_status = + FeatureIdTextureViewStatus::ErrorInvalidImageBytesPerChannel; + return; + } + + if (featureIdTexture.texCoord < 0) { + this->_status = FeatureIdTextureViewStatus::ErrorInvalidTexCoordSetIndex; return; } + this->_texCoordSetIndex = featureIdTexture.texCoord; const std::vector& channels = featureIdTexture.channels; if (channels.size() == 0 || channels.size() > 4 || @@ -51,6 +58,14 @@ FeatureIdTextureView::FeatureIdTextureView( this->_status = FeatureIdTextureViewStatus::ErrorInvalidChannels; return; } + + // Only channel values 0-3 are supported. + for (int i = 0; i < channels.size(); i++) { + if (channels[i] < 0 || channels[i] > 3) { + this->_status = FeatureIdTextureViewStatus::ErrorInvalidChannels; + return; + } + } this->_channels = channels; this->_status = FeatureIdTextureViewStatus::Valid; diff --git a/CesiumGltf/test/TestMeshFeaturesFeatureIdTextureView.cpp b/CesiumGltf/test/TestMeshFeaturesFeatureIdTextureView.cpp new file mode 100644 index 000000000..cde38cd03 --- /dev/null +++ b/CesiumGltf/test/TestMeshFeaturesFeatureIdTextureView.cpp @@ -0,0 +1,247 @@ +#include "CesiumGltf/ExtensionExtMeshFeatures.h" +#include "CesiumGltf/MeshFeaturesFeatureIdTextureView.h" + +#include +#include + +#include +#include +#include +#include +#include + +using namespace CesiumGltf; +using namespace CesiumGltf::MeshFeatures; + +TEST_CASE("Test FeatureIdTextureView on feature ID texture with invalid " + "texture index") { + Model model; + Mesh& mesh = model.meshes.emplace_back(); + MeshPrimitive& primitive = mesh.primitives.emplace_back(); + + ExtensionExtMeshFeatures& meshFeatures = + primitive.addExtension(); + + ExtensionExtMeshFeaturesFeatureIdTexture featureIdTexture; + featureIdTexture.index = -1; + featureIdTexture.texCoord = 0; + featureIdTexture.channels = {0}; + + ExtensionExtMeshFeaturesFeatureId featureId = + meshFeatures.featureIds.emplace_back(); + featureId.texture = featureIdTexture; + + FeatureIdTextureView view(model, featureIdTexture); + REQUIRE(view.status() == FeatureIdTextureViewStatus::ErrorInvalidTexture); +} + +TEST_CASE("Test FeatureIdTextureView on feature ID texture with invalid image " + "index") { + Model model; + Mesh& mesh = model.meshes.emplace_back(); + MeshPrimitive& primitive = mesh.primitives.emplace_back(); + + Texture& texture = model.textures.emplace_back(); + texture.sampler = 0; + texture.source = -1; + + ExtensionExtMeshFeatures& meshFeatures = + primitive.addExtension(); + + ExtensionExtMeshFeaturesFeatureIdTexture featureIdTexture; + featureIdTexture.index = 0; + featureIdTexture.texCoord = 0; + featureIdTexture.channels = {0}; + + ExtensionExtMeshFeaturesFeatureId featureId = + meshFeatures.featureIds.emplace_back(); + featureId.texture = featureIdTexture; + + FeatureIdTextureView view(model, featureIdTexture); + REQUIRE(view.status() == FeatureIdTextureViewStatus::ErrorInvalidImage); +} + +TEST_CASE("Test FeatureIdTextureView on feature ID texture with empty image") { + Model model; + Mesh& mesh = model.meshes.emplace_back(); + MeshPrimitive& primitive = mesh.primitives.emplace_back(); + + Image& image = model.images.emplace_back(); + image.cesium.width = 0; + image.cesium.height = 0; + + Texture& texture = model.textures.emplace_back(); + texture.sampler = 0; + texture.source = 0; + + ExtensionExtMeshFeatures& meshFeatures = + primitive.addExtension(); + + ExtensionExtMeshFeaturesFeatureIdTexture featureIdTexture; + featureIdTexture.index = 0; + featureIdTexture.texCoord = 0; + featureIdTexture.channels = {0}; + + ExtensionExtMeshFeaturesFeatureId featureId = + meshFeatures.featureIds.emplace_back(); + featureId.texture = featureIdTexture; + + FeatureIdTextureView view(model, featureIdTexture); + REQUIRE(view.status() == FeatureIdTextureViewStatus::ErrorEmptyImage); +} + +TEST_CASE("Test FeatureIdTextureView on feature ID texture with too many bytes " + "per channel") { + Model model; + Mesh& mesh = model.meshes.emplace_back(); + MeshPrimitive& primitive = mesh.primitives.emplace_back(); + + Image& image = model.images.emplace_back(); + image.cesium.width = 1; + image.cesium.height = 1; + image.cesium.bytesPerChannel = 2; + + Texture& texture = model.textures.emplace_back(); + texture.sampler = 0; + texture.source = 0; + + ExtensionExtMeshFeatures& meshFeatures = + primitive.addExtension(); + + ExtensionExtMeshFeaturesFeatureIdTexture featureIdTexture; + featureIdTexture.index = 0; + featureIdTexture.texCoord = 0; + featureIdTexture.channels = {0}; + + ExtensionExtMeshFeaturesFeatureId featureId = + meshFeatures.featureIds.emplace_back(); + featureId.texture = featureIdTexture; + + FeatureIdTextureView view(model, featureIdTexture); + REQUIRE( + view.status() == + FeatureIdTextureViewStatus::ErrorInvalidImageBytesPerChannel); +} + +TEST_CASE( + "Test FeatureIdTextureView on feature ID texture with negative texcoord " + "set index") { + Model model; + Mesh& mesh = model.meshes.emplace_back(); + MeshPrimitive& primitive = mesh.primitives.emplace_back(); + + Image& image = model.images.emplace_back(); + image.cesium.width = 1; + image.cesium.height = 1; + + Texture& texture = model.textures.emplace_back(); + texture.sampler = 0; + texture.source = 0; + + ExtensionExtMeshFeatures& meshFeatures = + primitive.addExtension(); + + ExtensionExtMeshFeaturesFeatureIdTexture featureIdTexture; + featureIdTexture.index = 0; + featureIdTexture.texCoord = -1; + featureIdTexture.channels = {0}; + + ExtensionExtMeshFeaturesFeatureId featureId = + meshFeatures.featureIds.emplace_back(); + featureId.texture = featureIdTexture; + + FeatureIdTextureView view(model, featureIdTexture); + REQUIRE( + view.status() == + FeatureIdTextureViewStatus::ErrorInvalidTexCoordSetIndex); +} + +TEST_CASE( + "Test FeatureIdTextureView on feature ID texture with zero channels") { + Model model; + Mesh& mesh = model.meshes.emplace_back(); + MeshPrimitive& primitive = mesh.primitives.emplace_back(); + + Image& image = model.images.emplace_back(); + image.cesium.width = 1; + image.cesium.height = 1; + + Texture& texture = model.textures.emplace_back(); + texture.sampler = 0; + texture.source = 0; + + ExtensionExtMeshFeatures& meshFeatures = + primitive.addExtension(); + + ExtensionExtMeshFeaturesFeatureIdTexture featureIdTexture; + featureIdTexture.index = 0; + featureIdTexture.texCoord = 0; + featureIdTexture.channels = {}; + + ExtensionExtMeshFeaturesFeatureId featureId = + meshFeatures.featureIds.emplace_back(); + featureId.texture = featureIdTexture; + + FeatureIdTextureView view(model, featureIdTexture); + REQUIRE(view.status() == FeatureIdTextureViewStatus::ErrorInvalidChannels); +} + +TEST_CASE( + "Test FeatureIdTextureView on feature ID texture with too many channels") { + Model model; + Mesh& mesh = model.meshes.emplace_back(); + MeshPrimitive& primitive = mesh.primitives.emplace_back(); + + Image& image = model.images.emplace_back(); + image.cesium.width = 1; + image.cesium.height = 1; + + Texture& texture = model.textures.emplace_back(); + texture.sampler = 0; + texture.source = 0; + + ExtensionExtMeshFeatures& meshFeatures = + primitive.addExtension(); + + ExtensionExtMeshFeaturesFeatureIdTexture featureIdTexture; + featureIdTexture.index = 0; + featureIdTexture.texCoord = 0; + featureIdTexture.channels = {0, 1, 2, 3, 3}; + + ExtensionExtMeshFeaturesFeatureId featureId = + meshFeatures.featureIds.emplace_back(); + featureId.texture = featureIdTexture; + + FeatureIdTextureView view(model, featureIdTexture); + REQUIRE(view.status() == FeatureIdTextureViewStatus::ErrorInvalidChannels); +} + +TEST_CASE("Test FeatureIdTextureView on feature ID texture with out of range " + "channel") { + Model model; + Mesh& mesh = model.meshes.emplace_back(); + MeshPrimitive& primitive = mesh.primitives.emplace_back(); + + Image& image = model.images.emplace_back(); + image.cesium.width = 1; + image.cesium.height = 1; + + Texture& texture = model.textures.emplace_back(); + texture.sampler = 0; + texture.source = 0; + + ExtensionExtMeshFeatures& meshFeatures = + primitive.addExtension(); + + ExtensionExtMeshFeaturesFeatureIdTexture featureIdTexture; + featureIdTexture.index = 0; + featureIdTexture.texCoord = 0; + featureIdTexture.channels = {4}; + + ExtensionExtMeshFeaturesFeatureId featureId = + meshFeatures.featureIds.emplace_back(); + featureId.texture = featureIdTexture; + + FeatureIdTextureView view(model, featureIdTexture); + REQUIRE(view.status() == FeatureIdTextureViewStatus::ErrorInvalidChannels); +} diff --git a/CesiumGltf/test/TestStructuralMetadataPropertyTexturePropertyView.cpp b/CesiumGltf/test/TestStructuralMetadataPropertyTexturePropertyView.cpp index 234b68d89..e697ceafc 100644 --- a/CesiumGltf/test/TestStructuralMetadataPropertyTexturePropertyView.cpp +++ b/CesiumGltf/test/TestStructuralMetadataPropertyTexturePropertyView.cpp @@ -162,8 +162,8 @@ TEST_CASE("Test PropertyTexturePropertyView on property with empty image") { REQUIRE(view.status() == PropertyTexturePropertyViewStatus::ErrorEmptyImage); } -TEST_CASE("Test PropertyTextureView on model with negative texture coordinate " - "set index") { +TEST_CASE("Test PropertyTextureView on property table property with negative " + "texcoord set index") { Model model; ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); @@ -205,7 +205,8 @@ TEST_CASE("Test PropertyTextureView on model with negative texture coordinate " PropertyTexturePropertyViewStatus::ErrorInvalidTexCoordSetIndex); } -TEST_CASE("Test PropertyTextureView on model with zero channels") { +TEST_CASE("Test PropertyTextureView on property texture property with zero " + "channels") { Model model; ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); @@ -247,7 +248,8 @@ TEST_CASE("Test PropertyTextureView on model with zero channels") { view.status() == PropertyTexturePropertyViewStatus::ErrorInvalidChannels); } -TEST_CASE("Test PropertyTextureView on model with too many channels") { +TEST_CASE("Test PropertyTextureView on property texture property with too many " + "channels") { Model model; ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); From d9f330c3ef06430046bc048d427aaa33f35b6bc3 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Fri, 2 Jun 2023 14:43:14 -0400 Subject: [PATCH 064/421] Fix formatting and wrong file names --- ...tureIDTextureView.h => MeshFeaturesFeatureIdTextureView.h} | 0 .../StructuralMetadataPropertyTexturePropertyView.h | 4 +--- ...IDTextureView.cpp => MeshFeaturesFeatureIdTextureView.cpp} | 0 3 files changed, 1 insertion(+), 3 deletions(-) rename CesiumGltf/include/CesiumGltf/{MeshFeaturesFeatureIDTextureView.h => MeshFeaturesFeatureIdTextureView.h} (100%) rename CesiumGltf/src/{MeshFeaturesFeatureIDTextureView.cpp => MeshFeaturesFeatureIdTextureView.cpp} (100%) diff --git a/CesiumGltf/include/CesiumGltf/MeshFeaturesFeatureIDTextureView.h b/CesiumGltf/include/CesiumGltf/MeshFeaturesFeatureIdTextureView.h similarity index 100% rename from CesiumGltf/include/CesiumGltf/MeshFeaturesFeatureIDTextureView.h rename to CesiumGltf/include/CesiumGltf/MeshFeaturesFeatureIdTextureView.h diff --git a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTexturePropertyView.h b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTexturePropertyView.h index c65140147..e8f858732 100644 --- a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTexturePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTexturePropertyView.h @@ -90,9 +90,7 @@ enum class PropertyTexturePropertyComponentType { * @tparam T The component type, must correspond to a valid * {@link PropertyTexturePropertyComponentType}. */ -template struct PropertyTexturePropertyValue { - T components[4]; -}; +template struct PropertyTexturePropertyValue { T components[4]; }; /** * @brief A view of the data specified by a diff --git a/CesiumGltf/src/MeshFeaturesFeatureIDTextureView.cpp b/CesiumGltf/src/MeshFeaturesFeatureIdTextureView.cpp similarity index 100% rename from CesiumGltf/src/MeshFeaturesFeatureIDTextureView.cpp rename to CesiumGltf/src/MeshFeaturesFeatureIdTextureView.cpp From 2744abd7fe8aea1ddf320604a9e6330876436538 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Mon, 5 Jun 2023 10:04:28 -0400 Subject: [PATCH 065/421] Fix CI errors --- .../CesiumGltf/MeshFeaturesFeatureIdTextureView.h | 2 +- CesiumGltf/src/MeshFeaturesFeatureIdTextureView.cpp | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/CesiumGltf/include/CesiumGltf/MeshFeaturesFeatureIdTextureView.h b/CesiumGltf/include/CesiumGltf/MeshFeaturesFeatureIdTextureView.h index 0962bc07b..c7df25c12 100644 --- a/CesiumGltf/include/CesiumGltf/MeshFeaturesFeatureIdTextureView.h +++ b/CesiumGltf/include/CesiumGltf/MeshFeaturesFeatureIdTextureView.h @@ -140,8 +140,8 @@ class FeatureIdTextureView { private: FeatureIdTextureViewStatus _status; - std::vector _channels; int64_t _texCoordSetIndex; + std::vector _channels; const ImageCesium* _pImage; }; diff --git a/CesiumGltf/src/MeshFeaturesFeatureIdTextureView.cpp b/CesiumGltf/src/MeshFeaturesFeatureIdTextureView.cpp index c147fdcdb..9dcf68a38 100644 --- a/CesiumGltf/src/MeshFeaturesFeatureIdTextureView.cpp +++ b/CesiumGltf/src/MeshFeaturesFeatureIdTextureView.cpp @@ -60,7 +60,7 @@ FeatureIdTextureView::FeatureIdTextureView( } // Only channel values 0-3 are supported. - for (int i = 0; i < channels.size(); i++) { + for (size_t i = 0; i < channels.size(); i++) { if (channels[i] < 0 || channels[i] > 3) { this->_status = FeatureIdTextureViewStatus::ErrorInvalidChannels; return; @@ -90,14 +90,16 @@ int64_t FeatureIdTextureView::getFeatureId(double u, double v) const noexcept { (y * this->_pImage->width + x); int64_t value = 0; + int64_t bitOffset = 0; // As stated in the spec: values from the selected channels are treated as // unsigned 8 bit integers, and represent the bytes of the actual feature ID, // in little-endian order. - for (int i = 0, offset = 0; i < this->_channels.size(); i++, offset += 8) { + for (size_t i = 0; i < this->_channels.size(); i++) { int64_t channelValue = static_cast( this->_pImage - ->pixelData[static_cast(pixelOffset + this->_channels[i])]); - value |= channelValue << offset; + ->pixelData[static_cast(pixelOffset) + this->_channels[i]]); + value |= channelValue << bitOffset; + bitOffset += 8; } return value; From bf8033d2ba8564f26521737240446f84a3139ccc Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Mon, 5 Jun 2023 10:09:47 -0400 Subject: [PATCH 066/421] Fix last CI error --- CesiumGltf/src/MeshFeaturesFeatureIdTextureView.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CesiumGltf/src/MeshFeaturesFeatureIdTextureView.cpp b/CesiumGltf/src/MeshFeaturesFeatureIdTextureView.cpp index 9dcf68a38..0727e091f 100644 --- a/CesiumGltf/src/MeshFeaturesFeatureIdTextureView.cpp +++ b/CesiumGltf/src/MeshFeaturesFeatureIdTextureView.cpp @@ -97,7 +97,7 @@ int64_t FeatureIdTextureView::getFeatureId(double u, double v) const noexcept { for (size_t i = 0; i < this->_channels.size(); i++) { int64_t channelValue = static_cast( this->_pImage - ->pixelData[static_cast(pixelOffset) + this->_channels[i]]); + ->pixelData[static_cast(pixelOffset + this->_channels[i])]); value |= channelValue << bitOffset; bitOffset += 8; } From fe9125c0cb8143f75452c8fbddff2bc0d35c7fd0 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Mon, 5 Jun 2023 15:50:17 -0400 Subject: [PATCH 067/421] Change draft-1.1 to main in generate-classes --- tools/generate-classes/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/generate-classes/package.json b/tools/generate-classes/package.json index 97f6ecbb1..63aa44184 100644 --- a/tools/generate-classes/package.json +++ b/tools/generate-classes/package.json @@ -4,7 +4,7 @@ "description": "Generate C++ classes from a JSON schema", "main": "index.js", "scripts": { - "generate-3d-tiles": "node index.js --schemas https://raw.githubusercontent.com/CesiumGS/3d-tiles/draft-1.1/specification/schema/tileset.schema.json https://raw.githubusercontent.com/CesiumGS/3d-tiles/draft-1.1/specification/schema/common/rootProperty.schema.json https://raw.githubusercontent.com/CesiumGS/3d-tiles/draft-1.1/specification/schema/PropertyTable/propertyTable.schema.json https://raw.githubusercontent.com/CesiumGS/3d-tiles/draft-1.1/specification/schema/Subtree/subtree.schema.json https://raw.githubusercontent.com/CesiumGS/3d-tiles/draft-1.1/specification/schema/Schema/schema.schema.json https://raw.githubusercontent.com/CesiumGS/3d-tiles/draft-1.1/specification/schema/Statistics/statistics.schema.json --output ../../Cesium3DTiles --readerOutput ../../Cesium3DTilesReader --writerOutput ../../Cesium3DTilesWriter --extensions https://raw.githubusercontent.com/CesiumGS/3d-tiles/draft-1.1/extensions/ --namespace Cesium3DTiles --readerNamespace Cesium3DTilesReader --writerNamespace Cesium3DTilesWriter --config 3dTiles.json", + "generate-3d-tiles": "node index.js --schemas https://raw.githubusercontent.com/CesiumGS/3d-tiles/main/specification/schema/tileset.schema.json https://raw.githubusercontent.com/CesiumGS/3d-tiles/main/specification/schema/common/rootProperty.schema.json https://raw.githubusercontent.com/CesiumGS/3d-tiles/main/specification/schema/PropertyTable/propertyTable.schema.json https://raw.githubusercontent.com/CesiumGS/3d-tiles/main/specification/schema/Subtree/subtree.schema.json https://raw.githubusercontent.com/CesiumGS/3d-tiles/main/specification/schema/Schema/schema.schema.json https://raw.githubusercontent.com/CesiumGS/3d-tiles/main/specification/schema/Statistics/statistics.schema.json --output ../../Cesium3DTiles --readerOutput ../../Cesium3DTilesReader --writerOutput ../../Cesium3DTilesWriter --extensions https://raw.githubusercontent.com/CesiumGS/3d-tiles/main/extensions/ --namespace Cesium3DTiles --readerNamespace Cesium3DTilesReader --writerNamespace Cesium3DTilesWriter --config 3dTiles.json", "generate-gltf": "node index.js --schemas https://raw.githubusercontent.com/CesiumGS/glTF/3d-tiles-next/specification/2.0/schema/glTF.schema.json --output ../../CesiumGltf --readerOutput ../../CesiumGltfReader --writerOutput ../../CesiumGltfWriter --extensions https://raw.githubusercontent.com/CesiumGS/glTF/3d-tiles-next/extensions/2.0/ --namespace CesiumGltf --readerNamespace CesiumGltfReader --writerNamespace CesiumGltfWriter --config glTF.json" }, "author": "CesiumGS, Inc.", From a50093c460050ab5d9ec364138d4a78559c23e27 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Tue, 6 Jun 2023 11:28:24 -0400 Subject: [PATCH 068/421] Rename files and take classes out of separate namespaces --- .../include/CesiumGltf/FeatureIDTextureView.h | 141 -- ...IdTextureView.h => FeatureIdTextureView.h} | 4 - .../include/CesiumGltf/MetadataArrayView.h | 129 -- .../CesiumGltf/MetadataFeatureTableView.h | 536 -------- .../include/CesiumGltf/MetadataPropertyView.h | 411 ------ ...etadataArrayView.h => PropertyArrayView.h} | 30 +- ...ertyView.h => PropertyTablePropertyView.h} | 36 +- ...ropertyTableView.h => PropertyTableView.h} | 117 +- ...tyView.h => PropertyTexturePropertyView.h} | 3 - ...rtyTextureView.h => PropertyTextureView.h} | 6 +- CesiumGltf/include/CesiumGltf/PropertyType.h | 36 +- .../include/CesiumGltf/PropertyTypeTraits.h | 197 ++- .../StructuralMetadataPropertyType.h | 59 - .../StructuralMetadataPropertyTypeTraits.h | 271 ---- .../CesiumGltf/getOffsetFromOffsetsBuffer.h | 6 +- CesiumGltf/src/FeatureIDTextureView.cpp | 91 -- ...xtureView.cpp => FeatureIdTextureView.cpp} | 5 +- CesiumGltf/src/FeatureTexturePropertyView.cpp | 101 -- CesiumGltf/src/FeatureTextureView.cpp | 68 - CesiumGltf/src/MetadataFeatureTableView.cpp | 386 ------ ...rtyTableView.cpp => PropertyTableView.cpp} | 38 +- ...ew.cpp => PropertyTexturePropertyView.cpp} | 4 +- ...extureView.cpp => PropertyTextureView.cpp} | 5 +- CesiumGltf/src/PropertyType.cpp | 237 +++- .../src/StructuralMetadataPropertyType.cpp | 224 ---- ...eView.cpp => TestFeatureIdTextureView.cpp} | 3 +- .../test/TestMetadataFeatureTableView.cpp | 1164 ----------------- ....cpp => TestPropertyTablePropertyView.cpp} | 64 +- ...ableView.cpp => TestPropertyTableView.cpp} | 383 +++--- ...pp => TestPropertyTexturePropertyView.cpp} | 3 +- ...reView.cpp => TestPropertyTextureView.cpp} | 3 +- CesiumGltf/test/TestPropertyType.cpp | 244 +++- CesiumGltf/test/TestPropertyTypeTrait.cpp | 82 -- ...eTraits.cpp => TestPropertyTypeTraits.cpp} | 26 +- CesiumGltf/test/TestPropertyView.cpp | 589 --------- .../TestStructuralMetadataPropertyType.cpp | 253 ---- 36 files changed, 862 insertions(+), 5093 deletions(-) delete mode 100644 CesiumGltf/include/CesiumGltf/FeatureIDTextureView.h rename CesiumGltf/include/CesiumGltf/{MeshFeaturesFeatureIdTextureView.h => FeatureIdTextureView.h} (98%) delete mode 100644 CesiumGltf/include/CesiumGltf/MetadataArrayView.h delete mode 100644 CesiumGltf/include/CesiumGltf/MetadataFeatureTableView.h delete mode 100644 CesiumGltf/include/CesiumGltf/MetadataPropertyView.h rename CesiumGltf/include/CesiumGltf/{StructuralMetadataArrayView.h => PropertyArrayView.h} (76%) rename CesiumGltf/include/CesiumGltf/{StructuralMetadataPropertyTablePropertyView.h => PropertyTablePropertyView.h} (93%) rename CesiumGltf/include/CesiumGltf/{StructuralMetadataPropertyTableView.h => PropertyTableView.h} (91%) rename CesiumGltf/include/CesiumGltf/{StructuralMetadataPropertyTexturePropertyView.h => PropertyTexturePropertyView.h} (99%) rename CesiumGltf/include/CesiumGltf/{StructuralMetadataPropertyTextureView.h => PropertyTextureView.h} (96%) delete mode 100644 CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyType.h delete mode 100644 CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTypeTraits.h delete mode 100644 CesiumGltf/src/FeatureIDTextureView.cpp rename CesiumGltf/src/{MeshFeaturesFeatureIdTextureView.cpp => FeatureIdTextureView.cpp} (96%) delete mode 100644 CesiumGltf/src/FeatureTexturePropertyView.cpp delete mode 100644 CesiumGltf/src/FeatureTextureView.cpp delete mode 100644 CesiumGltf/src/MetadataFeatureTableView.cpp rename CesiumGltf/src/{StructuralMetadataPropertyTableView.cpp => PropertyTableView.cpp} (93%) rename CesiumGltf/src/{StructuralMetadataPropertyTexturePropertyView.cpp => PropertyTexturePropertyView.cpp} (96%) rename CesiumGltf/src/{StructuralMetadataPropertyTextureView.cpp => PropertyTextureView.cpp} (94%) delete mode 100644 CesiumGltf/src/StructuralMetadataPropertyType.cpp rename CesiumGltf/test/{TestMeshFeaturesFeatureIdTextureView.cpp => TestFeatureIdTextureView.cpp} (98%) delete mode 100644 CesiumGltf/test/TestMetadataFeatureTableView.cpp rename CesiumGltf/test/{TestStructuralMetadataPropertyTablePropertyView.cpp => TestPropertyTablePropertyView.cpp} (93%) rename CesiumGltf/test/{TestStructuralMetadataPropertyTableView.cpp => TestPropertyTableView.cpp} (91%) rename CesiumGltf/test/{TestStructuralMetadataPropertyTexturePropertyView.cpp => TestPropertyTexturePropertyView.cpp} (98%) rename CesiumGltf/test/{TestStructuralMetadataPropertyTextureView.cpp => TestPropertyTextureView.cpp} (98%) delete mode 100644 CesiumGltf/test/TestPropertyTypeTrait.cpp rename CesiumGltf/test/{TestStructuralMetadataPropertyTypeTraits.cpp => TestPropertyTypeTraits.cpp} (95%) delete mode 100644 CesiumGltf/test/TestPropertyView.cpp delete mode 100644 CesiumGltf/test/TestStructuralMetadataPropertyType.cpp diff --git a/CesiumGltf/include/CesiumGltf/FeatureIDTextureView.h b/CesiumGltf/include/CesiumGltf/FeatureIDTextureView.h deleted file mode 100644 index c3a29c02f..000000000 --- a/CesiumGltf/include/CesiumGltf/FeatureIDTextureView.h +++ /dev/null @@ -1,141 +0,0 @@ -#pragma once - -#include "CesiumGltf/FeatureIDTexture.h" -#include "CesiumGltf/Texture.h" -#include "CesiumGltf/TextureAccessor.h" -#include "Image.h" -#include "ImageCesium.h" -#include "Model.h" - -#include -#include -#include -#include -#include -#include - -namespace CesiumGltf { - -/** - * @brief The status of a feature id texture view. - * - * The {@link FeatureIDTextureView} constructor always completes successfully, - * but it may not always reflect the actual content of the - * {@link FeatureIDTexture}. This enumeration provides the reason. - */ -enum class FeatureIDTextureViewStatus { - /** - * @brief This view is valid and ready to use. - */ - Valid, - - /** - * @brief This view has not yet been initialized. - */ - InvalidUninitialized, - - /** - * @brief This feature id texture has a texture index that doesn't exist in - * the glTF. - */ - InvalidTextureIndex, - - /** - * @brief This feature id texture has an image index that doesn't exist in - * the glTF. - */ - InvalidImageIndex, - - /** - * @brief This feature id texture has an unknown image channel. - */ - InvalidChannel, - - /** - * @brief This feature id texture has an empty image. - */ - InvalidEmptyImage -}; - -/** - * @brief A view on the image data of {@link FeatureIDTexture}. - * - * It provides the ability to sample the feature IDs from the - * {@link FeatureIDTexture} using texture coordinates. - */ -class FeatureIDTextureView { -public: - /** - * @brief Constructs an uninitialized and invalid view. - */ - FeatureIDTextureView() noexcept; - - /** - * @brief Construct a view of the data specified by a - * {@link FeatureIDTexture}. - * - * @param model The glTF in which to look for the feature id texture's data. - * @param featureIDTexture The feature id texture to create a view for. - */ - FeatureIDTextureView( - const Model& model, - const FeatureIDTexture& featureIDTexture) noexcept; - - /** - * @brief Get the Feature ID for the given texture coordinates. - * - * Will return -1 when the status is not Valid. - * - * @param u The u-component of the texture coordinates. Must be within - * [0.0, 1.0]. - * @param v The v-component of the texture coordinates. Must be within - * [0.0, 1.0]. - * @return The feature ID at the nearest pixel to the texture coordinates. - */ - int64_t getFeatureID(double u, double v) const noexcept; - - /** - * @brief Get the status of this view. - * - * If invalid, it will not be safe to sample feature ids from this view. - */ - FeatureIDTextureViewStatus status() const { return _status; } - - /** - * @brief Get the actual feature ID texture. - * - * This will be nullptr if the feature id texture view runs into problems - * during construction. - */ - const ImageCesium* getImage() const { return _pImage; } - - /** - * @brief Get the channel index that this feature ID texture uses. - */ - int32_t getChannel() const { return _channel; } - - /** - * @brief Get the name of the feature table associated with this feature ID - * texture. - */ - const std::string& getFeatureTableName() const { - return this->_featureTableName; - } - - /** - * @brief Get the texture coordinate attribute index for this feature id - * texture. - */ - int64_t getTextureCoordinateAttributeId() const { - return this->_textureCoordinateAttributeId; - } - -private: - const ImageCesium* _pImage; - int32_t _channel; - int64_t _textureCoordinateAttributeId; - std::string _featureTableName; - FeatureIDTextureViewStatus _status; -}; - -} // namespace CesiumGltf \ No newline at end of file diff --git a/CesiumGltf/include/CesiumGltf/MeshFeaturesFeatureIdTextureView.h b/CesiumGltf/include/CesiumGltf/FeatureIdTextureView.h similarity index 98% rename from CesiumGltf/include/CesiumGltf/MeshFeaturesFeatureIdTextureView.h rename to CesiumGltf/include/CesiumGltf/FeatureIdTextureView.h index c7df25c12..c579ca836 100644 --- a/CesiumGltf/include/CesiumGltf/MeshFeaturesFeatureIdTextureView.h +++ b/CesiumGltf/include/CesiumGltf/FeatureIdTextureView.h @@ -15,8 +15,6 @@ #include namespace CesiumGltf { -namespace MeshFeatures { - /** * @brief The status of a feature ID texture view. * @@ -145,6 +143,4 @@ class FeatureIdTextureView { const ImageCesium* _pImage; }; - -} // namespace MeshFeatures } // namespace CesiumGltf diff --git a/CesiumGltf/include/CesiumGltf/MetadataArrayView.h b/CesiumGltf/include/CesiumGltf/MetadataArrayView.h deleted file mode 100644 index fe6b94b0c..000000000 --- a/CesiumGltf/include/CesiumGltf/MetadataArrayView.h +++ /dev/null @@ -1,129 +0,0 @@ -#pragma once - -#include "CesiumGltf/PropertyType.h" - -#include - -#include - -#include -#include - -namespace CesiumGltf { -template class MetadataArrayView { -public: - MetadataArrayView() : _valueBuffer{} {} - - MetadataArrayView(const gsl::span& buffer) noexcept - : _valueBuffer{ - CesiumUtility::reintepretCastSpan(buffer)} {} - - const ElementType& operator[](int64_t index) const noexcept { - return _valueBuffer[index]; - } - - int64_t size() const noexcept { - return static_cast(_valueBuffer.size()); - } - -private: - gsl::span _valueBuffer; -}; - -template <> class MetadataArrayView { -public: - MetadataArrayView() : _valueBuffer{}, _bitOffset{0}, _instanceCount{0} {} - - MetadataArrayView( - const gsl::span& buffer, - int64_t bitOffset, - int64_t instanceCount) noexcept - : _valueBuffer{buffer}, - _bitOffset{bitOffset}, - _instanceCount{instanceCount} {} - - bool operator[](int64_t index) const noexcept { - index += _bitOffset; - const int64_t byteIndex = index / 8; - const int64_t bitIndex = index % 8; - const int bitValue = - static_cast(_valueBuffer[byteIndex] >> bitIndex) & 1; - return bitValue == 1; - } - - int64_t size() const noexcept { return _instanceCount; } - -private: - gsl::span _valueBuffer; - int64_t _bitOffset; - int64_t _instanceCount; -}; - -template <> class MetadataArrayView { -public: - MetadataArrayView() - : _valueBuffer{}, _offsetBuffer{}, _offsetType{}, _size{0} {} - - MetadataArrayView( - const gsl::span& buffer, - const gsl::span& offsetBuffer, - PropertyType offsetType, - int64_t size) noexcept - : _valueBuffer{buffer}, - _offsetBuffer{offsetBuffer}, - _offsetType{offsetType}, - _size{size} {} - - std::string_view operator[](int64_t index) const noexcept { - const size_t currentOffset = - getOffsetFromOffsetBuffer(index, _offsetBuffer, _offsetType); - const size_t nextOffset = - getOffsetFromOffsetBuffer(index + 1, _offsetBuffer, _offsetType); - return std::string_view( - reinterpret_cast(_valueBuffer.data() + currentOffset), - (nextOffset - currentOffset)); - } - - int64_t size() const noexcept { return _size; } - -private: - static size_t getOffsetFromOffsetBuffer( - size_t instance, - const gsl::span& offsetBuffer, - PropertyType offsetType) noexcept { - switch (offsetType) { - case PropertyType::Uint8: { - assert(instance < offsetBuffer.size() / sizeof(uint8_t)); - const uint8_t offset = *reinterpret_cast( - offsetBuffer.data() + instance * sizeof(uint8_t)); - return static_cast(offset); - } - case PropertyType::Uint16: { - assert(instance < offsetBuffer.size() / sizeof(uint16_t)); - const uint16_t offset = *reinterpret_cast( - offsetBuffer.data() + instance * sizeof(uint16_t)); - return static_cast(offset); - } - case PropertyType::Uint32: { - assert(instance < offsetBuffer.size() / sizeof(uint32_t)); - const uint32_t offset = *reinterpret_cast( - offsetBuffer.data() + instance * sizeof(uint32_t)); - return static_cast(offset); - } - case PropertyType::Uint64: { - assert(instance < offsetBuffer.size() / sizeof(uint64_t)); - const uint64_t offset = *reinterpret_cast( - offsetBuffer.data() + instance * sizeof(uint64_t)); - return static_cast(offset); - } - default: - assert(false && "Offset type has unknown type"); - return 0; - } - } - gsl::span _valueBuffer; - gsl::span _offsetBuffer; - PropertyType _offsetType; - int64_t _size; -}; -} // namespace CesiumGltf diff --git a/CesiumGltf/include/CesiumGltf/MetadataFeatureTableView.h b/CesiumGltf/include/CesiumGltf/MetadataFeatureTableView.h deleted file mode 100644 index cae3deb92..000000000 --- a/CesiumGltf/include/CesiumGltf/MetadataFeatureTableView.h +++ /dev/null @@ -1,536 +0,0 @@ -#pragma once - -#include "CesiumGltf/ExtensionModelExtFeatureMetadata.h" -#include "CesiumGltf/MetadataPropertyView.h" -#include "CesiumGltf/Model.h" -#include "CesiumGltf/PropertyType.h" - -#include - -#include - -namespace CesiumGltf { -/** - * @brief Utility to retrieve the data of FeatureTable - * - * This should be used to get {@link MetadataPropertyView} of a property since - * it will validate the EXT_Feature_Metadata format to make sure {@link MetadataPropertyView} - * not access out of bound - */ -class MetadataFeatureTableView { -public: - /** - * @brief Create an instance of MetadataFeatureTableView - * @param model The Gltf Model that stores featureTable data - * @param featureTable The FeatureTable that will be used to retrieve the data - * from - */ - MetadataFeatureTableView( - const Model* pModel, - const FeatureTable* pFeatureTable); - - /** - * @brief Find the {@link ClassProperty} which stores the type information of a property based on the property name - * @param propertyName The name of the property to retrieve type info - * @return ClassProperty of a property. Return nullptr if no property is found - */ - const ClassProperty* getClassProperty(const std::string& propertyName) const; - - /** - * @brief Get MetadataPropertyView to view the data of a property stored in - * the FeatureTable. - * - * This method will validate the EXT_Feature_Metadata format to ensure - * MetadataPropertyView retrieve the correct data. T must be uin8_t, int8_t, - * uint16_t, int16_t, uint32_t, int32_t, uint64_t, int64_t, float, double, - * bool, std::string_view, and MetadataArrayView with T must be one of the - * types mentioned above - * - * @param propertyName The name of the property to retrieve data from - * @return ClassProperty of a property. Return nullptr if no property is found - */ - template - MetadataPropertyView - getPropertyView(const std::string& propertyName) const { - if (_pFeatureTable->count < 0) { - return createInvalidPropertyView( - MetadataPropertyViewStatus::InvalidPropertyNotExist); - } - - const ClassProperty* pClassProperty = getClassProperty(propertyName); - if (!pClassProperty) { - return createInvalidPropertyView( - MetadataPropertyViewStatus::InvalidPropertyNotExist); - } - - return getPropertyViewImpl(propertyName, *pClassProperty); - } - - /** - * @brief Get MetadataPropertyView through a callback that accepts property - * name and std::optional> to view the data of a - * property stored in the FeatureTable. - * - * This method will validate the EXT_Feature_Metadata format to ensure - * MetadataPropertyView retrieve the correct data. T must be uin8_t, int8_t, - * uint16_t, int16_t, uint32_t, int32_t, uint64_t, int64_t, float, double, - * bool, std::string_view, and MetadataArrayView with T must be one of the - * types mentioned above. If the property is invalid, std::nullopt will be - * passed to the callback. Otherwise, a valid property view will be passed to - * the callback - * - * @param propertyName The name of the property to retrieve data from - * @tparam callback A callback function that accepts property name and - * std::optional> - */ - template - void - getPropertyView(const std::string& propertyName, Callback&& callback) const { - const ClassProperty* pClassProperty = getClassProperty(propertyName); - if (!pClassProperty) { - return; - } - - PropertyType type = convertStringToPropertyType(pClassProperty->type); - PropertyType componentType = PropertyType::None; - if (pClassProperty->componentType.has_value()) { - componentType = - convertStringToPropertyType(pClassProperty->componentType.value()); - } - - if (type != PropertyType::Array) { - getScalarPropertyViewImpl( - propertyName, - *pClassProperty, - type, - std::forward(callback)); - } else { - getArrayPropertyViewImpl( - propertyName, - *pClassProperty, - componentType, - std::forward(callback)); - } - } - - /** - * @brief Get MetadataPropertyView for each property in the FeatureTable - * through a callback that accepts property name and - * std::optional> to view the data stored in the - * FeatureTableProperty. - * - * This method will validate the EXT_Feature_Metadata format to ensure - * MetadataPropertyView retrieve the correct data. T must be uin8_t, int8_t, - * uint16_t, int16_t, uint32_t, int32_t, uint64_t, int64_t, float, double, - * bool, std::string_view, and MetadataArrayView with T must be one of the - * types mentioned above. If the property is invalid, std::nullopt will be - * passed to the callback. Otherwise, a valid property view will be passed to - * the callback - * - * @param propertyName The name of the property to retrieve data from - * @tparam callback A callback function that accepts property name and - * std::optional> - */ - template void forEachProperty(Callback&& callback) const { - for (const auto& property : this->_pClass->properties) { - getPropertyView(property.first, std::forward(callback)); - } - } - -private: - template - void getArrayPropertyViewImpl( - const std::string& propertyName, - const ClassProperty& classProperty, - PropertyType type, - Callback&& callback) const { - switch (type) { - case PropertyType::Int8: - callback( - propertyName, - getPropertyViewImpl>( - propertyName, - classProperty)); - break; - case PropertyType::Uint8: - callback( - propertyName, - getPropertyViewImpl>( - propertyName, - classProperty)); - break; - case PropertyType::Int16: - callback( - propertyName, - getPropertyViewImpl>( - propertyName, - classProperty)); - break; - case PropertyType::Uint16: - callback( - propertyName, - getPropertyViewImpl>( - propertyName, - classProperty)); - break; - case PropertyType::Int32: - callback( - propertyName, - getPropertyViewImpl>( - propertyName, - classProperty)); - break; - case PropertyType::Uint32: - callback( - propertyName, - getPropertyViewImpl>( - propertyName, - classProperty)); - break; - case PropertyType::Int64: - callback( - propertyName, - getPropertyViewImpl>( - propertyName, - classProperty)); - break; - case PropertyType::Uint64: - callback( - propertyName, - getPropertyViewImpl>( - propertyName, - classProperty)); - break; - case PropertyType::Float32: - callback( - propertyName, - getPropertyViewImpl>( - propertyName, - classProperty)); - break; - case PropertyType::Float64: - callback( - propertyName, - getPropertyViewImpl>( - propertyName, - classProperty)); - break; - case PropertyType::Boolean: - callback( - propertyName, - getPropertyViewImpl>( - propertyName, - classProperty)); - break; - case PropertyType::String: - callback( - propertyName, - getPropertyViewImpl>( - propertyName, - classProperty)); - break; - default: - break; - } - } - - template - void getScalarPropertyViewImpl( - const std::string& propertyName, - const ClassProperty& classProperty, - PropertyType type, - Callback&& callback) const { - switch (type) { - case PropertyType::Int8: - callback( - propertyName, - getPropertyViewImpl(propertyName, classProperty)); - break; - case PropertyType::Uint8: - callback( - propertyName, - getPropertyViewImpl(propertyName, classProperty)); - break; - case PropertyType::Int16: - callback( - propertyName, - getPropertyViewImpl(propertyName, classProperty)); - break; - case PropertyType::Uint16: - callback( - propertyName, - getPropertyViewImpl(propertyName, classProperty)); - break; - case PropertyType::Int32: - callback( - propertyName, - getPropertyViewImpl(propertyName, classProperty)); - break; - case PropertyType::Uint32: - callback( - propertyName, - getPropertyViewImpl(propertyName, classProperty)); - break; - case PropertyType::Int64: - callback( - propertyName, - getPropertyViewImpl(propertyName, classProperty)); - break; - case PropertyType::Uint64: - callback( - propertyName, - getPropertyViewImpl(propertyName, classProperty)); - break; - case PropertyType::Float32: - callback( - propertyName, - getPropertyViewImpl(propertyName, classProperty)); - break; - case PropertyType::Float64: - callback( - propertyName, - getPropertyViewImpl(propertyName, classProperty)); - break; - case PropertyType::Boolean: - callback( - propertyName, - getPropertyViewImpl(propertyName, classProperty)); - break; - case PropertyType::String: - callback( - propertyName, - getPropertyViewImpl(propertyName, classProperty)); - break; - default: - break; - } - } - - template - MetadataPropertyView getPropertyViewImpl( - const std::string& propertyName, - const ClassProperty& classProperty) const { - auto featureTablePropertyIter = - _pFeatureTable->properties.find(propertyName); - if (featureTablePropertyIter == _pFeatureTable->properties.end()) { - return createInvalidPropertyView( - MetadataPropertyViewStatus::InvalidPropertyNotExist); - } - - const FeatureTableProperty& featureTableProperty = - featureTablePropertyIter->second; - - if constexpr (IsMetadataNumeric::value || IsMetadataBoolean::value) { - return getPrimitivePropertyValues(classProperty, featureTableProperty); - } - - if constexpr (IsMetadataString::value) { - return getStringPropertyValues(classProperty, featureTableProperty); - } - - if constexpr ( - IsMetadataNumericArray::value || IsMetadataBooleanArray::value) { - return getPrimitiveArrayPropertyValues< - typename MetadataArrayType::type>( - classProperty, - featureTableProperty); - } - - if constexpr (IsMetadataStringArray::value) { - return getStringArrayPropertyValues(classProperty, featureTableProperty); - } - } - - template - MetadataPropertyView getPrimitivePropertyValues( - const ClassProperty& classProperty, - const FeatureTableProperty& featureTableProperty) const { - const PropertyType type = convertStringToPropertyType(classProperty.type); - if (TypeToPropertyType::value != type) { - return createInvalidPropertyView( - MetadataPropertyViewStatus::InvalidTypeMismatch); - } - - gsl::span valueBuffer; - const auto status = - getBufferSafe(featureTableProperty.bufferView, valueBuffer); - if (status != MetadataPropertyViewStatus::Valid) { - return createInvalidPropertyView(status); - } - - if (valueBuffer.size() % sizeof(T) != 0) { - return createInvalidPropertyView( - MetadataPropertyViewStatus:: - InvalidBufferViewSizeNotDivisibleByTypeSize); - } - - size_t maxRequiredBytes = 0; - if (IsMetadataBoolean::value) { - maxRequiredBytes = static_cast( - glm::ceil(static_cast(_pFeatureTable->count) / 8.0)); - } else { - maxRequiredBytes = _pFeatureTable->count * sizeof(T); - } - - if (valueBuffer.size() < maxRequiredBytes) { - return createInvalidPropertyView( - MetadataPropertyViewStatus::InvalidBufferViewSizeNotFitInstanceCount); - } - - return MetadataPropertyView( - MetadataPropertyViewStatus::Valid, - valueBuffer, - gsl::span(), - gsl::span(), - PropertyType::None, - 0, - _pFeatureTable->count, - classProperty.normalized); - } - - MetadataPropertyView getStringPropertyValues( - const ClassProperty& classProperty, - const FeatureTableProperty& featureTableProperty) const; - - template - MetadataPropertyView> getPrimitiveArrayPropertyValues( - const ClassProperty& classProperty, - const FeatureTableProperty& featureTableProperty) const { - if (classProperty.type != ClassProperty::Type::ARRAY) { - return createInvalidPropertyView>( - MetadataPropertyViewStatus::InvalidTypeMismatch); - } - - if (!classProperty.componentType.has_value()) { - return createInvalidPropertyView>( - MetadataPropertyViewStatus::InvalidTypeMismatch); - } - - const PropertyType componentType = - convertStringToPropertyType(classProperty.componentType.value()); - if (TypeToPropertyType::value != componentType) { - return createInvalidPropertyView>( - MetadataPropertyViewStatus::InvalidTypeMismatch); - } - - gsl::span valueBuffer; - auto status = getBufferSafe(featureTableProperty.bufferView, valueBuffer); - if (status != MetadataPropertyViewStatus::Valid) { - return createInvalidPropertyView>(status); - } - - if (valueBuffer.size() % sizeof(T) != 0) { - return createInvalidPropertyView>( - MetadataPropertyViewStatus:: - InvalidBufferViewSizeNotDivisibleByTypeSize); - } - - const int64_t componentCount = classProperty.componentCount.value_or(0); - if (componentCount > 0 && featureTableProperty.arrayOffsetBufferView >= 0) { - return createInvalidPropertyView>( - MetadataPropertyViewStatus:: - InvalidArrayComponentCountAndOffsetBufferCoexist); - } - - if (componentCount <= 0 && featureTableProperty.arrayOffsetBufferView < 0) { - return createInvalidPropertyView>( - MetadataPropertyViewStatus:: - InvalidArrayComponentCountOrOffsetBufferNotExist); - } - - // fixed array - if (componentCount > 0) { - size_t maxRequiredBytes = 0; - if constexpr (IsMetadataBoolean::value) { - maxRequiredBytes = static_cast(glm::ceil( - static_cast(_pFeatureTable->count * componentCount) / 8.0)); - } else { - maxRequiredBytes = static_cast( - _pFeatureTable->count * componentCount * sizeof(T)); - } - - if (valueBuffer.size() < maxRequiredBytes) { - return createInvalidPropertyView>( - MetadataPropertyViewStatus:: - InvalidBufferViewSizeNotFitInstanceCount); - } - - return MetadataPropertyView>( - MetadataPropertyViewStatus::Valid, - valueBuffer, - gsl::span(), - gsl::span(), - PropertyType::None, - static_cast(componentCount), - static_cast(_pFeatureTable->count), - classProperty.normalized); - } - - // dynamic array - const PropertyType offsetType = - convertOffsetStringToPropertyType(featureTableProperty.offsetType); - if (offsetType == PropertyType::None) { - return createInvalidPropertyView>( - MetadataPropertyViewStatus::InvalidOffsetType); - } - - constexpr bool checkBitsSize = IsMetadataBoolean::value; - gsl::span offsetBuffer; - status = getOffsetBufferSafe( - featureTableProperty.arrayOffsetBufferView, - offsetType, - valueBuffer.size(), - static_cast(_pFeatureTable->count), - checkBitsSize, - offsetBuffer); - if (status != MetadataPropertyViewStatus::Valid) { - return createInvalidPropertyView>(status); - } - - return MetadataPropertyView>( - MetadataPropertyViewStatus::Valid, - valueBuffer, - offsetBuffer, - gsl::span(), - offsetType, - 0, - static_cast(_pFeatureTable->count), - classProperty.normalized); - } - - MetadataPropertyView> - getStringArrayPropertyValues( - const ClassProperty& classProperty, - const FeatureTableProperty& featureTableProperty) const; - - MetadataPropertyViewStatus getBufferSafe( - int32_t bufferViewIdx, - gsl::span& buffer) const noexcept; - - MetadataPropertyViewStatus getOffsetBufferSafe( - int32_t bufferViewIdx, - PropertyType offsetType, - size_t valueBufferSize, - size_t instanceCount, - bool checkBitsSize, - gsl::span& offsetBuffer) const noexcept; - - template - static MetadataPropertyView - createInvalidPropertyView(MetadataPropertyViewStatus invalidStatus) noexcept { - return MetadataPropertyView( - invalidStatus, - gsl::span(), - gsl::span(), - gsl::span(), - PropertyType::None, - 0, - 0, - false); - } - - const Model* _pModel; - const FeatureTable* _pFeatureTable; - const Class* _pClass; -}; -} // namespace CesiumGltf diff --git a/CesiumGltf/include/CesiumGltf/MetadataPropertyView.h b/CesiumGltf/include/CesiumGltf/MetadataPropertyView.h deleted file mode 100644 index 6b535d27d..000000000 --- a/CesiumGltf/include/CesiumGltf/MetadataPropertyView.h +++ /dev/null @@ -1,411 +0,0 @@ -#pragma once - -#include "CesiumGltf/MetadataArrayView.h" -#include "CesiumGltf/PropertyType.h" -#include "CesiumGltf/PropertyTypeTraits.h" - -#include - -#include -#include -#include -#include -#include - -namespace CesiumGltf { -/** - * @brief Indicates the status of a property view. - * - * The {@link MetadataPropertyView} constructor always completes successfully. However, - * it may not always reflect the actual content of the {@link FeatureTableProperty}, but - * instead indicate that its {@link MetadataPropertyView::size} is 0. This enumeration - * provides the reason. - */ -enum class MetadataPropertyViewStatus { - /** - * @brief This property view is valid and ready to use. - */ - Valid, - - /** - * @brief This property view does not exist in the FeatureTable. - */ - InvalidPropertyNotExist, - - /** - * @brief This property view does not have a correct type with what is - * specified in {@link ClassProperty::type}. - */ - InvalidTypeMismatch, - - /** - * @brief This property view does not have a valid value buffer view index. - */ - InvalidValueBufferViewIndex, - - /** - * @brief This array property view does not have a valid array offset buffer - * view index. - */ - InvalidArrayOffsetBufferViewIndex, - - /** - * @brief This string property view does not have a valid string offset buffer - * view index. - */ - InvalidStringOffsetBufferViewIndex, - - /** - * @brief This property view has a valid value buffer view index, but buffer - * view specifies an invalid buffer index - */ - InvalidValueBufferIndex, - - /** - * @brief This property view has a valid array string buffer view index, but - * buffer view specifies an invalid buffer index - */ - InvalidArrayOffsetBufferIndex, - - /** - * @brief This property view has a valid string offset buffer view index, but - * buffer view specifies an invalid buffer index - */ - InvalidStringOffsetBufferIndex, - - /** - * @brief This property view has buffer view's offset not aligned by 8 bytes - */ - InvalidBufferViewNotAligned8Bytes, - - /** - * @brief This property view has an out-of-bound buffer view - */ - InvalidBufferViewOutOfBound, - - /** - * @brief This property view has an invalid buffer view's length which is not - * a multiple of the size of its type or offset type - */ - InvalidBufferViewSizeNotDivisibleByTypeSize, - - /** - * @brief This property view has an invalid buffer view's length which cannot - * fit all the instances of the feature table - */ - InvalidBufferViewSizeNotFitInstanceCount, - - /** - * @brief This array property view has both component count and offset buffer - * view - */ - InvalidArrayComponentCountAndOffsetBufferCoexist, - - /** - * @brief This array property view doesn't have either component count or - * offset buffer view - */ - InvalidArrayComponentCountOrOffsetBufferNotExist, - - /** - * @brief This property view have an unknown offset type - */ - InvalidOffsetType, - - /** - * @brief This property view has offset values not sorted ascendingly - */ - InvalidOffsetValuesNotSortedAscending, - - /** - * @brief This property view has an offset point to an out of bound value - */ - InvalidOffsetValuePointsToOutOfBoundBuffer -}; - -/** - * @brief A view on the data of the FeatureTableProperty - * - * It provides utility to retrieve the actual data stored in the - * {@link FeatureTableProperty::bufferView} like an array of elements. - * Data of each instance can be accessed through the {@link get(int64_t instance)} method - * - * @param ElementType must be uin8_t, int8_t, uint16_t, int16_t, - * uint32_t, int32_t, uint64_t, int64_t, float, double, bool, std::string_view, - * and MetadataArrayView with T must be one of the types mentioned above - */ -template class MetadataPropertyView { -public: - /** - * @brief Constructs a new instance viewing a non-existent property. - */ - MetadataPropertyView() - : _status{MetadataPropertyViewStatus::InvalidPropertyNotExist}, - _valueBuffer{}, - _arrayOffsetBuffer{}, - _stringOffsetBuffer{}, - _offsetType{}, - _offsetSize{}, - _componentCount{}, - _instanceCount{}, - _normalized{} {} - - /** - * @brief Construct a new instance pointing to the data specified by - * FeatureTableProperty - * @param valueBuffer The raw buffer specified by {@link FeatureTableProperty::bufferView} - * @param arrayOffsetBuffer The raw buffer specified by {@link FeatureTableProperty::arrayOffsetBufferView} - * @param stringOffsetBuffer The raw buffer specified by {@link FeatureTableProperty::stringOffsetBufferView} - * @param offsetType The offset type of the arrayOffsetBuffer and stringOffsetBuffer that is specified by {@link FeatureTableProperty::offsetType} - * @param componentCount The number of elements for fixed array value which is specified by {@link FeatureTableProperty::componentCount} - * @param instanceCount The number of instances specified by {@link FeatureTable::count} - * @param normalized Whether this property has a normalized integer type. - */ - MetadataPropertyView( - MetadataPropertyViewStatus status, - gsl::span valueBuffer, - gsl::span arrayOffsetBuffer, - gsl::span stringOffsetBuffer, - PropertyType offsetType, - int64_t componentCount, - int64_t instanceCount, - bool normalized) noexcept - : _status{status}, - _valueBuffer{valueBuffer}, - _arrayOffsetBuffer{arrayOffsetBuffer}, - _stringOffsetBuffer{stringOffsetBuffer}, - _offsetType{offsetType}, - _offsetSize{getOffsetSize(offsetType)}, - _componentCount{componentCount}, - _instanceCount{instanceCount}, - _normalized{normalized} {} - - /** - * @brief Gets the status of this property view. - * - * Indicates whether the view accurately reflects the property's data, or - * whether an error occurred. - */ - MetadataPropertyViewStatus status() const noexcept { return _status; } - - /** - * @brief Get the value of an instance of the FeatureTable. - * @param instance The instance index - * @return The value of the instance - */ - ElementType get(int64_t instance) const noexcept { - assert( - _status == MetadataPropertyViewStatus::Valid && - "Check the status() first to make sure view is valid"); - assert( - size() > 0 && - "Check the size() of the view to make sure it's not empty"); - assert(instance >= 0 && "instance index must be positive"); - - if constexpr (IsMetadataNumeric::value) { - return getNumeric(instance); - } - - if constexpr (IsMetadataBoolean::value) { - return getBoolean(instance); - } - - if constexpr (IsMetadataString::value) { - return getString(instance); - } - - if constexpr (IsMetadataNumericArray::value) { - return getNumericArray::type>( - instance); - } - - if constexpr (IsMetadataBooleanArray::value) { - return getBooleanArray(instance); - } - - if constexpr (IsMetadataStringArray::value) { - return getStringArray(instance); - } - } - - /** - * @brief Get the number of instances in the FeatureTable - * @return The number of instances in the FeatureTable - */ - int64_t size() const noexcept { return _instanceCount; } - - /** - * @brief Get the component count of this property. Only applicable when the - * property is an array type. - * - * @return The component count of this property. - */ - int64_t getComponentCount() const noexcept { return _componentCount; } - - /** - * @brief Whether this property has a normalized integer type. - * - * @return Whether this property has a normalized integer type. - */ - bool isNormalized() const noexcept { return _normalized; } - -private: - ElementType getNumeric(int64_t instance) const noexcept { - return reinterpret_cast(_valueBuffer.data())[instance]; - } - - bool getBoolean(int64_t instance) const noexcept { - const int64_t byteIndex = instance / 8; - const int64_t bitIndex = instance % 8; - const int bitValue = - static_cast(_valueBuffer[byteIndex] >> bitIndex) & 1; - return bitValue == 1; - } - - std::string_view getString(int64_t instance) const noexcept { - const size_t currentOffset = - getOffsetFromOffsetBuffer(instance, _stringOffsetBuffer, _offsetType); - const size_t nextOffset = getOffsetFromOffsetBuffer( - instance + 1, - _stringOffsetBuffer, - _offsetType); - return std::string_view( - reinterpret_cast(_valueBuffer.data() + currentOffset), - (nextOffset - currentOffset)); - } - - template - MetadataArrayView getNumericArray(int64_t instance) const noexcept { - if (_componentCount > 0) { - const gsl::span vals( - _valueBuffer.data() + instance * _componentCount * sizeof(T), - _componentCount * sizeof(T)); - return MetadataArrayView{vals}; - } - - const size_t currentOffset = - getOffsetFromOffsetBuffer(instance, _arrayOffsetBuffer, _offsetType); - const size_t nextOffset = getOffsetFromOffsetBuffer( - instance + 1, - _arrayOffsetBuffer, - _offsetType); - const gsl::span vals( - _valueBuffer.data() + currentOffset, - (nextOffset - currentOffset)); - return MetadataArrayView{vals}; - } - - MetadataArrayView - getStringArray(int64_t instance) const noexcept { - if (_componentCount > 0) { - const gsl::span offsetVals( - _stringOffsetBuffer.data() + instance * _componentCount * _offsetSize, - (_componentCount + 1) * _offsetSize); - return MetadataArrayView( - _valueBuffer, - offsetVals, - _offsetType, - _componentCount); - } - - const size_t currentOffset = - getOffsetFromOffsetBuffer(instance, _arrayOffsetBuffer, _offsetType); - const size_t nextOffset = getOffsetFromOffsetBuffer( - instance + 1, - _arrayOffsetBuffer, - _offsetType); - const gsl::span offsetVals( - _stringOffsetBuffer.data() + currentOffset, - (nextOffset - currentOffset + _offsetSize)); - return MetadataArrayView( - _valueBuffer, - offsetVals, - _offsetType, - (nextOffset - currentOffset) / _offsetSize); - } - - MetadataArrayView getBooleanArray(int64_t instance) const noexcept { - if (_componentCount > 0) { - const size_t offsetBits = _componentCount * instance; - const size_t nextOffsetBits = _componentCount * (instance + 1); - const gsl::span buffer( - _valueBuffer.data() + offsetBits / 8, - (nextOffsetBits / 8 - offsetBits / 8 + 1)); - return MetadataArrayView(buffer, offsetBits % 8, _componentCount); - } - - const size_t currentOffset = - getOffsetFromOffsetBuffer(instance, _arrayOffsetBuffer, _offsetType); - const size_t nextOffset = getOffsetFromOffsetBuffer( - instance + 1, - _arrayOffsetBuffer, - _offsetType); - - const size_t totalBits = nextOffset - currentOffset; - const gsl::span buffer( - _valueBuffer.data() + currentOffset / 8, - (nextOffset / 8 - currentOffset / 8 + 1)); - return MetadataArrayView(buffer, currentOffset % 8, totalBits); - } - - static int64_t getOffsetSize(PropertyType offsetType) noexcept { - switch (offsetType) { - case CesiumGltf::PropertyType::Uint8: - return sizeof(uint8_t); - case CesiumGltf::PropertyType::Uint16: - return sizeof(uint16_t); - case CesiumGltf::PropertyType::Uint32: - return sizeof(uint32_t); - case CesiumGltf::PropertyType::Uint64: - return sizeof(uint64_t); - default: - return 0; - } - } - - static size_t getOffsetFromOffsetBuffer( - size_t instance, - const gsl::span& offsetBuffer, - PropertyType offsetType) noexcept { - switch (offsetType) { - case PropertyType::Uint8: { - assert(instance < offsetBuffer.size() / sizeof(uint8_t)); - const uint8_t offset = *reinterpret_cast( - offsetBuffer.data() + instance * sizeof(uint8_t)); - return static_cast(offset); - } - case PropertyType::Uint16: { - assert(instance < offsetBuffer.size() / sizeof(uint16_t)); - const uint16_t offset = *reinterpret_cast( - offsetBuffer.data() + instance * sizeof(uint16_t)); - return static_cast(offset); - } - case PropertyType::Uint32: { - assert(instance < offsetBuffer.size() / sizeof(uint32_t)); - const uint32_t offset = *reinterpret_cast( - offsetBuffer.data() + instance * sizeof(uint32_t)); - return static_cast(offset); - } - case PropertyType::Uint64: { - assert(instance < offsetBuffer.size() / sizeof(uint64_t)); - const uint64_t offset = *reinterpret_cast( - offsetBuffer.data() + instance * sizeof(uint64_t)); - return static_cast(offset); - } - default: - assert(false && "Offset type has unknown type"); - return 0; - } - } - - MetadataPropertyViewStatus _status; - gsl::span _valueBuffer; - gsl::span _arrayOffsetBuffer; - gsl::span _stringOffsetBuffer; - PropertyType _offsetType; - int64_t _offsetSize; - int64_t _componentCount; - int64_t _instanceCount; - bool _normalized; -}; -} // namespace CesiumGltf diff --git a/CesiumGltf/include/CesiumGltf/StructuralMetadataArrayView.h b/CesiumGltf/include/CesiumGltf/PropertyArrayView.h similarity index 76% rename from CesiumGltf/include/CesiumGltf/StructuralMetadataArrayView.h rename to CesiumGltf/include/CesiumGltf/PropertyArrayView.h index e315584f4..9f3ea6d2f 100644 --- a/CesiumGltf/include/CesiumGltf/StructuralMetadataArrayView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyArrayView.h @@ -1,6 +1,6 @@ #pragma once -#include "CesiumGltf/StructuralMetadataPropertyType.h" +#include "CesiumGltf/PropertyType.h" #include "getOffsetFromOffsetsBuffer.h" #include @@ -11,8 +11,6 @@ #include namespace CesiumGltf { -namespace StructuralMetadata { - /** * @brief A view on an array element of a * {@link ExtensionExtStructuralMetadataPropertyTableProperty}. @@ -20,11 +18,11 @@ namespace StructuralMetadata { * Provides utility to retrieve the data stored in the array of * elements via the array index operator. */ -template class MetadataArrayView { +template class PropertyArrayView { public: - MetadataArrayView() : _values{} {} + PropertyArrayView() : _values{} {} - MetadataArrayView(const gsl::span& buffer) noexcept + PropertyArrayView(const gsl::span& buffer) noexcept : _values{CesiumUtility::reintepretCastSpan(buffer)} {} const ElementType& operator[](int64_t index) const noexcept { @@ -34,14 +32,14 @@ template class MetadataArrayView { int64_t size() const noexcept { return static_cast(_values.size()); } private: - const gsl::span _values; + gsl::span _values; }; -template <> class MetadataArrayView { +template <> class PropertyArrayView { public: - MetadataArrayView() : _values{}, _bitOffset{0}, _size{0} {} + PropertyArrayView() : _values{}, _bitOffset{0}, _size{0} {} - MetadataArrayView( + PropertyArrayView( const gsl::span& buffer, int64_t bitOffset, int64_t size) noexcept @@ -63,15 +61,15 @@ template <> class MetadataArrayView { int64_t _size; }; -template <> class MetadataArrayView { +template <> class PropertyArrayView { public: - MetadataArrayView() + PropertyArrayView() : _values{}, _stringOffsets{}, _stringOffsetType{}, _size{0} {} - MetadataArrayView( + PropertyArrayView( const gsl::span& values, const gsl::span& stringOffsets, - StructuralMetadata::PropertyComponentType stringOffsetType, + PropertyComponentType stringOffsetType, int64_t size) noexcept : _values{values}, _stringOffsets{stringOffsets}, @@ -95,9 +93,7 @@ template <> class MetadataArrayView { private: gsl::span _values; gsl::span _stringOffsets; - StructuralMetadata::PropertyComponentType _stringOffsetType; + PropertyComponentType _stringOffsetType; int64_t _size; }; - -} // namespace StructuralMetadata } // namespace CesiumGltf diff --git a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTablePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h similarity index 93% rename from CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTablePropertyView.h rename to CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h index 320beb349..451c5f14d 100644 --- a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTablePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h @@ -1,7 +1,7 @@ #pragma once -#include "CesiumGltf/StructuralMetadataArrayView.h" -#include "CesiumGltf/StructuralMetadataPropertyTypeTraits.h" +#include "CesiumGltf/PropertyArrayView.h" +#include "CesiumGltf/PropertyTypeTraits.h" #include @@ -12,8 +12,6 @@ #include namespace CesiumGltf { -namespace StructuralMetadata { - /** * @brief Indicates the status of a property table property view. * @@ -161,7 +159,7 @@ enum class PropertyTablePropertyViewStatus { /** * @brief A view on the data of the * {@link ExtensionExtStructuralMetadataPropertyTableProperty that is created by - * a {@link MetadataPropertyTableView}. + * a {@link PropertyTableView}. * * It provides utility to retrieve the actual data stored in the * {@link ExtensionExtStructuralMetadataPropertyTableProperty::values} like an array of elements. @@ -170,7 +168,7 @@ enum class PropertyTablePropertyViewStatus { * @param ElementType must be one of the following: a scalar (uint8_t, int8_t, * uint16_t, int16_t, uint32_t, int32_t, uint64_t, int64_t, float, double), a * glm vecN composed of one of the scalar types, a glm matN composed of one of - * the scalar types, bool, std::string_view, or MetadataArrayView with T as + * the scalar types, bool, std::string_view, or PropertyArrayView with T as * one of the aforementioned types. */ template class PropertyTablePropertyView { @@ -226,8 +224,8 @@ template class PropertyTablePropertyView { gsl::span values, gsl::span arrayOffsets, gsl::span stringOffsets, - StructuralMetadata::PropertyComponentType arrayOffsetType, - StructuralMetadata::PropertyComponentType stringOffsetType, + PropertyComponentType arrayOffsetType, + PropertyComponentType stringOffsetType, int64_t arrayCount, int64_t size, bool normalized) noexcept @@ -345,14 +343,14 @@ template class PropertyTablePropertyView { } template - MetadataArrayView getNumericArrayValues(int64_t index) const noexcept { + PropertyArrayView getNumericArrayValues(int64_t index) const noexcept { // Handle fixed-length arrays if (_arrayCount > 0) { size_t arraySize = _arrayCount * sizeof(T); const gsl::span values( _values.data() + index * arraySize, arraySize); - return MetadataArrayView{values}; + return PropertyArrayView{values}; } // Handle variable-length arrays @@ -363,19 +361,19 @@ template class PropertyTablePropertyView { const gsl::span values( _values.data() + currentOffset, nextOffset - currentOffset); - return MetadataArrayView{values}; + return PropertyArrayView{values}; } - MetadataArrayView + PropertyArrayView getStringArrayValues(int64_t index) const noexcept { // Handle fixed-length arrays if (_arrayCount > 0) { - // Copy the corresponding string offsets to pass to the MetadataArrayView. + // Copy the corresponding string offsets to pass to the PropertyArrayView. const size_t arraySize = _arrayCount * _stringOffsetTypeSize; const gsl::span stringOffsetValues( _stringOffsets.data() + index * arraySize, arraySize + _stringOffsetTypeSize); - return MetadataArrayView( + return PropertyArrayView( _values, stringOffsetValues, _stringOffsetType, @@ -391,14 +389,14 @@ template class PropertyTablePropertyView { const gsl::span stringOffsetValues( _stringOffsets.data() + currentArrayOffset, arraySize + _arrayOffsetTypeSize); - return MetadataArrayView( + return PropertyArrayView( _values, stringOffsetValues, _stringOffsetType, arraySize / _arrayOffsetTypeSize); } - MetadataArrayView getBooleanArrayValues(int64_t index) const noexcept { + PropertyArrayView getBooleanArrayValues(int64_t index) const noexcept { // Handle fixed-length arrays if (_arrayCount > 0) { const size_t offsetBits = _arrayCount * index; @@ -406,7 +404,7 @@ template class PropertyTablePropertyView { const gsl::span buffer( _values.data() + offsetBits / 8, (nextOffsetBits / 8 - offsetBits / 8 + 1)); - return MetadataArrayView(buffer, offsetBits % 8, _arrayCount); + return PropertyArrayView(buffer, offsetBits % 8, _arrayCount); } // Handle variable-length arrays @@ -418,7 +416,7 @@ template class PropertyTablePropertyView { const gsl::span buffer( _values.data() + currentOffset / 8, (nextOffset / 8 - currentOffset / 8 + 1)); - return MetadataArrayView(buffer, currentOffset % 8, totalBits); + return PropertyArrayView(buffer, currentOffset % 8, totalBits); } static int64_t getOffsetTypeSize(PropertyComponentType offsetType) noexcept { @@ -451,6 +449,4 @@ template class PropertyTablePropertyView { int64_t _size; bool _normalized; }; - -} // namespace StructuralMetadata } // namespace CesiumGltf diff --git a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTableView.h b/CesiumGltf/include/CesiumGltf/PropertyTableView.h similarity index 91% rename from CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTableView.h rename to CesiumGltf/include/CesiumGltf/PropertyTableView.h index e3c55db2d..84757d07f 100644 --- a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTableView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTableView.h @@ -2,16 +2,14 @@ #include "CesiumGltf/ExtensionModelExtStructuralMetadata.h" #include "CesiumGltf/Model.h" -#include "CesiumGltf/StructuralMetadataPropertyTablePropertyView.h" -#include "CesiumGltf/StructuralMetadataPropertyType.h" +#include "CesiumGltf/PropertyTablePropertyView.h" +#include "CesiumGltf/PropertyType.h" #include #include namespace CesiumGltf { -namespace StructuralMetadata { - /** * @brief Indicates the status of a property table view. * @@ -107,7 +105,7 @@ class PropertyTableView { * following: a scalar (uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t, * uint64_t, int64_t, float, double), a glm vecN composed of one of the scalar * types, a glm matN composed of one of the scalar types, bool, - * std::string_view, or {@link MetadataArrayView} with T as one of the + * std::string_view, or {@link PropertyArrayView} with T as one of the * aforementioned types. * * @param propertyName The name of the property to retrieve data from @@ -119,16 +117,14 @@ class PropertyTableView { getPropertyView(const std::string& propertyName) const { if (this->size() <= 0) { return createInvalidPropertyView( - StructuralMetadata::PropertyTablePropertyViewStatus:: - ErrorInvalidPropertyTable); + PropertyTablePropertyViewStatus::ErrorInvalidPropertyTable); } const ExtensionExtStructuralMetadataClassProperty* pClassProperty = getClassProperty(propertyName); if (!pClassProperty) { return createInvalidPropertyView( - StructuralMetadata::PropertyTablePropertyViewStatus:: - ErrorPropertyDoesNotExist); + PropertyTablePropertyViewStatus::ErrorPropertyDoesNotExist); } return getPropertyViewImpl(propertyName, *pClassProperty); @@ -144,7 +140,7 @@ class PropertyTableView { * following: a scalar (uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t, * uint64_t, int64_t, float, double), a glm vecN composed of one of the scalar * types, a glm matN composed of one of the scalar types, bool, - * std::string_view, or {@link MetadataArrayView} with T as one of the + * std::string_view, or {@link PropertyArrayView} with T as one of the * aforementioned types. If the property is invalid, an empty * {@link PropertyTablePropertyView} with an error status will be passed to the * callback. Otherwise, a valid property view will be passed to the callback. @@ -160,8 +156,7 @@ class PropertyTableView { callback( propertyName, createInvalidPropertyView( - StructuralMetadata::PropertyTablePropertyViewStatus:: - ErrorInvalidPropertyTable)); + PropertyTablePropertyViewStatus::ErrorInvalidPropertyTable)); return; } @@ -236,7 +231,7 @@ class PropertyTableView { * following: a scalar (uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t, * uint64_t, int64_t, float, double), a glm vecN composed of one of the scalar * types, a glm matN composed of one of the scalar types, bool, - * std::string_view, or {@link MetadataArrayView} with T as one of the + * std::string_view, or {@link PropertyArrayView} with T as one of the * aforementioned types. If the property is invalid, an empty * {@link PropertyTablePropertyView} with an error status code will be passed to the * callback. Otherwise, a valid property view will be passed to @@ -279,70 +274,70 @@ class PropertyTableView { case PropertyComponentType::Int8: callback( propertyName, - getPropertyViewImpl>( + getPropertyViewImpl>( propertyName, classProperty)); break; case PropertyComponentType::Uint8: callback( propertyName, - getPropertyViewImpl>( + getPropertyViewImpl>( propertyName, classProperty)); break; case PropertyComponentType::Int16: callback( propertyName, - getPropertyViewImpl>( + getPropertyViewImpl>( propertyName, classProperty)); break; case PropertyComponentType::Uint16: callback( propertyName, - getPropertyViewImpl>( + getPropertyViewImpl>( propertyName, classProperty)); break; case PropertyComponentType::Int32: callback( propertyName, - getPropertyViewImpl>( + getPropertyViewImpl>( propertyName, classProperty)); break; case PropertyComponentType::Uint32: callback( propertyName, - getPropertyViewImpl>( + getPropertyViewImpl>( propertyName, classProperty)); break; case PropertyComponentType::Int64: callback( propertyName, - getPropertyViewImpl>( + getPropertyViewImpl>( propertyName, classProperty)); break; case PropertyComponentType::Uint64: callback( propertyName, - getPropertyViewImpl>( + getPropertyViewImpl>( propertyName, classProperty)); break; case PropertyComponentType::Float32: callback( propertyName, - getPropertyViewImpl>( + getPropertyViewImpl>( propertyName, classProperty)); break; case PropertyComponentType::Float64: callback( propertyName, - getPropertyViewImpl>( + getPropertyViewImpl>( propertyName, classProperty)); break; @@ -365,70 +360,70 @@ class PropertyTableView { case PropertyComponentType::Int8: callback( propertyName, - getPropertyViewImpl>>( + getPropertyViewImpl>>( propertyName, classProperty)); break; case PropertyComponentType::Uint8: callback( propertyName, - getPropertyViewImpl>>( + getPropertyViewImpl>>( propertyName, classProperty)); break; case PropertyComponentType::Int16: callback( propertyName, - getPropertyViewImpl>>( + getPropertyViewImpl>>( propertyName, classProperty)); break; case PropertyComponentType::Uint16: callback( propertyName, - getPropertyViewImpl>>( + getPropertyViewImpl>>( propertyName, classProperty)); break; case PropertyComponentType::Int32: callback( propertyName, - getPropertyViewImpl>>( + getPropertyViewImpl>>( propertyName, classProperty)); break; case PropertyComponentType::Uint32: callback( propertyName, - getPropertyViewImpl>>( + getPropertyViewImpl>>( propertyName, classProperty)); break; case PropertyComponentType::Int64: callback( propertyName, - getPropertyViewImpl>>( + getPropertyViewImpl>>( propertyName, classProperty)); break; case PropertyComponentType::Uint64: callback( propertyName, - getPropertyViewImpl>>( + getPropertyViewImpl>>( propertyName, classProperty)); break; case PropertyComponentType::Float32: callback( propertyName, - getPropertyViewImpl>>( + getPropertyViewImpl>>( propertyName, classProperty)); break; case PropertyComponentType::Float64: callback( propertyName, - getPropertyViewImpl>>( + getPropertyViewImpl>>( propertyName, classProperty)); break; @@ -490,70 +485,70 @@ class PropertyTableView { case PropertyComponentType::Int8: callback( propertyName, - getPropertyViewImpl>>( + getPropertyViewImpl>>( propertyName, classProperty)); break; case PropertyComponentType::Uint8: callback( propertyName, - getPropertyViewImpl>>( + getPropertyViewImpl>>( propertyName, classProperty)); break; case PropertyComponentType::Int16: callback( propertyName, - getPropertyViewImpl>>( + getPropertyViewImpl>>( propertyName, classProperty)); break; case PropertyComponentType::Uint16: callback( propertyName, - getPropertyViewImpl>>( + getPropertyViewImpl>>( propertyName, classProperty)); break; case PropertyComponentType::Int32: callback( propertyName, - getPropertyViewImpl>>( + getPropertyViewImpl>>( propertyName, classProperty)); break; case PropertyComponentType::Uint32: callback( propertyName, - getPropertyViewImpl>>( + getPropertyViewImpl>>( propertyName, classProperty)); break; case PropertyComponentType::Int64: callback( propertyName, - getPropertyViewImpl>>( + getPropertyViewImpl>>( propertyName, classProperty)); break; case PropertyComponentType::Uint64: callback( propertyName, - getPropertyViewImpl>>( + getPropertyViewImpl>>( propertyName, classProperty)); break; case PropertyComponentType::Float32: callback( propertyName, - getPropertyViewImpl>>( + getPropertyViewImpl>>( propertyName, classProperty)); break; case PropertyComponentType::Float64: callback( propertyName, - getPropertyViewImpl>>( + getPropertyViewImpl>>( propertyName, classProperty)); break; @@ -635,14 +630,14 @@ class PropertyTableView { } else if (type == PropertyType::Boolean) { callback( propertyName, - getPropertyViewImpl>( + getPropertyViewImpl>( propertyName, classProperty)); } else if (type == PropertyType::String) { callback( propertyName, - getPropertyViewImpl>( + getPropertyViewImpl>( propertyName, classProperty)); } else { @@ -1036,7 +1031,7 @@ class PropertyTableView { if (values.size() % sizeof(T) != 0) { return createInvalidPropertyView( - StructuralMetadata::PropertyTablePropertyViewStatus:: + PropertyTablePropertyViewStatus:: ErrorBufferViewSizeNotDivisibleByTypeSize); } @@ -1067,19 +1062,19 @@ class PropertyTableView { propertyTableProperty) const; template - PropertyTablePropertyView> + PropertyTablePropertyView> getPrimitiveArrayPropertyValues( const ExtensionExtStructuralMetadataClassProperty& classProperty, const ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty) const { if (!classProperty.array) { - return createInvalidPropertyView>( + return createInvalidPropertyView>( PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); } const PropertyType type = convertStringToPropertyType(classProperty.type); if (TypeToPropertyType::value != type) { - return createInvalidPropertyView>( + return createInvalidPropertyView>( PropertyTablePropertyViewStatus::ErrorTypeMismatch); } @@ -1087,31 +1082,31 @@ class PropertyTableView { convertStringToPropertyComponentType( classProperty.componentType.value_or("")); if (TypeToPropertyType::component != componentType) { - return createInvalidPropertyView>( + return createInvalidPropertyView>( PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); } gsl::span values; auto status = getBufferSafe(propertyTableProperty.values, values); if (status != PropertyTablePropertyViewStatus::Valid) { - return createInvalidPropertyView>(status); + return createInvalidPropertyView>(status); } if (values.size() % sizeof(T) != 0) { - return createInvalidPropertyView>( + return createInvalidPropertyView>( PropertyTablePropertyViewStatus:: ErrorBufferViewSizeNotDivisibleByTypeSize); } const int64_t fixedLengthArrayCount = classProperty.count.value_or(0); if (fixedLengthArrayCount > 0 && propertyTableProperty.arrayOffsets >= 0) { - return createInvalidPropertyView>( + return createInvalidPropertyView>( PropertyTablePropertyViewStatus:: ErrorArrayCountAndOffsetBufferCoexist); } if (fixedLengthArrayCount <= 0 && propertyTableProperty.arrayOffsets < 0) { - return createInvalidPropertyView>( + return createInvalidPropertyView>( PropertyTablePropertyViewStatus:: ErrorArrayCountAndOffsetBufferDontExist); } @@ -1130,12 +1125,12 @@ class PropertyTableView { } if (values.size() < maxRequiredBytes) { - return createInvalidPropertyView>( + return createInvalidPropertyView>( PropertyTablePropertyViewStatus:: ErrorBufferViewSizeDoesNotMatchPropertyTableCount); } - return PropertyTablePropertyView>( + return PropertyTablePropertyView>( PropertyTablePropertyViewStatus::Valid, values, gsl::span(), @@ -1152,7 +1147,7 @@ class PropertyTableView { convertArrayOffsetTypeStringToPropertyComponentType( propertyTableProperty.arrayOffsetType); if (arrayOffsetType == PropertyComponentType::None) { - return createInvalidPropertyView>( + return createInvalidPropertyView>( PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); } @@ -1166,10 +1161,10 @@ class PropertyTableView { checkBitsSize, arrayOffsets); if (status != PropertyTablePropertyViewStatus::Valid) { - return createInvalidPropertyView>(status); + return createInvalidPropertyView>(status); } - return PropertyTablePropertyView>( + return PropertyTablePropertyView>( PropertyTablePropertyViewStatus::Valid, values, arrayOffsets, @@ -1181,7 +1176,7 @@ class PropertyTableView { classProperty.normalized); } - PropertyTablePropertyView> + PropertyTablePropertyView> getStringArrayPropertyValues( const ExtensionExtStructuralMetadataClassProperty& classProperty, const ExtensionExtStructuralMetadataPropertyTableProperty& @@ -1221,6 +1216,4 @@ class PropertyTableView { const ExtensionExtStructuralMetadataClass* _pClass; PropertyTableViewStatus _status; }; - -} // namespace StructuralMetadata } // namespace CesiumGltf diff --git a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTexturePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h similarity index 99% rename from CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTexturePropertyView.h rename to CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h index e8f858732..a6b97ccd1 100644 --- a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTexturePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h @@ -14,7 +14,6 @@ #include namespace CesiumGltf { -namespace StructuralMetadata { /** * @brief Indicates the status of a property texture property view. * @@ -241,6 +240,4 @@ class PropertyTexturePropertyView { int64_t _count; bool _normalized; }; - -} // namespace StructuralMetadata } // namespace CesiumGltf diff --git a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTextureView.h b/CesiumGltf/include/CesiumGltf/PropertyTextureView.h similarity index 96% rename from CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTextureView.h rename to CesiumGltf/include/CesiumGltf/PropertyTextureView.h index 9d053357e..69c0f70e2 100644 --- a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTextureView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTextureView.h @@ -4,7 +4,7 @@ #include "CesiumGltf/ExtensionExtStructuralMetadataClassProperty.h" #include "CesiumGltf/ExtensionExtStructuralMetadataPropertyTexture.h" #include "CesiumGltf/ExtensionModelExtStructuralMetadata.h" -#include "CesiumGltf/StructuralMetadataPropertyTexturePropertyView.h" +#include "CesiumGltf/PropertyTexturePropertyView.h" #include "CesiumGltf/Texture.h" #include "CesiumGltf/TextureAccessor.h" #include "Image.h" @@ -12,8 +12,6 @@ #include "Model.h" namespace CesiumGltf { -namespace StructuralMetadata { - /** * @brief Indicates the status of a property texture view. * @@ -119,6 +117,4 @@ class PropertyTextureView { std::unordered_map _propertyViews; PropertyTextureViewStatus _status; }; - -} // namespace StructuralMetadata } // namespace CesiumGltf diff --git a/CesiumGltf/include/CesiumGltf/PropertyType.h b/CesiumGltf/include/CesiumGltf/PropertyType.h index 130d37fad..e5771a015 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyType.h +++ b/CesiumGltf/include/CesiumGltf/PropertyType.h @@ -6,6 +6,20 @@ namespace CesiumGltf { enum class PropertyType { + Invalid, + Scalar, + Vec2, + Vec3, + Vec4, + Mat2, + Mat3, + Mat4, + String, + Boolean, + Enum +}; + +enum class PropertyComponentType { None, Int8, Uint8, @@ -17,15 +31,25 @@ enum class PropertyType { Uint64, Float32, Float64, - Boolean, - Enum, - String, - Array, }; -std::string convertPropertyTypeToString(CesiumGltf::PropertyType type); +std::string convertPropertyTypeToString(PropertyType type); PropertyType convertStringToPropertyType(const std::string& str); -PropertyType convertOffsetStringToPropertyType(const std::string& str); +std::string +convertPropertyComponentTypeToString(PropertyComponentType componentType); + +PropertyComponentType +convertStringToPropertyComponentType(const std::string& str); + +PropertyComponentType +convertArrayOffsetTypeStringToPropertyComponentType(const std::string& str); + +PropertyComponentType +convertStringOffsetTypeStringToPropertyComponentType(const std::string& str); + +bool isPropertyTypeVecN(PropertyType type); + +bool isPropertyTypeMatN(PropertyType type); } // namespace CesiumGltf diff --git a/CesiumGltf/include/CesiumGltf/PropertyTypeTraits.h b/CesiumGltf/include/CesiumGltf/PropertyTypeTraits.h index 144365665..999947f54 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTypeTraits.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTypeTraits.h @@ -1,27 +1,29 @@ #pragma once -#include "CesiumGltf/MetadataArrayView.h" +#include "CesiumGltf/PropertyArrayView.h" #include "CesiumGltf/PropertyType.h" +#include + #include #include namespace CesiumGltf { /** - * @brief Check if a C++ type can be represented as a numeric property type + * @brief Check if a C++ type can be represented as a scalar property type */ -template struct IsMetadataNumeric; -template struct IsMetadataNumeric : std::false_type {}; -template <> struct IsMetadataNumeric : std::true_type {}; -template <> struct IsMetadataNumeric : std::true_type {}; -template <> struct IsMetadataNumeric : std::true_type {}; -template <> struct IsMetadataNumeric : std::true_type {}; -template <> struct IsMetadataNumeric : std::true_type {}; -template <> struct IsMetadataNumeric : std::true_type {}; -template <> struct IsMetadataNumeric : std::true_type {}; -template <> struct IsMetadataNumeric : std::true_type {}; -template <> struct IsMetadataNumeric : std::true_type {}; -template <> struct IsMetadataNumeric : std::true_type {}; +template struct IsMetadataScalar; +template struct IsMetadataScalar : std::false_type {}; +template <> struct IsMetadataScalar : std::true_type {}; +template <> struct IsMetadataScalar : std::true_type {}; +template <> struct IsMetadataScalar : std::true_type {}; +template <> struct IsMetadataScalar : std::true_type {}; +template <> struct IsMetadataScalar : std::true_type {}; +template <> struct IsMetadataScalar : std::true_type {}; +template <> struct IsMetadataScalar : std::true_type {}; +template <> struct IsMetadataScalar : std::true_type {}; +template <> struct IsMetadataScalar : std::true_type {}; +template <> struct IsMetadataScalar : std::true_type {}; /** * @brief Check if a C++ type can be represented as an integer property type @@ -39,13 +41,40 @@ template <> struct IsMetadataInteger : std::true_type {}; /** * @brief Check if a C++ type can be represented as a floating-point property - * type + * type. */ template struct IsMetadataFloating; template struct IsMetadataFloating : std::false_type {}; template <> struct IsMetadataFloating : std::true_type {}; template <> struct IsMetadataFloating : std::true_type {}; +/** + * @brief Check if a C++ type can be represented as a vecN type. + */ +template struct IsMetadataVecN; +template struct IsMetadataVecN : std::false_type {}; +template +struct IsMetadataVecN> : IsMetadataScalar {}; + +/** + * @brief Check if a C++ type can be represented as a matN type. + */ +template struct IsMetadataMatN; +template struct IsMetadataMatN : std::false_type {}; +template +struct IsMetadataMatN> : IsMetadataScalar {}; + +/** + * @brief Check if a C++ type can be represented as a numeric property, i.e. + * a scalar / vecN / matN type. + */ +template struct IsMetadataNumeric; +template struct IsMetadataNumeric { + static constexpr bool value = IsMetadataScalar::value || + IsMetadataVecN::value || + IsMetadataMatN::value; +}; + /** * @brief Check if a C++ type can be represented as a boolean property type */ @@ -66,35 +95,35 @@ template <> struct IsMetadataString : std::true_type {}; template struct IsMetadataArray; template struct IsMetadataArray : std::false_type {}; template -struct IsMetadataArray> : std::true_type {}; +struct IsMetadataArray> : std::true_type {}; /** - * @brief Check if a C++ type can be represented as an array of number property - * type + * @brief Check if a C++ type can be represented as an array of numeric elements + * property type */ template struct IsMetadataNumericArray; template struct IsMetadataNumericArray : std::false_type {}; -template struct IsMetadataNumericArray> { +template struct IsMetadataNumericArray> { static constexpr bool value = IsMetadataNumeric::value; }; /** - * @brief Check if a C++ type can be represented as an array of boolean property - * type + * @brief Check if a C++ type can be represented as an array of booleans + * property type */ template struct IsMetadataBooleanArray; template struct IsMetadataBooleanArray : std::false_type {}; template <> -struct IsMetadataBooleanArray> : std::true_type {}; +struct IsMetadataBooleanArray> : std::true_type {}; /** - * @brief Check if a C++ type can be represented as an array of string property + * @brief Check if a C++ type can be represented as an array of strings property * type */ template struct IsMetadataStringArray; template struct IsMetadataStringArray : std::false_type {}; template <> -struct IsMetadataStringArray> +struct IsMetadataStringArray> : std::true_type {}; /** @@ -102,79 +131,137 @@ struct IsMetadataStringArray> */ template struct MetadataArrayType; template -struct MetadataArrayType> { +struct MetadataArrayType> { using type = T; }; /** - * @brief Convert a C++ type to PropertyType + * @brief Convert a C++ type to PropertyType and PropertyComponentType */ template struct TypeToPropertyType; +#pragma region Scalar Property Types + template <> struct TypeToPropertyType { - static constexpr PropertyType component = PropertyType::None; - static constexpr PropertyType value = PropertyType::Uint8; + static constexpr PropertyComponentType component = + PropertyComponentType::Uint8; + static constexpr PropertyType value = PropertyType::Scalar; }; template <> struct TypeToPropertyType { - static constexpr PropertyType component = PropertyType::None; - static constexpr PropertyType value = PropertyType::Int8; + static constexpr PropertyComponentType component = + PropertyComponentType::Int8; + static constexpr PropertyType value = PropertyType::Scalar; }; template <> struct TypeToPropertyType { - static constexpr PropertyType component = PropertyType::None; - static constexpr PropertyType value = PropertyType::Uint16; + static constexpr PropertyComponentType component = + PropertyComponentType::Uint16; + static constexpr PropertyType value = PropertyType::Scalar; }; template <> struct TypeToPropertyType { - static constexpr PropertyType component = PropertyType::None; - static constexpr PropertyType value = PropertyType::Int16; + static constexpr PropertyComponentType component = + PropertyComponentType::Int16; + static constexpr PropertyType value = PropertyType::Scalar; }; template <> struct TypeToPropertyType { - static constexpr PropertyType component = PropertyType::None; - static constexpr PropertyType value = PropertyType::Uint32; + static constexpr PropertyComponentType component = + PropertyComponentType::Uint32; + static constexpr PropertyType value = PropertyType::Scalar; }; template <> struct TypeToPropertyType { - static constexpr PropertyType component = PropertyType::None; - static constexpr PropertyType value = PropertyType::Int32; + static constexpr PropertyComponentType component = + PropertyComponentType::Int32; + static constexpr PropertyType value = PropertyType::Scalar; }; template <> struct TypeToPropertyType { - static constexpr PropertyType component = PropertyType::None; - static constexpr PropertyType value = PropertyType::Uint64; + static constexpr PropertyComponentType component = + PropertyComponentType::Uint64; + static constexpr PropertyType value = PropertyType::Scalar; }; template <> struct TypeToPropertyType { - static constexpr PropertyType component = PropertyType::None; - static constexpr PropertyType value = PropertyType::Int64; + static constexpr PropertyComponentType component = + PropertyComponentType::Int64; + static constexpr PropertyType value = PropertyType::Scalar; }; template <> struct TypeToPropertyType { - static constexpr PropertyType component = PropertyType::None; - static constexpr PropertyType value = PropertyType::Float32; + static constexpr PropertyComponentType component = + PropertyComponentType::Float32; + static constexpr PropertyType value = PropertyType::Scalar; }; template <> struct TypeToPropertyType { - static constexpr PropertyType component = PropertyType::None; - static constexpr PropertyType value = PropertyType::Float64; + static constexpr PropertyComponentType component = + PropertyComponentType::Float64; + static constexpr PropertyType value = PropertyType::Scalar; }; +#pragma endregion + +#pragma region Vector Property Types + +template +struct TypeToPropertyType> { + static constexpr PropertyComponentType component = + TypeToPropertyType::component; + static constexpr PropertyType value = PropertyType::Vec2; +}; + +template +struct TypeToPropertyType> { + static constexpr PropertyComponentType component = + TypeToPropertyType::component; + static constexpr PropertyType value = PropertyType::Vec3; +}; + +template +struct TypeToPropertyType> { + static constexpr PropertyComponentType component = + TypeToPropertyType::component; + static constexpr PropertyType value = PropertyType::Vec4; +}; + +#pragma endregion + +#pragma region Matrix Property Types + +template +struct TypeToPropertyType> { + static constexpr PropertyComponentType component = + TypeToPropertyType::component; + static constexpr PropertyType value = PropertyType::Mat2; +}; + +template +struct TypeToPropertyType> { + static constexpr PropertyComponentType component = + TypeToPropertyType::component; + static constexpr PropertyType value = PropertyType::Mat3; +}; + +template +struct TypeToPropertyType> { + static constexpr PropertyComponentType component = + TypeToPropertyType::component; + static constexpr PropertyType value = PropertyType::Mat4; +}; + +#pragma endregion template <> struct TypeToPropertyType { - static constexpr PropertyType component = PropertyType::None; + static constexpr PropertyComponentType component = + PropertyComponentType::None; static constexpr PropertyType value = PropertyType::Boolean; }; template <> struct TypeToPropertyType { - static constexpr PropertyType component = PropertyType::None; + static constexpr PropertyComponentType component = + PropertyComponentType::None; static constexpr PropertyType value = PropertyType::String; }; - -template -struct TypeToPropertyType> { - static constexpr PropertyType component = TypeToPropertyType::value; - static constexpr PropertyType value = PropertyType::Array; -}; - } // namespace CesiumGltf diff --git a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyType.h b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyType.h deleted file mode 100644 index 4648c7c12..000000000 --- a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyType.h +++ /dev/null @@ -1,59 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace CesiumGltf { -namespace StructuralMetadata { - -enum class PropertyType { - Invalid, - Scalar, - Vec2, - Vec3, - Vec4, - Mat2, - Mat3, - Mat4, - String, - Boolean, - Enum -}; - -enum class PropertyComponentType { - None, - Int8, - Uint8, - Int16, - Uint16, - Int32, - Uint32, - Int64, - Uint64, - Float32, - Float64, -}; - -std::string convertPropertyTypeToString(PropertyType type); - -PropertyType convertStringToPropertyType(const std::string& str); - -std::string -convertPropertyComponentTypeToString(PropertyComponentType componentType); - -PropertyComponentType -convertStringToPropertyComponentType(const std::string& str); - -PropertyComponentType -convertArrayOffsetTypeStringToPropertyComponentType(const std::string& str); - -PropertyComponentType -convertStringOffsetTypeStringToPropertyComponentType(const std::string& str); - -bool isPropertyTypeVecN(PropertyType type); - -bool isPropertyTypeMatN(PropertyType type); - -} // namespace StructuralMetadata -} // namespace CesiumGltf diff --git a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTypeTraits.h b/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTypeTraits.h deleted file mode 100644 index 535667d9a..000000000 --- a/CesiumGltf/include/CesiumGltf/StructuralMetadataPropertyTypeTraits.h +++ /dev/null @@ -1,271 +0,0 @@ -#pragma once - -#include "CesiumGltf/StructuralMetadataArrayView.h" -#include "CesiumGltf/StructuralMetadataPropertyType.h" - -#include - -#include -#include - -namespace CesiumGltf { -namespace StructuralMetadata { - -/** - * @brief Check if a C++ type can be represented as a scalar property type - */ -template struct IsMetadataScalar; -template struct IsMetadataScalar : std::false_type {}; -template <> struct IsMetadataScalar : std::true_type {}; -template <> struct IsMetadataScalar : std::true_type {}; -template <> struct IsMetadataScalar : std::true_type {}; -template <> struct IsMetadataScalar : std::true_type {}; -template <> struct IsMetadataScalar : std::true_type {}; -template <> struct IsMetadataScalar : std::true_type {}; -template <> struct IsMetadataScalar : std::true_type {}; -template <> struct IsMetadataScalar : std::true_type {}; -template <> struct IsMetadataScalar : std::true_type {}; -template <> struct IsMetadataScalar : std::true_type {}; - -/** - * @brief Check if a C++ type can be represented as an integer property type - */ -template struct IsMetadataInteger; -template struct IsMetadataInteger : std::false_type {}; -template <> struct IsMetadataInteger : std::true_type {}; -template <> struct IsMetadataInteger : std::true_type {}; -template <> struct IsMetadataInteger : std::true_type {}; -template <> struct IsMetadataInteger : std::true_type {}; -template <> struct IsMetadataInteger : std::true_type {}; -template <> struct IsMetadataInteger : std::true_type {}; -template <> struct IsMetadataInteger : std::true_type {}; -template <> struct IsMetadataInteger : std::true_type {}; - -/** - * @brief Check if a C++ type can be represented as a floating-point property - * type. - */ -template struct IsMetadataFloating; -template struct IsMetadataFloating : std::false_type {}; -template <> struct IsMetadataFloating : std::true_type {}; -template <> struct IsMetadataFloating : std::true_type {}; - -/** - * @brief Check if a C++ type can be represented as a vecN type. - */ -template struct IsMetadataVecN; -template struct IsMetadataVecN : std::false_type {}; -template -struct IsMetadataVecN> : IsMetadataScalar {}; - -/** - * @brief Check if a C++ type can be represented as a matN type. - */ -template struct IsMetadataMatN; -template struct IsMetadataMatN : std::false_type {}; -template -struct IsMetadataMatN> : IsMetadataScalar {}; - -/** - * @brief Check if a C++ type can be represented as a numeric property, i.e. - * a scalar / vecN / matN type. - */ -template struct IsMetadataNumeric; -template struct IsMetadataNumeric { - static constexpr bool value = IsMetadataScalar::value || - IsMetadataVecN::value || - IsMetadataMatN::value; -}; - -/** - * @brief Check if a C++ type can be represented as a boolean property type - */ -template struct IsMetadataBoolean; -template struct IsMetadataBoolean : std::false_type {}; -template <> struct IsMetadataBoolean : std::true_type {}; - -/** - * @brief Check if a C++ type can be represented as a string property type - */ -template struct IsMetadataString; -template struct IsMetadataString : std::false_type {}; -template <> struct IsMetadataString : std::true_type {}; - -/** - * @brief Check if a C++ type can be represented as an array. - */ -template struct IsMetadataArray; -template struct IsMetadataArray : std::false_type {}; -template -struct IsMetadataArray> : std::true_type {}; - -/** - * @brief Check if a C++ type can be represented as an array of numeric elements - * property type - */ -template struct IsMetadataNumericArray; -template struct IsMetadataNumericArray : std::false_type {}; -template struct IsMetadataNumericArray> { - static constexpr bool value = IsMetadataNumeric::value; -}; - -/** - * @brief Check if a C++ type can be represented as an array of booleans - * property type - */ -template struct IsMetadataBooleanArray; -template struct IsMetadataBooleanArray : std::false_type {}; -template <> -struct IsMetadataBooleanArray> : std::true_type {}; - -/** - * @brief Check if a C++ type can be represented as an array of strings property - * type - */ -template struct IsMetadataStringArray; -template struct IsMetadataStringArray : std::false_type {}; -template <> -struct IsMetadataStringArray> - : std::true_type {}; - -/** - * @brief Retrieve the component type of a metadata array - */ -template struct MetadataArrayType; -template -struct MetadataArrayType> { - using type = T; -}; - -/** - * @brief Convert a C++ type to PropertyType and PropertyComponentType - */ -template struct TypeToPropertyType; - -#pragma region Scalar Property Types - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Uint8; - static constexpr PropertyType value = PropertyType::Scalar; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Int8; - static constexpr PropertyType value = PropertyType::Scalar; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Uint16; - static constexpr PropertyType value = PropertyType::Scalar; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Int16; - static constexpr PropertyType value = PropertyType::Scalar; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Uint32; - static constexpr PropertyType value = PropertyType::Scalar; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Int32; - static constexpr PropertyType value = PropertyType::Scalar; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Uint64; - static constexpr PropertyType value = PropertyType::Scalar; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Int64; - static constexpr PropertyType value = PropertyType::Scalar; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Float32; - static constexpr PropertyType value = PropertyType::Scalar; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::Float64; - static constexpr PropertyType value = PropertyType::Scalar; -}; -#pragma endregion - -#pragma region Vector Property Types - -template -struct TypeToPropertyType> { - static constexpr PropertyComponentType component = - TypeToPropertyType::component; - static constexpr PropertyType value = PropertyType::Vec2; -}; - -template -struct TypeToPropertyType> { - static constexpr PropertyComponentType component = - TypeToPropertyType::component; - static constexpr PropertyType value = PropertyType::Vec3; -}; - -template -struct TypeToPropertyType> { - static constexpr PropertyComponentType component = - TypeToPropertyType::component; - static constexpr PropertyType value = PropertyType::Vec4; -}; - -#pragma endregion - -#pragma region Matrix Property Types - -template -struct TypeToPropertyType> { - static constexpr PropertyComponentType component = - TypeToPropertyType::component; - static constexpr PropertyType value = PropertyType::Mat2; -}; - -template -struct TypeToPropertyType> { - static constexpr PropertyComponentType component = - TypeToPropertyType::component; - static constexpr PropertyType value = PropertyType::Mat3; -}; - -template -struct TypeToPropertyType> { - static constexpr PropertyComponentType component = - TypeToPropertyType::component; - static constexpr PropertyType value = PropertyType::Mat4; -}; - -#pragma endregion - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::None; - static constexpr PropertyType value = PropertyType::Boolean; -}; - -template <> struct TypeToPropertyType { - static constexpr PropertyComponentType component = - PropertyComponentType::None; - static constexpr PropertyType value = PropertyType::String; -}; - -} // namespace StructuralMetadata -} // namespace CesiumGltf diff --git a/CesiumGltf/include/CesiumGltf/getOffsetFromOffsetsBuffer.h b/CesiumGltf/include/CesiumGltf/getOffsetFromOffsetsBuffer.h index 431430bf4..762f698cc 100644 --- a/CesiumGltf/include/CesiumGltf/getOffsetFromOffsetsBuffer.h +++ b/CesiumGltf/include/CesiumGltf/getOffsetFromOffsetsBuffer.h @@ -1,6 +1,6 @@ #pragma once -#include "CesiumGltf/StructuralMetadataPropertyType.h" +#include "CesiumGltf/PropertyType.h" #include @@ -10,8 +10,6 @@ #include namespace CesiumGltf { -namespace StructuralMetadata { - static size_t getOffsetFromOffsetsBuffer( size_t index, const gsl::span& offsetBuffer, @@ -46,6 +44,4 @@ static size_t getOffsetFromOffsetsBuffer( return 0; } } - -} // namespace StructuralMetadata } // namespace CesiumGltf diff --git a/CesiumGltf/src/FeatureIDTextureView.cpp b/CesiumGltf/src/FeatureIDTextureView.cpp deleted file mode 100644 index 2847314c3..000000000 --- a/CesiumGltf/src/FeatureIDTextureView.cpp +++ /dev/null @@ -1,91 +0,0 @@ -#include "CesiumGltf/FeatureIDTextureView.h" - -namespace CesiumGltf { - -FeatureIDTextureView::FeatureIDTextureView() noexcept - : _pImage(nullptr), - _channel(0), - _textureCoordinateAttributeId(-1), - _featureTableName(), - _status(FeatureIDTextureViewStatus::InvalidUninitialized) {} - -FeatureIDTextureView::FeatureIDTextureView( - const Model& model, - const FeatureIDTexture& featureIDTexture) noexcept - : _pImage(nullptr), - _channel(0), - _textureCoordinateAttributeId(-1), - _featureTableName(featureIDTexture.featureTable), - _status(FeatureIDTextureViewStatus::InvalidUninitialized) { - - this->_textureCoordinateAttributeId = - featureIDTexture.featureIds.texture.texCoord; - - int32_t textureIndex = featureIDTexture.featureIds.texture.index; - if (textureIndex < 0 || - static_cast(textureIndex) >= model.textures.size()) { - this->_status = FeatureIDTextureViewStatus::InvalidTextureIndex; - return; - } - - const Texture& texture = model.textures[static_cast(textureIndex)]; - - // Ignore sampler, we will always use nearest pixel sampling. - if (texture.source < 0 || - static_cast(texture.source) >= model.images.size()) { - this->_status = FeatureIDTextureViewStatus::InvalidImageIndex; - return; - } - - this->_pImage = &model.images[static_cast(texture.source)].cesium; - - // This assumes that if the channel is g, there must be at least two - // channels (r and g). If it is b there must be r, g, and b. If there is a, - // then there must be r, g, b, and a. - if (featureIDTexture.featureIds.channels == "r") { - this->_channel = 0; - } else if (featureIDTexture.featureIds.channels == "g") { - this->_channel = 1; - } else if (featureIDTexture.featureIds.channels == "b") { - this->_channel = 2; - } else if (featureIDTexture.featureIds.channels == "a") { - this->_channel = 3; - } else { - this->_status = FeatureIDTextureViewStatus::InvalidChannel; - return; - } - - if (this->_pImage->width < 1 || this->_pImage->height < 1) { - this->_status = FeatureIDTextureViewStatus::InvalidEmptyImage; - return; - } - - // TODO: once compressed texture support is merged, check that the image is - // decompressed here. - - this->_status = FeatureIDTextureViewStatus::Valid; -} - -int64_t FeatureIDTextureView::getFeatureID(double u, double v) const noexcept { - if (this->_status != FeatureIDTextureViewStatus::Valid) { - return -1; - } - - int64_t x = std::clamp( - std::llround(u * this->_pImage->width), - 0LL, - (long long)this->_pImage->width); - int64_t y = std::clamp( - std::llround(v * this->_pImage->height), - 0LL, - (long long)this->_pImage->height); - - int64_t pixelOffset = this->_pImage->bytesPerChannel * - this->_pImage->channels * - (y * this->_pImage->width + x); - - return static_cast( - this->_pImage - ->pixelData[static_cast(pixelOffset + this->_channel)]); -} -} // namespace CesiumGltf \ No newline at end of file diff --git a/CesiumGltf/src/MeshFeaturesFeatureIdTextureView.cpp b/CesiumGltf/src/FeatureIdTextureView.cpp similarity index 96% rename from CesiumGltf/src/MeshFeaturesFeatureIdTextureView.cpp rename to CesiumGltf/src/FeatureIdTextureView.cpp index 0727e091f..589f98ac2 100644 --- a/CesiumGltf/src/MeshFeaturesFeatureIdTextureView.cpp +++ b/CesiumGltf/src/FeatureIdTextureView.cpp @@ -1,7 +1,6 @@ -#include "CesiumGltf/MeshFeaturesFeatureIdTextureView.h" +#include "CesiumGltf/FeatureIdTextureView.h" namespace CesiumGltf { -namespace MeshFeatures { FeatureIdTextureView::FeatureIdTextureView() noexcept : _status(FeatureIdTextureViewStatus::ErrorUninitialized), _texCoordSetIndex(-1), @@ -104,6 +103,4 @@ int64_t FeatureIdTextureView::getFeatureId(double u, double v) const noexcept { return value; } - -} // namespace MeshFeatures } // namespace CesiumGltf diff --git a/CesiumGltf/src/FeatureTexturePropertyView.cpp b/CesiumGltf/src/FeatureTexturePropertyView.cpp deleted file mode 100644 index 0fe9d7e3c..000000000 --- a/CesiumGltf/src/FeatureTexturePropertyView.cpp +++ /dev/null @@ -1,101 +0,0 @@ - -#include "CesiumGltf/FeatureTexturePropertyView.h" - -namespace CesiumGltf { - -FeatureTexturePropertyView::FeatureTexturePropertyView() noexcept - : _pSampler(nullptr), - _pImage(nullptr), - _pClassProperty(nullptr), - _pSwizzle(nullptr), - _textureCoordinateAttributeId(-1), - _status(FeatureTexturePropertyViewStatus::InvalidUninitialized), - _channelOffsets(), - _type(FeatureTexturePropertyComponentType::Uint8), - _componentCount(0), - _normalized(false) {} - -FeatureTexturePropertyView::FeatureTexturePropertyView( - const Model& model, - const ClassProperty& classProperty, - const TextureAccessor& textureAccessor) noexcept - : _pSampler(nullptr), - _pImage(nullptr), - _pClassProperty(&classProperty), - _pSwizzle(&textureAccessor.channels), - _textureCoordinateAttributeId(textureAccessor.texture.texCoord), - _status(FeatureTexturePropertyViewStatus::InvalidUninitialized), - _channelOffsets(), - _type(FeatureTexturePropertyComponentType::Uint8), - _componentCount(0), - _normalized(false) { - - if (textureAccessor.texture.index < 0 || - static_cast(textureAccessor.texture.index) >= - model.textures.size()) { - this->_status = FeatureTexturePropertyViewStatus::InvalidTextureIndex; - return; - } - - const Texture& texture = - model.textures[static_cast(textureAccessor.texture.index)]; - if (texture.sampler < 0 || - static_cast(texture.sampler) >= model.samplers.size()) { - this->_status = - FeatureTexturePropertyViewStatus::InvalidTextureSamplerIndex; - return; - } - - this->_pSampler = &model.samplers[static_cast(texture.sampler)]; - - if (texture.source < 0 || - static_cast(texture.source) >= model.images.size()) { - this->_status = FeatureTexturePropertyViewStatus::InvalidImageIndex; - return; - } - - this->_pImage = &model.images[static_cast(texture.source)].cesium; - - if (this->_pImage->width < 1 || this->_pImage->height < 1) { - this->_status = FeatureTexturePropertyViewStatus::InvalidEmptyImage; - return; - } - - // TODO: support more types - // this->_type = ... - this->_componentCount = this->_pClassProperty->componentCount - ? *this->_pClassProperty->componentCount - : 1; - this->_normalized = this->_pClassProperty->normalized; - if (textureAccessor.channels.length() > 4 || - textureAccessor.channels.length() > - static_cast(this->_pImage->channels) || - textureAccessor.channels.length() != - static_cast(this->_componentCount)) { - this->_status = FeatureTexturePropertyViewStatus::InvalidChannelsString; - return; - } - - for (size_t i = 0; i < textureAccessor.channels.length(); ++i) { - switch (textureAccessor.channels[i]) { - case 'r': - this->_channelOffsets.r = 0; - break; - case 'g': - this->_channelOffsets.g = 1; - break; - case 'b': - this->_channelOffsets.b = 2; - break; - case 'a': - this->_channelOffsets.a = 3; - break; - default: - this->_status = FeatureTexturePropertyViewStatus::InvalidChannelsString; - return; - } - } - - this->_status = FeatureTexturePropertyViewStatus::Valid; -} -} // namespace CesiumGltf \ No newline at end of file diff --git a/CesiumGltf/src/FeatureTextureView.cpp b/CesiumGltf/src/FeatureTextureView.cpp deleted file mode 100644 index 3ef0bfb55..000000000 --- a/CesiumGltf/src/FeatureTextureView.cpp +++ /dev/null @@ -1,68 +0,0 @@ -#include "CesiumGltf/FeatureTextureView.h" - -namespace CesiumGltf { - -FeatureTextureView::FeatureTextureView() noexcept - : _pModel(nullptr), - _pFeatureTexture(nullptr), - _pClass(nullptr), - _propertyViews(), - _status(FeatureTextureViewStatus::InvalidUninitialized) {} - -FeatureTextureView::FeatureTextureView( - const Model& model, - const FeatureTexture& featureTexture) noexcept - : _pModel(&model), - _pFeatureTexture(&featureTexture), - _pClass(nullptr), - _propertyViews(), - _status(FeatureTextureViewStatus::InvalidUninitialized) { - - const ExtensionModelExtFeatureMetadata* pMetadata = - model.getExtension(); - - if (!pMetadata) { - this->_status = FeatureTextureViewStatus::InvalidMissingMetadataExtension; - return; - } - - if (!pMetadata->schema) { - this->_status = FeatureTextureViewStatus::InvalidMissingSchema; - return; - } - - const auto& classIt = - pMetadata->schema->classes.find(featureTexture.classProperty); - if (classIt == pMetadata->schema->classes.end()) { - this->_status = FeatureTextureViewStatus::InvalidClassNotFound; - return; - } - - this->_pClass = &classIt->second; - - this->_propertyViews.reserve(featureTexture.properties.size()); - for (const auto& property : featureTexture.properties) { - auto classPropertyIt = this->_pClass->properties.find(property.first); - - if (classPropertyIt == this->_pClass->properties.end()) { - this->_status = FeatureTextureViewStatus::InvalidClassPropertyNotFound; - return; - } - - this->_propertyViews[property.first] = FeatureTexturePropertyView( - model, - classPropertyIt->second, - property.second); - } - - for (const auto& propertyView : this->_propertyViews) { - if (propertyView.second.status() != - FeatureTexturePropertyViewStatus::Valid) { - this->_status = FeatureTextureViewStatus::InvalidPropertyViewStatus; - return; - } - } - - this->_status = FeatureTextureViewStatus::Valid; -} -} // namespace CesiumGltf \ No newline at end of file diff --git a/CesiumGltf/src/MetadataFeatureTableView.cpp b/CesiumGltf/src/MetadataFeatureTableView.cpp deleted file mode 100644 index 5c403e8c0..000000000 --- a/CesiumGltf/src/MetadataFeatureTableView.cpp +++ /dev/null @@ -1,386 +0,0 @@ -#include "CesiumGltf/MetadataFeatureTableView.h" - -namespace CesiumGltf { -template -static MetadataPropertyViewStatus checkOffsetBuffer( - const gsl::span& offsetBuffer, - size_t valueBufferSize, - size_t instanceCount, - bool checkBitSize) noexcept { - if (offsetBuffer.size() % sizeof(T) != 0) { - return MetadataPropertyViewStatus:: - InvalidBufferViewSizeNotDivisibleByTypeSize; - } - - const size_t size = offsetBuffer.size() / sizeof(T); - if (size != instanceCount + 1) { - return MetadataPropertyViewStatus::InvalidBufferViewSizeNotFitInstanceCount; - } - - const gsl::span offsetValues( - reinterpret_cast(offsetBuffer.data()), - size); - - for (size_t i = 1; i < offsetValues.size(); ++i) { - if (offsetValues[i] < offsetValues[i - 1]) { - return MetadataPropertyViewStatus::InvalidOffsetValuesNotSortedAscending; - } - } - - if (!checkBitSize) { - if (offsetValues.back() <= valueBufferSize) { - return MetadataPropertyViewStatus::Valid; - } else { - return MetadataPropertyViewStatus:: - InvalidOffsetValuePointsToOutOfBoundBuffer; - } - } - - if (offsetValues.back() / 8 <= valueBufferSize) { - return MetadataPropertyViewStatus::Valid; - } else { - return MetadataPropertyViewStatus:: - InvalidOffsetValuePointsToOutOfBoundBuffer; - } -} - -template -static MetadataPropertyViewStatus checkStringArrayOffsetBuffer( - const gsl::span& arrayOffsetBuffer, - const gsl::span& stringOffsetBuffer, - size_t valueBufferSize, - size_t instanceCount) noexcept { - const auto status = checkOffsetBuffer( - arrayOffsetBuffer, - stringOffsetBuffer.size(), - instanceCount, - false); - if (status != MetadataPropertyViewStatus::Valid) { - return status; - } - - const T* pValue = reinterpret_cast(arrayOffsetBuffer.data()); - return checkOffsetBuffer( - stringOffsetBuffer, - valueBufferSize, - pValue[instanceCount] / sizeof(T), - false); -} - -MetadataFeatureTableView::MetadataFeatureTableView( - const Model* pModel, - const FeatureTable* pFeatureTable) - : _pModel{pModel}, _pFeatureTable{pFeatureTable}, _pClass{nullptr} { - assert(pModel != nullptr && "model must not be nullptr"); - assert(pFeatureTable != nullptr && "featureTable must not be nullptr"); - - const ExtensionModelExtFeatureMetadata* pMetadata = - pModel->getExtension(); - assert( - pMetadata != nullptr && - "Model must contain ExtensionModelExtFeatureMetadata to use " - "FeatureTableView"); - - const std::optional& schema = pMetadata->schema; - assert( - schema != std::nullopt && "ExtensionModelExtFeatureMetadata must contain " - "Schema to use FeatureTableView"); - - auto classIter = - schema->classes.find(_pFeatureTable->classProperty.value_or("")); - if (classIter != schema->classes.end()) { - _pClass = &classIter->second; - } -} - -const ClassProperty* MetadataFeatureTableView::getClassProperty( - const std::string& propertyName) const { - if (_pClass == nullptr) { - return nullptr; - } - - auto propertyIter = _pClass->properties.find(propertyName); - if (propertyIter == _pClass->properties.end()) { - return nullptr; - } - - return &propertyIter->second; -} - -MetadataPropertyViewStatus MetadataFeatureTableView::getBufferSafe( - int32_t bufferViewIdx, - gsl::span& buffer) const noexcept { - buffer = {}; - - const BufferView* pBufferView = - _pModel->getSafe(&_pModel->bufferViews, bufferViewIdx); - if (!pBufferView) { - return MetadataPropertyViewStatus::InvalidValueBufferViewIndex; - } - - const Buffer* pBuffer = - _pModel->getSafe(&_pModel->buffers, pBufferView->buffer); - if (!pBuffer) { - return MetadataPropertyViewStatus::InvalidValueBufferIndex; - } - - // This is technically required for the EXT_feature_metadata spec, but not - // necessarily required for EXT_mesh_features. Due to the discrepancy between - // the two specs, a lot of EXT_feature_metadata glTFs fail to be 8-byte - // aligned. To be forgiving and more compatible, we do not enforce this. - /* - if (pBufferView->byteOffset % 8 != 0) { - return MetadataPropertyViewStatus::InvalidBufferViewNotAligned8Bytes; - } - */ - - if (pBufferView->byteOffset + pBufferView->byteLength > - static_cast(pBuffer->cesium.data.size())) { - return MetadataPropertyViewStatus::InvalidBufferViewOutOfBound; - } - - buffer = gsl::span( - pBuffer->cesium.data.data() + pBufferView->byteOffset, - static_cast(pBufferView->byteLength)); - return MetadataPropertyViewStatus::Valid; -} - -MetadataPropertyViewStatus MetadataFeatureTableView::getOffsetBufferSafe( - int32_t bufferViewIdx, - PropertyType offsetType, - size_t valueBufferSize, - size_t instanceCount, - bool checkBitsSize, - gsl::span& offsetBuffer) const noexcept { - auto status = getBufferSafe(bufferViewIdx, offsetBuffer); - if (status != MetadataPropertyViewStatus::Valid) { - return status; - } - - switch (offsetType) { - case PropertyType::Uint8: - status = checkOffsetBuffer( - offsetBuffer, - valueBufferSize, - instanceCount, - checkBitsSize); - break; - case PropertyType::Uint16: - status = checkOffsetBuffer( - offsetBuffer, - valueBufferSize, - instanceCount, - checkBitsSize); - break; - case PropertyType::Uint32: - status = checkOffsetBuffer( - offsetBuffer, - valueBufferSize, - instanceCount, - checkBitsSize); - break; - case PropertyType::Uint64: - status = checkOffsetBuffer( - offsetBuffer, - valueBufferSize, - instanceCount, - checkBitsSize); - break; - default: - status = MetadataPropertyViewStatus::InvalidOffsetType; - break; - } - - return status; -} - -MetadataPropertyView -MetadataFeatureTableView::getStringPropertyValues( - const ClassProperty& classProperty, - const FeatureTableProperty& featureTableProperty) const { - if (classProperty.type != ClassProperty::Type::STRING) { - return createInvalidPropertyView( - MetadataPropertyViewStatus::InvalidTypeMismatch); - } - - gsl::span valueBuffer; - auto status = getBufferSafe(featureTableProperty.bufferView, valueBuffer); - if (status != MetadataPropertyViewStatus::Valid) { - return createInvalidPropertyView(status); - } - - const PropertyType offsetType = - convertOffsetStringToPropertyType(featureTableProperty.offsetType); - if (offsetType == PropertyType::None) { - return createInvalidPropertyView( - MetadataPropertyViewStatus::InvalidOffsetType); - } - - gsl::span offsetBuffer; - status = getOffsetBufferSafe( - featureTableProperty.stringOffsetBufferView, - offsetType, - valueBuffer.size(), - static_cast(_pFeatureTable->count), - false, - offsetBuffer); - if (status != MetadataPropertyViewStatus::Valid) { - return createInvalidPropertyView(status); - } - - return MetadataPropertyView( - MetadataPropertyViewStatus::Valid, - valueBuffer, - gsl::span(), - offsetBuffer, - offsetType, - 0, - _pFeatureTable->count, - classProperty.normalized); -} - -MetadataPropertyView> -MetadataFeatureTableView::getStringArrayPropertyValues( - const ClassProperty& classProperty, - const FeatureTableProperty& featureTableProperty) const { - if (classProperty.type != ClassProperty::Type::ARRAY) { - return createInvalidPropertyView>( - MetadataPropertyViewStatus::InvalidTypeMismatch); - } - - if (classProperty.componentType != ClassProperty::ComponentType::STRING) { - return createInvalidPropertyView>( - MetadataPropertyViewStatus::InvalidTypeMismatch); - } - - // get value buffer - gsl::span valueBuffer; - auto status = getBufferSafe(featureTableProperty.bufferView, valueBuffer); - if (status != MetadataPropertyViewStatus::Valid) { - return createInvalidPropertyView>( - status); - } - - // check fixed or dynamic array - const int64_t componentCount = classProperty.componentCount.value_or(0); - if (componentCount > 0 && featureTableProperty.arrayOffsetBufferView >= 0) { - return createInvalidPropertyView>( - MetadataPropertyViewStatus:: - InvalidArrayComponentCountAndOffsetBufferCoexist); - } - - if (componentCount <= 0 && featureTableProperty.arrayOffsetBufferView < 0) { - return createInvalidPropertyView>( - MetadataPropertyViewStatus:: - InvalidArrayComponentCountOrOffsetBufferNotExist); - } - - // get offset type - const PropertyType offsetType = - convertOffsetStringToPropertyType(featureTableProperty.offsetType); - if (offsetType == PropertyType::None) { - return createInvalidPropertyView>( - MetadataPropertyViewStatus::InvalidOffsetType); - } - - // get string offset buffer - if (featureTableProperty.stringOffsetBufferView < 0) { - return createInvalidPropertyView>( - MetadataPropertyViewStatus::InvalidStringOffsetBufferViewIndex); - } - - // fixed array - if (componentCount > 0) { - gsl::span stringOffsetBuffer; - status = getOffsetBufferSafe( - featureTableProperty.stringOffsetBufferView, - offsetType, - valueBuffer.size(), - static_cast(_pFeatureTable->count * componentCount), - false, - stringOffsetBuffer); - if (status != MetadataPropertyViewStatus::Valid) { - return createInvalidPropertyView>( - status); - } - - return MetadataPropertyView>( - MetadataPropertyViewStatus::Valid, - valueBuffer, - gsl::span(), - stringOffsetBuffer, - offsetType, - componentCount, - _pFeatureTable->count, - classProperty.normalized); - } - - // dynamic array - gsl::span stringOffsetBuffer; - status = getBufferSafe( - featureTableProperty.stringOffsetBufferView, - stringOffsetBuffer); - if (status != MetadataPropertyViewStatus::Valid) { - return createInvalidPropertyView>( - status); - } - - gsl::span arrayOffsetBuffer; - status = getBufferSafe( - featureTableProperty.arrayOffsetBufferView, - arrayOffsetBuffer); - if (status != MetadataPropertyViewStatus::Valid) { - return createInvalidPropertyView>( - status); - } - - switch (offsetType) { - case PropertyType::Uint8: - status = checkStringArrayOffsetBuffer( - arrayOffsetBuffer, - stringOffsetBuffer, - valueBuffer.size(), - static_cast(_pFeatureTable->count)); - break; - case PropertyType::Uint16: - status = checkStringArrayOffsetBuffer( - arrayOffsetBuffer, - stringOffsetBuffer, - valueBuffer.size(), - static_cast(_pFeatureTable->count)); - break; - case PropertyType::Uint32: - status = checkStringArrayOffsetBuffer( - arrayOffsetBuffer, - stringOffsetBuffer, - valueBuffer.size(), - static_cast(_pFeatureTable->count)); - break; - case PropertyType::Uint64: - status = checkStringArrayOffsetBuffer( - arrayOffsetBuffer, - stringOffsetBuffer, - valueBuffer.size(), - static_cast(_pFeatureTable->count)); - break; - default: - status = MetadataPropertyViewStatus::InvalidOffsetType; - break; - } - - if (status != MetadataPropertyViewStatus::Valid) { - return createInvalidPropertyView>( - status); - } - - return MetadataPropertyView>( - MetadataPropertyViewStatus::Valid, - valueBuffer, - arrayOffsetBuffer, - stringOffsetBuffer, - offsetType, - 0, - _pFeatureTable->count, - classProperty.normalized); -} -} // namespace CesiumGltf diff --git a/CesiumGltf/src/StructuralMetadataPropertyTableView.cpp b/CesiumGltf/src/PropertyTableView.cpp similarity index 93% rename from CesiumGltf/src/StructuralMetadataPropertyTableView.cpp rename to CesiumGltf/src/PropertyTableView.cpp index b6e58fa87..0e06b7d0f 100644 --- a/CesiumGltf/src/StructuralMetadataPropertyTableView.cpp +++ b/CesiumGltf/src/PropertyTableView.cpp @@ -1,8 +1,6 @@ -#include "CesiumGltf/StructuralMetadataPropertyTableView.h" +#include "CesiumGltf/PropertyTableView.h" namespace CesiumGltf { -namespace StructuralMetadata { - template static PropertyTablePropertyViewStatus checkOffsetsBuffer( const gsl::span& offsetBuffer, @@ -333,38 +331,38 @@ PropertyTableView::getStringPropertyValues( classProperty.normalized); } -PropertyTablePropertyView> +PropertyTablePropertyView> PropertyTableView::getStringArrayPropertyValues( const ExtensionExtStructuralMetadataClassProperty& classProperty, const ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty) const { if (!classProperty.array) { - return createInvalidPropertyView>( + return createInvalidPropertyView>( PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); } if (classProperty.type != ExtensionExtStructuralMetadataClassProperty::Type::STRING) { - return createInvalidPropertyView>( + return createInvalidPropertyView>( PropertyTablePropertyViewStatus::ErrorTypeMismatch); } gsl::span values; auto status = getBufferSafe(propertyTableProperty.values, values); if (status != PropertyTablePropertyViewStatus::Valid) { - return createInvalidPropertyView>( + return createInvalidPropertyView>( status); } // Check if array is fixed or variable length const int64_t fixedLengthArrayCount = classProperty.count.value_or(0); if (fixedLengthArrayCount > 0 && propertyTableProperty.arrayOffsets >= 0) { - return createInvalidPropertyView>( + return createInvalidPropertyView>( PropertyTablePropertyViewStatus::ErrorArrayCountAndOffsetBufferCoexist); } if (fixedLengthArrayCount <= 0 && propertyTableProperty.arrayOffsets < 0) { - return createInvalidPropertyView>( + return createInvalidPropertyView>( PropertyTablePropertyViewStatus:: ErrorArrayCountAndOffsetBufferDontExist); } @@ -374,12 +372,12 @@ PropertyTableView::getStringArrayPropertyValues( convertStringOffsetTypeStringToPropertyComponentType( propertyTableProperty.stringOffsetType); if (stringOffsetType == PropertyComponentType::None) { - return createInvalidPropertyView>( + return createInvalidPropertyView>( PropertyTablePropertyViewStatus::ErrorInvalidStringOffsetType); } if (propertyTableProperty.stringOffsets < 0) { - return createInvalidPropertyView>( + return createInvalidPropertyView>( PropertyTablePropertyViewStatus::ErrorInvalidStringOffsetBufferView); } @@ -393,11 +391,11 @@ PropertyTableView::getStringArrayPropertyValues( static_cast(_pPropertyTable->count * fixedLengthArrayCount), stringOffsets); if (status != PropertyTablePropertyViewStatus::Valid) { - return createInvalidPropertyView>( + return createInvalidPropertyView>( status); } - return PropertyTablePropertyView>( + return PropertyTablePropertyView>( PropertyTablePropertyViewStatus::Valid, values, gsl::span(), @@ -414,12 +412,12 @@ PropertyTableView::getStringArrayPropertyValues( convertArrayOffsetTypeStringToPropertyComponentType( propertyTableProperty.arrayOffsetType); if (arrayOffsetType == PropertyComponentType::None) { - return createInvalidPropertyView>( + return createInvalidPropertyView>( PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); } if (propertyTableProperty.arrayOffsets < 0) { - return createInvalidPropertyView>( + return createInvalidPropertyView>( PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetBufferView); } @@ -427,14 +425,14 @@ PropertyTableView::getStringArrayPropertyValues( gsl::span stringOffsets; status = getBufferSafe(propertyTableProperty.stringOffsets, stringOffsets); if (status != PropertyTablePropertyViewStatus::Valid) { - return createInvalidPropertyView>( + return createInvalidPropertyView>( status); } gsl::span arrayOffsets; status = getBufferSafe(propertyTableProperty.arrayOffsets, arrayOffsets); if (status != PropertyTablePropertyViewStatus::Valid) { - return createInvalidPropertyView>( + return createInvalidPropertyView>( status); } @@ -477,11 +475,11 @@ PropertyTableView::getStringArrayPropertyValues( } if (status != PropertyTablePropertyViewStatus::Valid) { - return createInvalidPropertyView>( + return createInvalidPropertyView>( status); } - return PropertyTablePropertyView>( + return PropertyTablePropertyView>( PropertyTablePropertyViewStatus::Valid, values, arrayOffsets, @@ -492,6 +490,4 @@ PropertyTableView::getStringArrayPropertyValues( _pPropertyTable->count, classProperty.normalized); } - -} // namespace StructuralMetadata } // namespace CesiumGltf diff --git a/CesiumGltf/src/StructuralMetadataPropertyTexturePropertyView.cpp b/CesiumGltf/src/PropertyTexturePropertyView.cpp similarity index 96% rename from CesiumGltf/src/StructuralMetadataPropertyTexturePropertyView.cpp rename to CesiumGltf/src/PropertyTexturePropertyView.cpp index 409c29166..d854468d2 100644 --- a/CesiumGltf/src/StructuralMetadataPropertyTexturePropertyView.cpp +++ b/CesiumGltf/src/PropertyTexturePropertyView.cpp @@ -1,8 +1,7 @@ -#include "CesiumGltf/StructuralMetadataPropertyTexturePropertyView.h" +#include "CesiumGltf/PropertyTexturePropertyView.h" namespace CesiumGltf { -namespace StructuralMetadata { PropertyTexturePropertyView::PropertyTexturePropertyView() noexcept : _status(PropertyTexturePropertyViewStatus::ErrorUninitialized), _pClassProperty(nullptr), @@ -103,5 +102,4 @@ PropertyTexturePropertyView::PropertyTexturePropertyView( this->_status = PropertyTexturePropertyViewStatus::Valid; } -} // namespace StructuralMetadata } // namespace CesiumGltf diff --git a/CesiumGltf/src/StructuralMetadataPropertyTextureView.cpp b/CesiumGltf/src/PropertyTextureView.cpp similarity index 94% rename from CesiumGltf/src/StructuralMetadataPropertyTextureView.cpp rename to CesiumGltf/src/PropertyTextureView.cpp index 7ebcdab2a..7f917c8a9 100644 --- a/CesiumGltf/src/StructuralMetadataPropertyTextureView.cpp +++ b/CesiumGltf/src/PropertyTextureView.cpp @@ -1,7 +1,6 @@ -#include "CesiumGltf/StructuralMetadataPropertyTextureView.h" +#include "CesiumGltf/PropertyTextureView.h" namespace CesiumGltf { -namespace StructuralMetadata { PropertyTextureView::PropertyTextureView() noexcept : _pModel(nullptr), _pPropertyTexture(nullptr), @@ -80,6 +79,4 @@ PropertyTextureView::getClassProperty(const std::string& propertyName) const { return &propertyIter->second; } - -} // namespace StructuralMetadata } // namespace CesiumGltf diff --git a/CesiumGltf/src/PropertyType.cpp b/CesiumGltf/src/PropertyType.cpp index 91c0e9787..826997cf8 100644 --- a/CesiumGltf/src/PropertyType.cpp +++ b/CesiumGltf/src/PropertyType.cpp @@ -1,123 +1,220 @@ #include "CesiumGltf/PropertyType.h" -#include "CesiumGltf/ClassProperty.h" -#include "CesiumGltf/FeatureTable.h" +#include "CesiumGltf/ExtensionExtStructuralMetadataClassProperty.h" +#include "CesiumGltf/ExtensionExtStructuralMetadataPropertyTable.h" namespace CesiumGltf { -std::string convertPropertyTypeToString(CesiumGltf::PropertyType type) { +std::string convertPropertyTypeToString(PropertyType type) { switch (type) { - case PropertyType::None: - return "NONE"; - case PropertyType::Uint8: - return ClassProperty::Type::UINT8; - case PropertyType::Int8: - return ClassProperty::Type::INT8; - case PropertyType::Uint16: - return ClassProperty::Type::UINT16; - case PropertyType::Int16: - return ClassProperty::Type::INT16; - case PropertyType::Uint32: - return ClassProperty::Type::UINT32; - case PropertyType::Int32: - return ClassProperty::Type::INT32; - case PropertyType::Uint64: - return ClassProperty::Type::UINT64; - case PropertyType::Int64: - return ClassProperty::Type::INT64; - case PropertyType::Float32: - return ClassProperty::Type::FLOAT32; - case PropertyType::Float64: - return ClassProperty::Type::FLOAT64; + case PropertyType::Scalar: + return ExtensionExtStructuralMetadataClassProperty::Type::SCALAR; + case PropertyType::Vec2: + return ExtensionExtStructuralMetadataClassProperty::Type::VEC2; + case PropertyType::Vec3: + return ExtensionExtStructuralMetadataClassProperty::Type::VEC3; + case PropertyType::Vec4: + return ExtensionExtStructuralMetadataClassProperty::Type::VEC4; + case PropertyType::Mat2: + return ExtensionExtStructuralMetadataClassProperty::Type::MAT2; + case PropertyType::Mat3: + return ExtensionExtStructuralMetadataClassProperty::Type::MAT3; + case PropertyType::Mat4: + return ExtensionExtStructuralMetadataClassProperty::Type::MAT4; case PropertyType::Boolean: - return ClassProperty::Type::BOOLEAN; + return ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN; case PropertyType::Enum: - return ClassProperty::Type::ENUM; + return ExtensionExtStructuralMetadataClassProperty::Type::ENUM; case PropertyType::String: - return ClassProperty::Type::STRING; - case PropertyType::Array: - return ClassProperty::Type::ARRAY; + return ExtensionExtStructuralMetadataClassProperty::Type::STRING; default: - return "NONE"; + return "INVALID"; } } PropertyType convertStringToPropertyType(const std::string& str) { - if (str == ClassProperty::Type::UINT8) { - return PropertyType::Uint8; + if (str == ExtensionExtStructuralMetadataClassProperty::Type::SCALAR) { + return PropertyType::Scalar; } - if (str == ClassProperty::Type::INT8) { - return PropertyType::Int8; + if (str == ExtensionExtStructuralMetadataClassProperty::Type::VEC2) { + return PropertyType::Vec2; } - if (str == ClassProperty::Type::UINT16) { - return PropertyType::Uint16; + if (str == ExtensionExtStructuralMetadataClassProperty::Type::VEC3) { + return PropertyType::Vec3; } - if (str == ClassProperty::Type::INT16) { - return PropertyType::Int16; + if (str == ExtensionExtStructuralMetadataClassProperty::Type::VEC4) { + return PropertyType::Vec4; } - if (str == ClassProperty::Type::UINT32) { - return PropertyType::Uint32; + if (str == ExtensionExtStructuralMetadataClassProperty::Type::MAT2) { + return PropertyType::Mat2; } - if (str == ClassProperty::Type::INT32) { - return PropertyType::Int32; + if (str == ExtensionExtStructuralMetadataClassProperty::Type::MAT3) { + return PropertyType::Mat3; } - if (str == ClassProperty::Type::UINT64) { - return PropertyType::Uint64; + if (str == ExtensionExtStructuralMetadataClassProperty::Type::MAT4) { + return PropertyType::Mat4; } - if (str == ClassProperty::Type::INT64) { - return PropertyType::Int64; + if (str == ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN) { + return PropertyType::Boolean; } - if (str == ClassProperty::Type::FLOAT32) { - return PropertyType::Float32; + if (str == ExtensionExtStructuralMetadataClassProperty::Type::STRING) { + return PropertyType::String; } - if (str == ClassProperty::Type::FLOAT64) { - return PropertyType::Float64; + if (str == ExtensionExtStructuralMetadataClassProperty::Type::ENUM) { + return PropertyType::Enum; } - if (str == ClassProperty::Type::BOOLEAN) { - return PropertyType::Boolean; + return PropertyType::Invalid; +} + +std::string convertPropertyComponentTypeToString(PropertyComponentType type) { + switch (type) { + case PropertyComponentType::None: + return "NONE"; + case PropertyComponentType::Uint8: + return ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT8; + case PropertyComponentType::Int8: + return ExtensionExtStructuralMetadataClassProperty::ComponentType::INT8; + case PropertyComponentType::Uint16: + return ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT16; + case PropertyComponentType::Int16: + return ExtensionExtStructuralMetadataClassProperty::ComponentType::INT16; + case PropertyComponentType::Uint32: + return ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT32; + case PropertyComponentType::Int32: + return ExtensionExtStructuralMetadataClassProperty::ComponentType::INT32; + case PropertyComponentType::Uint64: + return ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT64; + case PropertyComponentType::Int64: + return ExtensionExtStructuralMetadataClassProperty::ComponentType::INT64; + case PropertyComponentType::Float32: + return ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT32; + case PropertyComponentType::Float64: + return ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT64; + default: + return "NONE"; } +} - if (str == ClassProperty::Type::STRING) { - return PropertyType::String; +PropertyComponentType +convertStringToPropertyComponentType(const std::string& str) { + if (str == + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT8) { + return PropertyComponentType::Uint8; } - if (str == ClassProperty::Type::ENUM) { - return PropertyType::Enum; + if (str == ExtensionExtStructuralMetadataClassProperty::ComponentType::INT8) { + return PropertyComponentType::Int8; + } + + if (str == + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT16) { + return PropertyComponentType::Uint16; } - if (str == ClassProperty::Type::ARRAY) { - return PropertyType::Array; + if (str == + ExtensionExtStructuralMetadataClassProperty::ComponentType::INT16) { + return PropertyComponentType::Int16; } - return PropertyType::None; + if (str == + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT32) { + return PropertyComponentType::Uint32; + } + + if (str == + ExtensionExtStructuralMetadataClassProperty::ComponentType::INT32) { + return PropertyComponentType::Int32; + } + + if (str == + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT64) { + return PropertyComponentType::Uint64; + } + + if (str == + ExtensionExtStructuralMetadataClassProperty::ComponentType::INT64) { + return PropertyComponentType::Int64; + } + + if (str == + ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT32) { + return PropertyComponentType::Float32; + } + + if (str == + ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT64) { + return PropertyComponentType::Float64; + } + + return PropertyComponentType::None; } -PropertyType convertOffsetStringToPropertyType(const std::string& str) { - if (str == FeatureTableProperty::OffsetType::UINT8) { - return PropertyType::Uint8; +PropertyComponentType +convertArrayOffsetTypeStringToPropertyComponentType(const std::string& str) { + if (str == ExtensionExtStructuralMetadataPropertyTableProperty:: + ArrayOffsetType::UINT8) { + return PropertyComponentType::Uint8; + } + + if (str == ExtensionExtStructuralMetadataPropertyTableProperty:: + ArrayOffsetType::UINT16) { + return PropertyComponentType::Uint16; } - if (str == FeatureTableProperty::OffsetType::UINT16) { - return PropertyType::Uint16; + if (str == ExtensionExtStructuralMetadataPropertyTableProperty:: + ArrayOffsetType::UINT32) { + return PropertyComponentType::Uint32; } - if (str == FeatureTableProperty::OffsetType::UINT32) { - return PropertyType::Uint32; + if (str == ExtensionExtStructuralMetadataPropertyTableProperty:: + ArrayOffsetType::UINT64) { + return PropertyComponentType::Uint64; } - if (str == FeatureTableProperty::OffsetType::UINT64) { - return PropertyType::Uint64; + return PropertyComponentType::None; +} + +PropertyComponentType +convertStringOffsetTypeStringToPropertyComponentType(const std::string& str) { + if (str == ExtensionExtStructuralMetadataPropertyTableProperty:: + StringOffsetType::UINT8) { + return PropertyComponentType::Uint8; } - return PropertyType::None; + if (str == ExtensionExtStructuralMetadataPropertyTableProperty:: + StringOffsetType::UINT16) { + return PropertyComponentType::Uint16; + } + + if (str == ExtensionExtStructuralMetadataPropertyTableProperty:: + StringOffsetType::UINT32) { + return PropertyComponentType::Uint32; + } + + if (str == ExtensionExtStructuralMetadataPropertyTableProperty:: + StringOffsetType::UINT64) { + return PropertyComponentType::Uint64; + } + + return PropertyComponentType::None; +} + +bool isPropertyTypeVecN(PropertyType type) { + return type == PropertyType::Vec2 || type == PropertyType::Vec3 || + type == PropertyType::Vec4; +} + +bool isPropertyTypeMatN(PropertyType type) { + return type == PropertyType::Mat2 || type == PropertyType::Mat3 || + type == PropertyType::Mat4; } } // namespace CesiumGltf diff --git a/CesiumGltf/src/StructuralMetadataPropertyType.cpp b/CesiumGltf/src/StructuralMetadataPropertyType.cpp deleted file mode 100644 index ca7613023..000000000 --- a/CesiumGltf/src/StructuralMetadataPropertyType.cpp +++ /dev/null @@ -1,224 +0,0 @@ -#include "CesiumGltf/StructuralMetadataPropertyType.h" - -#include "CesiumGltf/ExtensionExtStructuralMetadataClassProperty.h" -#include "CesiumGltf/ExtensionExtStructuralMetadataPropertyTable.h" - -namespace CesiumGltf { -namespace StructuralMetadata { - -std::string convertPropertyTypeToString(StructuralMetadata::PropertyType type) { - switch (type) { - case PropertyType::Scalar: - return ExtensionExtStructuralMetadataClassProperty::Type::SCALAR; - case PropertyType::Vec2: - return ExtensionExtStructuralMetadataClassProperty::Type::VEC2; - case PropertyType::Vec3: - return ExtensionExtStructuralMetadataClassProperty::Type::VEC3; - case PropertyType::Vec4: - return ExtensionExtStructuralMetadataClassProperty::Type::VEC4; - case PropertyType::Mat2: - return ExtensionExtStructuralMetadataClassProperty::Type::MAT2; - case PropertyType::Mat3: - return ExtensionExtStructuralMetadataClassProperty::Type::MAT3; - case PropertyType::Mat4: - return ExtensionExtStructuralMetadataClassProperty::Type::MAT4; - case PropertyType::Boolean: - return ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN; - case PropertyType::Enum: - return ExtensionExtStructuralMetadataClassProperty::Type::ENUM; - case PropertyType::String: - return ExtensionExtStructuralMetadataClassProperty::Type::STRING; - default: - return "INVALID"; - } -} - -PropertyType convertStringToPropertyType(const std::string& str) { - if (str == ExtensionExtStructuralMetadataClassProperty::Type::SCALAR) { - return PropertyType::Scalar; - } - - if (str == ExtensionExtStructuralMetadataClassProperty::Type::VEC2) { - return PropertyType::Vec2; - } - - if (str == ExtensionExtStructuralMetadataClassProperty::Type::VEC3) { - return PropertyType::Vec3; - } - - if (str == ExtensionExtStructuralMetadataClassProperty::Type::VEC4) { - return PropertyType::Vec4; - } - - if (str == ExtensionExtStructuralMetadataClassProperty::Type::MAT2) { - return PropertyType::Mat2; - } - - if (str == ExtensionExtStructuralMetadataClassProperty::Type::MAT3) { - return PropertyType::Mat3; - } - - if (str == ExtensionExtStructuralMetadataClassProperty::Type::MAT4) { - return PropertyType::Mat4; - } - - if (str == ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN) { - return PropertyType::Boolean; - } - - if (str == ExtensionExtStructuralMetadataClassProperty::Type::STRING) { - return PropertyType::String; - } - - if (str == ExtensionExtStructuralMetadataClassProperty::Type::ENUM) { - return PropertyType::Enum; - } - - return PropertyType::Invalid; -} - -std::string convertPropertyComponentTypeToString(PropertyComponentType type) { - switch (type) { - case PropertyComponentType::None: - return "NONE"; - case PropertyComponentType::Uint8: - return ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT8; - case PropertyComponentType::Int8: - return ExtensionExtStructuralMetadataClassProperty::ComponentType::INT8; - case PropertyComponentType::Uint16: - return ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT16; - case PropertyComponentType::Int16: - return ExtensionExtStructuralMetadataClassProperty::ComponentType::INT16; - case PropertyComponentType::Uint32: - return ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT32; - case PropertyComponentType::Int32: - return ExtensionExtStructuralMetadataClassProperty::ComponentType::INT32; - case PropertyComponentType::Uint64: - return ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT64; - case PropertyComponentType::Int64: - return ExtensionExtStructuralMetadataClassProperty::ComponentType::INT64; - case PropertyComponentType::Float32: - return ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT32; - case PropertyComponentType::Float64: - return ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT64; - default: - return "NONE"; - } -} - -PropertyComponentType -convertStringToPropertyComponentType(const std::string& str) { - if (str == - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT8) { - return PropertyComponentType::Uint8; - } - - if (str == ExtensionExtStructuralMetadataClassProperty::ComponentType::INT8) { - return PropertyComponentType::Int8; - } - - if (str == - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT16) { - return PropertyComponentType::Uint16; - } - - if (str == - ExtensionExtStructuralMetadataClassProperty::ComponentType::INT16) { - return PropertyComponentType::Int16; - } - - if (str == - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT32) { - return PropertyComponentType::Uint32; - } - - if (str == - ExtensionExtStructuralMetadataClassProperty::ComponentType::INT32) { - return PropertyComponentType::Int32; - } - - if (str == - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT64) { - return PropertyComponentType::Uint64; - } - - if (str == - ExtensionExtStructuralMetadataClassProperty::ComponentType::INT64) { - return PropertyComponentType::Int64; - } - - if (str == - ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT32) { - return PropertyComponentType::Float32; - } - - if (str == - ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT64) { - return PropertyComponentType::Float64; - } - - return PropertyComponentType::None; -} - -PropertyComponentType -convertArrayOffsetTypeStringToPropertyComponentType(const std::string& str) { - if (str == ExtensionExtStructuralMetadataPropertyTableProperty:: - ArrayOffsetType::UINT8) { - return PropertyComponentType::Uint8; - } - - if (str == ExtensionExtStructuralMetadataPropertyTableProperty:: - ArrayOffsetType::UINT16) { - return PropertyComponentType::Uint16; - } - - if (str == ExtensionExtStructuralMetadataPropertyTableProperty:: - ArrayOffsetType::UINT32) { - return PropertyComponentType::Uint32; - } - - if (str == ExtensionExtStructuralMetadataPropertyTableProperty:: - ArrayOffsetType::UINT64) { - return PropertyComponentType::Uint64; - } - - return PropertyComponentType::None; -} - -PropertyComponentType -convertStringOffsetTypeStringToPropertyComponentType(const std::string& str) { - if (str == ExtensionExtStructuralMetadataPropertyTableProperty:: - StringOffsetType::UINT8) { - return PropertyComponentType::Uint8; - } - - if (str == ExtensionExtStructuralMetadataPropertyTableProperty:: - StringOffsetType::UINT16) { - return PropertyComponentType::Uint16; - } - - if (str == ExtensionExtStructuralMetadataPropertyTableProperty:: - StringOffsetType::UINT32) { - return PropertyComponentType::Uint32; - } - - if (str == ExtensionExtStructuralMetadataPropertyTableProperty:: - StringOffsetType::UINT64) { - return PropertyComponentType::Uint64; - } - - return PropertyComponentType::None; -} - -bool isPropertyTypeVecN(PropertyType type) { - return type == PropertyType::Vec2 || type == PropertyType::Vec3 || - type == PropertyType::Vec4; -} - -bool isPropertyTypeMatN(PropertyType type) { - return type == PropertyType::Mat2 || type == PropertyType::Mat3 || - type == PropertyType::Mat4; -} - -} // namespace StructuralMetadata -} // namespace CesiumGltf diff --git a/CesiumGltf/test/TestMeshFeaturesFeatureIdTextureView.cpp b/CesiumGltf/test/TestFeatureIdTextureView.cpp similarity index 98% rename from CesiumGltf/test/TestMeshFeaturesFeatureIdTextureView.cpp rename to CesiumGltf/test/TestFeatureIdTextureView.cpp index cde38cd03..a6755667f 100644 --- a/CesiumGltf/test/TestMeshFeaturesFeatureIdTextureView.cpp +++ b/CesiumGltf/test/TestFeatureIdTextureView.cpp @@ -1,5 +1,5 @@ #include "CesiumGltf/ExtensionExtMeshFeatures.h" -#include "CesiumGltf/MeshFeaturesFeatureIdTextureView.h" +#include "CesiumGltf/FeatureIdTextureView.h" #include #include @@ -11,7 +11,6 @@ #include using namespace CesiumGltf; -using namespace CesiumGltf::MeshFeatures; TEST_CASE("Test FeatureIdTextureView on feature ID texture with invalid " "texture index") { diff --git a/CesiumGltf/test/TestMetadataFeatureTableView.cpp b/CesiumGltf/test/TestMetadataFeatureTableView.cpp deleted file mode 100644 index 9aabf9e6e..000000000 --- a/CesiumGltf/test/TestMetadataFeatureTableView.cpp +++ /dev/null @@ -1,1164 +0,0 @@ -#include "CesiumGltf/MetadataFeatureTableView.h" - -#include - -#include - -using namespace CesiumGltf; - -TEST_CASE("Test numeric properties") { - Model model; - - // store property value - std::vector values = {12, 34, 30, 11, 34, 34, 11, 33, 122, 33}; - - // Construct buffers in the scope to make sure that tests below doesn't use - // the temp variables. Use index to access the buffer and buffer view instead - size_t valueBufferIndex = 0; - size_t valueBufferViewIndex = 0; - { - Buffer& valueBuffer = model.buffers.emplace_back(); - valueBuffer.cesium.data.resize(values.size() * sizeof(uint32_t)); - valueBuffer.byteLength = - static_cast(valueBuffer.cesium.data.size()); - std::memcpy( - valueBuffer.cesium.data.data(), - values.data(), - valueBuffer.cesium.data.size()); - valueBufferIndex = model.buffers.size() - 1; - - BufferView& valueBufferView = model.bufferViews.emplace_back(); - valueBufferView.buffer = static_cast(model.buffers.size() - 1); - valueBufferView.byteOffset = 0; - valueBufferView.byteLength = valueBuffer.byteLength; - valueBufferViewIndex = model.bufferViews.size() - 1; - } - - // setup metadata - ExtensionModelExtFeatureMetadata& metadata = - model.addExtension(); - - // setup schema - Schema& schema = metadata.schema.emplace(); - Class& testClass = schema.classes["TestClass"]; - ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; - testClassProperty.type = ClassProperty::Type::UINT32; - - // setup feature table - FeatureTable& featureTable = metadata.featureTables["TestFeatureTable"]; - featureTable.classProperty = "TestClass"; - featureTable.count = static_cast(values.size()); - - // setup feature table property - FeatureTableProperty& featureTableProperty = - featureTable.properties["TestClassProperty"]; - featureTableProperty.bufferView = static_cast(valueBufferViewIndex); - - // test feature table view - MetadataFeatureTableView view(&model, &featureTable); - const ClassProperty* classProperty = - view.getClassProperty("TestClassProperty"); - REQUIRE(classProperty->type == ClassProperty::Type::UINT32); - REQUIRE(classProperty->componentCount == std::nullopt); - REQUIRE(classProperty->componentType == std::nullopt); - - SECTION("Access correct type") { - MetadataPropertyView uint32Property = - view.getPropertyView("TestClassProperty"); - REQUIRE(uint32Property.status() == MetadataPropertyViewStatus::Valid); - REQUIRE(uint32Property.size() > 0); - - for (int64_t i = 0; i < uint32Property.size(); ++i) { - REQUIRE(uint32Property.get(i) == values[static_cast(i)]); - } - } - - SECTION("Access wrong type") { - MetadataPropertyView boolInvalid = - view.getPropertyView("TestClassProperty"); - REQUIRE( - boolInvalid.status() == - MetadataPropertyViewStatus::InvalidTypeMismatch); - - MetadataPropertyView uint8Invalid = - view.getPropertyView("TestClassProperty"); - REQUIRE( - uint8Invalid.status() == - MetadataPropertyViewStatus::InvalidTypeMismatch); - - MetadataPropertyView int32Invalid = - view.getPropertyView("TestClassProperty"); - REQUIRE( - int32Invalid.status() == - MetadataPropertyViewStatus::InvalidTypeMismatch); - - MetadataPropertyView unt64Invalid = - view.getPropertyView("TestClassProperty"); - REQUIRE( - unt64Invalid.status() == - MetadataPropertyViewStatus::InvalidTypeMismatch); - - MetadataPropertyView stringInvalid = - view.getPropertyView("TestClassProperty"); - REQUIRE( - stringInvalid.status() == - MetadataPropertyViewStatus::InvalidTypeMismatch); - - MetadataPropertyView> uint32ArrayInvalid = - view.getPropertyView>("TestClassProperty"); - REQUIRE( - uint32ArrayInvalid.status() == - MetadataPropertyViewStatus::InvalidTypeMismatch); - - MetadataPropertyView> boolArrayInvalid = - view.getPropertyView>("TestClassProperty"); - REQUIRE( - boolArrayInvalid.status() == - MetadataPropertyViewStatus::InvalidTypeMismatch); - - MetadataPropertyView> - stringArrayInvalid = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - stringArrayInvalid.status() == - MetadataPropertyViewStatus::InvalidTypeMismatch); - } - - SECTION("Wrong buffer index") { - model.bufferViews[valueBufferViewIndex].buffer = 2; - MetadataPropertyView uint32Property = - view.getPropertyView("TestClassProperty"); - REQUIRE( - uint32Property.status() == - MetadataPropertyViewStatus::InvalidValueBufferIndex); - } - - SECTION("Wrong buffer view index") { - featureTableProperty.bufferView = -1; - MetadataPropertyView uint32Property = - view.getPropertyView("TestClassProperty"); - REQUIRE( - uint32Property.status() == - MetadataPropertyViewStatus::InvalidValueBufferViewIndex); - } - - SECTION("Buffer view points outside of the real buffer length") { - model.buffers[valueBufferIndex].cesium.data.resize(12); - MetadataPropertyView uint32Property = - view.getPropertyView("TestClassProperty"); - REQUIRE( - uint32Property.status() == - MetadataPropertyViewStatus::InvalidBufferViewOutOfBound); - } - - // Even though the EXT_feature_metadata spec technically compels us to - // enforce an 8-byte alignment, we avoid doing so for compatibility with - // incorrect glTFs. - /* - SECTION("Buffer view offset is not a multiple of 8") { - model.bufferViews[valueBufferViewIndex].byteOffset = 1; - MetadataPropertyView uint32Property = - view.getPropertyView("TestClassProperty"); - REQUIRE( - uint32Property.status() == - MetadataPropertyViewStatus::InvalidBufferViewNotAligned8Bytes); - } - */ - - SECTION("Buffer view length isn't multiple of sizeof(T)") { - model.bufferViews[valueBufferViewIndex].byteLength = 13; - MetadataPropertyView uint32Property = - view.getPropertyView("TestClassProperty"); - REQUIRE( - uint32Property.status() == - MetadataPropertyViewStatus:: - InvalidBufferViewSizeNotDivisibleByTypeSize); - } - - SECTION("Buffer view length doesn't match with featureTableCount") { - model.bufferViews[valueBufferViewIndex].byteLength = 12; - MetadataPropertyView uint32Property = - view.getPropertyView("TestClassProperty"); - REQUIRE( - uint32Property.status() == - MetadataPropertyViewStatus::InvalidBufferViewSizeNotFitInstanceCount); - } -} - -TEST_CASE("Test boolean properties") { - Model model; - - // store property value - int64_t instanceCount = 21; - std::vector expected; - std::vector values; - values.resize(3); - for (int64_t i = 0; i < instanceCount; ++i) { - if (i % 2 == 0) { - expected.emplace_back(true); - } else { - expected.emplace_back(false); - } - - uint8_t expectedValue = expected.back(); - int64_t byteIndex = i / 8; - int64_t bitIndex = i % 8; - values[static_cast(byteIndex)] = static_cast( - (expectedValue << bitIndex) | values[static_cast(byteIndex)]); - } - - // Create buffers in the scope, so that tests below don't accidentally refer - // to temp variable. Use index instead if the buffer is needed - { - Buffer& valueBuffer = model.buffers.emplace_back(); - valueBuffer.cesium.data.resize(values.size()); - valueBuffer.byteLength = - static_cast(valueBuffer.cesium.data.size()); - std::memcpy( - valueBuffer.cesium.data.data(), - values.data(), - valueBuffer.cesium.data.size()); - - BufferView& valueBufferView = model.bufferViews.emplace_back(); - valueBufferView.buffer = static_cast(model.buffers.size() - 1); - valueBufferView.byteOffset = 0; - valueBufferView.byteLength = valueBuffer.byteLength; - } - - // setup metadata - ExtensionModelExtFeatureMetadata& metadata = - model.addExtension(); - - // setup schema - Schema& schema = metadata.schema.emplace(); - Class& testClass = schema.classes["TestClass"]; - ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; - testClassProperty.type = ClassProperty::Type::BOOLEAN; - - // setup feature table - FeatureTable& featureTable = metadata.featureTables["TestFeatureTable"]; - featureTable.classProperty = "TestClass"; - featureTable.count = static_cast(instanceCount); - - // setup feature table property - FeatureTableProperty& featureTableProperty = - featureTable.properties["TestClassProperty"]; - featureTableProperty.bufferView = - static_cast(model.bufferViews.size() - 1); - - // test feature table view - MetadataFeatureTableView view(&model, &featureTable); - const ClassProperty* classProperty = - view.getClassProperty("TestClassProperty"); - REQUIRE(classProperty->type == ClassProperty::Type::BOOLEAN); - REQUIRE(classProperty->componentCount == std::nullopt); - REQUIRE(classProperty->componentType == std::nullopt); - - SECTION("Access correct type") { - MetadataPropertyView boolProperty = - view.getPropertyView("TestClassProperty"); - REQUIRE(boolProperty.status() == MetadataPropertyViewStatus::Valid); - REQUIRE(boolProperty.size() == instanceCount); - for (int64_t i = 0; i < boolProperty.size(); ++i) { - bool expectedValue = expected[static_cast(i)]; - REQUIRE(boolProperty.get(i) == expectedValue); - } - } - - SECTION("Buffer size doesn't match with feature table count") { - featureTable.count = 66; - MetadataPropertyView boolProperty = - view.getPropertyView("TestClassProperty"); - REQUIRE( - boolProperty.status() == - MetadataPropertyViewStatus::InvalidBufferViewSizeNotFitInstanceCount); - } -} - -TEST_CASE("Test string property") { - Model model; - - std::vector expected{"What's up", "Test_0", "Test_1", "", ""}; - size_t totalBytes = 0; - for (const std::string& expectedValue : expected) { - totalBytes += expectedValue.size(); - } - - std::vector offsets((expected.size() + 1) * sizeof(uint32_t)); - std::vector values(totalBytes); - uint32_t* offsetValue = reinterpret_cast(offsets.data()); - for (size_t i = 0; i < expected.size(); ++i) { - const std::string& expectedValue = expected[i]; - std::memcpy( - values.data() + offsetValue[i], - expectedValue.c_str(), - expectedValue.size()); - offsetValue[i + 1] = - offsetValue[i] + static_cast(expectedValue.size()); - } - - // Create buffers in the scope, so that tests below don't accidentally refer - // to temp variable. Use index instead if the buffer is needed. - // Store property value - size_t valueBufferIndex = 0; - size_t valueBufferViewIndex = 0; - { - Buffer& valueBuffer = model.buffers.emplace_back(); - valueBuffer.byteLength = static_cast(values.size()); - valueBuffer.cesium.data = std::move(values); - valueBufferIndex = model.buffers.size() - 1; - - BufferView& valueBufferView = model.bufferViews.emplace_back(); - valueBufferView.buffer = static_cast(valueBufferIndex); - valueBufferView.byteOffset = 0; - valueBufferView.byteLength = valueBuffer.byteLength; - valueBufferViewIndex = model.bufferViews.size() - 1; - } - - // Create buffers in the scope, so that tests below don't accidentally refer - // to temp variable. Use index instead if the buffer is needed. - // Store string offset buffer - size_t offsetBufferIndex = 0; - size_t offsetBufferViewIndex = 0; - { - Buffer& offsetBuffer = model.buffers.emplace_back(); - offsetBuffer.byteLength = static_cast(offsets.size()); - offsetBuffer.cesium.data = std::move(offsets); - offsetBufferIndex = model.buffers.size() - 1; - - BufferView& offsetBufferView = model.bufferViews.emplace_back(); - offsetBufferView.buffer = static_cast(offsetBufferIndex); - offsetBufferView.byteOffset = 0; - offsetBufferView.byteLength = offsetBuffer.byteLength; - offsetBufferViewIndex = model.bufferViews.size() - 1; - } - - // setup metadata - ExtensionModelExtFeatureMetadata& metadata = - model.addExtension(); - - // setup schema - Schema& schema = metadata.schema.emplace(); - Class& testClass = schema.classes["TestClass"]; - ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; - testClassProperty.type = ClassProperty::Type::STRING; - - // setup feature table - FeatureTable& featureTable = metadata.featureTables["TestFeatureTable"]; - featureTable.classProperty = "TestClass"; - featureTable.count = static_cast(expected.size()); - - // setup feature table property - FeatureTableProperty& featureTableProperty = - featureTable.properties["TestClassProperty"]; - featureTableProperty.offsetType = FeatureTableProperty::OffsetType::UINT32; - featureTableProperty.bufferView = static_cast(valueBufferViewIndex); - featureTableProperty.stringOffsetBufferView = - static_cast(offsetBufferViewIndex); - - // test feature table view - MetadataFeatureTableView view(&model, &featureTable); - const ClassProperty* classProperty = - view.getClassProperty("TestClassProperty"); - REQUIRE(classProperty->type == ClassProperty::Type::STRING); - REQUIRE(classProperty->componentCount == std::nullopt); - REQUIRE(classProperty->componentType == std::nullopt); - - SECTION("Access correct type") { - MetadataPropertyView stringProperty = - view.getPropertyView("TestClassProperty"); - REQUIRE(stringProperty.status() == MetadataPropertyViewStatus::Valid); - for (size_t i = 0; i < expected.size(); ++i) { - REQUIRE(stringProperty.get(static_cast(i)) == expected[i]); - } - } - - SECTION("Wrong offset type") { - featureTableProperty.offsetType = FeatureTableProperty::OffsetType::UINT8; - MetadataPropertyView stringProperty = - view.getPropertyView("TestClassProperty"); - REQUIRE( - stringProperty.status() == - MetadataPropertyViewStatus::InvalidBufferViewSizeNotFitInstanceCount); - - featureTableProperty.offsetType = FeatureTableProperty::OffsetType::UINT64; - stringProperty = - view.getPropertyView("TestClassProperty"); - REQUIRE( - stringProperty.status() == - MetadataPropertyViewStatus::InvalidBufferViewSizeNotFitInstanceCount); - - featureTableProperty.offsetType = "NONSENSE"; - stringProperty = - view.getPropertyView("TestClassProperty"); - REQUIRE( - stringProperty.status() == - MetadataPropertyViewStatus::InvalidOffsetType); - } - - SECTION("Offset values are not sorted ascending") { - uint32_t* offset = reinterpret_cast( - model.buffers[offsetBufferIndex].cesium.data.data()); - offset[2] = - static_cast(model.buffers[valueBufferIndex].byteLength + 4); - MetadataPropertyView stringProperty = - view.getPropertyView("TestClassProperty"); - REQUIRE( - stringProperty.status() == - MetadataPropertyViewStatus::InvalidOffsetValuesNotSortedAscending); - } - - SECTION("Offset value points outside of value buffer") { - uint32_t* offset = reinterpret_cast( - model.buffers[offsetBufferIndex].cesium.data.data()); - offset[featureTable.count] = - static_cast(model.buffers[valueBufferIndex].byteLength + 4); - MetadataPropertyView stringProperty = - view.getPropertyView("TestClassProperty"); - REQUIRE( - stringProperty.status() == - MetadataPropertyViewStatus::InvalidOffsetValuePointsToOutOfBoundBuffer); - } -} - -TEST_CASE("Test fixed numeric array") { - Model model; - - // store property value - std::vector values = - {12, 34, 30, 11, 34, 34, 11, 33, 122, 33, 223, 11}; - - size_t valueBufferViewIndex = 0; - { - Buffer& valueBuffer = model.buffers.emplace_back(); - valueBuffer.cesium.data.resize(values.size() * sizeof(uint32_t)); - valueBuffer.byteLength = - static_cast(valueBuffer.cesium.data.size()); - std::memcpy( - valueBuffer.cesium.data.data(), - values.data(), - valueBuffer.cesium.data.size()); - - BufferView& valueBufferView = model.bufferViews.emplace_back(); - valueBufferView.buffer = static_cast(model.buffers.size() - 1); - valueBufferView.byteOffset = 0; - valueBufferView.byteLength = valueBuffer.byteLength; - valueBufferViewIndex = model.bufferViews.size() - 1; - } - - // setup metadata - ExtensionModelExtFeatureMetadata& metadata = - model.addExtension(); - - // setup schema - Schema& schema = metadata.schema.emplace(); - Class& testClass = schema.classes["TestClass"]; - ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; - testClassProperty.type = ClassProperty::Type::ARRAY; - testClassProperty.componentType = ClassProperty::ComponentType::UINT32; - testClassProperty.componentCount = 3; - - // setup feature table - FeatureTable& featureTable = metadata.featureTables["TestFeatureTable"]; - featureTable.classProperty = "TestClass"; - featureTable.count = static_cast( - values.size() / - static_cast(testClassProperty.componentCount.value())); - - // setup feature table property - FeatureTableProperty& featureTableProperty = - featureTable.properties["TestClassProperty"]; - featureTableProperty.bufferView = - static_cast(model.bufferViews.size() - 1); - - // test feature table view - MetadataFeatureTableView view(&model, &featureTable); - const ClassProperty* classProperty = - view.getClassProperty("TestClassProperty"); - REQUIRE(classProperty->type == ClassProperty::Type::ARRAY); - REQUIRE(classProperty->componentCount == 3); - REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT32); - - SECTION("Access the right type") { - MetadataPropertyView> arrayProperty = - view.getPropertyView>("TestClassProperty"); - REQUIRE(arrayProperty.status() == MetadataPropertyViewStatus::Valid); - - for (int64_t i = 0; i < arrayProperty.size(); ++i) { - MetadataArrayView member = arrayProperty.get(i); - for (int64_t j = 0; j < member.size(); ++j) { - REQUIRE(member[j] == values[static_cast(i * 3 + j)]); - } - } - } - - SECTION("Wrong component type") { - testClassProperty.componentType = ClassProperty::ComponentType::UINT8; - MetadataPropertyView> arrayProperty = - view.getPropertyView>("TestClassProperty"); - REQUIRE( - arrayProperty.status() == - MetadataPropertyViewStatus::InvalidTypeMismatch); - } - - SECTION("Buffer size is not a multiple of type size") { - model.bufferViews[valueBufferViewIndex].byteLength = 13; - MetadataPropertyView> arrayProperty = - view.getPropertyView>("TestClassProperty"); - REQUIRE( - arrayProperty.status() == - MetadataPropertyViewStatus:: - InvalidBufferViewSizeNotDivisibleByTypeSize); - } - - SECTION("Negative component count") { - testClassProperty.componentCount = -1; - MetadataPropertyView> arrayProperty = - view.getPropertyView>("TestClassProperty"); - REQUIRE( - arrayProperty.status() == - MetadataPropertyViewStatus:: - InvalidArrayComponentCountOrOffsetBufferNotExist); - } - - SECTION("Value buffer doesn't fit into feature table count") { - testClassProperty.componentCount = 55; - MetadataPropertyView> arrayProperty = - view.getPropertyView>("TestClassProperty"); - REQUIRE( - arrayProperty.status() == - MetadataPropertyViewStatus::InvalidBufferViewSizeNotFitInstanceCount); - } -} - -TEST_CASE("Test dynamic numeric array") { - Model model; - - // store property value - std::vector> expected{ - {12, 33, 11, 344, 112, 444, 1}, - {}, - {}, - {122, 23, 333, 12}, - {}, - {333, 311, 22, 34}, - {}, - {33, 1888, 233, 33019}}; - size_t numOfElements = 0; - for (const auto& expectedMember : expected) { - numOfElements += expectedMember.size(); - } - - std::vector values(numOfElements * sizeof(uint16_t)); - std::vector offsets((expected.size() + 1) * sizeof(uint64_t)); - uint64_t* offsetValue = reinterpret_cast(offsets.data()); - for (size_t i = 0; i < expected.size(); ++i) { - std::memcpy( - values.data() + offsetValue[i], - expected[i].data(), - expected[i].size() * sizeof(uint16_t)); - offsetValue[i + 1] = offsetValue[i] + expected[i].size() * sizeof(uint16_t); - } - - // store property value - size_t valueBufferViewIndex = 0; - { - Buffer& valueBuffer = model.buffers.emplace_back(); - valueBuffer.byteLength = static_cast(values.size()); - valueBuffer.cesium.data = std::move(values); - - BufferView& valueBufferView = model.bufferViews.emplace_back(); - valueBufferView.buffer = static_cast(model.buffers.size() - 1); - valueBufferView.byteOffset = 0; - valueBufferView.byteLength = valueBuffer.byteLength; - valueBufferViewIndex = model.bufferViews.size() - 1; - } - - // store string offset buffer - size_t offsetBufferViewIndex = 0; - { - Buffer& offsetBuffer = model.buffers.emplace_back(); - offsetBuffer.byteLength = static_cast(offsets.size()); - offsetBuffer.cesium.data = std::move(offsets); - - BufferView& offsetBufferView = model.bufferViews.emplace_back(); - offsetBufferView.buffer = static_cast(model.buffers.size() - 1); - offsetBufferView.byteOffset = 0; - offsetBufferView.byteLength = offsetBuffer.byteLength; - offsetBufferViewIndex = model.bufferViews.size() - 1; - } - - // setup metadata - ExtensionModelExtFeatureMetadata& metadata = - model.addExtension(); - - // setup schema - Schema& schema = metadata.schema.emplace(); - Class& testClass = schema.classes["TestClass"]; - ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; - testClassProperty.type = ClassProperty::Type::ARRAY; - testClassProperty.componentType = ClassProperty::ComponentType::UINT16; - - // setup feature table - FeatureTable& featureTable = metadata.featureTables["TestFeatureTable"]; - featureTable.classProperty = "TestClass"; - featureTable.count = static_cast(expected.size()); - - // setup feature table property - FeatureTableProperty& featureTableProperty = - featureTable.properties["TestClassProperty"]; - featureTableProperty.bufferView = static_cast(valueBufferViewIndex); - featureTableProperty.arrayOffsetBufferView = - static_cast(offsetBufferViewIndex); - featureTableProperty.offsetType = FeatureTableProperty::OffsetType::UINT64; - - // test feature table view - MetadataFeatureTableView view(&model, &featureTable); - const ClassProperty* classProperty = - view.getClassProperty("TestClassProperty"); - REQUIRE(classProperty->type == ClassProperty::Type::ARRAY); - REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT16); - - SECTION("Access the correct type") { - MetadataPropertyView> property = - view.getPropertyView>("TestClassProperty"); - REQUIRE(property.status() == MetadataPropertyViewStatus::Valid); - for (size_t i = 0; i < expected.size(); ++i) { - MetadataArrayView valueMember = - property.get(static_cast(i)); - REQUIRE(valueMember.size() == static_cast(expected[i].size())); - for (size_t j = 0; j < expected[i].size(); ++j) { - REQUIRE(expected[i][j] == valueMember[static_cast(j)]); - } - } - } - - SECTION("Component count and offset buffer appear at the same time") { - testClassProperty.componentCount = 3; - MetadataPropertyView> property = - view.getPropertyView>("TestClassProperty"); - REQUIRE( - property.status() == - MetadataPropertyViewStatus:: - InvalidArrayComponentCountAndOffsetBufferCoexist); - } -} - -TEST_CASE("Test fixed boolean array") { - Model model; - - // store property value - std::vector expected = { - true, - false, - false, - true, - false, - false, - true, - true, - true, - false, - false, - true}; - std::vector values; - size_t requiredBytesSize = static_cast( - glm::ceil(static_cast(expected.size()) / 8.0)); - values.resize(requiredBytesSize); - for (size_t i = 0; i < expected.size(); ++i) { - uint8_t expectedValue = expected[i]; - size_t byteIndex = i / 8; - size_t bitIndex = i % 8; - values[byteIndex] = - static_cast((expectedValue << bitIndex) | values[byteIndex]); - } - - { - Buffer& valueBuffer = model.buffers.emplace_back(); - valueBuffer.cesium.data.resize(values.size()); - valueBuffer.byteLength = - static_cast(valueBuffer.cesium.data.size()); - std::memcpy( - valueBuffer.cesium.data.data(), - values.data(), - valueBuffer.cesium.data.size()); - - BufferView& valueBufferView = model.bufferViews.emplace_back(); - valueBufferView.buffer = static_cast(model.buffers.size() - 1); - valueBufferView.byteOffset = 0; - valueBufferView.byteLength = valueBuffer.byteLength; - } - - // setup metadata - ExtensionModelExtFeatureMetadata& metadata = - model.addExtension(); - - // setup schema - Schema& schema = metadata.schema.emplace(); - Class& testClass = schema.classes["TestClass"]; - ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; - testClassProperty.type = ClassProperty::Type::ARRAY; - testClassProperty.componentType = ClassProperty::ComponentType::BOOLEAN; - testClassProperty.componentCount = 3; - - // setup feature table - FeatureTable& featureTable = metadata.featureTables["TestFeatureTable"]; - featureTable.classProperty = "TestClass"; - featureTable.count = static_cast( - expected.size() / - static_cast(testClassProperty.componentCount.value())); - - // setup feature table property - FeatureTableProperty& featureTableProperty = - featureTable.properties["TestClassProperty"]; - featureTableProperty.bufferView = - static_cast(model.bufferViews.size() - 1); - - // test feature table view - MetadataFeatureTableView view(&model, &featureTable); - const ClassProperty* classProperty = - view.getClassProperty("TestClassProperty"); - REQUIRE(classProperty->type == ClassProperty::Type::ARRAY); - REQUIRE(classProperty->componentCount == 3); - REQUIRE( - classProperty->componentType == ClassProperty::ComponentType::BOOLEAN); - - SECTION("Access correct type") { - MetadataPropertyView> boolProperty = - view.getPropertyView>("TestClassProperty"); - REQUIRE(boolProperty.status() == MetadataPropertyViewStatus::Valid); - REQUIRE(boolProperty.size() == featureTable.count); - REQUIRE(boolProperty.size() > 0); - for (int64_t i = 0; i < boolProperty.size(); ++i) { - MetadataArrayView valueMember = boolProperty.get(i); - for (int64_t j = 0; j < valueMember.size(); ++j) { - REQUIRE(valueMember[j] == expected[static_cast(i * 3 + j)]); - } - } - } - - SECTION("Value buffer doesn't have enough required bytes") { - testClassProperty.componentCount = 11; - MetadataPropertyView> boolProperty = - view.getPropertyView>("TestClassProperty"); - REQUIRE( - boolProperty.status() == - MetadataPropertyViewStatus::InvalidBufferViewSizeNotFitInstanceCount); - } - - SECTION("Component count is negative") { - testClassProperty.componentCount = -1; - MetadataPropertyView> boolProperty = - view.getPropertyView>("TestClassProperty"); - REQUIRE( - boolProperty.status() == - MetadataPropertyViewStatus:: - InvalidArrayComponentCountOrOffsetBufferNotExist); - } -} - -TEST_CASE("Test dynamic bool array") { - Model model; - - // store property value - std::vector> expected{ - {true, false, true, true, false, true, true}, - {}, - {}, - {}, - {false, false, false, false}, - {true, false, true}, - {false}, - {true, true, true, true, true, false, false}}; - size_t numOfElements = 0; - for (const auto& expectedMember : expected) { - numOfElements += expectedMember.size(); - } - - size_t requiredBytesSize = - static_cast(glm::ceil(static_cast(numOfElements) / 8.0)); - std::vector values(requiredBytesSize); - std::vector offsets((expected.size() + 1) * sizeof(uint64_t)); - uint64_t* offsetValue = reinterpret_cast(offsets.data()); - size_t indexSoFar = 0; - for (size_t i = 0; i < expected.size(); ++i) { - for (size_t j = 0; j < expected[i].size(); ++j) { - uint8_t expectedValue = expected[i][j]; - size_t byteIndex = indexSoFar / 8; - size_t bitIndex = indexSoFar % 8; - values[byteIndex] = static_cast( - (expectedValue << bitIndex) | static_cast(values[byteIndex])); - ++indexSoFar; - } - offsetValue[i + 1] = offsetValue[i] + expected[i].size(); - } - - // store property value - size_t valueBufferViewIndex = 0; - { - Buffer& valueBuffer = model.buffers.emplace_back(); - valueBuffer.byteLength = static_cast(values.size()); - valueBuffer.cesium.data = std::move(values); - - BufferView& valueBufferView = model.bufferViews.emplace_back(); - valueBufferView.buffer = static_cast(model.buffers.size() - 1); - valueBufferView.byteOffset = 0; - valueBufferView.byteLength = valueBuffer.byteLength; - valueBufferViewIndex = model.bufferViews.size() - 1; - } - - // store string offset buffer - size_t offsetBufferViewIndex = 0; - { - Buffer& offsetBuffer = model.buffers.emplace_back(); - offsetBuffer.byteLength = static_cast(offsets.size()); - offsetBuffer.cesium.data = std::move(offsets); - - BufferView& offsetBufferView = model.bufferViews.emplace_back(); - offsetBufferView.buffer = static_cast(model.buffers.size() - 1); - offsetBufferView.byteOffset = 0; - offsetBufferView.byteLength = offsetBuffer.byteLength; - offsetBufferViewIndex = model.bufferViews.size() - 1; - } - - // setup metadata - ExtensionModelExtFeatureMetadata& metadata = - model.addExtension(); - - // setup schema - Schema& schema = metadata.schema.emplace(); - Class& testClass = schema.classes["TestClass"]; - ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; - testClassProperty.type = ClassProperty::Type::ARRAY; - testClassProperty.componentType = ClassProperty::ComponentType::BOOLEAN; - - // setup feature table - FeatureTable& featureTable = metadata.featureTables["TestFeatureTable"]; - featureTable.classProperty = "TestClass"; - featureTable.count = static_cast(expected.size()); - - // setup feature table property - FeatureTableProperty& featureTableProperty = - featureTable.properties["TestClassProperty"]; - featureTableProperty.bufferView = static_cast(valueBufferViewIndex); - featureTableProperty.arrayOffsetBufferView = - static_cast(offsetBufferViewIndex); - featureTableProperty.offsetType = FeatureTableProperty::OffsetType::UINT64; - - // test feature table view - MetadataFeatureTableView view(&model, &featureTable); - const ClassProperty* classProperty = - view.getClassProperty("TestClassProperty"); - REQUIRE(classProperty->type == ClassProperty::Type::ARRAY); - REQUIRE( - classProperty->componentType == ClassProperty::ComponentType::BOOLEAN); - - SECTION("Access correct type") { - MetadataPropertyView> boolProperty = - view.getPropertyView>("TestClassProperty"); - REQUIRE(boolProperty.status() == MetadataPropertyViewStatus::Valid); - for (size_t i = 0; i < expected.size(); ++i) { - MetadataArrayView arrayMember = - boolProperty.get(static_cast(i)); - REQUIRE(arrayMember.size() == static_cast(expected[i].size())); - for (size_t j = 0; j < expected[i].size(); ++j) { - REQUIRE(expected[i][j] == arrayMember[static_cast(j)]); - } - } - } - - SECTION("Component count and array offset appear at the same time") { - testClassProperty.componentCount = 3; - MetadataPropertyView> boolProperty = - view.getPropertyView>("TestClassProperty"); - REQUIRE( - boolProperty.status() == - MetadataPropertyViewStatus:: - InvalidArrayComponentCountAndOffsetBufferCoexist); - } -} - -TEST_CASE("Test fixed array of string") { - Model model; - - std::vector expected{ - "What's up", - "Breaking news!!! Aliens no longer attacks the US first", - "But they still abduct my cows! Those milk thiefs! 👽 🐮", - "I'm not crazy. My mother had me tested 🤪", - "I love you, meat bags! ❤️", - "Book in the freezer"}; - - size_t totalBytes = 0; - for (const std::string& expectedValue : expected) { - totalBytes += expectedValue.size(); - } - - std::vector offsets((expected.size() + 1) * sizeof(uint32_t)); - std::vector values(totalBytes); - uint32_t* offsetValue = reinterpret_cast(offsets.data()); - for (size_t i = 0; i < expected.size(); ++i) { - const std::string& expectedValue = expected[i]; - std::memcpy( - values.data() + offsetValue[i], - expectedValue.c_str(), - expectedValue.size()); - offsetValue[i + 1] = - offsetValue[i] + static_cast(expectedValue.size()); - } - - // store property value - size_t valueBufferViewIndex = 0; - { - Buffer& valueBuffer = model.buffers.emplace_back(); - valueBuffer.byteLength = static_cast(values.size()); - valueBuffer.cesium.data = std::move(values); - - BufferView& valueBufferView = model.bufferViews.emplace_back(); - valueBufferView.buffer = static_cast(model.buffers.size() - 1); - valueBufferView.byteOffset = 0; - valueBufferView.byteLength = valueBuffer.byteLength; - valueBufferViewIndex = model.bufferViews.size() - 1; - } - - // store string offset buffer - size_t offsetBufferViewIndex = 0; - { - Buffer& offsetBuffer = model.buffers.emplace_back(); - offsetBuffer.byteLength = static_cast(offsets.size()); - offsetBuffer.cesium.data = std::move(offsets); - - BufferView& offsetBufferView = model.bufferViews.emplace_back(); - offsetBufferView.buffer = static_cast(model.buffers.size() - 1); - offsetBufferView.byteOffset = 0; - offsetBufferView.byteLength = offsetBuffer.byteLength; - offsetBufferViewIndex = model.bufferViews.size() - 1; - } - - // setup metadata - ExtensionModelExtFeatureMetadata& metadata = - model.addExtension(); - - // setup schema - Schema& schema = metadata.schema.emplace(); - Class& testClass = schema.classes["TestClass"]; - ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; - testClassProperty.type = ClassProperty::Type::ARRAY; - testClassProperty.componentType = ClassProperty::ComponentType::STRING; - testClassProperty.componentCount = 2; - - // setup feature table - FeatureTable& featureTable = metadata.featureTables["TestFeatureTable"]; - featureTable.classProperty = "TestClass"; - featureTable.count = static_cast( - expected.size() / - static_cast(testClassProperty.componentCount.value())); - - // setup feature table property - FeatureTableProperty& featureTableProperty = - featureTable.properties["TestClassProperty"]; - featureTableProperty.offsetType = FeatureTableProperty::OffsetType::UINT32; - featureTableProperty.bufferView = static_cast(valueBufferViewIndex); - featureTableProperty.stringOffsetBufferView = - static_cast(offsetBufferViewIndex); - - // test feature table view - MetadataFeatureTableView view(&model, &featureTable); - const ClassProperty* classProperty = - view.getClassProperty("TestClassProperty"); - REQUIRE(classProperty->type == ClassProperty::Type::ARRAY); - REQUIRE(classProperty->componentCount == 2); - REQUIRE(classProperty->componentType == ClassProperty::ComponentType::STRING); - - SECTION("Access correct type") { - MetadataPropertyView> stringProperty = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE(stringProperty.status() == MetadataPropertyViewStatus::Valid); - REQUIRE(stringProperty.size() == 3); - - MetadataArrayView v0 = stringProperty.get(0); - REQUIRE(v0.size() == 2); - REQUIRE(v0[0] == "What's up"); - REQUIRE(v0[1] == "Breaking news!!! Aliens no longer attacks the US first"); - - MetadataArrayView v1 = stringProperty.get(1); - REQUIRE(v1.size() == 2); - REQUIRE(v1[0] == "But they still abduct my cows! Those milk thiefs! 👽 🐮"); - REQUIRE(v1[1] == "I'm not crazy. My mother had me tested 🤪"); - - MetadataArrayView v2 = stringProperty.get(2); - REQUIRE(v2.size() == 2); - REQUIRE(v2[0] == "I love you, meat bags! ❤️"); - REQUIRE(v2[1] == "Book in the freezer"); - } - - SECTION("Component count is negative") { - testClassProperty.componentCount = -1; - MetadataPropertyView> stringProperty = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - stringProperty.status() == - MetadataPropertyViewStatus:: - InvalidArrayComponentCountOrOffsetBufferNotExist); - } - - SECTION("Offset type is unknown") { - featureTableProperty.offsetType = "NONSENSE"; - MetadataPropertyView> stringProperty = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - stringProperty.status() == - MetadataPropertyViewStatus::InvalidOffsetType); - } - - SECTION("string offset buffer doesn't exist") { - featureTableProperty.stringOffsetBufferView = -1; - MetadataPropertyView> stringProperty = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - stringProperty.status() == - MetadataPropertyViewStatus::InvalidStringOffsetBufferViewIndex); - } -} - -TEST_CASE("Test dynamic array of string") { - Model model; - - std::vector> expected{ - {"What's up"}, - {"Breaking news!!! Aliens no longer attacks the US first", - "But they still abduct my cows! Those milk thiefs! 👽 🐮"}, - {"I'm not crazy. My mother had me tested 🤪", - "I love you, meat bags! ❤️", - "Book in the freezer"}}; - - size_t totalBytes = 0; - size_t numOfElements = 0; - for (const auto& expectedValues : expected) { - for (const auto& value : expectedValues) { - totalBytes += value.size(); - } - - numOfElements += expectedValues.size(); - } - - std::vector offsets((expected.size() + 1) * sizeof(uint32_t)); - std::vector stringOffsets((numOfElements + 1) * sizeof(uint32_t)); - std::vector values(totalBytes); - uint32_t* offsetValue = reinterpret_cast(offsets.data()); - uint32_t* stringOffsetValue = - reinterpret_cast(stringOffsets.data()); - size_t strOffsetIdx = 0; - for (size_t i = 0; i < expected.size(); ++i) { - for (size_t j = 0; j < expected[i].size(); ++j) { - const std::string& expectedValue = expected[i][j]; - std::memcpy( - values.data() + stringOffsetValue[strOffsetIdx], - expectedValue.c_str(), - expectedValue.size()); - - stringOffsetValue[strOffsetIdx + 1] = - stringOffsetValue[strOffsetIdx] + - static_cast(expectedValue.size()); - ++strOffsetIdx; - } - - offsetValue[i + 1] = - offsetValue[i] + - static_cast(expected[i].size() * sizeof(uint32_t)); - } - - // store property value - size_t valueBufferViewIndex = 0; - { - Buffer& valueBuffer = model.buffers.emplace_back(); - valueBuffer.byteLength = static_cast(values.size()); - valueBuffer.cesium.data = std::move(values); - - BufferView& valueBufferView = model.bufferViews.emplace_back(); - valueBufferView.buffer = static_cast(model.buffers.size() - 1); - valueBufferView.byteOffset = 0; - valueBufferView.byteLength = valueBuffer.byteLength; - valueBufferViewIndex = model.bufferViews.size() - 1; - } - - // store array offset buffer - size_t offsetBufferViewIndex = 0; - { - Buffer& offsetBuffer = model.buffers.emplace_back(); - offsetBuffer.byteLength = static_cast(offsets.size()); - offsetBuffer.cesium.data = std::move(offsets); - - BufferView& offsetBufferView = model.bufferViews.emplace_back(); - offsetBufferView.buffer = static_cast(model.buffers.size() - 1); - offsetBufferView.byteOffset = 0; - offsetBufferView.byteLength = offsetBuffer.byteLength; - offsetBufferViewIndex = model.bufferViews.size() - 1; - } - - // store string offset buffer - size_t strOffsetBufferViewIndex = 0; - { - Buffer& strOffsetBuffer = model.buffers.emplace_back(); - strOffsetBuffer.byteLength = static_cast(stringOffsets.size()); - strOffsetBuffer.cesium.data = std::move(stringOffsets); - - BufferView& strOffsetBufferView = model.bufferViews.emplace_back(); - strOffsetBufferView.buffer = static_cast(model.buffers.size() - 1); - strOffsetBufferView.byteOffset = 0; - strOffsetBufferView.byteLength = strOffsetBuffer.byteLength; - strOffsetBufferViewIndex = model.bufferViews.size() - 1; - } - - // setup metadata - ExtensionModelExtFeatureMetadata& metadata = - model.addExtension(); - - // setup schema - Schema& schema = metadata.schema.emplace(); - Class& testClass = schema.classes["TestClass"]; - ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; - testClassProperty.type = ClassProperty::Type::ARRAY; - testClassProperty.componentType = ClassProperty::ComponentType::STRING; - - // setup feature table - FeatureTable& featureTable = metadata.featureTables["TestFeatureTable"]; - featureTable.classProperty = "TestClass"; - featureTable.count = static_cast(expected.size()); - - // setup feature table property - FeatureTableProperty& featureTableProperty = - featureTable.properties["TestClassProperty"]; - featureTableProperty.offsetType = FeatureTableProperty::OffsetType::UINT32; - featureTableProperty.bufferView = static_cast(valueBufferViewIndex); - featureTableProperty.arrayOffsetBufferView = - static_cast(offsetBufferViewIndex); - featureTableProperty.stringOffsetBufferView = - static_cast(strOffsetBufferViewIndex); - - // test feature table view - MetadataFeatureTableView view(&model, &featureTable); - const ClassProperty* classProperty = - view.getClassProperty("TestClassProperty"); - REQUIRE(classProperty->type == ClassProperty::Type::ARRAY); - REQUIRE(classProperty->componentType == ClassProperty::ComponentType::STRING); - - SECTION("Access correct type") { - MetadataPropertyView> stringProperty = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE(stringProperty.status() == MetadataPropertyViewStatus::Valid); - for (size_t i = 0; i < expected.size(); ++i) { - MetadataArrayView stringArray = - stringProperty.get(static_cast(i)); - for (size_t j = 0; j < expected[i].size(); ++j) { - REQUIRE(stringArray[static_cast(j)] == expected[i][j]); - } - } - } -} diff --git a/CesiumGltf/test/TestStructuralMetadataPropertyTablePropertyView.cpp b/CesiumGltf/test/TestPropertyTablePropertyView.cpp similarity index 93% rename from CesiumGltf/test/TestStructuralMetadataPropertyTablePropertyView.cpp rename to CesiumGltf/test/TestPropertyTablePropertyView.cpp index d77e0a2a6..ff1e44cec 100644 --- a/CesiumGltf/test/TestStructuralMetadataPropertyTablePropertyView.cpp +++ b/CesiumGltf/test/TestPropertyTablePropertyView.cpp @@ -1,4 +1,4 @@ -#include "CesiumGltf/StructuralMetadataPropertyTablePropertyView.h" +#include "CesiumGltf/PropertyTablePropertyView.h" #include #include @@ -9,7 +9,7 @@ #include #include -using namespace CesiumGltf::StructuralMetadata; +using namespace CesiumGltf; template static void checkNumeric(const std::vector& expected) { std::vector data; @@ -46,7 +46,7 @@ static void checkVariableLengthArray( offsets.data(), offsets.size() * sizeof(OffsetType)); - PropertyTablePropertyView> property( + PropertyTablePropertyView> property( PropertyTablePropertyViewStatus::Valid, gsl::span(buffer.data(), buffer.size()), gsl::span(offsetBuffer.data(), offsetBuffer.size()), @@ -59,7 +59,7 @@ static void checkVariableLengthArray( size_t expectedIdx = 0; for (int64_t i = 0; i < property.size(); ++i) { - MetadataArrayView values = property.get(i); + PropertyArrayView values = property.get(i); for (int64_t j = 0; j < values.size(); ++j) { REQUIRE(values[j] == data[expectedIdx]); ++expectedIdx; @@ -78,7 +78,7 @@ static void checkFixedLengthArray( buffer.resize(data.size() * sizeof(T)); std::memcpy(buffer.data(), data.data(), data.size() * sizeof(T)); - PropertyTablePropertyView> property( + PropertyTablePropertyView> property( PropertyTablePropertyViewStatus::Valid, gsl::span(buffer.data(), buffer.size()), gsl::span(), @@ -91,7 +91,7 @@ static void checkFixedLengthArray( size_t expectedIdx = 0; for (int64_t i = 0; i < property.size(); ++i) { - MetadataArrayView values = property.get(i); + PropertyArrayView values = property.get(i); for (int64_t j = 0; j < values.size(); ++j) { REQUIRE(values[j] == data[expectedIdx]); ++expectedIdx; @@ -101,7 +101,7 @@ static void checkFixedLengthArray( REQUIRE(expectedIdx == data.size()); } -TEST_CASE("Check StructuralMetadata scalar numeric property view") { +TEST_CASE("Check scalar numeric property view") { SECTION("Uint8 Scalar") { std::vector data{12, 33, 56, 67}; checkNumeric(data); @@ -127,7 +127,7 @@ TEST_CASE("Check StructuralMetadata scalar numeric property view") { } } -TEST_CASE("Check StructuralMetadata vecN numeric property view") { +TEST_CASE("Check vecN numeric property view") { SECTION("Float Vec2") { std::vector data{ glm::vec2(10.001f, 0.005f), @@ -156,7 +156,7 @@ TEST_CASE("Check StructuralMetadata vecN numeric property view") { } } -TEST_CASE("Check StructuralMetadata matN numeric property view") { +TEST_CASE("Check matN numeric property view") { SECTION("Float Mat2") { // clang-format off std::vector data{ @@ -218,7 +218,7 @@ TEST_CASE("Check StructuralMetadata matN numeric property view") { } } -TEST_CASE("Check StructuralMetadata boolean property") { +TEST_CASE("Check boolean property") { std::bitset bits = 0b11110101; unsigned long val = bits.to_ulong(); std::vector data(sizeof(val)); @@ -235,7 +235,7 @@ TEST_CASE("Check StructuralMetadata boolean property") { } } -TEST_CASE("Check StructuralMetadata string property") { +TEST_CASE("Check string property") { std::vector strings{ "This is a fine test", "What's going on", @@ -287,7 +287,7 @@ TEST_CASE("Check StructuralMetadata string property") { } } -TEST_CASE("Check StructuralMetadata fixed-length scalar array property") { +TEST_CASE("Check fixed-length scalar array property") { SECTION("Fixed-length array of 4 uint8_ts") { // clang-format off std::vector data{ @@ -376,7 +376,7 @@ TEST_CASE("Check StructuralMetadata fixed-length scalar array property") { } } -TEST_CASE("Check StructuralMetadata fixed-length vecN array property") { +TEST_CASE("Check fixed-length vecN array property") { SECTION("Fixed-length array of 4 u8vec2s") { // clang-format off std::vector data{ @@ -419,7 +419,7 @@ TEST_CASE("Check StructuralMetadata fixed-length vecN array property") { } } -TEST_CASE("Check StructuralMetadata fixed-length matN array property") { +TEST_CASE("Check fixed-length matN array property") { SECTION("Fixed-length array of 4 i8mat2x2") { // clang-format off std::vector data{ @@ -520,7 +520,7 @@ TEST_CASE("Check StructuralMetadata fixed-length matN array property") { } } -TEST_CASE("Check StructuralMetadata variable-length scalar array property") { +TEST_CASE("Check variable-length scalar array property") { SECTION("Variable-length array of uint8_t") { // clang-format off std::vector data{ @@ -572,7 +572,7 @@ TEST_CASE("Check StructuralMetadata variable-length scalar array property") { } } -TEST_CASE("Check StructuralMetadata variable-length vecN array property") { +TEST_CASE("Check variable-length vecN array property") { SECTION("Variable-length array of ivec2") { // clang-format off std::vector data{ @@ -636,7 +636,7 @@ TEST_CASE("Check StructuralMetadata variable-length vecN array property") { } } -TEST_CASE("Check StructuralMetadata variable-length matN array property") { +TEST_CASE("Check variable-length matN array property") { SECTION("Variable-length array of dmat2") { // clang-format off std::vector data0{ @@ -786,7 +786,7 @@ TEST_CASE("Check StructuralMetadata variable-length matN array property") { } } -TEST_CASE("Check StructuralMetadata fixed-length array of string") { +TEST_CASE("Check fixed-length array of string") { std::vector strings{ "Test 1", "Test 2", @@ -831,7 +831,7 @@ TEST_CASE("Check StructuralMetadata fixed-length array of string") { ¤tStringOffset, sizeof(uint32_t)); - PropertyTablePropertyView> property( + PropertyTablePropertyView> property( PropertyTablePropertyViewStatus::Valid, gsl::span(buffer.data(), buffer.size()), gsl::span(), @@ -844,7 +844,7 @@ TEST_CASE("Check StructuralMetadata fixed-length array of string") { size_t expectedIdx = 0; for (int64_t i = 0; i < property.size(); ++i) { - MetadataArrayView values = property.get(i); + PropertyArrayView values = property.get(i); for (int64_t j = 0; j < values.size(); ++j) { std::string_view v = values[j]; REQUIRE(v == strings[expectedIdx]); @@ -856,7 +856,7 @@ TEST_CASE("Check StructuralMetadata fixed-length array of string") { } TEST_CASE( - "Check StructuralMetadata variable-length array of strings property") { + "Check variable-length array of strings property") { // clang-format off std::vector arrayOffsets{ 0, @@ -904,7 +904,7 @@ TEST_CASE( ¤tOffset, sizeof(uint32_t)); - PropertyTablePropertyView> property( + PropertyTablePropertyView> property( PropertyTablePropertyViewStatus::Valid, gsl::span(buffer.data(), buffer.size()), gsl::span( @@ -919,7 +919,7 @@ TEST_CASE( size_t expectedIdx = 0; for (int64_t i = 0; i < property.size(); ++i) { - MetadataArrayView values = property.get(i); + PropertyArrayView values = property.get(i); for (int64_t j = 0; j < values.size(); ++j) { std::string_view v = values[j]; REQUIRE(v == strings[expectedIdx]); @@ -930,13 +930,13 @@ TEST_CASE( REQUIRE(expectedIdx == stringCount); } -TEST_CASE("Check StructuralMetadata fixed-length boolean array property") { +TEST_CASE("Check fixed-length boolean array property") { std::vector buffer{ static_cast(0b10101111), static_cast(0b11111010), static_cast(0b11100111)}; - PropertyTablePropertyView> property( + PropertyTablePropertyView> property( PropertyTablePropertyViewStatus::Valid, gsl::span(buffer.data(), buffer.size()), gsl::span(), @@ -949,7 +949,7 @@ TEST_CASE("Check StructuralMetadata fixed-length boolean array property") { REQUIRE(property.size() == 2); - MetadataArrayView val0 = property.get(0); + PropertyArrayView val0 = property.get(0); REQUIRE(val0.size() == 12); REQUIRE(static_cast(val0[0]) == 1); REQUIRE(static_cast(val0[1]) == 1); @@ -964,7 +964,7 @@ TEST_CASE("Check StructuralMetadata fixed-length boolean array property") { REQUIRE(static_cast(val0[10]) == 0); REQUIRE(static_cast(val0[11]) == 1); - MetadataArrayView val1 = property.get(1); + PropertyArrayView val1 = property.get(1); REQUIRE(static_cast(val1[0]) == 1); REQUIRE(static_cast(val1[1]) == 1); REQUIRE(static_cast(val1[2]) == 1); @@ -979,7 +979,7 @@ TEST_CASE("Check StructuralMetadata fixed-length boolean array property") { REQUIRE(static_cast(val1[11]) == 1); } -TEST_CASE("Check StructuralMetadata variable-length boolean array property") { +TEST_CASE("Check variable-length boolean array property") { std::vector buffer{ static_cast(0b10101111), static_cast(0b11111010), @@ -988,7 +988,7 @@ TEST_CASE("Check StructuralMetadata variable-length boolean array property") { std::vector offsetBuffer{0, 3, 12, 28}; - PropertyTablePropertyView> property( + PropertyTablePropertyView> property( PropertyTablePropertyViewStatus::Valid, gsl::span(buffer.data(), buffer.size()), gsl::span( @@ -1003,13 +1003,13 @@ TEST_CASE("Check StructuralMetadata variable-length boolean array property") { REQUIRE(property.size() == 3); - MetadataArrayView val0 = property.get(0); + PropertyArrayView val0 = property.get(0); REQUIRE(val0.size() == 3); REQUIRE(static_cast(val0[0]) == 1); REQUIRE(static_cast(val0[1]) == 1); REQUIRE(static_cast(val0[2]) == 1); - MetadataArrayView val1 = property.get(1); + PropertyArrayView val1 = property.get(1); REQUIRE(val1.size() == 9); REQUIRE(static_cast(val1[0]) == 1); REQUIRE(static_cast(val1[1]) == 0); @@ -1021,7 +1021,7 @@ TEST_CASE("Check StructuralMetadata variable-length boolean array property") { REQUIRE(static_cast(val1[7]) == 0); REQUIRE(static_cast(val1[8]) == 1); - MetadataArrayView val2 = property.get(2); + PropertyArrayView val2 = property.get(2); REQUIRE(val2.size() == 16); REQUIRE(static_cast(val2[0]) == 1); REQUIRE(static_cast(val2[1]) == 1); diff --git a/CesiumGltf/test/TestStructuralMetadataPropertyTableView.cpp b/CesiumGltf/test/TestPropertyTableView.cpp similarity index 91% rename from CesiumGltf/test/TestStructuralMetadataPropertyTableView.cpp rename to CesiumGltf/test/TestPropertyTableView.cpp index 952f6d4cd..cfadc098b 100644 --- a/CesiumGltf/test/TestStructuralMetadataPropertyTableView.cpp +++ b/CesiumGltf/test/TestPropertyTableView.cpp @@ -1,11 +1,10 @@ -#include "CesiumGltf/StructuralMetadataPropertyTableView.h" +#include "CesiumGltf/PropertyTableView.h" #include #include using namespace CesiumGltf; -using namespace CesiumGltf::StructuralMetadata; TEST_CASE("Test PropertyTableView on model without EXT_structural_metadata " "extension") { @@ -87,7 +86,7 @@ TEST_CASE("Test property table with nonexistent class") { REQUIRE(!classProperty); } -TEST_CASE("Test StructuralMetadata scalar property") { +TEST_CASE("Test scalar property") { Model model; std::vector values = {12, 34, 30, 11, 34, 34, 11, 33, 122, 33}; @@ -210,8 +209,8 @@ TEST_CASE("Test StructuralMetadata scalar property") { } SECTION("Access incorrectly as array") { - PropertyTablePropertyView> uint32ArrayInvalid = - view.getPropertyView>("TestClassProperty"); + PropertyTablePropertyView> uint32ArrayInvalid = + view.getPropertyView>("TestClassProperty"); REQUIRE( uint32ArrayInvalid.status() == PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); @@ -265,7 +264,7 @@ TEST_CASE("Test StructuralMetadata scalar property") { } } -TEST_CASE("Test StructuralMetadata vecN property") { +TEST_CASE("Test vecN property") { Model model; std::vector values = { @@ -398,8 +397,8 @@ TEST_CASE("Test StructuralMetadata vecN property") { } SECTION("Access incorrectly as array") { - PropertyTablePropertyView> ivec3ArrayInvalid = - view.getPropertyView>( + PropertyTablePropertyView> ivec3ArrayInvalid = + view.getPropertyView>( "TestClassProperty"); REQUIRE( ivec3ArrayInvalid.status() == @@ -454,7 +453,7 @@ TEST_CASE("Test StructuralMetadata vecN property") { } } -TEST_CASE("Test StructuralMetadata matN property") { +TEST_CASE("Test matN property") { Model model; // clang-format off @@ -598,9 +597,9 @@ TEST_CASE("Test StructuralMetadata matN property") { } SECTION("Access incorrectly as array") { - PropertyTablePropertyView> + PropertyTablePropertyView> u32mat2x2ArrayInvalid = - view.getPropertyView>( + view.getPropertyView>( "TestClassProperty"); REQUIRE( u32mat2x2ArrayInvalid.status() == @@ -657,7 +656,7 @@ TEST_CASE("Test StructuralMetadata matN property") { } } -TEST_CASE("Test StructuralMetadata boolean property") { +TEST_CASE("Test boolean property") { Model model; int64_t instanceCount = 21; @@ -752,7 +751,7 @@ TEST_CASE("Test StructuralMetadata boolean property") { } } -TEST_CASE("Test StructuralMetadata string property") { +TEST_CASE("Test string property") { Model model; std::vector expected{"What's up", "Test_0", "Test_1", "", "Hi"}; @@ -855,9 +854,9 @@ TEST_CASE("Test StructuralMetadata string property") { } SECTION("Wrong array type") { - PropertyTablePropertyView> + PropertyTablePropertyView> stringArrayInvalid = - view.getPropertyView>( + view.getPropertyView>( "TestClassProperty"); REQUIRE( stringArrayInvalid.status() == @@ -928,7 +927,7 @@ TEST_CASE("Test StructuralMetadata string property") { } } -TEST_CASE("Test StructuralMetadata fixed-length scalar array") { +TEST_CASE("Test fixed-length scalar array") { Model model; std::vector values = @@ -994,12 +993,12 @@ TEST_CASE("Test StructuralMetadata fixed-length scalar array") { REQUIRE(classProperty->count == 3); SECTION("Access the right type") { - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>("TestClassProperty"); + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>("TestClassProperty"); REQUIRE(arrayProperty.status() == PropertyTablePropertyViewStatus::Valid); for (int64_t i = 0; i < arrayProperty.size(); ++i) { - MetadataArrayView member = arrayProperty.get(i); + PropertyArrayView member = arrayProperty.get(i); for (int64_t j = 0; j < member.size(); ++j) { REQUIRE(member[j] == values[static_cast(i * 3 + j)]); } @@ -1007,14 +1006,14 @@ TEST_CASE("Test StructuralMetadata fixed-length scalar array") { } SECTION("Wrong type") { - PropertyTablePropertyView> boolArrayInvalid = - view.getPropertyView>("TestClassProperty"); + PropertyTablePropertyView> boolArrayInvalid = + view.getPropertyView>("TestClassProperty"); REQUIRE( boolArrayInvalid.status() == PropertyTablePropertyViewStatus::ErrorTypeMismatch); - PropertyTablePropertyView> uvec2ArrayInvalid = - view.getPropertyView>( + PropertyTablePropertyView> uvec2ArrayInvalid = + view.getPropertyView>( "TestClassProperty"); REQUIRE( uvec2ArrayInvalid.status() == @@ -1022,8 +1021,8 @@ TEST_CASE("Test StructuralMetadata fixed-length scalar array") { } SECTION("Wrong component type") { - PropertyTablePropertyView> int32ArrayInvalid = - view.getPropertyView>("TestClassProperty"); + PropertyTablePropertyView> int32ArrayInvalid = + view.getPropertyView>("TestClassProperty"); REQUIRE( int32ArrayInvalid.status() == PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); @@ -1039,8 +1038,8 @@ TEST_CASE("Test StructuralMetadata fixed-length scalar array") { SECTION("Buffer size is not a multiple of type size") { model.bufferViews[valueBufferViewIndex].byteLength = 13; - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>("TestClassProperty"); + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>("TestClassProperty"); REQUIRE( arrayProperty.status() == PropertyTablePropertyViewStatus:: @@ -1049,8 +1048,8 @@ TEST_CASE("Test StructuralMetadata fixed-length scalar array") { SECTION("Negative component count") { testClassProperty.count = -1; - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>("TestClassProperty"); + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>("TestClassProperty"); REQUIRE( arrayProperty.status() == PropertyTablePropertyViewStatus:: ErrorArrayCountAndOffsetBufferDontExist); @@ -1058,8 +1057,8 @@ TEST_CASE("Test StructuralMetadata fixed-length scalar array") { SECTION("Value buffer doesn't fit into property table count") { testClassProperty.count = 55; - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>("TestClassProperty"); + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>("TestClassProperty"); REQUIRE( arrayProperty.status() == PropertyTablePropertyViewStatus:: @@ -1067,7 +1066,7 @@ TEST_CASE("Test StructuralMetadata fixed-length scalar array") { } } -TEST_CASE("Test Structural Metadata variable-length scalar array") { +TEST_CASE("Test variable-length scalar array") { Model model; std::vector> expected{ @@ -1169,11 +1168,11 @@ TEST_CASE("Test Structural Metadata variable-length scalar array") { REQUIRE(!classProperty->count); SECTION("Access the correct type") { - PropertyTablePropertyView> property = - view.getPropertyView>("TestClassProperty"); + PropertyTablePropertyView> property = + view.getPropertyView>("TestClassProperty"); REQUIRE(property.status() == PropertyTablePropertyViewStatus::Valid); for (size_t i = 0; i < expected.size(); ++i) { - MetadataArrayView valueMember = + PropertyArrayView valueMember = property.get(static_cast(i)); REQUIRE(valueMember.size() == static_cast(expected[i].size())); for (size_t j = 0; j < expected[i].size(); ++j) { @@ -1185,8 +1184,8 @@ TEST_CASE("Test Structural Metadata variable-length scalar array") { propertyTableProperty.arrayOffsetType = ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: UINT8; - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>("TestClassProperty"); + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>("TestClassProperty"); REQUIRE( arrayProperty.status() == PropertyTablePropertyViewStatus:: @@ -1196,7 +1195,7 @@ TEST_CASE("Test Structural Metadata variable-length scalar array") { ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: UINT16; arrayProperty = - view.getPropertyView>("TestClassProperty"); + view.getPropertyView>("TestClassProperty"); REQUIRE( arrayProperty.status() == PropertyTablePropertyViewStatus:: @@ -1204,7 +1203,7 @@ TEST_CASE("Test Structural Metadata variable-length scalar array") { propertyTableProperty.arrayOffsetType = "NONSENSE"; arrayProperty = - view.getPropertyView>("TestClassProperty"); + view.getPropertyView>("TestClassProperty"); REQUIRE( arrayProperty.status() == PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); @@ -1214,7 +1213,7 @@ TEST_CASE("Test Structural Metadata variable-length scalar array") { ExtensionExtStructuralMetadataPropertyTableProperty::StringOffsetType:: UINT64; arrayProperty = - view.getPropertyView>("TestClassProperty"); + view.getPropertyView>("TestClassProperty"); REQUIRE( arrayProperty.status() == PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); @@ -1227,8 +1226,8 @@ TEST_CASE("Test Structural Metadata variable-length scalar array") { uint64_t* offset = reinterpret_cast( model.buffers[offsetBufferIndex].cesium.data.data()); offset[propertyTable.count] = 0; - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>("TestClassProperty"); + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>("TestClassProperty"); REQUIRE( arrayProperty.status() == PropertyTablePropertyViewStatus::ErrorArrayOffsetsNotSorted); @@ -1239,8 +1238,8 @@ TEST_CASE("Test Structural Metadata variable-length scalar array") { model.buffers[offsetBufferIndex].cesium.data.data()); offset[propertyTable.count] = static_cast(model.buffers[valueBufferIndex].byteLength + 4); - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>("TestClassProperty"); + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>("TestClassProperty"); REQUIRE( arrayProperty.status() == PropertyTablePropertyViewStatus::ErrorArrayOffsetOutOfBounds); @@ -1248,15 +1247,15 @@ TEST_CASE("Test Structural Metadata variable-length scalar array") { SECTION("Count and offset buffer are both present") { testClassProperty.count = 3; - PropertyTablePropertyView> property = - view.getPropertyView>("TestClassProperty"); + PropertyTablePropertyView> property = + view.getPropertyView>("TestClassProperty"); REQUIRE( property.status() == PropertyTablePropertyViewStatus::ErrorArrayCountAndOffsetBufferCoexist); } } -TEST_CASE("Test StructuralMetadata fixed-length vecN array") { +TEST_CASE("Test fixed-length vecN array") { Model model; std::vector values = { @@ -1328,13 +1327,13 @@ TEST_CASE("Test StructuralMetadata fixed-length vecN array") { REQUIRE(classProperty->count == 2); SECTION("Access the right type") { - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>( + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>( "TestClassProperty"); REQUIRE(arrayProperty.status() == PropertyTablePropertyViewStatus::Valid); for (int64_t i = 0; i < arrayProperty.size(); ++i) { - MetadataArrayView member = arrayProperty.get(i); + PropertyArrayView member = arrayProperty.get(i); for (int64_t j = 0; j < member.size(); ++j) { REQUIRE(member[j] == values[static_cast(i * 2 + j)]); } @@ -1342,14 +1341,14 @@ TEST_CASE("Test StructuralMetadata fixed-length vecN array") { } SECTION("Wrong type") { - PropertyTablePropertyView> int32ArrayInvalid = - view.getPropertyView>("TestClassProperty"); + PropertyTablePropertyView> int32ArrayInvalid = + view.getPropertyView>("TestClassProperty"); REQUIRE( int32ArrayInvalid.status() == PropertyTablePropertyViewStatus::ErrorTypeMismatch); - PropertyTablePropertyView> ivec2ArrayInvalid = - view.getPropertyView>( + PropertyTablePropertyView> ivec2ArrayInvalid = + view.getPropertyView>( "TestClassProperty"); REQUIRE( ivec2ArrayInvalid.status() == @@ -1357,8 +1356,8 @@ TEST_CASE("Test StructuralMetadata fixed-length vecN array") { } SECTION("Wrong component type") { - PropertyTablePropertyView> uvec3ArrayInvalid = - view.getPropertyView>( + PropertyTablePropertyView> uvec3ArrayInvalid = + view.getPropertyView>( "TestClassProperty"); REQUIRE( uvec3ArrayInvalid.status() == @@ -1375,8 +1374,8 @@ TEST_CASE("Test StructuralMetadata fixed-length vecN array") { SECTION("Buffer size is not a multiple of type size") { model.bufferViews[valueBufferViewIndex].byteLength = 13; - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>( + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>( "TestClassProperty"); REQUIRE( arrayProperty.status() == @@ -1386,8 +1385,8 @@ TEST_CASE("Test StructuralMetadata fixed-length vecN array") { SECTION("Negative component count") { testClassProperty.count = -1; - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>( + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>( "TestClassProperty"); REQUIRE( arrayProperty.status() == PropertyTablePropertyViewStatus:: @@ -1396,8 +1395,8 @@ TEST_CASE("Test StructuralMetadata fixed-length vecN array") { SECTION("Value buffer doesn't fit into property table count") { testClassProperty.count = 55; - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>( + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>( "TestClassProperty"); REQUIRE( arrayProperty.status() == @@ -1406,7 +1405,7 @@ TEST_CASE("Test StructuralMetadata fixed-length vecN array") { } } -TEST_CASE("Test Structural Metadata variable-length vecN array") { +TEST_CASE("Test variable-length vecN array") { Model model; // clang-format off @@ -1510,12 +1509,12 @@ TEST_CASE("Test Structural Metadata variable-length vecN array") { REQUIRE(!classProperty->count); SECTION("Access the correct type") { - PropertyTablePropertyView> property = - view.getPropertyView>( + PropertyTablePropertyView> property = + view.getPropertyView>( "TestClassProperty"); REQUIRE(property.status() == PropertyTablePropertyViewStatus::Valid); for (size_t i = 0; i < expected.size(); ++i) { - MetadataArrayView valueMember = + PropertyArrayView valueMember = property.get(static_cast(i)); REQUIRE(valueMember.size() == static_cast(expected[i].size())); for (size_t j = 0; j < expected[i].size(); ++j) { @@ -1528,8 +1527,8 @@ TEST_CASE("Test Structural Metadata variable-length vecN array") { propertyTableProperty.arrayOffsetType = ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: UINT8; - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>( + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>( "TestClassProperty"); REQUIRE( arrayProperty.status() == @@ -1539,7 +1538,7 @@ TEST_CASE("Test Structural Metadata variable-length vecN array") { propertyTableProperty.arrayOffsetType = ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: UINT16; - arrayProperty = view.getPropertyView>( + arrayProperty = view.getPropertyView>( "TestClassProperty"); REQUIRE( arrayProperty.status() == @@ -1547,7 +1546,7 @@ TEST_CASE("Test Structural Metadata variable-length vecN array") { ErrorBufferViewSizeDoesNotMatchPropertyTableCount); propertyTableProperty.arrayOffsetType = "NONSENSE"; - arrayProperty = view.getPropertyView>( + arrayProperty = view.getPropertyView>( "TestClassProperty"); REQUIRE( arrayProperty.status() == @@ -1557,7 +1556,7 @@ TEST_CASE("Test Structural Metadata variable-length vecN array") { propertyTableProperty.stringOffsetType = ExtensionExtStructuralMetadataPropertyTableProperty::StringOffsetType:: UINT64; - arrayProperty = view.getPropertyView>( + arrayProperty = view.getPropertyView>( "TestClassProperty"); REQUIRE( arrayProperty.status() == @@ -1571,8 +1570,8 @@ TEST_CASE("Test Structural Metadata variable-length vecN array") { uint64_t* offset = reinterpret_cast( model.buffers[offsetBufferIndex].cesium.data.data()); offset[propertyTable.count] = 0; - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>( + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>( "TestClassProperty"); REQUIRE( arrayProperty.status() == @@ -1584,8 +1583,8 @@ TEST_CASE("Test Structural Metadata variable-length vecN array") { model.buffers[offsetBufferIndex].cesium.data.data()); offset[propertyTable.count] = static_cast(model.buffers[valueBufferIndex].byteLength + 4); - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>( + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>( "TestClassProperty"); REQUIRE( arrayProperty.status() == @@ -1594,8 +1593,8 @@ TEST_CASE("Test Structural Metadata variable-length vecN array") { SECTION("Count and offset buffer are both present") { testClassProperty.count = 3; - PropertyTablePropertyView> property = - view.getPropertyView>( + PropertyTablePropertyView> property = + view.getPropertyView>( "TestClassProperty"); REQUIRE( property.status() == @@ -1603,7 +1602,7 @@ TEST_CASE("Test Structural Metadata variable-length vecN array") { } } -TEST_CASE("Test StructuralMetadata fixed-length matN array") { +TEST_CASE("Test fixed-length matN array") { Model model; // clang-format off @@ -1689,13 +1688,13 @@ TEST_CASE("Test StructuralMetadata fixed-length matN array") { REQUIRE(classProperty->count == 2); SECTION("Access the right type") { - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>( + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>( "TestClassProperty"); REQUIRE(arrayProperty.status() == PropertyTablePropertyViewStatus::Valid); for (int64_t i = 0; i < arrayProperty.size(); ++i) { - MetadataArrayView member = arrayProperty.get(i); + PropertyArrayView member = arrayProperty.get(i); for (int64_t j = 0; j < member.size(); ++j) { REQUIRE(member[j] == values[static_cast(i * 2 + j)]); } @@ -1703,14 +1702,14 @@ TEST_CASE("Test StructuralMetadata fixed-length matN array") { } SECTION("Wrong type") { - PropertyTablePropertyView> int32ArrayInvalid = - view.getPropertyView>("TestClassProperty"); + PropertyTablePropertyView> int32ArrayInvalid = + view.getPropertyView>("TestClassProperty"); REQUIRE( int32ArrayInvalid.status() == PropertyTablePropertyViewStatus::ErrorTypeMismatch); - PropertyTablePropertyView> ivec2ArrayInvalid = - view.getPropertyView>( + PropertyTablePropertyView> ivec2ArrayInvalid = + view.getPropertyView>( "TestClassProperty"); REQUIRE( ivec2ArrayInvalid.status() == @@ -1718,9 +1717,9 @@ TEST_CASE("Test StructuralMetadata fixed-length matN array") { } SECTION("Wrong component type") { - PropertyTablePropertyView> + PropertyTablePropertyView> u32mat2x2ArrayInvalid = - view.getPropertyView>( + view.getPropertyView>( "TestClassProperty"); REQUIRE( u32mat2x2ArrayInvalid.status() == @@ -1737,8 +1736,8 @@ TEST_CASE("Test StructuralMetadata fixed-length matN array") { SECTION("Buffer size is not a multiple of type size") { model.bufferViews[valueBufferViewIndex].byteLength = 13; - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>( + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>( "TestClassProperty"); REQUIRE( arrayProperty.status() == @@ -1748,8 +1747,8 @@ TEST_CASE("Test StructuralMetadata fixed-length matN array") { SECTION("Negative component count") { testClassProperty.count = -1; - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>( + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>( "TestClassProperty"); REQUIRE( arrayProperty.status() == PropertyTablePropertyViewStatus:: @@ -1758,8 +1757,8 @@ TEST_CASE("Test StructuralMetadata fixed-length matN array") { SECTION("Value buffer doesn't fit into property table count") { testClassProperty.count = 55; - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>( + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>( "TestClassProperty"); REQUIRE( arrayProperty.status() == @@ -1768,7 +1767,7 @@ TEST_CASE("Test StructuralMetadata fixed-length matN array") { } } -TEST_CASE("Test Structural Metadata variable-length matN array") { +TEST_CASE("Test variable-length matN array") { Model model; // clang-format off @@ -1892,12 +1891,12 @@ TEST_CASE("Test Structural Metadata variable-length matN array") { REQUIRE(!classProperty->count); SECTION("Access the correct type") { - PropertyTablePropertyView> property = - view.getPropertyView>( + PropertyTablePropertyView> property = + view.getPropertyView>( "TestClassProperty"); REQUIRE(property.status() == PropertyTablePropertyViewStatus::Valid); for (size_t i = 0; i < expected.size(); ++i) { - MetadataArrayView valueMember = + PropertyArrayView valueMember = property.get(static_cast(i)); REQUIRE(valueMember.size() == static_cast(expected[i].size())); for (size_t j = 0; j < expected[i].size(); ++j) { @@ -1910,8 +1909,8 @@ TEST_CASE("Test Structural Metadata variable-length matN array") { propertyTableProperty.arrayOffsetType = ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: UINT8; - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>( + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>( "TestClassProperty"); REQUIRE( arrayProperty.status() == @@ -1921,7 +1920,7 @@ TEST_CASE("Test Structural Metadata variable-length matN array") { propertyTableProperty.arrayOffsetType = ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: UINT16; - arrayProperty = view.getPropertyView>( + arrayProperty = view.getPropertyView>( "TestClassProperty"); REQUIRE( arrayProperty.status() == @@ -1929,7 +1928,7 @@ TEST_CASE("Test Structural Metadata variable-length matN array") { ErrorBufferViewSizeDoesNotMatchPropertyTableCount); propertyTableProperty.arrayOffsetType = "NONSENSE"; - arrayProperty = view.getPropertyView>( + arrayProperty = view.getPropertyView>( "TestClassProperty"); REQUIRE( arrayProperty.status() == @@ -1939,7 +1938,7 @@ TEST_CASE("Test Structural Metadata variable-length matN array") { propertyTableProperty.stringOffsetType = ExtensionExtStructuralMetadataPropertyTableProperty::StringOffsetType:: UINT64; - arrayProperty = view.getPropertyView>( + arrayProperty = view.getPropertyView>( "TestClassProperty"); REQUIRE( arrayProperty.status() == @@ -1953,8 +1952,8 @@ TEST_CASE("Test Structural Metadata variable-length matN array") { uint64_t* offset = reinterpret_cast( model.buffers[offsetBufferIndex].cesium.data.data()); offset[propertyTable.count] = 0; - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>( + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>( "TestClassProperty"); REQUIRE( arrayProperty.status() == @@ -1966,8 +1965,8 @@ TEST_CASE("Test Structural Metadata variable-length matN array") { model.buffers[offsetBufferIndex].cesium.data.data()); offset[propertyTable.count] = static_cast(model.buffers[valueBufferIndex].byteLength + 4); - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>( + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>( "TestClassProperty"); REQUIRE( arrayProperty.status() == @@ -1976,8 +1975,8 @@ TEST_CASE("Test Structural Metadata variable-length matN array") { SECTION("Count and offset buffer are both present") { testClassProperty.count = 3; - PropertyTablePropertyView> property = - view.getPropertyView>( + PropertyTablePropertyView> property = + view.getPropertyView>( "TestClassProperty"); REQUIRE( property.status() == @@ -1985,7 +1984,7 @@ TEST_CASE("Test Structural Metadata variable-length matN array") { } } -TEST_CASE("Test StructuralMetadata fixed-length boolean array") { +TEST_CASE("Test fixed-length boolean array") { Model model; std::vector expected = { @@ -2066,14 +2065,14 @@ TEST_CASE("Test StructuralMetadata fixed-length boolean array") { REQUIRE(classProperty->count == 3); SECTION("Access correct type") { - PropertyTablePropertyView> boolArrayProperty = - view.getPropertyView>("TestClassProperty"); + PropertyTablePropertyView> boolArrayProperty = + view.getPropertyView>("TestClassProperty"); REQUIRE( boolArrayProperty.status() == PropertyTablePropertyViewStatus::Valid); REQUIRE(boolArrayProperty.size() == propertyTable.count); REQUIRE(boolArrayProperty.size() > 0); for (int64_t i = 0; i < boolArrayProperty.size(); ++i) { - MetadataArrayView valueMember = boolArrayProperty.get(i); + PropertyArrayView valueMember = boolArrayProperty.get(i); for (int64_t j = 0; j < valueMember.size(); ++j) { REQUIRE(valueMember[j] == expected[static_cast(i * 3 + j)]); } @@ -2081,8 +2080,8 @@ TEST_CASE("Test StructuralMetadata fixed-length boolean array") { } SECTION("Wrong type") { - PropertyTablePropertyView> uint8ArrayInvalid = - view.getPropertyView>("TestClassProperty"); + PropertyTablePropertyView> uint8ArrayInvalid = + view.getPropertyView>("TestClassProperty"); REQUIRE( uint8ArrayInvalid.status() == PropertyTablePropertyViewStatus::ErrorTypeMismatch); @@ -2098,8 +2097,8 @@ TEST_CASE("Test StructuralMetadata fixed-length boolean array") { SECTION("Value buffer doesn't have enough required bytes") { testClassProperty.count = 11; - PropertyTablePropertyView> boolArrayProperty = - view.getPropertyView>("TestClassProperty"); + PropertyTablePropertyView> boolArrayProperty = + view.getPropertyView>("TestClassProperty"); REQUIRE( boolArrayProperty.status() == PropertyTablePropertyViewStatus:: @@ -2108,8 +2107,8 @@ TEST_CASE("Test StructuralMetadata fixed-length boolean array") { SECTION("Count is negative") { testClassProperty.count = -1; - PropertyTablePropertyView> boolArrayProperty = - view.getPropertyView>("TestClassProperty"); + PropertyTablePropertyView> boolArrayProperty = + view.getPropertyView>("TestClassProperty"); REQUIRE( boolArrayProperty.status() == PropertyTablePropertyViewStatus:: @@ -2117,7 +2116,7 @@ TEST_CASE("Test StructuralMetadata fixed-length boolean array") { } } -TEST_CASE("Test StructuralMetadata variable-length boolean array") { +TEST_CASE("Test variable-length boolean array") { Model model; std::vector> expected{ @@ -2221,12 +2220,12 @@ TEST_CASE("Test StructuralMetadata variable-length boolean array") { REQUIRE(!classProperty->count); SECTION("Access correct type") { - PropertyTablePropertyView> boolArrayProperty = - view.getPropertyView>("TestClassProperty"); + PropertyTablePropertyView> boolArrayProperty = + view.getPropertyView>("TestClassProperty"); REQUIRE( boolArrayProperty.status() == PropertyTablePropertyViewStatus::Valid); for (size_t i = 0; i < expected.size(); ++i) { - MetadataArrayView arrayMember = + PropertyArrayView arrayMember = boolArrayProperty.get(static_cast(i)); REQUIRE(arrayMember.size() == static_cast(expected[i].size())); for (size_t j = 0; j < expected[i].size(); ++j) { @@ -2239,8 +2238,8 @@ TEST_CASE("Test StructuralMetadata variable-length boolean array") { propertyTableProperty.arrayOffsetType = ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: UINT8; - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>("TestClassProperty"); + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>("TestClassProperty"); REQUIRE( arrayProperty.status() == PropertyTablePropertyViewStatus:: @@ -2250,7 +2249,7 @@ TEST_CASE("Test StructuralMetadata variable-length boolean array") { ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: UINT16; arrayProperty = - view.getPropertyView>("TestClassProperty"); + view.getPropertyView>("TestClassProperty"); REQUIRE( arrayProperty.status() == PropertyTablePropertyViewStatus:: @@ -2258,7 +2257,7 @@ TEST_CASE("Test StructuralMetadata variable-length boolean array") { propertyTableProperty.arrayOffsetType = "NONSENSE"; arrayProperty = - view.getPropertyView>("TestClassProperty"); + view.getPropertyView>("TestClassProperty"); REQUIRE( arrayProperty.status() == PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); @@ -2268,7 +2267,7 @@ TEST_CASE("Test StructuralMetadata variable-length boolean array") { ExtensionExtStructuralMetadataPropertyTableProperty::StringOffsetType:: UINT64; arrayProperty = - view.getPropertyView>("TestClassProperty"); + view.getPropertyView>("TestClassProperty"); REQUIRE( arrayProperty.status() == PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); @@ -2281,8 +2280,8 @@ TEST_CASE("Test StructuralMetadata variable-length boolean array") { uint64_t* offset = reinterpret_cast( model.buffers[offsetBufferIndex].cesium.data.data()); offset[propertyTable.count] = 0; - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>("TestClassProperty"); + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>("TestClassProperty"); REQUIRE( arrayProperty.status() == PropertyTablePropertyViewStatus::ErrorArrayOffsetsNotSorted); @@ -2293,8 +2292,8 @@ TEST_CASE("Test StructuralMetadata variable-length boolean array") { model.buffers[offsetBufferIndex].cesium.data.data()); offset[propertyTable.count] = static_cast( model.buffers[valueBufferIndex].byteLength * 8 + 20); - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>("TestClassProperty"); + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>("TestClassProperty"); REQUIRE( arrayProperty.status() == PropertyTablePropertyViewStatus::ErrorArrayOffsetOutOfBounds); @@ -2302,15 +2301,15 @@ TEST_CASE("Test StructuralMetadata variable-length boolean array") { SECTION("Count and offset buffer both present") { testClassProperty.count = 3; - PropertyTablePropertyView> boolArrayProperty = - view.getPropertyView>("TestClassProperty"); + PropertyTablePropertyView> boolArrayProperty = + view.getPropertyView>("TestClassProperty"); REQUIRE( boolArrayProperty.status() == PropertyTablePropertyViewStatus::ErrorArrayCountAndOffsetBufferCoexist); } } -TEST_CASE("Test StructuralMetadata fixed-length arrays of strings") { +TEST_CASE("Test fixed-length arrays of strings") { Model model; std::vector expected{ @@ -2406,24 +2405,24 @@ TEST_CASE("Test StructuralMetadata fixed-length arrays of strings") { REQUIRE(classProperty->count == 2); SECTION("Access correct type") { - PropertyTablePropertyView> + PropertyTablePropertyView> stringProperty = - view.getPropertyView>( + view.getPropertyView>( "TestClassProperty"); REQUIRE(stringProperty.status() == PropertyTablePropertyViewStatus::Valid); REQUIRE(stringProperty.size() == 3); - MetadataArrayView v0 = stringProperty.get(0); + PropertyArrayView v0 = stringProperty.get(0); REQUIRE(v0.size() == 2); REQUIRE(v0[0] == "What's up"); REQUIRE(v0[1] == "Breaking news!!! Aliens no longer attacks the US first"); - MetadataArrayView v1 = stringProperty.get(1); + PropertyArrayView v1 = stringProperty.get(1); REQUIRE(v1.size() == 2); REQUIRE(v1[0] == "But they still abduct my cows! Those milk thiefs! 👽 🐮"); REQUIRE(v1[1] == "I'm not crazy. My mother had me tested 🤪"); - MetadataArrayView v2 = stringProperty.get(2); + PropertyArrayView v2 = stringProperty.get(2); REQUIRE(v2.size() == 2); REQUIRE(v2[0] == "I love you, meat bags! ❤️"); REQUIRE(v2[1] == "Book in the freezer"); @@ -2439,9 +2438,9 @@ TEST_CASE("Test StructuralMetadata fixed-length arrays of strings") { SECTION("Count is negative") { testClassProperty.count = -1; - PropertyTablePropertyView> + PropertyTablePropertyView> stringProperty = - view.getPropertyView>( + view.getPropertyView>( "TestClassProperty"); REQUIRE( stringProperty.status() == PropertyTablePropertyViewStatus:: @@ -2450,9 +2449,9 @@ TEST_CASE("Test StructuralMetadata fixed-length arrays of strings") { SECTION("Offset type is unknown") { propertyTableProperty.stringOffsetType = "NONSENSE"; - PropertyTablePropertyView> + PropertyTablePropertyView> stringProperty = - view.getPropertyView>( + view.getPropertyView>( "TestClassProperty"); REQUIRE( stringProperty.status() == @@ -2462,7 +2461,7 @@ TEST_CASE("Test StructuralMetadata fixed-length arrays of strings") { propertyTableProperty.arrayOffsetType = ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: UINT32; - stringProperty = view.getPropertyView>( + stringProperty = view.getPropertyView>( "TestClassProperty"); REQUIRE( stringProperty.status() == @@ -2471,9 +2470,9 @@ TEST_CASE("Test StructuralMetadata fixed-length arrays of strings") { SECTION("String offsets don't exist") { propertyTableProperty.stringOffsets = -1; - PropertyTablePropertyView> + PropertyTablePropertyView> stringProperty = - view.getPropertyView>( + view.getPropertyView>( "TestClassProperty"); REQUIRE( stringProperty.status() == @@ -2481,7 +2480,7 @@ TEST_CASE("Test StructuralMetadata fixed-length arrays of strings") { } } -TEST_CASE("Test StructuralMetadata variable-length arrays of strings") { +TEST_CASE("Test variable-length arrays of strings") { Model model; std::vector> expected{ @@ -2616,13 +2615,13 @@ TEST_CASE("Test StructuralMetadata variable-length arrays of strings") { REQUIRE(!classProperty->count); SECTION("Access correct type") { - PropertyTablePropertyView> + PropertyTablePropertyView> stringProperty = - view.getPropertyView>( + view.getPropertyView>( "TestClassProperty"); REQUIRE(stringProperty.status() == PropertyTablePropertyViewStatus::Valid); for (size_t i = 0; i < expected.size(); ++i) { - MetadataArrayView stringArray = + PropertyArrayView stringArray = stringProperty.get(static_cast(i)); for (size_t j = 0; j < expected[i].size(); ++j) { REQUIRE(stringArray[static_cast(j)] == expected[i][j]); @@ -2634,9 +2633,9 @@ TEST_CASE("Test StructuralMetadata variable-length arrays of strings") { propertyTableProperty.arrayOffsetType = ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: UINT8; - PropertyTablePropertyView> + PropertyTablePropertyView> arrayProperty = - view.getPropertyView>( + view.getPropertyView>( "TestClassProperty"); REQUIRE( arrayProperty.status() == @@ -2646,7 +2645,7 @@ TEST_CASE("Test StructuralMetadata variable-length arrays of strings") { propertyTableProperty.arrayOffsetType = ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: UINT16; - arrayProperty = view.getPropertyView>( + arrayProperty = view.getPropertyView>( "TestClassProperty"); REQUIRE( arrayProperty.status() == @@ -2654,7 +2653,7 @@ TEST_CASE("Test StructuralMetadata variable-length arrays of strings") { ErrorBufferViewSizeDoesNotMatchPropertyTableCount); propertyTableProperty.arrayOffsetType = "NONSENSE"; - arrayProperty = view.getPropertyView>( + arrayProperty = view.getPropertyView>( "TestClassProperty"); REQUIRE( arrayProperty.status() == @@ -2668,9 +2667,9 @@ TEST_CASE("Test StructuralMetadata variable-length arrays of strings") { propertyTableProperty.stringOffsetType = ExtensionExtStructuralMetadataPropertyTableProperty::StringOffsetType:: UINT8; - PropertyTablePropertyView> + PropertyTablePropertyView> arrayProperty = - view.getPropertyView>( + view.getPropertyView>( "TestClassProperty"); REQUIRE( arrayProperty.status() == @@ -2680,7 +2679,7 @@ TEST_CASE("Test StructuralMetadata variable-length arrays of strings") { propertyTableProperty.stringOffsetType = ExtensionExtStructuralMetadataPropertyTableProperty::StringOffsetType:: UINT16; - arrayProperty = view.getPropertyView>( + arrayProperty = view.getPropertyView>( "TestClassProperty"); REQUIRE( arrayProperty.status() == @@ -2688,7 +2687,7 @@ TEST_CASE("Test StructuralMetadata variable-length arrays of strings") { ErrorBufferViewSizeDoesNotMatchPropertyTableCount); propertyTableProperty.stringOffsetType = "NONSENSE"; - arrayProperty = view.getPropertyView>( + arrayProperty = view.getPropertyView>( "TestClassProperty"); REQUIRE( arrayProperty.status() == @@ -2702,9 +2701,9 @@ TEST_CASE("Test StructuralMetadata variable-length arrays of strings") { uint32_t* offset = reinterpret_cast( model.buffers[arrayOffsetBuffer].cesium.data.data()); offset[0] = static_cast(1000); - PropertyTablePropertyView> + PropertyTablePropertyView> arrayProperty = - view.getPropertyView>( + view.getPropertyView>( "TestClassProperty"); REQUIRE( arrayProperty.status() == @@ -2716,9 +2715,9 @@ TEST_CASE("Test StructuralMetadata variable-length arrays of strings") { uint32_t* offset = reinterpret_cast( model.buffers[stringOffsetBuffer].cesium.data.data()); offset[0] = static_cast(1000); - PropertyTablePropertyView> + PropertyTablePropertyView> arrayProperty = - view.getPropertyView>( + view.getPropertyView>( "TestClassProperty"); REQUIRE( arrayProperty.status() == @@ -2731,9 +2730,9 @@ TEST_CASE("Test StructuralMetadata variable-length arrays of strings") { model.buffers[arrayOffsetBuffer].cesium.data.data()); uint32_t previousValue = offset[propertyTable.count]; offset[propertyTable.count] = static_cast(100000); - PropertyTablePropertyView> + PropertyTablePropertyView> arrayProperty = - view.getPropertyView>( + view.getPropertyView>( "TestClassProperty"); REQUIRE( arrayProperty.status() == @@ -2746,9 +2745,9 @@ TEST_CASE("Test StructuralMetadata variable-length arrays of strings") { model.buffers[stringOffsetBuffer].cesium.data.data()); uint32_t previousValue = offset[6]; offset[6] = static_cast(100000); - PropertyTablePropertyView> + PropertyTablePropertyView> arrayProperty = - view.getPropertyView>( + view.getPropertyView>( "TestClassProperty"); REQUIRE( arrayProperty.status() == @@ -2758,9 +2757,9 @@ TEST_CASE("Test StructuralMetadata variable-length arrays of strings") { SECTION("Count and offset buffer both present") { testClassProperty.count = 3; - PropertyTablePropertyView> + PropertyTablePropertyView> boolArrayProperty = - view.getPropertyView>( + view.getPropertyView>( "TestClassProperty"); REQUIRE( boolArrayProperty.status() == @@ -2768,7 +2767,7 @@ TEST_CASE("Test StructuralMetadata variable-length arrays of strings") { } } -TEST_CASE("Test StructuralMetadata callback on invalid property table view") { +TEST_CASE("Test callback on invalid property table view") { Model model; ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); @@ -2808,7 +2807,7 @@ TEST_CASE("Test StructuralMetadata callback on invalid property table view") { REQUIRE(invokedCallbackCount == 1); } -TEST_CASE("Test StructuralMetadata callback for invalid property") { +TEST_CASE("Test callback for invalid property") { Model model; ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); @@ -2857,7 +2856,7 @@ TEST_CASE("Test StructuralMetadata callback for invalid property") { REQUIRE(invokedCallbackCount == 2); } -TEST_CASE("Test StructuralMetadata callback for scalar property") { +TEST_CASE("Test callback for scalar property") { Model model; std::vector values = {12, 34, 30, 11, 34, 34, 11, 33, 122, 33}; @@ -2948,7 +2947,7 @@ TEST_CASE("Test StructuralMetadata callback for scalar property") { REQUIRE(invokedCallbackCount == 1); } -TEST_CASE("Test StructuralMetadata callback for vecN property") { +TEST_CASE("Test callback for vecN property") { Model model; std::vector values = { @@ -3044,7 +3043,7 @@ TEST_CASE("Test StructuralMetadata callback for vecN property") { REQUIRE(invokedCallbackCount == 1); } -TEST_CASE("Test StructuralMetadata callback for matN property") { +TEST_CASE("Test callback for matN property") { Model model; // clang-format off @@ -3150,7 +3149,7 @@ TEST_CASE("Test StructuralMetadata callback for matN property") { REQUIRE(invokedCallbackCount == 1); } -TEST_CASE("Test StructuralMetadata callback for boolean property") { +TEST_CASE("Test callback for boolean property") { Model model; int64_t instanceCount = 21; @@ -3253,7 +3252,7 @@ TEST_CASE("Test StructuralMetadata callback for boolean property") { REQUIRE(invokedCallbackCount == 1); } -TEST_CASE("Test StructuralMetadata callback for string property") { +TEST_CASE("Test callback for string property") { Model model; std::vector expected{"What's up", "Test_0", "Test_1", "", "Hi"}; @@ -3375,7 +3374,7 @@ TEST_CASE("Test StructuralMetadata callback for string property") { REQUIRE(invokedCallbackCount == 1); } -TEST_CASE("Test StructuralMetadata callback for scalar array") { +TEST_CASE("Test callback for scalar array") { Model model; std::vector values = @@ -3451,10 +3450,10 @@ TEST_CASE("Test StructuralMetadata callback for scalar array") { if constexpr (std::is_same_v< PropertyTablePropertyView< - MetadataArrayView>, + PropertyArrayView>, decltype(propertyValue)>) { for (int64_t i = 0; i < propertyValue.size(); ++i) { - MetadataArrayView member = propertyValue.get(i); + PropertyArrayView member = propertyValue.get(i); for (int64_t j = 0; j < member.size(); ++j) { REQUIRE(member[j] == values[static_cast(i * 3 + j)]); } @@ -3469,7 +3468,7 @@ TEST_CASE("Test StructuralMetadata callback for scalar array") { REQUIRE(invokedCallbackCount == 1); } -TEST_CASE("Test StructuralMetadata callback for vecN array") { +TEST_CASE("Test callback for vecN array") { Model model; std::vector values = { @@ -3551,10 +3550,10 @@ TEST_CASE("Test StructuralMetadata callback for vecN array") { if constexpr (std::is_same_v< PropertyTablePropertyView< - MetadataArrayView>, + PropertyArrayView>, decltype(propertyValue)>) { for (int64_t i = 0; i < propertyValue.size(); ++i) { - MetadataArrayView member = propertyValue.get(i); + PropertyArrayView member = propertyValue.get(i); for (int64_t j = 0; j < member.size(); ++j) { REQUIRE(member[j] == values[static_cast(i * 2 + j)]); } @@ -3569,7 +3568,7 @@ TEST_CASE("Test StructuralMetadata callback for vecN array") { REQUIRE(invokedCallbackCount == 1); } -TEST_CASE("Test StructuralMetadata callback for matN array") { +TEST_CASE("Test callback for matN array") { Model model; // clang-format off @@ -3665,10 +3664,10 @@ TEST_CASE("Test StructuralMetadata callback for matN array") { if constexpr (std::is_same_v< PropertyTablePropertyView< - MetadataArrayView>, + PropertyArrayView>, decltype(propertyValue)>) { for (int64_t i = 0; i < propertyValue.size(); ++i) { - MetadataArrayView member = propertyValue.get(i); + PropertyArrayView member = propertyValue.get(i); for (int64_t j = 0; j < member.size(); ++j) { REQUIRE(member[j] == values[static_cast(i * 2 + j)]); } @@ -3683,7 +3682,7 @@ TEST_CASE("Test StructuralMetadata callback for matN array") { REQUIRE(invokedCallbackCount == 1); } -TEST_CASE("Test StructuralMetadata callback for boolean array") { +TEST_CASE("Test callback for boolean array") { Model model; std::vector expected = { @@ -3775,10 +3774,10 @@ TEST_CASE("Test StructuralMetadata callback for boolean array") { REQUIRE(propertyValue.size() > 0); if constexpr (std::is_same_v< - PropertyTablePropertyView>, + PropertyTablePropertyView>, decltype(propertyValue)>) { for (int64_t i = 0; i < propertyValue.size(); ++i) { - MetadataArrayView member = propertyValue.get(i); + PropertyArrayView member = propertyValue.get(i); for (int64_t j = 0; j < member.size(); ++j) { REQUIRE(member[j] == expected[static_cast(i * 3 + j)]); } @@ -3793,7 +3792,7 @@ TEST_CASE("Test StructuralMetadata callback for boolean array") { REQUIRE(invokedCallbackCount == 1); } -TEST_CASE("Test StructuralMetadata callback for array of strings") { +TEST_CASE("Test callback for array of strings") { Model model; std::vector expected{ @@ -3901,22 +3900,22 @@ TEST_CASE("Test StructuralMetadata callback for array of strings") { if constexpr (std::is_same_v< PropertyTablePropertyView< - MetadataArrayView>, + PropertyArrayView>, decltype(propertyValue)>) { - MetadataArrayView v0 = propertyValue.get(0); + PropertyArrayView v0 = propertyValue.get(0); REQUIRE(v0.size() == 2); REQUIRE(v0[0] == "What's up"); REQUIRE( v0[1] == "Breaking news!!! Aliens no longer attacks the US first"); - MetadataArrayView v1 = propertyValue.get(1); + PropertyArrayView v1 = propertyValue.get(1); REQUIRE(v1.size() == 2); REQUIRE( v1[0] == "But they still abduct my cows! Those milk thiefs! 👽 🐮"); REQUIRE(v1[1] == "I'm not crazy. My mother had me tested 🤪"); - MetadataArrayView v2 = propertyValue.get(2); + PropertyArrayView v2 = propertyValue.get(2); REQUIRE(v2.size() == 2); REQUIRE(v2[0] == "I love you, meat bags! ❤️"); REQUIRE(v2[1] == "Book in the freezer"); diff --git a/CesiumGltf/test/TestStructuralMetadataPropertyTexturePropertyView.cpp b/CesiumGltf/test/TestPropertyTexturePropertyView.cpp similarity index 98% rename from CesiumGltf/test/TestStructuralMetadataPropertyTexturePropertyView.cpp rename to CesiumGltf/test/TestPropertyTexturePropertyView.cpp index e697ceafc..e6d96520e 100644 --- a/CesiumGltf/test/TestStructuralMetadataPropertyTexturePropertyView.cpp +++ b/CesiumGltf/test/TestPropertyTexturePropertyView.cpp @@ -1,4 +1,4 @@ -#include "CesiumGltf/StructuralMetadataPropertyTexturePropertyView.h" +#include "CesiumGltf/PropertyTexturePropertyView.h" #include #include @@ -10,7 +10,6 @@ #include using namespace CesiumGltf; -using namespace CesiumGltf::StructuralMetadata; TEST_CASE( "Test PropertyTexturePropertyView on property with invalid texture index") { diff --git a/CesiumGltf/test/TestStructuralMetadataPropertyTextureView.cpp b/CesiumGltf/test/TestPropertyTextureView.cpp similarity index 98% rename from CesiumGltf/test/TestStructuralMetadataPropertyTextureView.cpp rename to CesiumGltf/test/TestPropertyTextureView.cpp index 01cfed051..6ca05cad2 100644 --- a/CesiumGltf/test/TestStructuralMetadataPropertyTextureView.cpp +++ b/CesiumGltf/test/TestPropertyTextureView.cpp @@ -1,4 +1,4 @@ -#include "CesiumGltf/StructuralMetadataPropertyTextureView.h" +#include "CesiumGltf/PropertyTextureView.h" #include #include @@ -10,7 +10,6 @@ #include using namespace CesiumGltf; -using namespace CesiumGltf::StructuralMetadata; TEST_CASE("Test PropertyTextureView on model without EXT_structural_metadata " "extension") { diff --git a/CesiumGltf/test/TestPropertyType.cpp b/CesiumGltf/test/TestPropertyType.cpp index 4629a7736..9f345f6aa 100644 --- a/CesiumGltf/test/TestPropertyType.cpp +++ b/CesiumGltf/test/TestPropertyType.cpp @@ -1,5 +1,5 @@ -#include "CesiumGltf/ClassProperty.h" -#include "CesiumGltf/FeatureTableProperty.h" +#include "CesiumGltf/ExtensionExtStructuralMetadataClassProperty.h" +#include "CesiumGltf/ExtensionExtStructuralMetadataPropertyTableProperty.h" #include "CesiumGltf/PropertyType.h" #include @@ -9,132 +9,244 @@ using namespace CesiumGltf; TEST_CASE("Test PropertyType utilities function") { SECTION("Convert string to PropertyType") { REQUIRE( - convertStringToPropertyType(ClassProperty::Type::UINT8) == - PropertyType::Uint8); + convertStringToPropertyType( + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR) == + PropertyType::Scalar); REQUIRE( - convertStringToPropertyType(ClassProperty::Type::INT8) == - PropertyType::Int8); + convertStringToPropertyType( + ExtensionExtStructuralMetadataClassProperty::Type::VEC2) == + PropertyType::Vec2); REQUIRE( - convertStringToPropertyType(ClassProperty::Type::UINT16) == - PropertyType::Uint16); + convertStringToPropertyType( + ExtensionExtStructuralMetadataClassProperty::Type::VEC3) == + PropertyType::Vec3); REQUIRE( - convertStringToPropertyType(ClassProperty::Type::INT16) == - PropertyType::Int16); + convertStringToPropertyType( + ExtensionExtStructuralMetadataClassProperty::Type::VEC4) == + PropertyType::Vec4); REQUIRE( - convertStringToPropertyType(ClassProperty::Type::UINT32) == - PropertyType::Uint32); + convertStringToPropertyType( + ExtensionExtStructuralMetadataClassProperty::Type::MAT2) == + PropertyType::Mat2); REQUIRE( - convertStringToPropertyType(ClassProperty::Type::INT32) == - PropertyType::Int32); + convertStringToPropertyType( + ExtensionExtStructuralMetadataClassProperty::Type::MAT3) == + PropertyType::Mat3); REQUIRE( - convertStringToPropertyType(ClassProperty::Type::UINT64) == - PropertyType::Uint64); + convertStringToPropertyType( + ExtensionExtStructuralMetadataClassProperty::Type::MAT4) == + PropertyType::Mat4); REQUIRE( - convertStringToPropertyType(ClassProperty::Type::INT64) == - PropertyType::Int64); + convertStringToPropertyType( + ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN) == + PropertyType::Boolean); REQUIRE( - convertStringToPropertyType(ClassProperty::Type::FLOAT32) == - PropertyType::Float32); + convertStringToPropertyType( + ExtensionExtStructuralMetadataClassProperty::Type::STRING) == + PropertyType::String); REQUIRE( - convertStringToPropertyType(ClassProperty::Type::FLOAT64) == - PropertyType::Float64); + convertStringToPropertyType( + ExtensionExtStructuralMetadataClassProperty::Type::ENUM) == + PropertyType::Enum); + + REQUIRE(convertStringToPropertyType("invalid") == PropertyType::Invalid); + } + SECTION("Convert string to PropertyComponentType") { REQUIRE( - convertStringToPropertyType(ClassProperty::Type::STRING) == - PropertyType::String); + convertStringToPropertyComponentType( + ExtensionExtStructuralMetadataClassProperty::ComponentType:: + UINT8) == PropertyComponentType::Uint8); REQUIRE( - convertStringToPropertyType(ClassProperty::Type::BOOLEAN) == - PropertyType::Boolean); + convertStringToPropertyComponentType( + ExtensionExtStructuralMetadataClassProperty::ComponentType::INT8) == + PropertyComponentType::Int8); REQUIRE( - convertStringToPropertyType(ClassProperty::Type::ARRAY) == - PropertyType::Array); + convertStringToPropertyComponentType( + ExtensionExtStructuralMetadataClassProperty::ComponentType:: + UINT16) == PropertyComponentType::Uint16); + REQUIRE( + convertStringToPropertyComponentType( + ExtensionExtStructuralMetadataClassProperty::ComponentType:: + INT16) == PropertyComponentType::Int16); - REQUIRE(convertStringToPropertyType("NONESENSE") == PropertyType::None); - } + REQUIRE( + convertStringToPropertyComponentType( + ExtensionExtStructuralMetadataClassProperty::ComponentType:: + UINT32) == PropertyComponentType::Uint32); + + REQUIRE( + convertStringToPropertyComponentType( + ExtensionExtStructuralMetadataClassProperty::ComponentType:: + INT32) == PropertyComponentType::Int32); + REQUIRE( + convertStringToPropertyComponentType( + ExtensionExtStructuralMetadataClassProperty::ComponentType:: + UINT64) == PropertyComponentType::Uint64); - SECTION("PropertyType to String") { REQUIRE( - convertPropertyTypeToString(PropertyType::Uint8) == - ClassProperty::Type::UINT8); + convertStringToPropertyComponentType( + ExtensionExtStructuralMetadataClassProperty::ComponentType:: + INT64) == PropertyComponentType::Int64); REQUIRE( - convertPropertyTypeToString(PropertyType::Int8) == - ClassProperty::Type::INT8); + convertStringToPropertyComponentType( + ExtensionExtStructuralMetadataClassProperty::ComponentType:: + FLOAT32) == PropertyComponentType::Float32); + REQUIRE( + convertStringToPropertyComponentType( + ExtensionExtStructuralMetadataClassProperty::ComponentType:: + FLOAT64) == PropertyComponentType::Float64); + + REQUIRE( + convertStringToPropertyComponentType("invalid") == + PropertyComponentType::None); + } + SECTION("Convert PropertyType to string") { REQUIRE( - convertPropertyTypeToString(PropertyType::Uint16) == - ClassProperty::Type::UINT16); + convertPropertyTypeToString(PropertyType::Scalar) == + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR); REQUIRE( - convertPropertyTypeToString(PropertyType::Int16) == - ClassProperty::Type::INT16); + convertPropertyTypeToString(PropertyType::Vec2) == + ExtensionExtStructuralMetadataClassProperty::Type::VEC2); REQUIRE( - convertPropertyTypeToString(PropertyType::Uint32) == - ClassProperty::Type::UINT32); + convertPropertyTypeToString(PropertyType::Vec3) == + ExtensionExtStructuralMetadataClassProperty::Type::VEC3); REQUIRE( - convertPropertyTypeToString(PropertyType::Int32) == - ClassProperty::Type::INT32); + convertPropertyTypeToString(PropertyType::Vec4) == + ExtensionExtStructuralMetadataClassProperty::Type::VEC4); REQUIRE( - convertPropertyTypeToString(PropertyType::Uint64) == - ClassProperty::Type::UINT64); + convertPropertyTypeToString(PropertyType::Mat2) == + ExtensionExtStructuralMetadataClassProperty::Type::MAT2); REQUIRE( - convertPropertyTypeToString(PropertyType::Int64) == - ClassProperty::Type::INT64); + convertPropertyTypeToString(PropertyType::Mat3) == + ExtensionExtStructuralMetadataClassProperty::Type::MAT3); REQUIRE( - convertPropertyTypeToString(PropertyType::Float32) == - ClassProperty::Type::FLOAT32); + convertPropertyTypeToString(PropertyType::Mat4) == + ExtensionExtStructuralMetadataClassProperty::Type::MAT4); REQUIRE( - convertPropertyTypeToString(PropertyType::Float64) == - ClassProperty::Type::FLOAT64); + convertPropertyTypeToString(PropertyType::Boolean) == + ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN); REQUIRE( convertPropertyTypeToString(PropertyType::String) == - ClassProperty::Type::STRING); + ExtensionExtStructuralMetadataClassProperty::Type::STRING); REQUIRE( - convertPropertyTypeToString(PropertyType::Boolean) == - ClassProperty::Type::BOOLEAN); + convertPropertyTypeToString(PropertyType::Enum) == + ExtensionExtStructuralMetadataClassProperty::Type::ENUM); + } + + SECTION("Convert PropertyComponentType to string") { + REQUIRE( + convertPropertyComponentTypeToString(PropertyComponentType::Uint8) == + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT8); + + REQUIRE( + convertPropertyComponentTypeToString(PropertyComponentType::Int8) == + ExtensionExtStructuralMetadataClassProperty::ComponentType::INT8); + + REQUIRE( + convertPropertyComponentTypeToString(PropertyComponentType::Uint16) == + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT16); + + REQUIRE( + convertPropertyComponentTypeToString(PropertyComponentType::Int16) == + ExtensionExtStructuralMetadataClassProperty::ComponentType::INT16); + + REQUIRE( + convertPropertyComponentTypeToString(PropertyComponentType::Uint32) == + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT32); + + REQUIRE( + convertPropertyComponentTypeToString(PropertyComponentType::Int32) == + ExtensionExtStructuralMetadataClassProperty::ComponentType::INT32); + + REQUIRE( + convertPropertyComponentTypeToString(PropertyComponentType::Uint64) == + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT64); + + REQUIRE( + convertPropertyComponentTypeToString(PropertyComponentType::Int64) == + ExtensionExtStructuralMetadataClassProperty::ComponentType::INT64); + + REQUIRE( + convertPropertyComponentTypeToString(PropertyComponentType::Float32) == + ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT32); + + REQUIRE( + convertPropertyComponentTypeToString(PropertyComponentType::Float64) == + ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT64); + } + + SECTION("Convert array offset type string to PropertyComponentType") { + REQUIRE( + convertArrayOffsetTypeStringToPropertyComponentType( + ExtensionExtStructuralMetadataPropertyTableProperty:: + ArrayOffsetType::UINT8) == PropertyComponentType::Uint8); + + REQUIRE( + convertArrayOffsetTypeStringToPropertyComponentType( + ExtensionExtStructuralMetadataPropertyTableProperty:: + ArrayOffsetType::UINT16) == PropertyComponentType::Uint16); + + REQUIRE( + convertArrayOffsetTypeStringToPropertyComponentType( + ExtensionExtStructuralMetadataPropertyTableProperty:: + ArrayOffsetType::UINT32) == PropertyComponentType::Uint32); + + REQUIRE( + convertArrayOffsetTypeStringToPropertyComponentType( + ExtensionExtStructuralMetadataPropertyTableProperty:: + ArrayOffsetType::UINT64) == PropertyComponentType::Uint64); REQUIRE( - convertPropertyTypeToString(PropertyType::Array) == - ClassProperty::Type::ARRAY); + convertArrayOffsetTypeStringToPropertyComponentType("invalid") == + PropertyComponentType::None); } - SECTION("OffsetString to PropertyType") { + SECTION("Convert string offset type string to PropertyComponentType") { REQUIRE( - convertOffsetStringToPropertyType( - FeatureTableProperty::OffsetType::UINT8) == PropertyType::Uint8); + convertStringOffsetTypeStringToPropertyComponentType( + ExtensionExtStructuralMetadataPropertyTableProperty:: + StringOffsetType::UINT8) == PropertyComponentType::Uint8); REQUIRE( - convertOffsetStringToPropertyType( - FeatureTableProperty::OffsetType::UINT16) == PropertyType::Uint16); + convertStringOffsetTypeStringToPropertyComponentType( + ExtensionExtStructuralMetadataPropertyTableProperty:: + StringOffsetType::UINT16) == PropertyComponentType::Uint16); REQUIRE( - convertOffsetStringToPropertyType( - FeatureTableProperty::OffsetType::UINT32) == PropertyType::Uint32); + convertStringOffsetTypeStringToPropertyComponentType( + ExtensionExtStructuralMetadataPropertyTableProperty:: + StringOffsetType::UINT32) == PropertyComponentType::Uint32); REQUIRE( - convertOffsetStringToPropertyType( - FeatureTableProperty::OffsetType::UINT64) == PropertyType::Uint64); + convertStringOffsetTypeStringToPropertyComponentType( + ExtensionExtStructuralMetadataPropertyTableProperty:: + StringOffsetType::UINT64) == PropertyComponentType::Uint64); REQUIRE( - convertOffsetStringToPropertyType("NONESENSE") == PropertyType::None); + convertStringOffsetTypeStringToPropertyComponentType("invalid") == + PropertyComponentType::None); } } diff --git a/CesiumGltf/test/TestPropertyTypeTrait.cpp b/CesiumGltf/test/TestPropertyTypeTrait.cpp deleted file mode 100644 index b85157eaa..000000000 --- a/CesiumGltf/test/TestPropertyTypeTrait.cpp +++ /dev/null @@ -1,82 +0,0 @@ -#include "CesiumGltf/PropertyTypeTraits.h" - -#include - -TEST_CASE("Test PropertyTypeTrait") { - SECTION("IsNumeric") { - REQUIRE(CesiumGltf::IsMetadataNumeric::value); - REQUIRE(CesiumGltf::IsMetadataNumeric::value); - - REQUIRE(CesiumGltf::IsMetadataNumeric::value); - REQUIRE(CesiumGltf::IsMetadataNumeric::value); - - REQUIRE(CesiumGltf::IsMetadataNumeric::value); - REQUIRE(CesiumGltf::IsMetadataNumeric::value); - - REQUIRE(CesiumGltf::IsMetadataNumeric::value); - REQUIRE(CesiumGltf::IsMetadataNumeric::value); - - REQUIRE(CesiumGltf::IsMetadataNumeric::value); - REQUIRE(CesiumGltf::IsMetadataNumeric::value); - - REQUIRE(CesiumGltf::IsMetadataNumeric::value == false); - REQUIRE(CesiumGltf::IsMetadataNumeric::value == false); - } - - SECTION("IsBoolean") { - REQUIRE(CesiumGltf::IsMetadataBoolean::value); - REQUIRE(CesiumGltf::IsMetadataBoolean::value == false); - } - - SECTION("IsString") { - REQUIRE(CesiumGltf::IsMetadataString::value); - REQUIRE(CesiumGltf::IsMetadataString::value == false); - } - - SECTION("IsNumericArray") { - REQUIRE(CesiumGltf::IsMetadataNumericArray< - CesiumGltf::MetadataArrayView>::value); - - REQUIRE( - CesiumGltf::IsMetadataNumericArray< - CesiumGltf::MetadataArrayView>::value == false); - } - - SECTION("IsBooleanArray") { - REQUIRE(CesiumGltf::IsMetadataBooleanArray< - CesiumGltf::MetadataArrayView>::value); - - REQUIRE( - CesiumGltf::IsMetadataBooleanArray< - CesiumGltf::MetadataArrayView>::value == false); - } - - SECTION("IsStringArray") { - REQUIRE(CesiumGltf::IsMetadataStringArray< - CesiumGltf::MetadataArrayView>::value); - - REQUIRE( - CesiumGltf::IsMetadataStringArray< - CesiumGltf::MetadataArrayView>::value == false); - } - - SECTION("ArrayType") { - using type = CesiumGltf::MetadataArrayType< - CesiumGltf::MetadataArrayView>::type; - REQUIRE(std::is_same_v); - } - - SECTION("TypeToPropertyType") { - CesiumGltf::PropertyType value = - CesiumGltf::TypeToPropertyType::value; - REQUIRE(value == CesiumGltf::PropertyType::Uint32); - - auto component = CesiumGltf::TypeToPropertyType< - CesiumGltf::MetadataArrayView>::component; - value = CesiumGltf::TypeToPropertyType< - CesiumGltf::MetadataArrayView>::value; - CesiumGltf::PropertyType expected = CesiumGltf::PropertyType::Array; - REQUIRE(value == expected); - REQUIRE(component == CesiumGltf::PropertyType::Uint32); - } -} diff --git a/CesiumGltf/test/TestStructuralMetadataPropertyTypeTraits.cpp b/CesiumGltf/test/TestPropertyTypeTraits.cpp similarity index 95% rename from CesiumGltf/test/TestStructuralMetadataPropertyTypeTraits.cpp rename to CesiumGltf/test/TestPropertyTypeTraits.cpp index 42c0a5313..caa8d1eaf 100644 --- a/CesiumGltf/test/TestStructuralMetadataPropertyTypeTraits.cpp +++ b/CesiumGltf/test/TestPropertyTypeTraits.cpp @@ -1,10 +1,10 @@ -#include "CesiumGltf/StructuralMetadataPropertyTypeTraits.h" +#include "CesiumGltf/PropertyTypeTraits.h" #include -using namespace CesiumGltf::StructuralMetadata; +using namespace CesiumGltf; -TEST_CASE("Test StructuralMetadata PropertyTypeTraits") { +TEST_CASE("Test PropertyTypeTraits") { SECTION("IsScalar") { REQUIRE(IsMetadataScalar::value); REQUIRE(IsMetadataScalar::value); @@ -140,25 +140,25 @@ TEST_CASE("Test StructuralMetadata PropertyTypeTraits") { } SECTION("IsNumericArray") { - REQUIRE(IsMetadataNumericArray>::value); - REQUIRE(IsMetadataNumericArray>::value); - REQUIRE(IsMetadataNumericArray>::value); - REQUIRE(!IsMetadataNumericArray>::value); + REQUIRE(IsMetadataNumericArray>::value); + REQUIRE(IsMetadataNumericArray>::value); + REQUIRE(IsMetadataNumericArray>::value); + REQUIRE(!IsMetadataNumericArray>::value); } SECTION("IsBooleanArray") { - REQUIRE(IsMetadataBooleanArray>::value); - REQUIRE(!IsMetadataBooleanArray>::value); + REQUIRE(IsMetadataBooleanArray>::value); + REQUIRE(!IsMetadataBooleanArray>::value); } SECTION("IsStringArray") { - REQUIRE(IsMetadataStringArray>::value); - REQUIRE(!IsMetadataStringArray>::value); - REQUIRE(!IsMetadataStringArray>::value); + REQUIRE(IsMetadataStringArray>::value); + REQUIRE(!IsMetadataStringArray>::value); + REQUIRE(!IsMetadataStringArray>::value); } SECTION("ArrayType") { - using type = MetadataArrayType>::type; + using type = MetadataArrayType>::type; REQUIRE(std::is_same_v); } diff --git a/CesiumGltf/test/TestPropertyView.cpp b/CesiumGltf/test/TestPropertyView.cpp deleted file mode 100644 index 42a112ec8..000000000 --- a/CesiumGltf/test/TestPropertyView.cpp +++ /dev/null @@ -1,589 +0,0 @@ -#include "CesiumGltf/MetadataPropertyView.h" - -#include -#include - -#include -#include -#include -#include -#include - -template static void checkNumeric(const std::vector& expected) { - std::vector data; - data.resize(expected.size() * sizeof(T)); - std::memcpy(data.data(), expected.data(), data.size()); - - CesiumGltf::MetadataPropertyView property( - CesiumGltf::MetadataPropertyViewStatus::Valid, - gsl::span(data.data(), data.size()), - gsl::span(), - gsl::span(), - CesiumGltf::PropertyType::None, - 0, - static_cast(expected.size()), - false); - - for (int64_t i = 0; i < property.size(); ++i) { - REQUIRE(property.get(i) == expected[static_cast(i)]); - } -} - -template -static void checkDynamicArray( - const std::vector& data, - const std::vector offset, - CesiumGltf::PropertyType offsetType, - int64_t instanceCount) { - // copy data to buffer - std::vector buffer; - buffer.resize(data.size() * sizeof(T)); - std::memcpy(buffer.data(), data.data(), data.size() * sizeof(T)); - - // copy offset to buffer - std::vector offsetBuffer; - offsetBuffer.resize(offset.size() * sizeof(E)); - std::memcpy(offsetBuffer.data(), offset.data(), offset.size() * sizeof(E)); - - CesiumGltf::MetadataPropertyView> property( - CesiumGltf::MetadataPropertyViewStatus::Valid, - gsl::span(buffer.data(), buffer.size()), - gsl::span(offsetBuffer.data(), offsetBuffer.size()), - gsl::span(), - offsetType, - 0, - instanceCount, - false); - - size_t expectedIdx = 0; - for (int64_t i = 0; i < property.size(); ++i) { - CesiumGltf::MetadataArrayView vals = property.get(i); - for (int64_t j = 0; j < vals.size(); ++j) { - REQUIRE(vals[j] == data[expectedIdx]); - ++expectedIdx; - } - } - - REQUIRE(expectedIdx == data.size()); -} - -template -static void checkFixedArray( - const std::vector& data, - int64_t componentCount, - int64_t instanceCount) { - std::vector buffer; - buffer.resize(data.size() * sizeof(T)); - std::memcpy(buffer.data(), data.data(), data.size() * sizeof(T)); - - CesiumGltf::MetadataPropertyView> property( - CesiumGltf::MetadataPropertyViewStatus::Valid, - gsl::span(buffer.data(), buffer.size()), - gsl::span(), - gsl::span(), - CesiumGltf::PropertyType::None, - componentCount, - instanceCount, - false); - - size_t expectedIdx = 0; - for (int64_t i = 0; i < property.size(); ++i) { - CesiumGltf::MetadataArrayView vals = property.get(i); - for (int64_t j = 0; j < vals.size(); ++j) { - REQUIRE(vals[j] == data[expectedIdx]); - ++expectedIdx; - } - } - - REQUIRE(expectedIdx == data.size()); -} - -TEST_CASE("Check create numeric property view") { - SECTION("Uint8") { - std::vector data{12, 33, 56, 67}; - checkNumeric(data); - } - - SECTION("Int32") { - std::vector data{111222, -11133, -56000, 670000}; - checkNumeric(data); - } - - SECTION("Float") { - std::vector data{12.3333f, -12.44555f, -5.6111f, 6.7421f}; - checkNumeric(data); - } - - SECTION("Double") { - std::vector data{ - 12222.3302121, - -12000.44555, - -5000.6113111, - 6.7421}; - checkNumeric(data); - } -} - -TEST_CASE("Check boolean value") { - std::bitset bits = 0b11110101; - unsigned long val = bits.to_ulong(); - std::vector data(sizeof(val)); - std::memcpy(data.data(), &val, sizeof(val)); - - size_t instanceCount = sizeof(unsigned long) * CHAR_BIT; - CesiumGltf::MetadataPropertyView property( - CesiumGltf::MetadataPropertyViewStatus::Valid, - gsl::span(data.data(), data.size()), - gsl::span(), - gsl::span(), - CesiumGltf::PropertyType::None, - 0, - static_cast(instanceCount), - false); - for (int64_t i = 0; i < property.size(); ++i) { - REQUIRE(property.get(i) == bits[static_cast(i)]); - } -} - -TEST_CASE("Check string value") { - std::vector strings{ - "This is a fine test", - "What's going on", - "Good morning"}; - size_t totalSize = 0; - for (const auto& s : strings) { - totalSize += s.size(); - } - - uint32_t currentOffset = 0; - std::vector buffer; - buffer.resize(totalSize); - for (size_t i = 0; i < strings.size(); ++i) { - std::memcpy( - buffer.data() + currentOffset, - strings[i].data(), - strings[i].size()); - currentOffset += static_cast(strings[i].size()); - } - - // copy offset to buffer - std::vector offsetBuffer; - offsetBuffer.resize((strings.size() + 1) * sizeof(uint32_t)); - currentOffset = 0; - for (size_t i = 0; i < strings.size(); ++i) { - std::memcpy( - offsetBuffer.data() + i * sizeof(uint32_t), - ¤tOffset, - sizeof(uint32_t)); - currentOffset += static_cast(strings[i].size()); - } - std::memcpy( - offsetBuffer.data() + strings.size() * sizeof(uint32_t), - ¤tOffset, - sizeof(uint32_t)); - - CesiumGltf::MetadataPropertyView property( - CesiumGltf::MetadataPropertyViewStatus::Valid, - gsl::span(buffer.data(), buffer.size()), - gsl::span(), - gsl::span(offsetBuffer.data(), offsetBuffer.size()), - CesiumGltf::PropertyType::Uint32, - 0, - static_cast(strings.size()), - false); - for (int64_t i = 0; i < property.size(); ++i) { - REQUIRE(property.get(i) == strings[static_cast(i)]); - } -} - -TEST_CASE("Check fixed numeric array") { - SECTION("Fixed array of 4 uint8_ts") { - // clang-format off - std::vector data{ - 210, 211, 3, 42, - 122, 22, 1, 45}; - // clang-format on - checkFixedArray(data, 4, static_cast(data.size() / 4)); - } - - SECTION("Fixed array of 3 int8_ts") { - // clang-format off - std::vector data{ - 122, -12, 3, - 44, 11, -2, - 5, 6, -22, - 5, 6, 1}; - // clang-format on - checkFixedArray(data, 3, static_cast(data.size() / 3)); - } - - SECTION("Fixed array of 4 int16_ts") { - // clang-format off - std::vector data{ - -122, 12, 3, 44, - 11, 2, 5, -6000, - 119, 30, 51, 200, - 22000, -500, 6000, 1}; - // clang-format on - checkFixedArray(data, 4, static_cast(data.size() / 4)); - } - - SECTION("Fixed array of 6 uint32_ts") { - // clang-format off - std::vector data{ - 122, 12, 3, 44, 34444, 2222, - 11, 2, 5, 6000, 1111, 2222, - 119, 30, 51, 200, 12534, 11, - 22000, 500, 6000, 1, 3, 7}; - // clang-format on - checkFixedArray(data, 6, static_cast(data.size() / 6)); - } - - SECTION("Fixed array of 2 int32_ts") { - // clang-format off - std::vector data{ - 122, 12, - 3, 44}; - // clang-format on - checkFixedArray(data, 2, static_cast(data.size() / 2)); - } - - SECTION("Fixed array of 4 uint64_ts") { - // clang-format off - std::vector data{ - 10022, 120000, 2422, 1111, - 3, 440000, 333, 1455}; - // clang-format on - checkFixedArray(data, 4, static_cast(data.size() / 4)); - } - - SECTION("Fixed array of 4 int64_ts") { - // clang-format off - std::vector data{ - 10022, -120000, 2422, 1111, - 3, 440000, -333, 1455}; - // clang-format on - checkFixedArray(data, 4, static_cast(data.size() / 4)); - } - - SECTION("Fixed array of 4 floats") { - // clang-format off - std::vector data{ - 10.022f, -12.43f, 242.2f, 1.111f, - 3.333f, 440000.1f, -33.3f, 14.55f}; - // clang-format on - checkFixedArray(data, 4, static_cast(data.size() / 4)); - } - - SECTION("Fixed array of 4 double") { - // clang-format off - std::vector data{ - 10.022, -12.43, 242.2, 1.111, - 3.333, 440000.1, -33.3, 14.55}; - // clang-format on - checkFixedArray(data, 4, static_cast(data.size() / 4)); - } -} - -TEST_CASE("Check numeric dynamic array") { - SECTION("array of uint8_t") { - // clang-format off - std::vector data{ - 3, 2, - 0, 45, 2, 1, 4, - 1, 3, 2, - 1, 3, 4, 1 - }; - std::vector offset{ - 0, 2, 7, 10, 14 - }; - // clang-format on - - checkDynamicArray(data, offset, CesiumGltf::PropertyType::Uint32, 4); - } - - SECTION("array of int32_t") { - // clang-format off - std::vector data{ - 3, 200, - 0, 450, 200, 1, 4, - 1, 3, 2, - 1, 3, 4, 1 - }; - std::vector offset{ - 0, 2 * sizeof(int32_t), 7 * sizeof(int32_t), 10 * sizeof(int32_t), 14 * sizeof(int32_t) - }; - // clang-format on - - checkDynamicArray(data, offset, CesiumGltf::PropertyType::Uint32, 4); - } - - SECTION("array of double") { - // clang-format off - std::vector data{ - 3.333, 200.2, - 0.1122, 4.50, 2.30, 1.22, 4.444, - 1.4, 3.3, 2.2, - 1.11, 3.2, 4.111, 1.44 - }; - std::vector offset{ - 0, 2 * sizeof(double), 7 * sizeof(double), 10 * sizeof(double), 14 * sizeof(double) - }; - // clang-format on - - checkDynamicArray(data, offset, CesiumGltf::PropertyType::Uint32, 4); - } -} - -TEST_CASE("Check fixed array of string") { - std::vector strings{ - "Test 1", - "Test 2", - "Test 3", - "Test 4", - "Test 5", - "Test 6", - "This is a fine test", - "What's going on", - "Good morning"}; - size_t totalSize = 0; - for (const auto& s : strings) { - totalSize += s.size(); - } - - uint32_t currentOffset = 0; - std::vector buffer; - buffer.resize(totalSize); - for (size_t i = 0; i < strings.size(); ++i) { - std::memcpy( - buffer.data() + currentOffset, - strings[i].data(), - strings[i].size()); - currentOffset += static_cast(strings[i].size()); - } - - // copy offset to buffer - std::vector offsetBuffer; - offsetBuffer.resize((strings.size() + 1) * sizeof(uint32_t)); - currentOffset = 0; - for (size_t i = 0; i < strings.size(); ++i) { - std::memcpy( - offsetBuffer.data() + i * sizeof(uint32_t), - ¤tOffset, - sizeof(uint32_t)); - currentOffset += static_cast(strings[i].size()); - } - std::memcpy( - offsetBuffer.data() + strings.size() * sizeof(uint32_t), - ¤tOffset, - sizeof(uint32_t)); - - CesiumGltf::MetadataPropertyView< - CesiumGltf::MetadataArrayView> - property( - CesiumGltf::MetadataPropertyViewStatus::Valid, - gsl::span(buffer.data(), buffer.size()), - gsl::span(), - gsl::span(offsetBuffer.data(), offsetBuffer.size()), - CesiumGltf::PropertyType::Uint32, - 3, - static_cast(strings.size() / 3), - false); - - size_t expectedIdx = 0; - for (int64_t i = 0; i < property.size(); ++i) { - CesiumGltf::MetadataArrayView vals = property.get(i); - for (int64_t j = 0; j < vals.size(); ++j) { - std::string_view v = vals[j]; - REQUIRE(v == strings[expectedIdx]); - ++expectedIdx; - } - } - - REQUIRE(expectedIdx == strings.size()); -} - -TEST_CASE("Check dynamic array of string") { - // clang-format off - std::vector arrayOffset{ - 0, - 4 * sizeof(uint32_t), - 7 * sizeof(uint32_t), - 11 * sizeof(uint32_t) - }; - - std::vector strings{ - "Test 1", "Test 2", "Test 3", "Test 4", - "Test 5", "Test 6", "Test 7", - "test 8", "Test 9", "Test 10", "Test 11" - }; - // clang-format on - - size_t totalSize = 0; - for (const auto& s : strings) { - totalSize += s.size(); - } - - uint32_t currentOffset = 0; - std::vector buffer; - buffer.resize(totalSize); - for (size_t i = 0; i < strings.size(); ++i) { - std::memcpy( - buffer.data() + currentOffset, - strings[i].data(), - strings[i].size()); - currentOffset += static_cast(strings[i].size()); - } - - std::vector offsetBuffer; - offsetBuffer.resize((strings.size() + 1) * sizeof(uint32_t)); - currentOffset = 0; - for (size_t i = 0; i < strings.size(); ++i) { - std::memcpy( - offsetBuffer.data() + i * sizeof(uint32_t), - ¤tOffset, - sizeof(uint32_t)); - currentOffset += static_cast(strings[i].size()); - } - std::memcpy( - offsetBuffer.data() + strings.size() * sizeof(uint32_t), - ¤tOffset, - sizeof(uint32_t)); - - CesiumGltf::MetadataPropertyView< - CesiumGltf::MetadataArrayView> - property( - CesiumGltf::MetadataPropertyViewStatus::Valid, - gsl::span(buffer.data(), buffer.size()), - gsl::span( - reinterpret_cast(arrayOffset.data()), - arrayOffset.size() * sizeof(uint32_t)), - gsl::span(offsetBuffer.data(), offsetBuffer.size()), - CesiumGltf::PropertyType::Uint32, - 0, - 3, - false); - - size_t expectedIdx = 0; - for (int64_t i = 0; i < property.size(); ++i) { - CesiumGltf::MetadataArrayView vals = property.get(i); - for (int64_t j = 0; j < vals.size(); ++j) { - std::string_view v = vals[j]; - REQUIRE(v == strings[expectedIdx]); - ++expectedIdx; - } - } - - REQUIRE(expectedIdx == strings.size()); -} - -TEST_CASE("Check fixed array of boolean") { - std::vector buffer{ - static_cast(0b10101111), - static_cast(0b11111010), - static_cast(0b11100111)}; - - CesiumGltf::MetadataPropertyView> - property( - CesiumGltf::MetadataPropertyViewStatus::Valid, - gsl::span(buffer.data(), buffer.size()), - gsl::span(), - gsl::span(), - CesiumGltf::PropertyType::Uint32, - 12, - 2, - false); - - REQUIRE(property.size() == 2); - - CesiumGltf::MetadataArrayView val0 = property.get(0); - REQUIRE(val0.size() == 12); - REQUIRE(static_cast(val0[0]) == 1); - REQUIRE(static_cast(val0[1]) == 1); - REQUIRE(static_cast(val0[2]) == 1); - REQUIRE(static_cast(val0[3]) == 1); - REQUIRE(static_cast(val0[4]) == 0); - REQUIRE(static_cast(val0[5]) == 1); - REQUIRE(static_cast(val0[6]) == 0); - REQUIRE(static_cast(val0[7]) == 1); - REQUIRE(static_cast(val0[8]) == 0); - REQUIRE(static_cast(val0[9]) == 1); - REQUIRE(static_cast(val0[10]) == 0); - REQUIRE(static_cast(val0[11]) == 1); - - CesiumGltf::MetadataArrayView val1 = property.get(1); - REQUIRE(static_cast(val1[0]) == 1); - REQUIRE(static_cast(val1[1]) == 1); - REQUIRE(static_cast(val1[2]) == 1); - REQUIRE(static_cast(val1[3]) == 1); - REQUIRE(static_cast(val1[4]) == 1); - REQUIRE(static_cast(val1[5]) == 1); - REQUIRE(static_cast(val1[6]) == 1); - REQUIRE(static_cast(val1[7]) == 0); - REQUIRE(static_cast(val1[8]) == 0); - REQUIRE(static_cast(val1[9]) == 1); - REQUIRE(static_cast(val1[10]) == 1); - REQUIRE(static_cast(val1[11]) == 1); -} - -TEST_CASE("Check dynamic array of boolean") { - std::vector buffer{ - static_cast(0b10101111), - static_cast(0b11111010), - static_cast(0b11100111), - static_cast(0b11110110)}; - - std::vector offsetBuffer{0, 3, 12, 28}; - - CesiumGltf::MetadataPropertyView> - property( - CesiumGltf::MetadataPropertyViewStatus::Valid, - gsl::span(buffer.data(), buffer.size()), - gsl::span( - reinterpret_cast(offsetBuffer.data()), - offsetBuffer.size() * sizeof(uint32_t)), - gsl::span(), - CesiumGltf::PropertyType::Uint32, - 0, - 3, - false); - - REQUIRE(property.size() == 3); - - CesiumGltf::MetadataArrayView val0 = property.get(0); - REQUIRE(val0.size() == 3); - REQUIRE(static_cast(val0[0]) == 1); - REQUIRE(static_cast(val0[1]) == 1); - REQUIRE(static_cast(val0[2]) == 1); - - CesiumGltf::MetadataArrayView val1 = property.get(1); - REQUIRE(val1.size() == 9); - REQUIRE(static_cast(val1[0]) == 1); - REQUIRE(static_cast(val1[1]) == 0); - REQUIRE(static_cast(val1[2]) == 1); - REQUIRE(static_cast(val1[3]) == 0); - REQUIRE(static_cast(val1[4]) == 1); - REQUIRE(static_cast(val1[5]) == 0); - REQUIRE(static_cast(val1[6]) == 1); - REQUIRE(static_cast(val1[7]) == 0); - REQUIRE(static_cast(val1[8]) == 1); - - CesiumGltf::MetadataArrayView val2 = property.get(2); - REQUIRE(val2.size() == 16); - REQUIRE(static_cast(val2[0]) == 1); - REQUIRE(static_cast(val2[1]) == 1); - REQUIRE(static_cast(val2[2]) == 1); - REQUIRE(static_cast(val2[3]) == 1); - REQUIRE(static_cast(val2[4]) == 1); - REQUIRE(static_cast(val2[5]) == 1); - REQUIRE(static_cast(val2[6]) == 1); - REQUIRE(static_cast(val2[7]) == 0); - REQUIRE(static_cast(val2[8]) == 0); - REQUIRE(static_cast(val2[9]) == 1); - REQUIRE(static_cast(val2[10]) == 1); - REQUIRE(static_cast(val2[11]) == 1); - REQUIRE(static_cast(val2[12]) == 0); - REQUIRE(static_cast(val2[13]) == 1); - REQUIRE(static_cast(val2[14]) == 1); - REQUIRE(static_cast(val2[15]) == 0); -} diff --git a/CesiumGltf/test/TestStructuralMetadataPropertyType.cpp b/CesiumGltf/test/TestStructuralMetadataPropertyType.cpp deleted file mode 100644 index fbd27e44a..000000000 --- a/CesiumGltf/test/TestStructuralMetadataPropertyType.cpp +++ /dev/null @@ -1,253 +0,0 @@ -#include "CesiumGltf/ExtensionExtStructuralMetadataClassProperty.h" -#include "CesiumGltf/ExtensionExtStructuralMetadataPropertyTableProperty.h" -#include "CesiumGltf/StructuralMetadataPropertyType.h" - -#include - -using namespace CesiumGltf; -using namespace StructuralMetadata; - -TEST_CASE("Test StructuralMetadata PropertyType utilities function") { - SECTION("Convert string to PropertyType") { - REQUIRE( - convertStringToPropertyType( - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR) == - PropertyType::Scalar); - - REQUIRE( - convertStringToPropertyType( - ExtensionExtStructuralMetadataClassProperty::Type::VEC2) == - PropertyType::Vec2); - - REQUIRE( - convertStringToPropertyType( - ExtensionExtStructuralMetadataClassProperty::Type::VEC3) == - PropertyType::Vec3); - - REQUIRE( - convertStringToPropertyType( - ExtensionExtStructuralMetadataClassProperty::Type::VEC4) == - PropertyType::Vec4); - - REQUIRE( - convertStringToPropertyType( - ExtensionExtStructuralMetadataClassProperty::Type::MAT2) == - PropertyType::Mat2); - - REQUIRE( - convertStringToPropertyType( - ExtensionExtStructuralMetadataClassProperty::Type::MAT3) == - PropertyType::Mat3); - - REQUIRE( - convertStringToPropertyType( - ExtensionExtStructuralMetadataClassProperty::Type::MAT4) == - PropertyType::Mat4); - - REQUIRE( - convertStringToPropertyType( - ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN) == - PropertyType::Boolean); - - REQUIRE( - convertStringToPropertyType( - ExtensionExtStructuralMetadataClassProperty::Type::STRING) == - PropertyType::String); - - REQUIRE( - convertStringToPropertyType( - ExtensionExtStructuralMetadataClassProperty::Type::ENUM) == - PropertyType::Enum); - - REQUIRE(convertStringToPropertyType("invalid") == PropertyType::Invalid); - } - - SECTION("Convert string to PropertyComponentType") { - REQUIRE( - convertStringToPropertyComponentType( - ExtensionExtStructuralMetadataClassProperty::ComponentType:: - UINT8) == PropertyComponentType::Uint8); - - REQUIRE( - convertStringToPropertyComponentType( - ExtensionExtStructuralMetadataClassProperty::ComponentType::INT8) == - PropertyComponentType::Int8); - - REQUIRE( - convertStringToPropertyComponentType( - ExtensionExtStructuralMetadataClassProperty::ComponentType:: - UINT16) == PropertyComponentType::Uint16); - REQUIRE( - convertStringToPropertyComponentType( - ExtensionExtStructuralMetadataClassProperty::ComponentType:: - INT16) == PropertyComponentType::Int16); - - REQUIRE( - convertStringToPropertyComponentType( - ExtensionExtStructuralMetadataClassProperty::ComponentType:: - UINT32) == PropertyComponentType::Uint32); - - REQUIRE( - convertStringToPropertyComponentType( - ExtensionExtStructuralMetadataClassProperty::ComponentType:: - INT32) == PropertyComponentType::Int32); - REQUIRE( - convertStringToPropertyComponentType( - ExtensionExtStructuralMetadataClassProperty::ComponentType:: - UINT64) == PropertyComponentType::Uint64); - - REQUIRE( - convertStringToPropertyComponentType( - ExtensionExtStructuralMetadataClassProperty::ComponentType:: - INT64) == PropertyComponentType::Int64); - - REQUIRE( - convertStringToPropertyComponentType( - ExtensionExtStructuralMetadataClassProperty::ComponentType:: - FLOAT32) == PropertyComponentType::Float32); - REQUIRE( - convertStringToPropertyComponentType( - ExtensionExtStructuralMetadataClassProperty::ComponentType:: - FLOAT64) == PropertyComponentType::Float64); - - REQUIRE( - convertStringToPropertyComponentType("invalid") == - PropertyComponentType::None); - } - - SECTION("Convert PropertyType to string") { - REQUIRE( - convertPropertyTypeToString(PropertyType::Scalar) == - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR); - - REQUIRE( - convertPropertyTypeToString(PropertyType::Vec2) == - ExtensionExtStructuralMetadataClassProperty::Type::VEC2); - - REQUIRE( - convertPropertyTypeToString(PropertyType::Vec3) == - ExtensionExtStructuralMetadataClassProperty::Type::VEC3); - - REQUIRE( - convertPropertyTypeToString(PropertyType::Vec4) == - ExtensionExtStructuralMetadataClassProperty::Type::VEC4); - - REQUIRE( - convertPropertyTypeToString(PropertyType::Mat2) == - ExtensionExtStructuralMetadataClassProperty::Type::MAT2); - - REQUIRE( - convertPropertyTypeToString(PropertyType::Mat3) == - ExtensionExtStructuralMetadataClassProperty::Type::MAT3); - - REQUIRE( - convertPropertyTypeToString(PropertyType::Mat4) == - ExtensionExtStructuralMetadataClassProperty::Type::MAT4); - - REQUIRE( - convertPropertyTypeToString(PropertyType::Boolean) == - ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN); - - REQUIRE( - convertPropertyTypeToString(PropertyType::String) == - ExtensionExtStructuralMetadataClassProperty::Type::STRING); - - REQUIRE( - convertPropertyTypeToString(PropertyType::Enum) == - ExtensionExtStructuralMetadataClassProperty::Type::ENUM); - } - - SECTION("Convert PropertyComponentType to string") { - REQUIRE( - convertPropertyComponentTypeToString(PropertyComponentType::Uint8) == - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT8); - - REQUIRE( - convertPropertyComponentTypeToString(PropertyComponentType::Int8) == - ExtensionExtStructuralMetadataClassProperty::ComponentType::INT8); - - REQUIRE( - convertPropertyComponentTypeToString(PropertyComponentType::Uint16) == - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT16); - - REQUIRE( - convertPropertyComponentTypeToString(PropertyComponentType::Int16) == - ExtensionExtStructuralMetadataClassProperty::ComponentType::INT16); - - REQUIRE( - convertPropertyComponentTypeToString(PropertyComponentType::Uint32) == - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT32); - - REQUIRE( - convertPropertyComponentTypeToString(PropertyComponentType::Int32) == - ExtensionExtStructuralMetadataClassProperty::ComponentType::INT32); - - REQUIRE( - convertPropertyComponentTypeToString(PropertyComponentType::Uint64) == - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT64); - - REQUIRE( - convertPropertyComponentTypeToString(PropertyComponentType::Int64) == - ExtensionExtStructuralMetadataClassProperty::ComponentType::INT64); - - REQUIRE( - convertPropertyComponentTypeToString(PropertyComponentType::Float32) == - ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT32); - - REQUIRE( - convertPropertyComponentTypeToString(PropertyComponentType::Float64) == - ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT64); - } - - SECTION("Convert array offset type string to PropertyComponentType") { - REQUIRE( - convertArrayOffsetTypeStringToPropertyComponentType( - ExtensionExtStructuralMetadataPropertyTableProperty:: - ArrayOffsetType::UINT8) == PropertyComponentType::Uint8); - - REQUIRE( - convertArrayOffsetTypeStringToPropertyComponentType( - ExtensionExtStructuralMetadataPropertyTableProperty:: - ArrayOffsetType::UINT16) == PropertyComponentType::Uint16); - - REQUIRE( - convertArrayOffsetTypeStringToPropertyComponentType( - ExtensionExtStructuralMetadataPropertyTableProperty:: - ArrayOffsetType::UINT32) == PropertyComponentType::Uint32); - - REQUIRE( - convertArrayOffsetTypeStringToPropertyComponentType( - ExtensionExtStructuralMetadataPropertyTableProperty:: - ArrayOffsetType::UINT64) == PropertyComponentType::Uint64); - - REQUIRE( - convertArrayOffsetTypeStringToPropertyComponentType("invalid") == - PropertyComponentType::None); - } - - SECTION("Convert string offset type string to PropertyComponentType") { - REQUIRE( - convertStringOffsetTypeStringToPropertyComponentType( - ExtensionExtStructuralMetadataPropertyTableProperty:: - StringOffsetType::UINT8) == PropertyComponentType::Uint8); - - REQUIRE( - convertStringOffsetTypeStringToPropertyComponentType( - ExtensionExtStructuralMetadataPropertyTableProperty:: - StringOffsetType::UINT16) == PropertyComponentType::Uint16); - - REQUIRE( - convertStringOffsetTypeStringToPropertyComponentType( - ExtensionExtStructuralMetadataPropertyTableProperty:: - StringOffsetType::UINT32) == PropertyComponentType::Uint32); - - REQUIRE( - convertStringOffsetTypeStringToPropertyComponentType( - ExtensionExtStructuralMetadataPropertyTableProperty:: - StringOffsetType::UINT64) == PropertyComponentType::Uint64); - - REQUIRE( - convertStringOffsetTypeStringToPropertyComponentType("invalid") == - PropertyComponentType::None); - } -} From bc67ba84908f68c3e0c7f4211582eabe1fc66587 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Tue, 6 Jun 2023 11:37:35 -0400 Subject: [PATCH 069/421] Fix other files --- .../src/BatchTableToGltfStructuralMetadata.cpp | 5 ++--- .../TestUpgradeBatchTableToExtStructuralMetadata.cpp | 11 +++++------ CesiumGltf/test/TestPropertyTablePropertyView.cpp | 3 +-- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp b/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp index 1262edbc8..46e63c715 100644 --- a/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp +++ b/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp @@ -6,8 +6,8 @@ #include #include #include -#include -#include +#include +#include #include #include @@ -19,7 +19,6 @@ #include using namespace CesiumGltf; -using namespace CesiumGltf::StructuralMetadata; using namespace Cesium3DTilesSelection::CesiumImpl; namespace Cesium3DTilesSelection { diff --git a/Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp b/Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp index 712098bff..702ec8bb7 100644 --- a/Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp +++ b/Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp @@ -5,8 +5,8 @@ #include #include #include -#include -#include +#include +#include #include #include @@ -18,7 +18,6 @@ #include using namespace CesiumGltf; -using namespace CesiumGltf::StructuralMetadata; using namespace Cesium3DTilesSelection; using namespace CesiumUtility; @@ -88,13 +87,13 @@ static void checkArrayProperty( REQUIRE(view.status() == PropertyTableViewStatus::Valid); REQUIRE(view.size() == propertyTable.count); - PropertyTablePropertyView> propertyView = - view.getPropertyView>(propertyName); + PropertyTablePropertyView> propertyView = + view.getPropertyView>(propertyName); REQUIRE(propertyView.status() == PropertyTablePropertyViewStatus::Valid); REQUIRE(propertyView.size() == propertyTable.count); REQUIRE(propertyView.size() == static_cast(expectedTotalInstances)); for (size_t i = 0; i < expectedTotalInstances; ++i) { - MetadataArrayView value = + PropertyArrayView value = propertyView.get(static_cast(i)); if (expectedCount > 0) { REQUIRE(value.size() == expectedCount); diff --git a/CesiumGltf/test/TestPropertyTablePropertyView.cpp b/CesiumGltf/test/TestPropertyTablePropertyView.cpp index ff1e44cec..ae1075ec3 100644 --- a/CesiumGltf/test/TestPropertyTablePropertyView.cpp +++ b/CesiumGltf/test/TestPropertyTablePropertyView.cpp @@ -855,8 +855,7 @@ TEST_CASE("Check fixed-length array of string") { REQUIRE(expectedIdx == stringCount); } -TEST_CASE( - "Check variable-length array of strings property") { +TEST_CASE("Check variable-length array of strings property") { // clang-format off std::vector arrayOffsets{ 0, From c34b321b303d109a1207d2e27c4a03ef6b661e27 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Tue, 6 Jun 2023 12:10:02 -0400 Subject: [PATCH 070/421] Add changelog and delete unused files --- CHANGES.md | 22 ++ .../CesiumGltf/FeatureTexturePropertyView.h | 253 ------------------ .../include/CesiumGltf/FeatureTextureView.h | 110 -------- 3 files changed, 22 insertions(+), 363 deletions(-) delete mode 100644 CesiumGltf/include/CesiumGltf/FeatureTexturePropertyView.h delete mode 100644 CesiumGltf/include/CesiumGltf/FeatureTextureView.h diff --git a/CHANGES.md b/CHANGES.md index 13c2ed0d8..5343e5d35 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,27 @@ # Change Log +### ? - ? + +##### Breaking Changes :mega: + +Many classes have been overhauled to support `EXT_mesh_features` and `EXT_structural_metadata`. This includes the following changes: + +- Replaced `FeatureIDTextureView` with `FeatureIdTextureView`, which views a `ExtensionExtMeshFeaturesFeatureIdTexture`. + - Replaced `FeatureIDTextureViewStatus` with `FeatureIdTextureViewStatus`. + - Replaced `getChannel` with `getChannels`. This retrieves the channels as a vector of integers, instead of a single integer. + - Renamed `getTextureCoordinateAttributeId` to `getTexCoordSetIndex`. +- Replaced `MetadataFeatureTableView` with `PropertyTableView`, which views a `ExtensionExtStructuralMetadataPropertyTable`. + - Added `PropertyTableViewStatus` to indicate whether or not `PropertyTableView` is valid. +- Renamed `MetadataArrayView` to `PropertyArrayView`. +- Replaced `FeatureTextureView` with `PropertyTextureView`, which views a `ExtensionExtMeshFeaturesPropertyTexture`. + - Replaced `FeatureTextureViewStatus` with `PropertyTextureViewStatus`. +- Replaced `FeatureTexturePropertyView` with `PropertyTexturePropertyView`, which views a `ExtensionExtMeshFeaturesPropertyTextureProperty`. + - Replaced `FeatureTexturePropertyViewStatus` with `PropertyTexturePropertyViewStatus`. +- Refactored the `PropertyType` enum to reflect the values of `type` in an `ExtensionExtStructuralMetadataClassProperty`. +- Added the `PropertyComponentType` enum to reflect the values of `componentType` in an `ExtensionExtStructuralMetadataClassProperty`. + +Additionally, views of the data contained by `EXT_feature_metadata` will no longer supported by Cesium Native. The extension will still be parsed, but it will log a warning. Batch tables will also be converted to `EXT_structural_metadata` instead of `EXT_feature_metadata`. + ### v0.25.0 - 2023-06-01 ##### Additions :tada: diff --git a/CesiumGltf/include/CesiumGltf/FeatureTexturePropertyView.h b/CesiumGltf/include/CesiumGltf/FeatureTexturePropertyView.h deleted file mode 100644 index bbe87a546..000000000 --- a/CesiumGltf/include/CesiumGltf/FeatureTexturePropertyView.h +++ /dev/null @@ -1,253 +0,0 @@ -#pragma once - -#include "CesiumGltf/ClassProperty.h" -#include "CesiumGltf/ExtensionModelExtFeatureMetadata.h" -#include "CesiumGltf/FeatureTexture.h" -#include "CesiumGltf/Sampler.h" -#include "CesiumGltf/Texture.h" -#include "CesiumGltf/TextureAccessor.h" -#include "CesiumGltf/TextureInfo.h" -#include "Image.h" -#include "ImageCesium.h" -#include "Model.h" - -#include -#include -#include - -namespace CesiumGltf { - -/** - * @brief Indicates the status of a feature texture property view. - * - * The {@link FeatureTexturePropertyView} constructor always completes - * successfully. However it may not always reflect the actual content of the - * corresponding feature texture property. This enumeration provides the reason. - */ -enum class FeatureTexturePropertyViewStatus { - /** - * @brief This view is valid and ready to use. - */ - Valid, - - /** - * @brief This view has not been initialized. - */ - InvalidUninitialized, - - /** - * @brief This feature texture property has a texture index that does not - * exist in the glTF. - */ - InvalidTextureIndex, - - /** - * @brief This feature texture property has a texture sampler index that does - * not exist in the glTF. - */ - InvalidTextureSamplerIndex, - - /** - * @brief This feature texture property has an image index that does not - * exist in the glTF. - */ - InvalidImageIndex, - - /** - * @brief This feature texture property points to an empty image. - */ - InvalidEmptyImage, - - /** - * @brief This feature texture property has an invalid channels string. - */ - InvalidChannelsString -}; - -/** - * @brief The supported component types that can exist in feature id textures. - */ -enum class FeatureTexturePropertyComponentType { - Uint8 - // TODO: add more types. Currently this is the only one outputted by stb, - // so change stb call to output more of the original types. -}; - -/** - * @brief Specifies which channel each component exists in or -1 if the channel - * isn't present. This can be used to un-swizzle pixel data. - */ -struct FeatureTexturePropertyChannelOffsets { - int32_t r = -1; - int32_t g = -1; - int32_t b = -1; - int32_t a = -1; -}; - -/** - * @brief The feature texture property value for a pixel. This will contain - * four channels of the specified type. - * - * Only the first n components will be valid, where n is the number of channels - * in this feature texture property. - * - * @tparam T The component type, must correspond to a valid - * {@link FeatureTexturePropertyComponentType}. - */ -template struct FeatureTexturePropertyValue { T components[4]; }; - -/** - * @brief A view of the data specified by a property from a - * {@link FeatureTexture}. - * - * Provides utilities to sample the feature texture property using texture - * coordinates. - */ -class FeatureTexturePropertyView { -public: - /** - * @brief Construct an uninitialized, invalid view. - */ - FeatureTexturePropertyView() noexcept; - - /** - * @brief Construct a view of the data specified by a feature texture - * property. - * - * @param model The glTF in which to look for the data specified by the - * feature texture property. - * @param classProperty The property description. - * @param textureAccessor The texture accessor for this property. - */ - FeatureTexturePropertyView( - const Model& model, - const ClassProperty& classProperty, - const TextureAccessor& textureAccessor) noexcept; - - /** - * @brief Get the property for the given texture coordinates. - * - * Will return -1s when the status is not Valid or when the templated - * component type doesn't match the image's channel byte-size. - * - * @tparam T The component type to use when interpreting the channels of the - * property's pixel value. - * @param u The u-component of the texture coordinates. Must be within - * [0.0, 1.0]. - * @param v The v-component of the texture coordinates. Must be within - * [0.0, 1.0]. - * @return The property at the nearest pixel to the texture coordinates. - */ - template - FeatureTexturePropertyValue - getProperty(double u, double v) const noexcept { - if (this->_status != FeatureTexturePropertyViewStatus::Valid || - sizeof(T) != this->_pImage->bytesPerChannel) { - FeatureTexturePropertyValue property; - property.components[0] = -1; - property.components[1] = -1; - property.components[2] = -1; - property.components[3] = -1; - return property; - } - - // TODO: actually use the sampler?? - int64_t x = std::clamp( - std::llround(u * this->_pImage->width), - 0LL, - (long long)this->_pImage->width); - int64_t y = std::clamp( - std::llround(v * this->_pImage->height), - 0LL, - (long long)this->_pImage->height); - - int64_t pixelOffset = this->_pImage->bytesPerChannel * - this->_pImage->channels * - (y * this->_pImage->width + x); - const T* pRedChannel = reinterpret_cast( - this->_pImage->pixelData.data() + pixelOffset); - - FeatureTexturePropertyValue property; - property.components[0] = *(pRedChannel + this->_channelOffsets.r); - property.components[1] = *(pRedChannel + this->_channelOffsets.g); - property.components[2] = *(pRedChannel + this->_channelOffsets.b); - property.components[3] = *(pRedChannel + this->_channelOffsets.a); - - return property; - } - - /** - * @brief Get the status of this view. - * - * If invalid, it will not be safe to sample feature ids from this view. - */ - FeatureTexturePropertyViewStatus status() const noexcept { - return this->_status; - } - - /** - * @brief Get the component type for this property. - */ - FeatureTexturePropertyComponentType getPropertyType() const noexcept { - return this->_type; - } - - /** - * @brief Get the component count for this property. - * - * This is also how many channels a pixel value for this property will use. - */ - int64_t getComponentCount() const noexcept { return this->_componentCount; } - - /** - * @brief Get the texture coordinate attribute index for this property. - */ - int64_t getTextureCoordinateAttributeId() const noexcept { - return this->_textureCoordinateAttributeId; - } - - /** - * @brief Whether the component type for this property should be normalized. - */ - bool isNormalized() const noexcept { return this->_normalized; } - - /** - * @brief Get the image containing this property's data. - * - * This will be nullptr if the feature texture property view runs into - * problems during construction. - */ - const ImageCesium* getImage() const noexcept { return this->_pImage; } - - /** - * @brief Get the swizzle string for this texture's channels. Used to - * determine which channel represents red, green, blue, and alpha - * respectively. - */ - const std::string& getSwizzle() const noexcept { - const static std::string empty_str = ""; - return this->_pSwizzle ? *this->_pSwizzle : empty_str; - } - - /** - * @brief Get the {@link FeatureTexturePropertyChannelOffsets} that specifies - * how to un-swizzle this property's pixel values. - */ - const FeatureTexturePropertyChannelOffsets& - getChannelOffsets() const noexcept { - return this->_channelOffsets; - } - -private: - const Sampler* _pSampler; - const ImageCesium* _pImage; - const ClassProperty* _pClassProperty; - const std::string* _pSwizzle; - int64_t _textureCoordinateAttributeId; - FeatureTexturePropertyViewStatus _status; - FeatureTexturePropertyChannelOffsets _channelOffsets; - FeatureTexturePropertyComponentType _type; - int64_t _componentCount; - bool _normalized; -}; -} // namespace CesiumGltf \ No newline at end of file diff --git a/CesiumGltf/include/CesiumGltf/FeatureTextureView.h b/CesiumGltf/include/CesiumGltf/FeatureTextureView.h deleted file mode 100644 index 8b6410579..000000000 --- a/CesiumGltf/include/CesiumGltf/FeatureTextureView.h +++ /dev/null @@ -1,110 +0,0 @@ -#pragma once - -#include "CesiumGltf/Class.h" -#include "CesiumGltf/ClassProperty.h" -#include "CesiumGltf/ExtensionModelExtFeatureMetadata.h" -#include "CesiumGltf/FeatureTexture.h" -#include "CesiumGltf/FeatureTexturePropertyView.h" -#include "CesiumGltf/Texture.h" -#include "CesiumGltf/TextureAccessor.h" -#include "Image.h" -#include "ImageCesium.h" -#include "Model.h" - -namespace CesiumGltf { - -/** - * @brief Indicates the status of a feature texture view. - * - * The {@link FeatureTextureView} constructor always completes successfully. - * However it may not always reflect the actual content of the - * {@link FeatureTexture}. This enumeration provides the reason. - */ -enum class FeatureTextureViewStatus { - /** - * @brief This feature texture view is valid and ready to use. - */ - Valid, - - /** - * @brief This feature texture view is not initialized. - */ - InvalidUninitialized, - - /** - * @brief The glTF is missing the EXT_feature_metadata extension. - */ - InvalidMissingMetadataExtension, - - /** - * @brief The glTF EXT_feature_metadata extension doesn't contain a schema. - */ - InvalidMissingSchema, - - /** - * @brief The feature texture's specified class could not be found in the - * extension. - * - */ - InvalidClassNotFound, - - /** - * @brief A property name specified in the feature texture could not be found - * in the class. - */ - InvalidClassPropertyNotFound, - - /** - * @brief A property view for one of this feature texture's properties failed - * to initialize successfully. Look for the invalid property view's status to - * find why it failed. - */ - InvalidPropertyViewStatus -}; - -/** - * @brief A view on the {@link FeatureTexture}. - * - * Provides access to views on the feature texture properties. - */ -class FeatureTextureView { -public: - /** - * @brief Construct an uninitialized, invalid feature texture view. - */ - FeatureTextureView() noexcept; - - /** - * @brief Construct a view for the feature texture. - * - * @param model The glTF in which to look for the feature texture's data. - * @param featureTexture The feature texture to create a view for. - */ - FeatureTextureView( - const Model& model, - const FeatureTexture& featureTexture) noexcept; - - /** - * @brief Gets the status of this feature texture view. - * - * Indicates whether the view accurately reflects the feature texture's data, - * or whether an error occurred. - */ - FeatureTextureViewStatus status() const noexcept { return this->_status; } - - /** - * @brief Get the views for this feature texture's properties. - */ - const std::unordered_map& - getProperties() const noexcept { - return this->_propertyViews; - } - -private: - const Model* _pModel; - const FeatureTexture* _pFeatureTexture; - const Class* _pClass; - std::unordered_map _propertyViews; - FeatureTextureViewStatus _status; -}; -} // namespace CesiumGltf \ No newline at end of file From a743bb882e6add15928060b1f6c9d754aef77ec5 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Tue, 6 Jun 2023 12:54:28 -0400 Subject: [PATCH 071/421] Small changes to BatchTableToGltfStructuralMetadata --- .../BatchTableToGltfStructuralMetadata.cpp | 54 ++++++++++--------- 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp b/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp index 46e63c715..915756d45 100644 --- a/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp +++ b/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp @@ -120,82 +120,82 @@ struct CompatibleTypes { // MaskedType or MaskedArrayType, they are considered incompatible with the // other type. private: - std::variant type; + std::variant _type; public: - CompatibleTypes() : type(){}; + CompatibleTypes() : _type(){}; - CompatibleTypes(const MaskedType& maskedType) : type(maskedType){}; + CompatibleTypes(const MaskedType& maskedType) : _type(maskedType){}; CompatibleTypes(const MaskedArrayType& maskedArrayType) - : type(maskedArrayType){}; + : _type(maskedArrayType){}; /** * Whether this is exclusively compatible with array types. */ bool isExclusivelyArray() const { - return std::holds_alternative(type); + return std::holds_alternative(_type); } /** * Marks as incompatible with every type. Fully-incompatible types will be * treated as strings. */ - void makeIncompatible() { type = MaskedType(false); } + void makeIncompatible() { _type = MaskedType(false); } /** * Merges a MaskedType into this CompatibleTypes. */ void operator&=(const MaskedType& inMaskedType) { - if (std::holds_alternative(type)) { - MaskedType& maskedType = std::get(type); + if (std::holds_alternative(_type)) { + MaskedType& maskedType = std::get(_type); maskedType &= inMaskedType; return; } - if (std::holds_alternative(type)) { + if (std::holds_alternative(_type)) { makeIncompatible(); return; } - type = inMaskedType; + _type = inMaskedType; } /** * Merges a MaskedArrayType into this CompatibleTypes. */ void operator&=(const MaskedArrayType& inArrayType) { - if (std::holds_alternative(type)) { - MaskedArrayType& arrayType = std::get(type); + if (std::holds_alternative(_type)) { + MaskedArrayType& arrayType = std::get(_type); arrayType &= inArrayType; return; } - if (std::holds_alternative(type)) { + if (std::holds_alternative(_type)) { makeIncompatible(); return; } - type = inArrayType; + _type = inArrayType; } /** * Merges another CompatibleTypes into this one. */ void operator&=(const CompatibleTypes& inCompatibleTypes) { - if (std::holds_alternative(inCompatibleTypes.type)) { + if (std::holds_alternative(inCompatibleTypes._type)) { // The other CompatibleTypes is compatible with everything, so it does not // change this one. return; } - if (std::holds_alternative(inCompatibleTypes.type)) { + if (std::holds_alternative(inCompatibleTypes._type)) { const MaskedArrayType& arrayType = - std::get(inCompatibleTypes.type); + std::get(inCompatibleTypes._type); operator&=(arrayType); return; } - const MaskedType& maskedType = std::get(inCompatibleTypes.type); + const MaskedType& maskedType = std::get(inCompatibleTypes._type); operator&=(maskedType); } @@ -205,11 +205,11 @@ struct CompatibleTypes { * MaskedType. */ MaskedType toMaskedType() const { - if (std::holds_alternative(type)) { - return std::get(type); + if (std::holds_alternative(_type)) { + return std::get(_type); } - bool isArray = std::holds_alternative(type); + bool isArray = std::holds_alternative(_type); return MaskedType(!isArray); } @@ -219,11 +219,11 @@ struct CompatibleTypes { * incompatible MaskedArrayType. */ MaskedArrayType toMaskedArrayType() const { - if (std::holds_alternative(type)) { - return std::get(type); + if (std::holds_alternative(_type)) { + return std::get(_type); } - bool isNonArray = std::holds_alternative(type); + bool isNonArray = std::holds_alternative(_type); return MaskedArrayType(!isNonArray); } }; @@ -306,7 +306,7 @@ int64_t roundUp(int64_t num, int64_t multiple) noexcept { } template bool isInRangeForSignedInteger(int64_t value) noexcept { - // this only work if sizeof(T) is smaller than int64_t + // This only works if sizeof(T) is smaller than int64_t static_assert( !std::is_same_v && !std::is_same_v && !std::is_same_v); @@ -959,7 +959,8 @@ void updateBooleanArrayProperty( const TValueGetter& propertyValue) { assert(propertyValue.size() >= propertyTable.count); - classProperty.type = "BOOLEAN"; + classProperty.type = + ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN; classProperty.array = true; // Fixed-length array of booleans @@ -1493,6 +1494,7 @@ void convertBatchTableToGltfStructuralMetadataExtension( ExtensionExtStructuralMetadataPropertyTable& propertyTable = modelExtension.propertyTables.emplace_back(); + propertyTable.name = "default"; propertyTable.count = featureCount; propertyTable.classProperty = "default"; From 3e6e9ce764664499773ee6561e8c62a39e4021fc Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Tue, 6 Jun 2023 13:53:16 -0400 Subject: [PATCH 072/421] Fix formatting --- .../src/BatchTableToGltfStructuralMetadata.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp b/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp index 915756d45..9879ff418 100644 --- a/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp +++ b/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp @@ -195,7 +195,8 @@ struct CompatibleTypes { return; } - const MaskedType& maskedType = std::get(inCompatibleTypes._type); + const MaskedType& maskedType = + std::get(inCompatibleTypes._type); operator&=(maskedType); } From b25ad79bdcb6be8c12022c87d096c2c40b786103 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Fri, 16 Jun 2023 13:55:43 -0400 Subject: [PATCH 073/421] Start reworking property texture view --- .../include/CesiumGltf/FeatureIdTextureView.h | 12 +- .../CesiumGltf/PropertyTablePropertyView.h | 4 +- .../include/CesiumGltf/PropertyTableView.h | 7 +- .../CesiumGltf/PropertyTexturePropertyView.h | 93 ++++++-- .../include/CesiumGltf/PropertyTextureView.h | 9 +- CesiumGltf/src/FeatureIdTextureView.cpp | 19 +- .../src/PropertyTexturePropertyView.cpp | 83 +++++-- CesiumGltf/src/PropertyTextureView.cpp | 8 - CesiumGltf/test/TestFeatureIdTextureView.cpp | 220 ++++++++++++++++-- .../test/TestPropertyTexturePropertyView.cpp | 176 ++++++++++++-- CesiumGltf/test/TestPropertyTextureView.cpp | 6 +- 11 files changed, 524 insertions(+), 113 deletions(-) diff --git a/CesiumGltf/include/CesiumGltf/FeatureIdTextureView.h b/CesiumGltf/include/CesiumGltf/FeatureIdTextureView.h index c579ca836..dc86b9924 100644 --- a/CesiumGltf/include/CesiumGltf/FeatureIdTextureView.h +++ b/CesiumGltf/include/CesiumGltf/FeatureIdTextureView.h @@ -57,11 +57,6 @@ enum class FeatureIdTextureViewStatus { */ ErrorInvalidImageBytesPerChannel, - /** - * @brief This feature ID texture has a negative TEXCOORD set index. - */ - ErrorInvalidTexCoordSetIndex, - /** * @brief The channels of this feature ID texture property are invalid. * Channels must be in the range 0-3, with a minimum of one channel. Although @@ -97,9 +92,8 @@ class FeatureIdTextureView { featureIdTexture) noexcept; /** - * @brief Get the Feature ID for the given texture coordinates. - * - * Will return -1 when the status is not Valid. + * @brief Get the feature ID from the texture at the given texture + * coordinates. If the texture is somehow invalid, this returns -1. * * @param u The u-component of the texture coordinates. Must be within * [0.0, 1.0]. @@ -107,7 +101,7 @@ class FeatureIdTextureView { * [0.0, 1.0]. * @return The feature ID at the nearest pixel to the texture coordinates. */ - int64_t getFeatureId(double u, double v) const noexcept; + int64_t getFeatureID(double u, double v) const noexcept; /** * @brief Get the status of this view. diff --git a/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h index 451c5f14d..b9333402d 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h @@ -28,7 +28,7 @@ enum class PropertyTablePropertyViewStatus { Valid, /** - * @brief This property view was attempting to view an invalid + * @brief This property view was initialized from an invalid * {@link ExtensionExtStructuralMetadataPropertyTable}. */ ErrorInvalidPropertyTable, @@ -38,7 +38,7 @@ enum class PropertyTablePropertyViewStatus { * in the * {@link ExtensionExtStructuralMetadataPropertyTable}. */ - ErrorPropertyDoesNotExist, + ErrorNonexistentProperty, /** * @brief This property view's type does not match what is diff --git a/CesiumGltf/include/CesiumGltf/PropertyTableView.h b/CesiumGltf/include/CesiumGltf/PropertyTableView.h index 84757d07f..e36e9f1d5 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTableView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTableView.h @@ -124,7 +124,8 @@ class PropertyTableView { getClassProperty(propertyName); if (!pClassProperty) { return createInvalidPropertyView( - PropertyTablePropertyViewStatus::ErrorPropertyDoesNotExist); + StructuralMetadata::PropertyTablePropertyViewStatus:: + ErrorNonexistentProperty); } return getPropertyViewImpl(propertyName, *pClassProperty); @@ -166,7 +167,7 @@ class PropertyTableView { callback( propertyName, createInvalidPropertyView( - PropertyTablePropertyViewStatus::ErrorPropertyDoesNotExist)); + PropertyTablePropertyViewStatus::ErrorNonexistentProperty)); return; } @@ -971,7 +972,7 @@ class PropertyTableView { _pPropertyTable->properties.find(propertyName); if (propertyTablePropertyIter == _pPropertyTable->properties.end()) { return createInvalidPropertyView( - PropertyTablePropertyViewStatus::ErrorPropertyDoesNotExist); + PropertyTablePropertyViewStatus::ErrorNonexistentProperty); } const ExtensionExtStructuralMetadataPropertyTableProperty& diff --git a/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h index a6b97ccd1..18180cc2d 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h @@ -33,6 +33,12 @@ enum class PropertyTexturePropertyViewStatus { */ ErrorUninitialized, + /** + * @brief This property texture property is associated with a class property + * with an invalid or unsupported type. + */ + ErrorInvalidClassProperty, + /** * @brief This property texture property has a texture index that does not * exist in the glTF. @@ -56,11 +62,6 @@ enum class PropertyTexturePropertyViewStatus { */ ErrorEmptyImage, - /** - * @brief This property texture property has a negative TEXCOORD set index. - */ - ErrorInvalidTexCoordSetIndex, - /** * @brief The channels of this property texture property are invalid. * Channels must be in the range 0-3, with a minimum of one channel. Although @@ -89,7 +90,9 @@ enum class PropertyTexturePropertyComponentType { * @tparam T The component type, must correspond to a valid * {@link PropertyTexturePropertyComponentType}. */ -template struct PropertyTexturePropertyValue { T components[4]; }; +template struct PropertyTexturePropertyValue { + T components[4]; +}; /** * @brief A view of the data specified by a @@ -122,9 +125,10 @@ class PropertyTexturePropertyView { propertyTextureProperty) noexcept; /** - * @brief Gets the unswizzled property for the given texture coordinates. + * @brief Gets the unswizzled property value for the given texture + * coordinates. * - * Will return -1s when the status is not Valid or when the templated + * Will return 0s when the status is not Valid or when the templated * component type doesn't match the image's channel byte-size. * * @tparam T The component type to use when interpreting the channels of the @@ -136,28 +140,65 @@ class PropertyTexturePropertyView { * @return The property at the nearest pixel to the texture coordinates. */ template - PropertyTexturePropertyValue - getProperty(double u, double v) const noexcept { + PropertyTexturePropertyValue get(double u, double v) const noexcept { PropertyTexturePropertyValue property; - property.components[0] = -1; - property.components[1] = -1; - property.components[2] = -1; - property.components[3] = -1; + property.components[0] = 0; + property.components[1] = 0; + property.components[2] = 0; + property.components[3] = 0; if (this->_status != PropertyTexturePropertyViewStatus::Valid || sizeof(T) != this->_pImage->bytesPerChannel) { return property; } - // TODO: actually use the sampler?? + double fraction = 0, integral = 0; + int64_t integer = 0; + switch (this->_pSampler->wrapS) { + case Sampler::WrapS::REPEAT: + fraction = std::modf(u, &integral); + // Wrap negative values. + u = fraction < 0 ? 1.0 - fraction : fraction; + break; + case Sampler::WrapS::MIRRORED_REPEAT: + fraction = std::abs(std::modf(u, &integral)); + integer = static_cast(std::abs(integral)); + // If the integer part is odd, the direction is reversed. + u = integer % 2 == 1 ? 1.0 - fraction : fraction; + break; + case Sampler::WrapS::CLAMP_TO_EDGE: + default: + u = std::clamp(u, 0.0, 1.0); + break; + } + + switch (this->_pSampler->wrapT) { + case Sampler::WrapT::REPEAT: + fraction = std::modf(v, &integral); + // Wrap negative values. + v = fraction < 0 ? 1.0 - fraction : fraction; + break; + case Sampler::WrapT::MIRRORED_REPEAT: + fraction = std::abs(std::modf(v, &integral)); + integer = static_cast(std::abs(integral)); + // If the integer part is odd, the direction is reversed. + v = integer % 2 == 1 ? 1.0 - fraction : fraction; + break; + case Sampler::WrapT::CLAMP_TO_EDGE: + default: + v = std::clamp(u, 0.0, 1.0); + break; + } + + // Clamp here to ensure no out-of-bounds data access. int64_t x = std::clamp( std::llround(u * this->_pImage->width), 0LL, - (long long)this->_pImage->width); + static_cast(this->_pImage->width) - 1); int64_t y = std::clamp( std::llround(v * this->_pImage->height), 0LL, - (long long)this->_pImage->height); + static_cast(this->_pImage->height) - 1); int64_t pixelOffset = this->_pImage->bytesPerChannel * this->_pImage->channels * @@ -165,6 +206,9 @@ class PropertyTexturePropertyView { const T* pRedChannel = reinterpret_cast( this->_pImage->pixelData.data() + pixelOffset); + // TODO: account for the sampler filter. + // TODO: it is possible for channels to represent multi-byte values for a + // property. But we only support uint8 property types at the moment. for (size_t i = 0; i < this->_channels.size(); i++) { const size_t channel = static_cast(this->_channels[i]); property.components[channel] = *(pRedChannel + channel); @@ -185,15 +229,16 @@ class PropertyTexturePropertyView { /** * @brief Get the component type for this property. */ - PropertyTexturePropertyComponentType getPropertyType() const noexcept { - return this->_type; + PropertyTexturePropertyComponentType + getPropertyComponentType() const noexcept { + return this->_componentType; } /** - * @brief Get the count for this property. This is equivalent to how many - * channels a pixel value for this property will use. + * @brief Get the component count of this property. This is equivalent to how + * many channels a pixel value for this property will use. */ - int64_t getCount() const noexcept { return this->_count; } + int64_t getComponentCount() const noexcept { return this->_componentCount; } /** * @brief Get the texture coordinate set index for this property. @@ -236,8 +281,8 @@ class PropertyTexturePropertyView { int64_t _texCoordSetIndex; std::vector _channels; std::string _swizzle; - PropertyTexturePropertyComponentType _type; - int64_t _count; + PropertyTexturePropertyComponentType _componentType; + int64_t _componentCount; bool _normalized; }; } // namespace CesiumGltf diff --git a/CesiumGltf/include/CesiumGltf/PropertyTextureView.h b/CesiumGltf/include/CesiumGltf/PropertyTextureView.h index 69c0f70e2..5cd5e5b34 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTextureView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTextureView.h @@ -50,14 +50,7 @@ enum class PropertyTextureViewStatus { * @brief A property name specified in the property texture could not be found * in the class. */ - ErrorClassPropertyNotFound, - - /** - * @brief A property view for one of this property texture's properties failed - * to initialize successfully. Look for the invalid property view's status to - * find why it failed. - */ - ErrorInvalidPropertyView + ErrorClassPropertyNotFound }; /** diff --git a/CesiumGltf/src/FeatureIdTextureView.cpp b/CesiumGltf/src/FeatureIdTextureView.cpp index 589f98ac2..a51b72595 100644 --- a/CesiumGltf/src/FeatureIdTextureView.cpp +++ b/CesiumGltf/src/FeatureIdTextureView.cpp @@ -3,7 +3,7 @@ namespace CesiumGltf { FeatureIdTextureView::FeatureIdTextureView() noexcept : _status(FeatureIdTextureViewStatus::ErrorUninitialized), - _texCoordSetIndex(-1), + _texCoordSetIndex(0), _channels(), _pImage(nullptr) {} @@ -11,8 +11,8 @@ FeatureIdTextureView::FeatureIdTextureView( const Model& model, const ExtensionExtMeshFeaturesFeatureIdTexture& featureIdTexture) noexcept : _status(FeatureIdTextureViewStatus::ErrorUninitialized), - _texCoordSetIndex(-1), - _channels(featureIdTexture.channels), + _texCoordSetIndex(featureIdTexture.texCoord), + _channels(), _pImage(nullptr) { int32_t textureIndex = featureIdTexture.index; if (textureIndex < 0 || @@ -29,7 +29,6 @@ FeatureIdTextureView::FeatureIdTextureView( } // Ignore the texture's sampler, we will always use nearest pixel sampling. - this->_pImage = &model.images[static_cast(texture.source)].cesium; if (this->_pImage->width < 1 || this->_pImage->height < 1) { this->_status = FeatureIdTextureViewStatus::ErrorEmptyImage; @@ -45,12 +44,6 @@ FeatureIdTextureView::FeatureIdTextureView( return; } - if (featureIdTexture.texCoord < 0) { - this->_status = FeatureIdTextureViewStatus::ErrorInvalidTexCoordSetIndex; - return; - } - this->_texCoordSetIndex = featureIdTexture.texCoord; - const std::vector& channels = featureIdTexture.channels; if (channels.size() == 0 || channels.size() > 4 || channels.size() > static_cast(this->_pImage->channels)) { @@ -70,7 +63,7 @@ FeatureIdTextureView::FeatureIdTextureView( this->_status = FeatureIdTextureViewStatus::Valid; } -int64_t FeatureIdTextureView::getFeatureId(double u, double v) const noexcept { +int64_t FeatureIdTextureView::getFeatureID(double u, double v) const noexcept { if (this->_status != FeatureIdTextureViewStatus::Valid) { return -1; } @@ -78,11 +71,11 @@ int64_t FeatureIdTextureView::getFeatureId(double u, double v) const noexcept { int64_t x = std::clamp( std::llround(u * this->_pImage->width), 0LL, - (long long)this->_pImage->width); + (long long)this->_pImage->width - 1); int64_t y = std::clamp( std::llround(v * this->_pImage->height), 0LL, - (long long)this->_pImage->height); + (long long)this->_pImage->height - 1); int64_t pixelOffset = this->_pImage->bytesPerChannel * this->_pImage->channels * diff --git a/CesiumGltf/src/PropertyTexturePropertyView.cpp b/CesiumGltf/src/PropertyTexturePropertyView.cpp index d854468d2..495b5da55 100644 --- a/CesiumGltf/src/PropertyTexturePropertyView.cpp +++ b/CesiumGltf/src/PropertyTexturePropertyView.cpp @@ -1,17 +1,47 @@ #include "CesiumGltf/PropertyTexturePropertyView.h" +#include + namespace CesiumGltf { +namespace { +static std::unordered_set supportedTypes{ + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, + ExtensionExtStructuralMetadataClassProperty::Type::VEC2, + ExtensionExtStructuralMetadataClassProperty::Type::VEC3, + ExtensionExtStructuralMetadataClassProperty::Type::VEC4}; + +bool isValidClassProperty( + const ExtensionExtStructuralMetadataClassProperty& classProperty) { + if (supportedTypes.find(classProperty.type) == supportedTypes.end()) { + return false; + } + + // Non-arrays don't need further validation. + if (!classProperty.array) { + return true; + } + + if (classProperty.type != + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR) { + return false; + } + + int64_t count = classProperty.count.value_or(0); + return count > 0 && count <= 4; +} +} // namespace + PropertyTexturePropertyView::PropertyTexturePropertyView() noexcept : _status(PropertyTexturePropertyViewStatus::ErrorUninitialized), _pClassProperty(nullptr), _pSampler(nullptr), _pImage(nullptr), - _texCoordSetIndex(-1), + _texCoordSetIndex(0), _channels(), _swizzle(""), - _type(PropertyTexturePropertyComponentType::Uint8), - _count(0), + _componentType(PropertyTexturePropertyComponentType::Uint8), + _componentCount(0), _normalized(false) {} PropertyTexturePropertyView::PropertyTexturePropertyView( @@ -26,9 +56,36 @@ PropertyTexturePropertyView::PropertyTexturePropertyView( _texCoordSetIndex(propertyTextureProperty.texCoord), _channels(), _swizzle(""), - _type(PropertyTexturePropertyComponentType::Uint8), - _count(0), + _componentType(PropertyTexturePropertyComponentType::Uint8), + _componentCount(0), _normalized(false) { + + if (!isValidClassProperty(classProperty)) { + this->_status = + PropertyTexturePropertyViewStatus::ErrorInvalidClassProperty; + return; + } + + if (classProperty.array) { + this->_componentCount = *classProperty.count; + } else if ( + classProperty.type == + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR) { + this->_componentCount = 1; + } else if ( + classProperty.type == + ExtensionExtStructuralMetadataClassProperty::Type::VEC2) { + this->_componentCount = 2; + } else if ( + classProperty.type == + ExtensionExtStructuralMetadataClassProperty::Type::VEC3) { + this->_componentCount = 3; + } else if ( + classProperty.type == + ExtensionExtStructuralMetadataClassProperty::Type::VEC4) { + this->_componentCount = 4; + } + const int64_t index = propertyTextureProperty.index; if (index < 0 || static_cast(index) >= model.textures.size()) { this->_status = PropertyTexturePropertyViewStatus::ErrorInvalidTexture; @@ -57,23 +114,17 @@ PropertyTexturePropertyView::PropertyTexturePropertyView( return; } - if (this->_texCoordSetIndex < 0) { - this->_status = - PropertyTexturePropertyViewStatus::ErrorInvalidTexCoordSetIndex; - return; - } - - // TODO: support more types - // this->_type = ... + // TODO: support more component types + // this->_componentType = ... - this->_count = - this->_pClassProperty->count ? *this->_pClassProperty->count : 1; this->_normalized = this->_pClassProperty->normalized; + // TODO: channels can represent a multi-byte value, so the last check will + // need to change. const std::vector& channels = propertyTextureProperty.channels; if (channels.size() == 0 || channels.size() > 4 || channels.size() > static_cast(this->_pImage->channels) || - channels.size() != static_cast(this->_count)) { + channels.size() != static_cast(this->_componentCount)) { this->_status = PropertyTexturePropertyViewStatus::ErrorInvalidChannels; return; } diff --git a/CesiumGltf/src/PropertyTextureView.cpp b/CesiumGltf/src/PropertyTextureView.cpp index 7f917c8a9..bd795ad6f 100644 --- a/CesiumGltf/src/PropertyTextureView.cpp +++ b/CesiumGltf/src/PropertyTextureView.cpp @@ -55,14 +55,6 @@ PropertyTextureView::PropertyTextureView( property.second); } - for (const auto& propertyView : this->_propertyViews) { - if (propertyView.second.status() != - PropertyTexturePropertyViewStatus::Valid) { - this->_status = PropertyTextureViewStatus::ErrorInvalidPropertyView; - return; - } - } - this->_status = PropertyTextureViewStatus::Valid; } diff --git a/CesiumGltf/test/TestFeatureIdTextureView.cpp b/CesiumGltf/test/TestFeatureIdTextureView.cpp index a6755667f..112148ed0 100644 --- a/CesiumGltf/test/TestFeatureIdTextureView.cpp +++ b/CesiumGltf/test/TestFeatureIdTextureView.cpp @@ -123,8 +123,7 @@ TEST_CASE("Test FeatureIdTextureView on feature ID texture with too many bytes " } TEST_CASE( - "Test FeatureIdTextureView on feature ID texture with negative texcoord " - "set index") { + "Test FeatureIdTextureView on feature ID texture with zero channels") { Model model; Mesh& mesh = model.meshes.emplace_back(); MeshPrimitive& primitive = mesh.primitives.emplace_back(); @@ -142,21 +141,19 @@ TEST_CASE( ExtensionExtMeshFeaturesFeatureIdTexture featureIdTexture; featureIdTexture.index = 0; - featureIdTexture.texCoord = -1; - featureIdTexture.channels = {0}; + featureIdTexture.texCoord = 0; + featureIdTexture.channels = {}; ExtensionExtMeshFeaturesFeatureId featureId = meshFeatures.featureIds.emplace_back(); featureId.texture = featureIdTexture; FeatureIdTextureView view(model, featureIdTexture); - REQUIRE( - view.status() == - FeatureIdTextureViewStatus::ErrorInvalidTexCoordSetIndex); + REQUIRE(view.status() == FeatureIdTextureViewStatus::ErrorInvalidChannels); } TEST_CASE( - "Test FeatureIdTextureView on feature ID texture with zero channels") { + "Test FeatureIdTextureView on feature ID texture with too many channels") { Model model; Mesh& mesh = model.meshes.emplace_back(); MeshPrimitive& primitive = mesh.primitives.emplace_back(); @@ -175,7 +172,7 @@ TEST_CASE( ExtensionExtMeshFeaturesFeatureIdTexture featureIdTexture; featureIdTexture.index = 0; featureIdTexture.texCoord = 0; - featureIdTexture.channels = {}; + featureIdTexture.channels = {0, 1, 2, 3, 3}; ExtensionExtMeshFeaturesFeatureId featureId = meshFeatures.featureIds.emplace_back(); @@ -185,8 +182,8 @@ TEST_CASE( REQUIRE(view.status() == FeatureIdTextureViewStatus::ErrorInvalidChannels); } -TEST_CASE( - "Test FeatureIdTextureView on feature ID texture with too many channels") { +TEST_CASE("Test FeatureIdTextureView on feature ID texture with out of range " + "channel") { Model model; Mesh& mesh = model.meshes.emplace_back(); MeshPrimitive& primitive = mesh.primitives.emplace_back(); @@ -205,7 +202,7 @@ TEST_CASE( ExtensionExtMeshFeaturesFeatureIdTexture featureIdTexture; featureIdTexture.index = 0; featureIdTexture.texCoord = 0; - featureIdTexture.channels = {0, 1, 2, 3, 3}; + featureIdTexture.channels = {4}; ExtensionExtMeshFeaturesFeatureId featureId = meshFeatures.featureIds.emplace_back(); @@ -215,8 +212,36 @@ TEST_CASE( REQUIRE(view.status() == FeatureIdTextureViewStatus::ErrorInvalidChannels); } -TEST_CASE("Test FeatureIdTextureView on feature ID texture with out of range " - "channel") { +TEST_CASE("Test FeatureIdTextureView on valid feature ID texture") { + Model model; + Mesh& mesh = model.meshes.emplace_back(); + MeshPrimitive& primitive = mesh.primitives.emplace_back(); + + Image& image = model.images.emplace_back(); + image.cesium.width = 1; + image.cesium.height = 1; + + Texture& texture = model.textures.emplace_back(); + texture.sampler = 0; + texture.source = 0; + + ExtensionExtMeshFeatures& meshFeatures = + primitive.addExtension(); + + ExtensionExtMeshFeaturesFeatureIdTexture featureIdTexture; + featureIdTexture.index = 0; + featureIdTexture.texCoord = 0; + featureIdTexture.channels = {0}; + + ExtensionExtMeshFeaturesFeatureId featureId = + meshFeatures.featureIds.emplace_back(); + featureId.texture = featureIdTexture; + + FeatureIdTextureView view(model, featureIdTexture); + REQUIRE(view.status() == FeatureIdTextureViewStatus::Valid); +} + +TEST_CASE("Test getFeatureID on invalid feature ID texture view") { Model model; Mesh& mesh = model.meshes.emplace_back(); MeshPrimitive& primitive = mesh.primitives.emplace_back(); @@ -243,4 +268,171 @@ TEST_CASE("Test FeatureIdTextureView on feature ID texture with out of range " FeatureIdTextureView view(model, featureIdTexture); REQUIRE(view.status() == FeatureIdTextureViewStatus::ErrorInvalidChannels); + REQUIRE(view.getFeatureID(0, 0) == -1); +} + +TEST_CASE("Test getFeatureID on valid feature ID texture view") { + Model model; + Mesh& mesh = model.meshes.emplace_back(); + MeshPrimitive& primitive = mesh.primitives.emplace_back(); + + std::vector featureIDs{1, 2, 0, 7}; + + Image& image = model.images.emplace_back(); + image.cesium.width = 2; + image.cesium.height = 2; + image.cesium.channels = 1; + image.cesium.bytesPerChannel = 1; + image.cesium.pixelData.resize(featureIDs.size()); + std::memcpy( + image.cesium.pixelData.data(), + featureIDs.data(), + featureIDs.size()); + + Texture& texture = model.textures.emplace_back(); + texture.sampler = 0; + texture.source = 0; + + ExtensionExtMeshFeatures& meshFeatures = + primitive.addExtension(); + + ExtensionExtMeshFeaturesFeatureIdTexture featureIdTexture; + featureIdTexture.index = 0; + featureIdTexture.texCoord = 0; + featureIdTexture.channels = {0}; + + ExtensionExtMeshFeaturesFeatureId featureId = + meshFeatures.featureIds.emplace_back(); + featureId.texture = featureIdTexture; + + FeatureIdTextureView view(model, featureIdTexture); + REQUIRE(view.status() == FeatureIdTextureViewStatus::Valid); + REQUIRE(view.getFeatureID(0, 0) == 1); + REQUIRE(view.getFeatureID(1, 0) == 2); + REQUIRE(view.getFeatureID(0, 1) == 0); + REQUIRE(view.getFeatureID(1, 1) == 7); +} + +TEST_CASE("Test getFeatureID rounds to nearest pixel") { + Model model; + Mesh& mesh = model.meshes.emplace_back(); + MeshPrimitive& primitive = mesh.primitives.emplace_back(); + + std::vector featureIDs{1, 2, 0, 7}; + + Image& image = model.images.emplace_back(); + image.cesium.width = 2; + image.cesium.height = 2; + image.cesium.channels = 1; + image.cesium.bytesPerChannel = 1; + image.cesium.pixelData.resize(featureIDs.size()); + std::memcpy( + image.cesium.pixelData.data(), + featureIDs.data(), + featureIDs.size()); + + Texture& texture = model.textures.emplace_back(); + texture.sampler = 0; + texture.source = 0; + + ExtensionExtMeshFeatures& meshFeatures = + primitive.addExtension(); + + ExtensionExtMeshFeaturesFeatureIdTexture featureIdTexture; + featureIdTexture.index = 0; + featureIdTexture.texCoord = 0; + featureIdTexture.channels = {0}; + + ExtensionExtMeshFeaturesFeatureId featureId = + meshFeatures.featureIds.emplace_back(); + featureId.texture = featureIdTexture; + + FeatureIdTextureView view(model, featureIdTexture); + REQUIRE(view.status() == FeatureIdTextureViewStatus::Valid); + REQUIRE(view.getFeatureID(0.1, 0.24) == 1); + REQUIRE(view.getFeatureID(0.86, 0.2) == 2); + REQUIRE(view.getFeatureID(0.21, 0.555) == 0); + REQUIRE(view.getFeatureID(0.99, 0.81) == 7); +} + +TEST_CASE("Test getFeatureID clamps values") { + Model model; + Mesh& mesh = model.meshes.emplace_back(); + MeshPrimitive& primitive = mesh.primitives.emplace_back(); + + std::vector featureIDs{1, 2, 0, 7}; + + Image& image = model.images.emplace_back(); + image.cesium.width = 2; + image.cesium.height = 2; + image.cesium.channels = 1; + image.cesium.bytesPerChannel = 1; + + auto& data = image.cesium.pixelData; + data.resize(featureIDs.size() * sizeof(uint8_t)); + std::memcpy(data.data(), featureIDs.data(), data.size()); + + Texture& texture = model.textures.emplace_back(); + texture.sampler = 0; + texture.source = 0; + + ExtensionExtMeshFeatures& meshFeatures = + primitive.addExtension(); + + ExtensionExtMeshFeaturesFeatureIdTexture featureIdTexture; + featureIdTexture.index = 0; + featureIdTexture.texCoord = 0; + featureIdTexture.channels = {0}; + + ExtensionExtMeshFeaturesFeatureId featureId = + meshFeatures.featureIds.emplace_back(); + featureId.texture = featureIdTexture; + + FeatureIdTextureView view(model, featureIdTexture); + REQUIRE(view.status() == FeatureIdTextureViewStatus::Valid); + REQUIRE(view.getFeatureID(-1, -1) == 1); + REQUIRE(view.getFeatureID(2, 0) == 2); + REQUIRE(view.getFeatureID(-1, 2) == 0); + REQUIRE(view.getFeatureID(3, 4) == 7); +} + +TEST_CASE("Test getFeatureID handles multiple channels") { + Model model; + Mesh& mesh = model.meshes.emplace_back(); + MeshPrimitive& primitive = mesh.primitives.emplace_back(); + + std::vector featureIDs{260, 512, 8, 17}; + + Image& image = model.images.emplace_back(); + image.cesium.width = 2; + image.cesium.height = 2; + image.cesium.channels = 2; + image.cesium.bytesPerChannel = 1; + + auto& data = image.cesium.pixelData; + data.resize(featureIDs.size() * sizeof(uint16_t)); + std::memcpy(data.data(), featureIDs.data(), data.size()); + + Texture& texture = model.textures.emplace_back(); + texture.sampler = 0; + texture.source = 0; + + ExtensionExtMeshFeatures& meshFeatures = + primitive.addExtension(); + + ExtensionExtMeshFeaturesFeatureIdTexture featureIdTexture; + featureIdTexture.index = 0; + featureIdTexture.texCoord = 0; + featureIdTexture.channels = {0, 1}; + + ExtensionExtMeshFeaturesFeatureId featureId = + meshFeatures.featureIds.emplace_back(); + featureId.texture = featureIdTexture; + + FeatureIdTextureView view(model, featureIdTexture); + REQUIRE(view.status() == FeatureIdTextureViewStatus::Valid); + REQUIRE(view.getFeatureID(0, 0) == 260); + REQUIRE(view.getFeatureID(1, 0) == 512); + REQUIRE(view.getFeatureID(0, 1) == 8); + REQUIRE(view.getFeatureID(1, 1) == 17); } diff --git a/CesiumGltf/test/TestPropertyTexturePropertyView.cpp b/CesiumGltf/test/TestPropertyTexturePropertyView.cpp index e6d96520e..378fad54d 100644 --- a/CesiumGltf/test/TestPropertyTexturePropertyView.cpp +++ b/CesiumGltf/test/TestPropertyTexturePropertyView.cpp @@ -161,8 +161,8 @@ TEST_CASE("Test PropertyTexturePropertyView on property with empty image") { REQUIRE(view.status() == PropertyTexturePropertyViewStatus::ErrorEmptyImage); } -TEST_CASE("Test PropertyTextureView on property table property with negative " - "texcoord set index") { +TEST_CASE("Test PropertyTextureView on property texture property with zero " + "channels") { Model model; ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); @@ -179,6 +179,7 @@ TEST_CASE("Test PropertyTextureView on property table property with negative " Image& image = model.images.emplace_back(); image.cesium.width = 1; image.cesium.height = 1; + image.cesium.channels = 1; model.samplers.emplace_back(); @@ -192,19 +193,18 @@ TEST_CASE("Test PropertyTextureView on property table property with negative " ExtensionExtStructuralMetadataPropertyTextureProperty& propertyTextureProperty = propertyTexture.properties["TestClassProperty"]; propertyTextureProperty.index = 0; - propertyTextureProperty.texCoord = -1; - propertyTextureProperty.channels = {0}; + propertyTextureProperty.texCoord = 0; + propertyTextureProperty.channels = {}; PropertyTexturePropertyView view( model, testClassProperty, propertyTextureProperty); REQUIRE( - view.status() == - PropertyTexturePropertyViewStatus::ErrorInvalidTexCoordSetIndex); + view.status() == PropertyTexturePropertyViewStatus::ErrorInvalidChannels); } -TEST_CASE("Test PropertyTextureView on property texture property with zero " +TEST_CASE("Test PropertyTextureView on property texture property with too many " "channels") { Model model; ExtensionModelExtStructuralMetadata& metadata = @@ -237,7 +237,7 @@ TEST_CASE("Test PropertyTextureView on property texture property with zero " propertyTextureProperty = propertyTexture.properties["TestClassProperty"]; propertyTextureProperty.index = 0; propertyTextureProperty.texCoord = 0; - propertyTextureProperty.channels = {}; + propertyTextureProperty.channels = {0, 1}; PropertyTexturePropertyView view( model, @@ -247,8 +247,7 @@ TEST_CASE("Test PropertyTextureView on property texture property with zero " view.status() == PropertyTexturePropertyViewStatus::ErrorInvalidChannels); } -TEST_CASE("Test PropertyTextureView on property texture property with too many " - "channels") { +TEST_CASE("Test PropertyTexturePropertyView on valid property texture") { Model model; ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); @@ -280,12 +279,163 @@ TEST_CASE("Test PropertyTextureView on property texture property with too many " propertyTextureProperty = propertyTexture.properties["TestClassProperty"]; propertyTextureProperty.index = 0; propertyTextureProperty.texCoord = 0; - propertyTextureProperty.channels = {0, 1}; + propertyTextureProperty.channels = {0}; PropertyTexturePropertyView view( model, testClassProperty, propertyTextureProperty); - REQUIRE( - view.status() == PropertyTexturePropertyViewStatus::ErrorInvalidChannels); + REQUIRE(view.status() == PropertyTexturePropertyViewStatus::Valid); +} + +TEST_CASE("Test getSwizzle") { + Model model; + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); + ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; + ExtensionExtStructuralMetadataClassProperty& testClassProperty = + testClass.properties["TestClassProperty"]; + testClassProperty.type = + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR; + testClassProperty.componentType = + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT8; + testClassProperty.count = 4; + + Image& image = model.images.emplace_back(); + image.cesium.width = 1; + image.cesium.height = 1; + image.cesium.channels = 4; + + model.samplers.emplace_back(); + + Texture& texture = model.textures.emplace_back(); + texture.sampler = 0; + texture.source = 0; + + ExtensionExtStructuralMetadataPropertyTexture propertyTexture; + propertyTexture.classProperty = "TestClass"; + + ExtensionExtStructuralMetadataPropertyTextureProperty& + propertyTextureProperty = propertyTexture.properties["TestClassProperty"]; + propertyTextureProperty.index = 0; + propertyTextureProperty.texCoord = 0; + propertyTextureProperty.channels = {0, 2, 3, 1}; + + PropertyTexturePropertyView view( + model, + testClassProperty, + propertyTextureProperty); + REQUIRE(view.status() == PropertyTexturePropertyViewStatus::Valid); + REQUIRE(view.getCount() == 4); + REQUIRE(view.getSwizzle() == "rbag"); +} + +TEST_CASE("Test getting value from invalid view") { + Model model; + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); + ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; + ExtensionExtStructuralMetadataClassProperty& testClassProperty = + testClass.properties["TestClassProperty"]; + testClassProperty.type = + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR; + testClassProperty.componentType = + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT8; + + Image& image = model.images.emplace_back(); + image.cesium.width = 0; + image.cesium.height = 1; + image.cesium.channels = 1; + + model.samplers.emplace_back(); + + Texture& texture = model.textures.emplace_back(); + texture.sampler = 0; + texture.source = 0; + + ExtensionExtStructuralMetadataPropertyTexture propertyTexture; + propertyTexture.classProperty = "TestClass"; + + ExtensionExtStructuralMetadataPropertyTextureProperty& + propertyTextureProperty = propertyTexture.properties["TestClassProperty"]; + propertyTextureProperty.index = 0; + propertyTextureProperty.texCoord = 0; + propertyTextureProperty.channels = {0}; + + PropertyTexturePropertyView view( + model, + testClassProperty, + propertyTextureProperty); + REQUIRE(view.status() != PropertyTexturePropertyViewStatus::Valid); + PropertyTexturePropertyValue value = view.get(0, 0); + REQUIRE(value.components[0] == 0); + REQUIRE(value.components[1] == 0); + REQUIRE(value.components[2] == 0); + REQUIRE(value.components[3] == 0); +} + +TEST_CASE("Test getting value from valid view") { + Model model; + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); + ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; + ExtensionExtStructuralMetadataClassProperty& testClassProperty = + testClass.properties["TestClassProperty"]; + testClassProperty.type = + ExtensionExtStructuralMetadataClassProperty::Type::SCALAR; + testClassProperty.componentType = + ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT8; + + std::vector values{10, 8, 4, 22}; + + Image& image = model.images.emplace_back(); + image.cesium.width = 2; + image.cesium.height = 2; + image.cesium.channels = 1; + image.cesium.bytesPerChannel = 1; + image.cesium.pixelData.resize(values.size()); + std::memcpy(image.cesium.pixelData.data(), values.data(), values.size()); + + Sampler& sampler = model.samplers.emplace_back(); + sampler.wrapS = Sampler::WrapS::CLAMP_TO_EDGE; + sampler.wrapT = Sampler::WrapT::CLAMP_TO_EDGE; + + Texture& texture = model.textures.emplace_back(); + texture.sampler = 0; + texture.source = 0; + + ExtensionExtStructuralMetadataPropertyTexture propertyTexture; + propertyTexture.classProperty = "TestClass"; + + ExtensionExtStructuralMetadataPropertyTextureProperty& + propertyTextureProperty = propertyTexture.properties["TestClassProperty"]; + propertyTextureProperty.index = 0; + propertyTextureProperty.texCoord = 0; + propertyTextureProperty.channels = {0}; + + PropertyTexturePropertyView view( + model, + testClassProperty, + propertyTextureProperty); + REQUIRE(view.status() == PropertyTexturePropertyViewStatus::Valid); + + std::vector> viewValues{ + view.get(0, 0), + view.get(1.0, 0), + view.get(0, 1.0), + view.get(1.0, 1.0)}; + + for (size_t i = 0; i < viewValues.size(); i++) { + PropertyTexturePropertyValue& actual = viewValues[i]; + REQUIRE(actual.components[0] == values[i]); + REQUIRE(actual.components[1] == 0); + REQUIRE(actual.components[2] == 0); + REQUIRE(actual.components[3] == 0); + } } diff --git a/CesiumGltf/test/TestPropertyTextureView.cpp b/CesiumGltf/test/TestPropertyTextureView.cpp index 6ca05cad2..7e3e674e4 100644 --- a/CesiumGltf/test/TestPropertyTextureView.cpp +++ b/CesiumGltf/test/TestPropertyTextureView.cpp @@ -130,7 +130,7 @@ TEST_CASE("Test property texture with nonexistent class property") { REQUIRE(!classProperty); } -TEST_CASE("Test property texture with invalid property") { +TEST_CASE("Test property texture with invalid property is still valid") { Model model; ExtensionModelExtStructuralMetadata& metadata = @@ -154,7 +154,7 @@ TEST_CASE("Test property texture with invalid property") { propertyTextureProperty.index = -1; PropertyTextureView view(model, propertyTexture); - REQUIRE(view.status() == PropertyTextureViewStatus::ErrorInvalidPropertyView); + REQUIRE(view.status() == PropertyTextureViewStatus::Valid); auto properties = view.getProperties(); REQUIRE(properties.size() == 1); @@ -164,5 +164,5 @@ TEST_CASE("Test property texture with invalid property") { const ExtensionExtStructuralMetadataClassProperty* classProperty = view.getClassProperty("TestClassProperty"); - REQUIRE(!classProperty); + REQUIRE(classProperty == &testClassProperty); } From 2a29791df80dfa4eec4dee7e2272cc8526db4fb2 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Fri, 16 Jun 2023 14:31:58 -0400 Subject: [PATCH 074/421] Update upsampleGltfForRasterOverlays for EXT_structural_metadata --- .../src/upsampleGltfForRasterOverlays.cpp | 31 +++++++++---------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/Cesium3DTilesSelection/src/upsampleGltfForRasterOverlays.cpp b/Cesium3DTilesSelection/src/upsampleGltfForRasterOverlays.cpp index 3a0409607..8469f1134 100644 --- a/Cesium3DTilesSelection/src/upsampleGltfForRasterOverlays.cpp +++ b/Cesium3DTilesSelection/src/upsampleGltfForRasterOverlays.cpp @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include #include @@ -1244,24 +1244,21 @@ static int32_t copyBufferView( return static_cast(bufferViewId); } -// Copy and reconstruct buffer views and buffers from EXT_feature_metadata -// feature tables. +// Copy and reconstruct buffer views and buffers from EXT_structural_metadata +// property tables. static void copyMetadataTables(const Model& parentModel, Model& result) { - ExtensionModelExtFeatureMetadata* pMetadata = - result.getExtension(); + ExtensionModelExtStructuralMetadata* pMetadata = + result.getExtension(); if (pMetadata) { - for (auto& featureTablePair : pMetadata->featureTables) { - for (auto& propertyPair : featureTablePair.second.properties) { - FeatureTableProperty& property = propertyPair.second; - - property.bufferView = - copyBufferView(parentModel, property.bufferView, result); - property.arrayOffsetBufferView = - copyBufferView(parentModel, property.arrayOffsetBufferView, result); - property.stringOffsetBufferView = copyBufferView( - parentModel, - property.stringOffsetBufferView, - result); + for (auto& propertyTable : pMetadata->propertyTables) { + for (auto& propertyPair : propertyTable.properties) { + ExtensionExtStructuralMetadataPropertyTableProperty& property = + propertyPair.second; + property.values = copyBufferView(parentModel, property.values, result); + property.arrayOffsets = + copyBufferView(parentModel, property.arrayOffsets, result); + property.stringOffsets = + copyBufferView(parentModel, property.stringOffsets, result); } } } From e0f49321c55de1ab407df3a4e078878ec98a08b4 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Fri, 16 Jun 2023 15:16:05 -0400 Subject: [PATCH 075/421] Prefix EXT_feature_metadata files, rename EXT_mesh_features files --- .../BatchTableToGltfStructuralMetadata.cpp | 6 +- .../test/TestPntsToGltfConverter.cpp | 15 +- ...gradeBatchTableToExtStructuralMetadata.cpp | 15 +- ...s.h => ExtensionExtFeatureMetadataClass.h} | 13 +- ...xtensionExtFeatureMetadataClassProperty.h} | 5 +- ...ensionExtFeatureMetadataClassStatistics.h} | 12 +- ...um.h => ExtensionExtFeatureMetadataEnum.h} | 10 +- ...=> ExtensionExtFeatureMetadataEnumValue.h} | 6 +- ...ionExtFeatureMetadataFeatureIDAttribute.h} | 9 +- ...nsionExtFeatureMetadataFeatureIDTexture.h} | 9 +- ...> ExtensionExtFeatureMetadataFeatureIDs.h} | 5 +- ...ExtensionExtFeatureMetadataFeatureTable.h} | 12 +- ...nExtFeatureMetadataFeatureTableProperty.h} | 5 +- ...tensionExtFeatureMetadataFeatureTexture.h} | 12 +- ...ionExtFeatureMetadataPropertyStatistics.h} | 5 +- ....h => ExtensionExtFeatureMetadataSchema.h} | 16 +- ...> ExtensionExtFeatureMetadataStatistics.h} | 12 +- ...ensionExtFeatureMetadataTextureAccessor.h} | 5 +- .../CesiumGltf/ExtensionExtMeshFeatures.h | 4 +- ...ExtensionMeshPrimitiveExtFeatureMetadata.h | 10 +- .../ExtensionModelExtFeatureMetadata.h | 22 +- ...ExtMeshFeaturesFeatureId.h => FeatureId.h} | 10 +- ...sFeatureIdTexture.h => FeatureIdTexture.h} | 6 +- .../include/CesiumGltf/FeatureIdTextureView.h | 20 +- .../include/CesiumGltf/PropertyTextureView.h | 1 - CesiumGltf/src/FeatureIdTextureView.cpp | 2 +- CesiumGltf/test/TestFeatureIdTextureView.cpp | 40 +- .../generated/src/ClassJsonHandler.h | 41 - .../generated/src/EnumJsonHandler.h | 42 - ...ensionExtFeatureMetadataClassJsonHandler.h | 45 + ...FeatureMetadataClassPropertyJsonHandler.h} | 18 +- ...atureMetadataClassStatisticsJsonHandler.h} | 25 +- ...tensionExtFeatureMetadataEnumJsonHandler.h | 46 + ...nExtFeatureMetadataEnumValueJsonHandler.h} | 18 +- ...ureMetadataFeatureIDAttributeJsonHandler.h | 40 + ...atureMetadataFeatureIDTextureJsonHandler.h | 40 + ...ExtFeatureMetadataFeatureIDsJsonHandler.h} | 18 +- ...tFeatureMetadataFeatureTableJsonHandler.h} | 24 +- ...MetadataFeatureTablePropertyJsonHandler.h} | 17 +- ...eatureMetadataFeatureTextureJsonHandler.h} | 24 +- ...reMetadataPropertyStatisticsJsonHandler.h} | 19 +- ...nsionExtFeatureMetadataSchemaJsonHandler.h | 51 + ...nExtFeatureMetadataStatisticsJsonHandler.h | 42 + ...atureMetadataTextureAccessorJsonHandler.h} | 19 +- .../src/ExtensionExtMeshFeaturesJsonHandler.h | 9 +- ...shPrimitiveExtFeatureMetadataJsonHandler.h | 12 +- ...ensionModelExtFeatureMetadataJsonHandler.h | 23 +- .../src/FeatureIDAttributeJsonHandler.h | 39 - .../src/FeatureIDTextureJsonHandler.h | 39 - ...IdJsonHandler.h => FeatureIdJsonHandler.h} | 22 +- ...andler.h => FeatureIdTextureJsonHandler.h} | 20 +- .../generated/src/GeneratedJsonHandlers.cpp | 3509 ++++------------- .../generated/src/SchemaJsonHandler.h | 44 - .../generated/src/StatisticsJsonHandler.h | 40 - .../generated/src/registerExtensions.cpp | 123 +- .../generated/src/ModelJsonWriter.cpp | 177 +- .../generated/src/ModelJsonWriter.h | 136 +- tools/generate-classes/glTF.json | 34 +- 58 files changed, 1632 insertions(+), 3411 deletions(-) rename CesiumGltf/generated/include/CesiumGltf/{Class.h => ExtensionExtFeatureMetadataClass.h} (63%) rename CesiumGltf/generated/include/CesiumGltf/{ClassProperty.h => ExtensionExtFeatureMetadataClassProperty.h} (97%) rename CesiumGltf/generated/include/CesiumGltf/{ClassStatistics.h => ExtensionExtFeatureMetadataClassStatistics.h} (66%) rename CesiumGltf/generated/include/CesiumGltf/{Enum.h => ExtensionExtFeatureMetadataEnum.h} (80%) rename CesiumGltf/generated/include/CesiumGltf/{EnumValue.h => ExtensionExtFeatureMetadataEnumValue.h} (73%) rename CesiumGltf/generated/include/CesiumGltf/{FeatureIDAttribute.h => ExtensionExtFeatureMetadataFeatureIDAttribute.h} (72%) rename CesiumGltf/generated/include/CesiumGltf/{FeatureIDTexture.h => ExtensionExtFeatureMetadataFeatureIDTexture.h} (75%) rename CesiumGltf/generated/include/CesiumGltf/{FeatureIDs.h => ExtensionExtFeatureMetadataFeatureIDs.h} (87%) rename CesiumGltf/generated/include/CesiumGltf/{FeatureTable.h => ExtensionExtFeatureMetadataFeatureTable.h} (73%) rename CesiumGltf/generated/include/CesiumGltf/{FeatureTableProperty.h => ExtensionExtFeatureMetadataFeatureTableProperty.h} (95%) rename CesiumGltf/generated/include/CesiumGltf/{FeatureTexture.h => ExtensionExtFeatureMetadataFeatureTexture.h} (71%) rename CesiumGltf/generated/include/CesiumGltf/{PropertyStatistics.h => ExtensionExtFeatureMetadataPropertyStatistics.h} (95%) rename CesiumGltf/generated/include/CesiumGltf/{Schema.h => ExtensionExtFeatureMetadataSchema.h} (63%) rename CesiumGltf/generated/include/CesiumGltf/{Statistics.h => ExtensionExtFeatureMetadataStatistics.h} (61%) rename CesiumGltf/generated/include/CesiumGltf/{TextureAccessor.h => ExtensionExtFeatureMetadataTextureAccessor.h} (80%) rename CesiumGltf/generated/include/CesiumGltf/{ExtensionExtMeshFeaturesFeatureId.h => FeatureId.h} (79%) rename CesiumGltf/generated/include/CesiumGltf/{ExtensionExtMeshFeaturesFeatureIdTexture.h => FeatureIdTexture.h} (76%) delete mode 100644 CesiumGltfReader/generated/src/ClassJsonHandler.h delete mode 100644 CesiumGltfReader/generated/src/EnumJsonHandler.h create mode 100644 CesiumGltfReader/generated/src/ExtensionExtFeatureMetadataClassJsonHandler.h rename CesiumGltfReader/generated/src/{ClassPropertyJsonHandler.h => ExtensionExtFeatureMetadataClassPropertyJsonHandler.h} (70%) rename CesiumGltfReader/generated/src/{ClassStatisticsJsonHandler.h => ExtensionExtFeatureMetadataClassStatisticsJsonHandler.h} (50%) create mode 100644 CesiumGltfReader/generated/src/ExtensionExtFeatureMetadataEnumJsonHandler.h rename CesiumGltfReader/generated/src/{EnumValueJsonHandler.h => ExtensionExtFeatureMetadataEnumValueJsonHandler.h} (60%) create mode 100644 CesiumGltfReader/generated/src/ExtensionExtFeatureMetadataFeatureIDAttributeJsonHandler.h create mode 100644 CesiumGltfReader/generated/src/ExtensionExtFeatureMetadataFeatureIDTextureJsonHandler.h rename CesiumGltfReader/generated/src/{FeatureIDsJsonHandler.h => ExtensionExtFeatureMetadataFeatureIDsJsonHandler.h} (60%) rename CesiumGltfReader/generated/src/{FeatureTableJsonHandler.h => ExtensionExtFeatureMetadataFeatureTableJsonHandler.h} (53%) rename CesiumGltfReader/generated/src/{FeatureTablePropertyJsonHandler.h => ExtensionExtFeatureMetadataFeatureTablePropertyJsonHandler.h} (62%) rename CesiumGltfReader/generated/src/{FeatureTextureJsonHandler.h => ExtensionExtFeatureMetadataFeatureTextureJsonHandler.h} (50%) rename CesiumGltfReader/generated/src/{PropertyStatisticsJsonHandler.h => ExtensionExtFeatureMetadataPropertyStatisticsJsonHandler.h} (65%) create mode 100644 CesiumGltfReader/generated/src/ExtensionExtFeatureMetadataSchemaJsonHandler.h create mode 100644 CesiumGltfReader/generated/src/ExtensionExtFeatureMetadataStatisticsJsonHandler.h rename CesiumGltfReader/generated/src/{TextureAccessorJsonHandler.h => ExtensionExtFeatureMetadataTextureAccessorJsonHandler.h} (55%) delete mode 100644 CesiumGltfReader/generated/src/FeatureIDAttributeJsonHandler.h delete mode 100644 CesiumGltfReader/generated/src/FeatureIDTextureJsonHandler.h rename CesiumGltfReader/generated/src/{ExtensionExtMeshFeaturesFeatureIdJsonHandler.h => FeatureIdJsonHandler.h} (59%) rename CesiumGltfReader/generated/src/{ExtensionExtMeshFeaturesFeatureIdTextureJsonHandler.h => FeatureIdTextureJsonHandler.h} (53%) delete mode 100644 CesiumGltfReader/generated/src/SchemaJsonHandler.h delete mode 100644 CesiumGltfReader/generated/src/StatisticsJsonHandler.h diff --git a/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp b/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp index 9879ff418..93dce98d2 100644 --- a/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp +++ b/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp @@ -1631,8 +1631,7 @@ ErrorList BatchTableToGltfStructuralMetadata::convertFromB3dm( ExtensionExtMeshFeatures& extension = primitive.addExtension(); - ExtensionExtMeshFeaturesFeatureId& featureID = - extension.featureIds.emplace_back(); + FeatureId& featureID = extension.featureIds.emplace_back(); // No fast way to count the unique feature IDs in this primitive, so // subtitute the batch table length. featureID.featureCount = batchLength; @@ -1705,8 +1704,7 @@ ErrorList BatchTableToGltfStructuralMetadata::convertFromPnts( ExtensionExtMeshFeatures& extension = primitive.addExtension(); - ExtensionExtMeshFeaturesFeatureId& featureID = - extension.featureIds.emplace_back(); + FeatureId& featureID = extension.featureIds.emplace_back(); // Setting the feature count is sufficient for implicit feature IDs. featureID.featureCount = featureCount; diff --git a/Cesium3DTilesSelection/test/TestPntsToGltfConverter.cpp b/Cesium3DTilesSelection/test/TestPntsToGltfConverter.cpp index ca7bdca5a..e93e333f2 100644 --- a/Cesium3DTilesSelection/test/TestPntsToGltfConverter.cpp +++ b/Cesium3DTilesSelection/test/TestPntsToGltfConverter.cpp @@ -678,8 +678,7 @@ TEST_CASE("Converts point cloud with batch IDs to glTF with " auto primitiveExtension = primitive.getExtension(); REQUIRE(primitiveExtension); REQUIRE(primitiveExtension->featureIds.size() == 1); - ExtensionExtMeshFeaturesFeatureId& featureId = - primitiveExtension->featureIds[0]; + FeatureId& featureId = primitiveExtension->featureIds[0]; CHECK(featureId.featureCount == 8); CHECK(featureId.attribute == 0); @@ -757,8 +756,7 @@ TEST_CASE("Converts point cloud with per-point properties to glTF with " auto primitiveExtension = primitive.getExtension(); REQUIRE(primitiveExtension); REQUIRE(primitiveExtension->featureIds.size() == 1); - ExtensionExtMeshFeaturesFeatureId& featureId = - primitiveExtension->featureIds[0]; + FeatureId& featureId = primitiveExtension->featureIds[0]; // Check for implicit feature IDs CHECK(featureId.featureCount == pointsLength); CHECK(!featureId.attribute); @@ -816,8 +814,7 @@ TEST_CASE("Converts point cloud with Draco compression to glTF") { auto primitiveExtension = primitive.getExtension(); REQUIRE(primitiveExtension); REQUIRE(primitiveExtension->featureIds.size() == 1); - ExtensionExtMeshFeaturesFeatureId& featureId = - primitiveExtension->featureIds[0]; + FeatureId& featureId = primitiveExtension->featureIds[0]; // Check for implicit feature IDs CHECK(featureId.featureCount == pointsLength); CHECK(!featureId.attribute); @@ -960,8 +957,7 @@ TEST_CASE("Converts point cloud with partial Draco compression to glTF") { auto primitiveExtension = primitive.getExtension(); REQUIRE(primitiveExtension); REQUIRE(primitiveExtension->featureIds.size() == 1); - ExtensionExtMeshFeaturesFeatureId& featureId = - primitiveExtension->featureIds[0]; + FeatureId& featureId = primitiveExtension->featureIds[0]; // Check for implicit feature IDs CHECK(featureId.featureCount == pointsLength); CHECK(!featureId.attribute); @@ -1099,8 +1095,7 @@ TEST_CASE("Converts batched point cloud with Draco compression to glTF") { auto primitiveExtension = primitive.getExtension(); REQUIRE(primitiveExtension); REQUIRE(primitiveExtension->featureIds.size() == 1); - ExtensionExtMeshFeaturesFeatureId& featureId = - primitiveExtension->featureIds[0]; + FeatureId& featureId = primitiveExtension->featureIds[0]; CHECK(featureId.featureCount == 8); CHECK(featureId.attribute == 0); diff --git a/Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp b/Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp index 702ec8bb7..74c81c281 100644 --- a/Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp +++ b/Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp @@ -411,8 +411,7 @@ TEST_CASE("Converts JSON B3DM batch table to EXT_feature_metadata") { primitive.getExtension(); REQUIRE(pPrimitiveExtension); REQUIRE(pPrimitiveExtension->featureIds.size() == 1); - ExtensionExtMeshFeaturesFeatureId& featureId = - pPrimitiveExtension->featureIds[0]; + FeatureId& featureId = pPrimitiveExtension->featureIds[0]; CHECK(featureId.featureCount == 10); CHECK(featureId.attribute == 0); CHECK(featureId.propertyTable == 0); @@ -557,8 +556,7 @@ TEST_CASE("Convert binary B3DM batch table to EXT_structural_metadata") { primitive.getExtension(); REQUIRE(pPrimitiveExtension); REQUIRE(pPrimitiveExtension->featureIds.size() == 1); - const ExtensionExtMeshFeaturesFeatureId& featureId = - pPrimitiveExtension->featureIds[0]; + const FeatureId& featureId = pPrimitiveExtension->featureIds[0]; CHECK(featureId.featureCount == 10); CHECK(featureId.attribute == 0); CHECK(featureId.propertyTable == 0); @@ -780,8 +778,7 @@ TEST_CASE("Converts batched PNTS batch table to EXT_structural_metadata") { primitive.getExtension(); REQUIRE(pPrimitiveExtension); REQUIRE(pPrimitiveExtension->featureIds.size() == 1); - const ExtensionExtMeshFeaturesFeatureId& featureId = - pPrimitiveExtension->featureIds[0]; + const FeatureId& featureId = pPrimitiveExtension->featureIds[0]; CHECK(featureId.featureCount == 8); CHECK(featureId.attribute == 0); CHECK(featureId.propertyTable == 0); @@ -942,8 +939,7 @@ TEST_CASE("Converts per-point PNTS batch table to EXT_structural_metadata") { primitive.getExtension(); REQUIRE(pPrimitiveExtension); REQUIRE(pPrimitiveExtension->featureIds.size() == 1); - const ExtensionExtMeshFeaturesFeatureId& featureId = - pPrimitiveExtension->featureIds[0]; + const FeatureId& featureId = pPrimitiveExtension->featureIds[0]; CHECK(featureId.featureCount == 8); CHECK(!featureId.attribute); CHECK(featureId.propertyTable == 0); @@ -1104,8 +1100,7 @@ TEST_CASE("Converts Draco per-point PNTS batch table to " primitive.getExtension(); REQUIRE(pPrimitiveExtension); REQUIRE(pPrimitiveExtension->featureIds.size() == 1); - const ExtensionExtMeshFeaturesFeatureId& featureId = - pPrimitiveExtension->featureIds[0]; + const FeatureId& featureId = pPrimitiveExtension->featureIds[0]; CHECK(featureId.featureCount == 8); CHECK(!featureId.attribute); CHECK(featureId.propertyTable == 0); diff --git a/CesiumGltf/generated/include/CesiumGltf/Class.h b/CesiumGltf/generated/include/CesiumGltf/ExtensionExtFeatureMetadataClass.h similarity index 63% rename from CesiumGltf/generated/include/CesiumGltf/Class.h rename to CesiumGltf/generated/include/CesiumGltf/ExtensionExtFeatureMetadataClass.h index e48709493..85faeb5b3 100644 --- a/CesiumGltf/generated/include/CesiumGltf/Class.h +++ b/CesiumGltf/generated/include/CesiumGltf/ExtensionExtFeatureMetadataClass.h @@ -2,7 +2,7 @@ // DO NOT EDIT THIS FILE! #pragma once -#include "CesiumGltf/ClassProperty.h" +#include "CesiumGltf/ExtensionExtFeatureMetadataClassProperty.h" #include "CesiumGltf/Library.h" #include @@ -15,8 +15,10 @@ namespace CesiumGltf { /** * @brief A class containing a set of properties. */ -struct CESIUMGLTF_API Class final : public CesiumUtility::ExtensibleObject { - static inline constexpr const char* TypeName = "Class"; +struct CESIUMGLTF_API ExtensionExtFeatureMetadataClass final + : public CesiumUtility::ExtensibleObject { + static inline constexpr const char* TypeName = + "ExtensionExtFeatureMetadataClass"; /** * @brief The name of the class, e.g. for display purposes. @@ -32,6 +34,9 @@ struct CESIUMGLTF_API Class final : public CesiumUtility::ExtensibleObject { * @brief A dictionary, where each key is a property ID and each value is an * object defining the property. */ - std::unordered_map properties; + std::unordered_map< + std::string, + CesiumGltf::ExtensionExtFeatureMetadataClassProperty> + properties; }; } // namespace CesiumGltf diff --git a/CesiumGltf/generated/include/CesiumGltf/ClassProperty.h b/CesiumGltf/generated/include/CesiumGltf/ExtensionExtFeatureMetadataClassProperty.h similarity index 97% rename from CesiumGltf/generated/include/CesiumGltf/ClassProperty.h rename to CesiumGltf/generated/include/CesiumGltf/ExtensionExtFeatureMetadataClassProperty.h index 106d0d1b1..9ab79dd51 100644 --- a/CesiumGltf/generated/include/CesiumGltf/ClassProperty.h +++ b/CesiumGltf/generated/include/CesiumGltf/ExtensionExtFeatureMetadataClassProperty.h @@ -15,9 +15,10 @@ namespace CesiumGltf { /** * @brief A class property. */ -struct CESIUMGLTF_API ClassProperty final +struct CESIUMGLTF_API ExtensionExtFeatureMetadataClassProperty final : public CesiumUtility::ExtensibleObject { - static inline constexpr const char* TypeName = "ClassProperty"; + static inline constexpr const char* TypeName = + "ExtensionExtFeatureMetadataClassProperty"; /** * @brief Known values for The property type. If `ENUM` is used, then diff --git a/CesiumGltf/generated/include/CesiumGltf/ClassStatistics.h b/CesiumGltf/generated/include/CesiumGltf/ExtensionExtFeatureMetadataClassStatistics.h similarity index 66% rename from CesiumGltf/generated/include/CesiumGltf/ClassStatistics.h rename to CesiumGltf/generated/include/CesiumGltf/ExtensionExtFeatureMetadataClassStatistics.h index 83f8a5963..5a2439814 100644 --- a/CesiumGltf/generated/include/CesiumGltf/ClassStatistics.h +++ b/CesiumGltf/generated/include/CesiumGltf/ExtensionExtFeatureMetadataClassStatistics.h @@ -2,8 +2,8 @@ // DO NOT EDIT THIS FILE! #pragma once +#include "CesiumGltf/ExtensionExtFeatureMetadataPropertyStatistics.h" #include "CesiumGltf/Library.h" -#include "CesiumGltf/PropertyStatistics.h" #include @@ -15,9 +15,10 @@ namespace CesiumGltf { /** * @brief Statistics about features that conform to the class. */ -struct CESIUMGLTF_API ClassStatistics final +struct CESIUMGLTF_API ExtensionExtFeatureMetadataClassStatistics final : public CesiumUtility::ExtensibleObject { - static inline constexpr const char* TypeName = "ClassStatistics"; + static inline constexpr const char* TypeName = + "ExtensionExtFeatureMetadataClassStatistics"; /** * @brief The number of features that conform to the class. @@ -29,6 +30,9 @@ struct CESIUMGLTF_API ClassStatistics final * class' `properties` dictionary and each value is an object containing * statistics about property values. */ - std::unordered_map properties; + std::unordered_map< + std::string, + CesiumGltf::ExtensionExtFeatureMetadataPropertyStatistics> + properties; }; } // namespace CesiumGltf diff --git a/CesiumGltf/generated/include/CesiumGltf/Enum.h b/CesiumGltf/generated/include/CesiumGltf/ExtensionExtFeatureMetadataEnum.h similarity index 80% rename from CesiumGltf/generated/include/CesiumGltf/Enum.h rename to CesiumGltf/generated/include/CesiumGltf/ExtensionExtFeatureMetadataEnum.h index 2aac09e6a..37770f8d0 100644 --- a/CesiumGltf/generated/include/CesiumGltf/Enum.h +++ b/CesiumGltf/generated/include/CesiumGltf/ExtensionExtFeatureMetadataEnum.h @@ -2,7 +2,7 @@ // DO NOT EDIT THIS FILE! #pragma once -#include "CesiumGltf/EnumValue.h" +#include "CesiumGltf/ExtensionExtFeatureMetadataEnumValue.h" #include "CesiumGltf/Library.h" #include @@ -15,8 +15,10 @@ namespace CesiumGltf { /** * @brief An object defining the values of an enum. */ -struct CESIUMGLTF_API Enum final : public CesiumUtility::ExtensibleObject { - static inline constexpr const char* TypeName = "Enum"; +struct CESIUMGLTF_API ExtensionExtFeatureMetadataEnum final + : public CesiumUtility::ExtensibleObject { + static inline constexpr const char* TypeName = + "ExtensionExtFeatureMetadataEnum"; /** * @brief Known values for The type of the integer enum value. @@ -61,6 +63,6 @@ struct CESIUMGLTF_API Enum final : public CesiumUtility::ExtensibleObject { * @brief An array of enum values. Duplicate names or duplicate integer values * are not allowed. */ - std::vector values; + std::vector values; }; } // namespace CesiumGltf diff --git a/CesiumGltf/generated/include/CesiumGltf/EnumValue.h b/CesiumGltf/generated/include/CesiumGltf/ExtensionExtFeatureMetadataEnumValue.h similarity index 73% rename from CesiumGltf/generated/include/CesiumGltf/EnumValue.h rename to CesiumGltf/generated/include/CesiumGltf/ExtensionExtFeatureMetadataEnumValue.h index d1c505a4e..78480490c 100644 --- a/CesiumGltf/generated/include/CesiumGltf/EnumValue.h +++ b/CesiumGltf/generated/include/CesiumGltf/ExtensionExtFeatureMetadataEnumValue.h @@ -14,8 +14,10 @@ namespace CesiumGltf { /** * @brief An enum value. */ -struct CESIUMGLTF_API EnumValue final : public CesiumUtility::ExtensibleObject { - static inline constexpr const char* TypeName = "EnumValue"; +struct CESIUMGLTF_API ExtensionExtFeatureMetadataEnumValue final + : public CesiumUtility::ExtensibleObject { + static inline constexpr const char* TypeName = + "ExtensionExtFeatureMetadataEnumValue"; /** * @brief The name of the enum value. diff --git a/CesiumGltf/generated/include/CesiumGltf/FeatureIDAttribute.h b/CesiumGltf/generated/include/CesiumGltf/ExtensionExtFeatureMetadataFeatureIDAttribute.h similarity index 72% rename from CesiumGltf/generated/include/CesiumGltf/FeatureIDAttribute.h rename to CesiumGltf/generated/include/CesiumGltf/ExtensionExtFeatureMetadataFeatureIDAttribute.h index 458be2019..109391ce7 100644 --- a/CesiumGltf/generated/include/CesiumGltf/FeatureIDAttribute.h +++ b/CesiumGltf/generated/include/CesiumGltf/ExtensionExtFeatureMetadataFeatureIDAttribute.h @@ -2,7 +2,7 @@ // DO NOT EDIT THIS FILE! #pragma once -#include "CesiumGltf/FeatureIDs.h" +#include "CesiumGltf/ExtensionExtFeatureMetadataFeatureIDs.h" #include "CesiumGltf/Library.h" #include @@ -13,9 +13,10 @@ namespace CesiumGltf { /** * @brief An object mapping feature IDs to a feature table. */ -struct CESIUMGLTF_API FeatureIDAttribute final +struct CESIUMGLTF_API ExtensionExtFeatureMetadataFeatureIDAttribute final : public CesiumUtility::ExtensibleObject { - static inline constexpr const char* TypeName = "FeatureIDAttribute"; + static inline constexpr const char* TypeName = + "ExtensionExtFeatureMetadataFeatureIDAttribute"; /** * @brief The ID of the feature table in the model's root @@ -29,6 +30,6 @@ struct CESIUMGLTF_API FeatureIDAttribute final * `[0, count - 1]` (inclusive), where `count` is the total number of features * in the feature table. */ - CesiumGltf::FeatureIDs featureIds; + CesiumGltf::ExtensionExtFeatureMetadataFeatureIDs featureIds; }; } // namespace CesiumGltf diff --git a/CesiumGltf/generated/include/CesiumGltf/FeatureIDTexture.h b/CesiumGltf/generated/include/CesiumGltf/ExtensionExtFeatureMetadataFeatureIDTexture.h similarity index 75% rename from CesiumGltf/generated/include/CesiumGltf/FeatureIDTexture.h rename to CesiumGltf/generated/include/CesiumGltf/ExtensionExtFeatureMetadataFeatureIDTexture.h index 27bc4a51f..754f2117e 100644 --- a/CesiumGltf/generated/include/CesiumGltf/FeatureIDTexture.h +++ b/CesiumGltf/generated/include/CesiumGltf/ExtensionExtFeatureMetadataFeatureIDTexture.h @@ -2,8 +2,8 @@ // DO NOT EDIT THIS FILE! #pragma once +#include "CesiumGltf/ExtensionExtFeatureMetadataTextureAccessor.h" #include "CesiumGltf/Library.h" -#include "CesiumGltf/TextureAccessor.h" #include @@ -13,9 +13,10 @@ namespace CesiumGltf { /** * @brief An object describing a texture used for storing per-texel feature IDs. */ -struct CESIUMGLTF_API FeatureIDTexture final +struct CESIUMGLTF_API ExtensionExtFeatureMetadataFeatureIDTexture final : public CesiumUtility::ExtensibleObject { - static inline constexpr const char* TypeName = "FeatureIDTexture"; + static inline constexpr const char* TypeName = + "ExtensionExtFeatureMetadataFeatureIDTexture"; /** * @brief The ID of the feature table in the model's root @@ -31,6 +32,6 @@ struct CESIUMGLTF_API FeatureIDTexture final * must be read as integers. Texture filtering should be disabled when * fetching feature IDs. */ - CesiumGltf::TextureAccessor featureIds; + CesiumGltf::ExtensionExtFeatureMetadataTextureAccessor featureIds; }; } // namespace CesiumGltf diff --git a/CesiumGltf/generated/include/CesiumGltf/FeatureIDs.h b/CesiumGltf/generated/include/CesiumGltf/ExtensionExtFeatureMetadataFeatureIDs.h similarity index 87% rename from CesiumGltf/generated/include/CesiumGltf/FeatureIDs.h rename to CesiumGltf/generated/include/CesiumGltf/ExtensionExtFeatureMetadataFeatureIDs.h index 402625edc..ff40460e8 100644 --- a/CesiumGltf/generated/include/CesiumGltf/FeatureIDs.h +++ b/CesiumGltf/generated/include/CesiumGltf/ExtensionExtFeatureMetadataFeatureIDs.h @@ -15,9 +15,10 @@ namespace CesiumGltf { * @brief Feature IDs to be used as indices to property arrays in the feature * table. */ -struct CESIUMGLTF_API FeatureIDs final +struct CESIUMGLTF_API ExtensionExtFeatureMetadataFeatureIDs final : public CesiumUtility::ExtensibleObject { - static inline constexpr const char* TypeName = "FeatureIDs"; + static inline constexpr const char* TypeName = + "ExtensionExtFeatureMetadataFeatureIDs"; /** * @brief The name of the attribute containing feature IDs. diff --git a/CesiumGltf/generated/include/CesiumGltf/FeatureTable.h b/CesiumGltf/generated/include/CesiumGltf/ExtensionExtFeatureMetadataFeatureTable.h similarity index 73% rename from CesiumGltf/generated/include/CesiumGltf/FeatureTable.h rename to CesiumGltf/generated/include/CesiumGltf/ExtensionExtFeatureMetadataFeatureTable.h index 410f8b11a..d94cbf113 100644 --- a/CesiumGltf/generated/include/CesiumGltf/FeatureTable.h +++ b/CesiumGltf/generated/include/CesiumGltf/ExtensionExtFeatureMetadataFeatureTable.h @@ -2,7 +2,7 @@ // DO NOT EDIT THIS FILE! #pragma once -#include "CesiumGltf/FeatureTableProperty.h" +#include "CesiumGltf/ExtensionExtFeatureMetadataFeatureTableProperty.h" #include "CesiumGltf/Library.h" #include @@ -17,9 +17,10 @@ namespace CesiumGltf { * @brief A feature table defined by a class and property values stored in * arrays. */ -struct CESIUMGLTF_API FeatureTable final +struct CESIUMGLTF_API ExtensionExtFeatureMetadataFeatureTable final : public CesiumUtility::ExtensibleObject { - static inline constexpr const char* TypeName = "FeatureTable"; + static inline constexpr const char* TypeName = + "ExtensionExtFeatureMetadataFeatureTable"; /** * @brief The class that property values conform to. The value must be a class @@ -39,6 +40,9 @@ struct CESIUMGLTF_API FeatureTable final * property values are stored. Optional properties may be excluded from this * dictionary. */ - std::unordered_map properties; + std::unordered_map< + std::string, + CesiumGltf::ExtensionExtFeatureMetadataFeatureTableProperty> + properties; }; } // namespace CesiumGltf diff --git a/CesiumGltf/generated/include/CesiumGltf/FeatureTableProperty.h b/CesiumGltf/generated/include/CesiumGltf/ExtensionExtFeatureMetadataFeatureTableProperty.h similarity index 95% rename from CesiumGltf/generated/include/CesiumGltf/FeatureTableProperty.h rename to CesiumGltf/generated/include/CesiumGltf/ExtensionExtFeatureMetadataFeatureTableProperty.h index e314c3341..69dfbf0ce 100644 --- a/CesiumGltf/generated/include/CesiumGltf/FeatureTableProperty.h +++ b/CesiumGltf/generated/include/CesiumGltf/ExtensionExtFeatureMetadataFeatureTableProperty.h @@ -13,9 +13,10 @@ namespace CesiumGltf { /** * @brief An array of binary property values. */ -struct CESIUMGLTF_API FeatureTableProperty final +struct CESIUMGLTF_API ExtensionExtFeatureMetadataFeatureTableProperty final : public CesiumUtility::ExtensibleObject { - static inline constexpr const char* TypeName = "FeatureTableProperty"; + static inline constexpr const char* TypeName = + "ExtensionExtFeatureMetadataFeatureTableProperty"; /** * @brief Known values for The type of values in `arrayOffsetBufferView` and diff --git a/CesiumGltf/generated/include/CesiumGltf/FeatureTexture.h b/CesiumGltf/generated/include/CesiumGltf/ExtensionExtFeatureMetadataFeatureTexture.h similarity index 71% rename from CesiumGltf/generated/include/CesiumGltf/FeatureTexture.h rename to CesiumGltf/generated/include/CesiumGltf/ExtensionExtFeatureMetadataFeatureTexture.h index fe9666e18..5a9d2b029 100644 --- a/CesiumGltf/generated/include/CesiumGltf/FeatureTexture.h +++ b/CesiumGltf/generated/include/CesiumGltf/ExtensionExtFeatureMetadataFeatureTexture.h @@ -2,8 +2,8 @@ // DO NOT EDIT THIS FILE! #pragma once +#include "CesiumGltf/ExtensionExtFeatureMetadataTextureAccessor.h" #include "CesiumGltf/Library.h" -#include "CesiumGltf/TextureAccessor.h" #include @@ -16,9 +16,10 @@ namespace CesiumGltf { * channels. This is not to be confused with feature ID textures which store * feature IDs for use with a feature table. */ -struct CESIUMGLTF_API FeatureTexture final +struct CESIUMGLTF_API ExtensionExtFeatureMetadataFeatureTexture final : public CesiumUtility::ExtensibleObject { - static inline constexpr const char* TypeName = "FeatureTexture"; + static inline constexpr const char* TypeName = + "ExtensionExtFeatureMetadataFeatureTexture"; /** * @brief The class this feature texture conforms to. The value must be a @@ -31,6 +32,9 @@ struct CESIUMGLTF_API FeatureTexture final * class' `properties` dictionary and each value describes the texture * channels containing property values. */ - std::unordered_map properties; + std::unordered_map< + std::string, + CesiumGltf::ExtensionExtFeatureMetadataTextureAccessor> + properties; }; } // namespace CesiumGltf diff --git a/CesiumGltf/generated/include/CesiumGltf/PropertyStatistics.h b/CesiumGltf/generated/include/CesiumGltf/ExtensionExtFeatureMetadataPropertyStatistics.h similarity index 95% rename from CesiumGltf/generated/include/CesiumGltf/PropertyStatistics.h rename to CesiumGltf/generated/include/CesiumGltf/ExtensionExtFeatureMetadataPropertyStatistics.h index 97eae4d4a..c13d7e2f3 100644 --- a/CesiumGltf/generated/include/CesiumGltf/PropertyStatistics.h +++ b/CesiumGltf/generated/include/CesiumGltf/ExtensionExtFeatureMetadataPropertyStatistics.h @@ -14,9 +14,10 @@ namespace CesiumGltf { /** * @brief Statistics about property values. */ -struct CESIUMGLTF_API PropertyStatistics final +struct CESIUMGLTF_API ExtensionExtFeatureMetadataPropertyStatistics final : public CesiumUtility::ExtensibleObject { - static inline constexpr const char* TypeName = "PropertyStatistics"; + static inline constexpr const char* TypeName = + "ExtensionExtFeatureMetadataPropertyStatistics"; /** * @brief The minimum property value. Only applicable for numeric types and diff --git a/CesiumGltf/generated/include/CesiumGltf/Schema.h b/CesiumGltf/generated/include/CesiumGltf/ExtensionExtFeatureMetadataSchema.h similarity index 63% rename from CesiumGltf/generated/include/CesiumGltf/Schema.h rename to CesiumGltf/generated/include/CesiumGltf/ExtensionExtFeatureMetadataSchema.h index 838b1e567..bb6d741ea 100644 --- a/CesiumGltf/generated/include/CesiumGltf/Schema.h +++ b/CesiumGltf/generated/include/CesiumGltf/ExtensionExtFeatureMetadataSchema.h @@ -2,8 +2,8 @@ // DO NOT EDIT THIS FILE! #pragma once -#include "CesiumGltf/Class.h" -#include "CesiumGltf/Enum.h" +#include "CesiumGltf/ExtensionExtFeatureMetadataClass.h" +#include "CesiumGltf/ExtensionExtFeatureMetadataEnum.h" #include "CesiumGltf/Library.h" #include @@ -16,8 +16,10 @@ namespace CesiumGltf { /** * @brief An object defining classes and enums. */ -struct CESIUMGLTF_API Schema final : public CesiumUtility::ExtensibleObject { - static inline constexpr const char* TypeName = "Schema"; +struct CESIUMGLTF_API ExtensionExtFeatureMetadataSchema final + : public CesiumUtility::ExtensibleObject { + static inline constexpr const char* TypeName = + "ExtensionExtFeatureMetadataSchema"; /** * @brief The name of the schema. @@ -38,12 +40,14 @@ struct CESIUMGLTF_API Schema final : public CesiumUtility::ExtensibleObject { * @brief A dictionary, where each key is a class ID and each value is an * object defining the class. */ - std::unordered_map classes; + std::unordered_map + classes; /** * @brief A dictionary, where each key is an enum ID and each value is an * object defining the values for the enum. */ - std::unordered_map enums; + std::unordered_map + enums; }; } // namespace CesiumGltf diff --git a/CesiumGltf/generated/include/CesiumGltf/Statistics.h b/CesiumGltf/generated/include/CesiumGltf/ExtensionExtFeatureMetadataStatistics.h similarity index 61% rename from CesiumGltf/generated/include/CesiumGltf/Statistics.h rename to CesiumGltf/generated/include/CesiumGltf/ExtensionExtFeatureMetadataStatistics.h index 98f2ebc77..1c5b4adab 100644 --- a/CesiumGltf/generated/include/CesiumGltf/Statistics.h +++ b/CesiumGltf/generated/include/CesiumGltf/ExtensionExtFeatureMetadataStatistics.h @@ -2,7 +2,7 @@ // DO NOT EDIT THIS FILE! #pragma once -#include "CesiumGltf/ClassStatistics.h" +#include "CesiumGltf/ExtensionExtFeatureMetadataClassStatistics.h" #include "CesiumGltf/Library.h" #include @@ -13,15 +13,19 @@ namespace CesiumGltf { /** * @brief Statistics about features. */ -struct CESIUMGLTF_API Statistics final +struct CESIUMGLTF_API ExtensionExtFeatureMetadataStatistics final : public CesiumUtility::ExtensibleObject { - static inline constexpr const char* TypeName = "Statistics"; + static inline constexpr const char* TypeName = + "ExtensionExtFeatureMetadataStatistics"; /** * @brief A dictionary, where each key is a class ID declared in the `classes` * dictionary and each value is an object containing statistics about features * that conform to the class. */ - std::unordered_map classes; + std::unordered_map< + std::string, + CesiumGltf::ExtensionExtFeatureMetadataClassStatistics> + classes; }; } // namespace CesiumGltf diff --git a/CesiumGltf/generated/include/CesiumGltf/TextureAccessor.h b/CesiumGltf/generated/include/CesiumGltf/ExtensionExtFeatureMetadataTextureAccessor.h similarity index 80% rename from CesiumGltf/generated/include/CesiumGltf/TextureAccessor.h rename to CesiumGltf/generated/include/CesiumGltf/ExtensionExtFeatureMetadataTextureAccessor.h index 97b52e28a..f6c15d1af 100644 --- a/CesiumGltf/generated/include/CesiumGltf/TextureAccessor.h +++ b/CesiumGltf/generated/include/CesiumGltf/ExtensionExtFeatureMetadataTextureAccessor.h @@ -14,9 +14,10 @@ namespace CesiumGltf { * @brief A description of how to access property values from the color channels * of a texture. */ -struct CESIUMGLTF_API TextureAccessor final +struct CESIUMGLTF_API ExtensionExtFeatureMetadataTextureAccessor final : public CesiumUtility::ExtensibleObject { - static inline constexpr const char* TypeName = "TextureAccessor"; + static inline constexpr const char* TypeName = + "ExtensionExtFeatureMetadataTextureAccessor"; /** * @brief Texture channels containing property values. Channels are labeled by diff --git a/CesiumGltf/generated/include/CesiumGltf/ExtensionExtMeshFeatures.h b/CesiumGltf/generated/include/CesiumGltf/ExtensionExtMeshFeatures.h index 397b37282..3d02adb86 100644 --- a/CesiumGltf/generated/include/CesiumGltf/ExtensionExtMeshFeatures.h +++ b/CesiumGltf/generated/include/CesiumGltf/ExtensionExtMeshFeatures.h @@ -2,7 +2,7 @@ // DO NOT EDIT THIS FILE! #pragma once -#include "CesiumGltf/ExtensionExtMeshFeaturesFeatureId.h" +#include "CesiumGltf/FeatureId.h" #include "CesiumGltf/Library.h" #include @@ -21,6 +21,6 @@ struct CESIUMGLTF_API ExtensionExtMeshFeatures final /** * @brief An array of feature ID sets. */ - std::vector featureIds; + std::vector featureIds; }; } // namespace CesiumGltf diff --git a/CesiumGltf/generated/include/CesiumGltf/ExtensionMeshPrimitiveExtFeatureMetadata.h b/CesiumGltf/generated/include/CesiumGltf/ExtensionMeshPrimitiveExtFeatureMetadata.h index 5cf35aacb..d7008fbbb 100644 --- a/CesiumGltf/generated/include/CesiumGltf/ExtensionMeshPrimitiveExtFeatureMetadata.h +++ b/CesiumGltf/generated/include/CesiumGltf/ExtensionMeshPrimitiveExtFeatureMetadata.h @@ -2,8 +2,8 @@ // DO NOT EDIT THIS FILE! #pragma once -#include "CesiumGltf/FeatureIDAttribute.h" -#include "CesiumGltf/FeatureIDTexture.h" +#include "CesiumGltf/ExtensionExtFeatureMetadataFeatureIDAttribute.h" +#include "CesiumGltf/ExtensionExtFeatureMetadataFeatureIDTexture.h" #include "CesiumGltf/Library.h" #include @@ -26,13 +26,15 @@ struct CESIUMGLTF_API ExtensionMeshPrimitiveExtFeatureMetadata final * @brief An array of objects mapping per-vertex feature IDs to a feature * table. */ - std::vector featureIdAttributes; + std::vector + featureIdAttributes; /** * @brief An array of objects mapping per-texel feature IDs to a feature * table. */ - std::vector featureIdTextures; + std::vector + featureIdTextures; /** * @brief An array of IDs of feature textures from the root diff --git a/CesiumGltf/generated/include/CesiumGltf/ExtensionModelExtFeatureMetadata.h b/CesiumGltf/generated/include/CesiumGltf/ExtensionModelExtFeatureMetadata.h index 7fb51dc7c..049f350cf 100644 --- a/CesiumGltf/generated/include/CesiumGltf/ExtensionModelExtFeatureMetadata.h +++ b/CesiumGltf/generated/include/CesiumGltf/ExtensionModelExtFeatureMetadata.h @@ -2,11 +2,11 @@ // DO NOT EDIT THIS FILE! #pragma once -#include "CesiumGltf/FeatureTable.h" -#include "CesiumGltf/FeatureTexture.h" +#include "CesiumGltf/ExtensionExtFeatureMetadataFeatureTable.h" +#include "CesiumGltf/ExtensionExtFeatureMetadataFeatureTexture.h" +#include "CesiumGltf/ExtensionExtFeatureMetadataSchema.h" +#include "CesiumGltf/ExtensionExtFeatureMetadataStatistics.h" #include "CesiumGltf/Library.h" -#include "CesiumGltf/Schema.h" -#include "CesiumGltf/Statistics.h" #include @@ -27,7 +27,7 @@ struct CESIUMGLTF_API ExtensionModelExtFeatureMetadata final /** * @brief An object defining classes and enums. */ - std::optional schema; + std::optional schema; /** * @brief The URI (or IRI) of the external schema file. @@ -37,18 +37,24 @@ struct CESIUMGLTF_API ExtensionModelExtFeatureMetadata final /** * @brief An object containing statistics about features. */ - std::optional statistics; + std::optional statistics; /** * @brief A dictionary, where each key is a feature table ID and each value is * an object defining the feature table. */ - std::unordered_map featureTables; + std::unordered_map< + std::string, + CesiumGltf::ExtensionExtFeatureMetadataFeatureTable> + featureTables; /** * @brief A dictionary, where each key is a feature texture ID and each value * is an object defining the feature texture. */ - std::unordered_map featureTextures; + std::unordered_map< + std::string, + CesiumGltf::ExtensionExtFeatureMetadataFeatureTexture> + featureTextures; }; } // namespace CesiumGltf diff --git a/CesiumGltf/generated/include/CesiumGltf/ExtensionExtMeshFeaturesFeatureId.h b/CesiumGltf/generated/include/CesiumGltf/FeatureId.h similarity index 79% rename from CesiumGltf/generated/include/CesiumGltf/ExtensionExtMeshFeaturesFeatureId.h rename to CesiumGltf/generated/include/CesiumGltf/FeatureId.h index 5895e8ea4..aef4877e1 100644 --- a/CesiumGltf/generated/include/CesiumGltf/ExtensionExtMeshFeaturesFeatureId.h +++ b/CesiumGltf/generated/include/CesiumGltf/FeatureId.h @@ -2,7 +2,7 @@ // DO NOT EDIT THIS FILE! #pragma once -#include "CesiumGltf/ExtensionExtMeshFeaturesFeatureIdTexture.h" +#include "CesiumGltf/FeatureIdTexture.h" #include "CesiumGltf/Library.h" #include @@ -15,10 +15,8 @@ namespace CesiumGltf { /** * @brief Feature IDs stored in an attribute or texture. */ -struct CESIUMGLTF_API ExtensionExtMeshFeaturesFeatureId final - : public CesiumUtility::ExtensibleObject { - static inline constexpr const char* TypeName = - "ExtensionExtMeshFeaturesFeatureId"; +struct CESIUMGLTF_API FeatureId final : public CesiumUtility::ExtensibleObject { + static inline constexpr const char* TypeName = "FeatureId"; /** * @brief The number of unique features in the attribute or texture. @@ -48,7 +46,7 @@ struct CESIUMGLTF_API ExtensionExtMeshFeaturesFeatureId final /** * @brief A texture containing feature IDs. */ - std::optional texture; + std::optional texture; /** * @brief The index of the property table containing per-feature property diff --git a/CesiumGltf/generated/include/CesiumGltf/ExtensionExtMeshFeaturesFeatureIdTexture.h b/CesiumGltf/generated/include/CesiumGltf/FeatureIdTexture.h similarity index 76% rename from CesiumGltf/generated/include/CesiumGltf/ExtensionExtMeshFeaturesFeatureIdTexture.h rename to CesiumGltf/generated/include/CesiumGltf/FeatureIdTexture.h index de709adf1..2ce9dc76e 100644 --- a/CesiumGltf/generated/include/CesiumGltf/ExtensionExtMeshFeaturesFeatureIdTexture.h +++ b/CesiumGltf/generated/include/CesiumGltf/FeatureIdTexture.h @@ -12,10 +12,8 @@ namespace CesiumGltf { /** * @brief A texture containing feature IDs */ -struct CESIUMGLTF_API ExtensionExtMeshFeaturesFeatureIdTexture final - : public TextureInfo { - static inline constexpr const char* TypeName = - "ExtensionExtMeshFeaturesFeatureIdTexture"; +struct CESIUMGLTF_API FeatureIdTexture final : public TextureInfo { + static inline constexpr const char* TypeName = "FeatureIdTexture"; /** * @brief Texture channels containing feature IDs, identified by index. diff --git a/CesiumGltf/include/CesiumGltf/FeatureIdTextureView.h b/CesiumGltf/include/CesiumGltf/FeatureIdTextureView.h index c579ca836..11f6ee726 100644 --- a/CesiumGltf/include/CesiumGltf/FeatureIdTextureView.h +++ b/CesiumGltf/include/CesiumGltf/FeatureIdTextureView.h @@ -1,18 +1,15 @@ #pragma once -#include "CesiumGltf/ExtensionExtMeshFeaturesFeatureIdTexture.h" +#include "CesiumGltf/FeatureIdTexture.h" #include "CesiumGltf/Texture.h" -#include "CesiumGltf/TextureAccessor.h" #include "Image.h" #include "ImageCesium.h" #include "Model.h" #include -#include #include #include #include -#include namespace CesiumGltf { /** @@ -20,7 +17,7 @@ namespace CesiumGltf { * * The {@link FeatureIdTextureView} constructor always completes successfully, * but it may not always reflect the actual content of the - * {@link ExtensionExtMeshFeaturesFeatureIdTexture}. This enumeration provides the reason. + * {@link FeatureIdTexture}. This enumeration provides the reason. */ enum class FeatureIdTextureViewStatus { /** @@ -72,10 +69,10 @@ enum class FeatureIdTextureViewStatus { }; /** - * @brief A view on the image data of {@link ExtensionExtMeshFeaturesFeatureIdTexture}. + * @brief A view on the image data of {@link FeatureIdTexture}. * * It provides the ability to sample the feature IDs from the - * {@link ExtensionExtMeshFeaturesFeatureIdTexture} using texture coordinates. + * {@link FeatureIdTexture} using texture coordinates. */ class FeatureIdTextureView { public: @@ -86,15 +83,14 @@ class FeatureIdTextureView { /** * @brief Construct a view of the data specified by a - * {@link ExtensionExtMeshFeaturesFeatureIdTexture}. + * {@link FeatureIdTexture}. * - * @param model The glTF in which to look for the feature id texture's data. - * @param featureIDTexture The feature id texture to create a view for. + * @param model The glTF in which to look for the feature ID texture's data. + * @param featureIDTexture The feature ID texture to create a view for. */ FeatureIdTextureView( const Model& model, - const ExtensionExtMeshFeaturesFeatureIdTexture& - featureIdTexture) noexcept; + const FeatureIdTexture& featureIdTexture) noexcept; /** * @brief Get the Feature ID for the given texture coordinates. diff --git a/CesiumGltf/include/CesiumGltf/PropertyTextureView.h b/CesiumGltf/include/CesiumGltf/PropertyTextureView.h index 69c0f70e2..8fc438670 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTextureView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTextureView.h @@ -6,7 +6,6 @@ #include "CesiumGltf/ExtensionModelExtStructuralMetadata.h" #include "CesiumGltf/PropertyTexturePropertyView.h" #include "CesiumGltf/Texture.h" -#include "CesiumGltf/TextureAccessor.h" #include "Image.h" #include "ImageCesium.h" #include "Model.h" diff --git a/CesiumGltf/src/FeatureIdTextureView.cpp b/CesiumGltf/src/FeatureIdTextureView.cpp index 589f98ac2..6879069e0 100644 --- a/CesiumGltf/src/FeatureIdTextureView.cpp +++ b/CesiumGltf/src/FeatureIdTextureView.cpp @@ -9,7 +9,7 @@ FeatureIdTextureView::FeatureIdTextureView() noexcept FeatureIdTextureView::FeatureIdTextureView( const Model& model, - const ExtensionExtMeshFeaturesFeatureIdTexture& featureIdTexture) noexcept + const FeatureIdTexture& featureIdTexture) noexcept : _status(FeatureIdTextureViewStatus::ErrorUninitialized), _texCoordSetIndex(-1), _channels(featureIdTexture.channels), diff --git a/CesiumGltf/test/TestFeatureIdTextureView.cpp b/CesiumGltf/test/TestFeatureIdTextureView.cpp index a6755667f..d632db45b 100644 --- a/CesiumGltf/test/TestFeatureIdTextureView.cpp +++ b/CesiumGltf/test/TestFeatureIdTextureView.cpp @@ -21,13 +21,12 @@ TEST_CASE("Test FeatureIdTextureView on feature ID texture with invalid " ExtensionExtMeshFeatures& meshFeatures = primitive.addExtension(); - ExtensionExtMeshFeaturesFeatureIdTexture featureIdTexture; + FeatureIdTexture featureIdTexture; featureIdTexture.index = -1; featureIdTexture.texCoord = 0; featureIdTexture.channels = {0}; - ExtensionExtMeshFeaturesFeatureId featureId = - meshFeatures.featureIds.emplace_back(); + FeatureId featureId = meshFeatures.featureIds.emplace_back(); featureId.texture = featureIdTexture; FeatureIdTextureView view(model, featureIdTexture); @@ -47,13 +46,12 @@ TEST_CASE("Test FeatureIdTextureView on feature ID texture with invalid image " ExtensionExtMeshFeatures& meshFeatures = primitive.addExtension(); - ExtensionExtMeshFeaturesFeatureIdTexture featureIdTexture; + FeatureIdTexture featureIdTexture; featureIdTexture.index = 0; featureIdTexture.texCoord = 0; featureIdTexture.channels = {0}; - ExtensionExtMeshFeaturesFeatureId featureId = - meshFeatures.featureIds.emplace_back(); + FeatureId featureId = meshFeatures.featureIds.emplace_back(); featureId.texture = featureIdTexture; FeatureIdTextureView view(model, featureIdTexture); @@ -76,13 +74,12 @@ TEST_CASE("Test FeatureIdTextureView on feature ID texture with empty image") { ExtensionExtMeshFeatures& meshFeatures = primitive.addExtension(); - ExtensionExtMeshFeaturesFeatureIdTexture featureIdTexture; + FeatureIdTexture featureIdTexture; featureIdTexture.index = 0; featureIdTexture.texCoord = 0; featureIdTexture.channels = {0}; - ExtensionExtMeshFeaturesFeatureId featureId = - meshFeatures.featureIds.emplace_back(); + FeatureId featureId = meshFeatures.featureIds.emplace_back(); featureId.texture = featureIdTexture; FeatureIdTextureView view(model, featureIdTexture); @@ -107,13 +104,12 @@ TEST_CASE("Test FeatureIdTextureView on feature ID texture with too many bytes " ExtensionExtMeshFeatures& meshFeatures = primitive.addExtension(); - ExtensionExtMeshFeaturesFeatureIdTexture featureIdTexture; + FeatureIdTexture featureIdTexture; featureIdTexture.index = 0; featureIdTexture.texCoord = 0; featureIdTexture.channels = {0}; - ExtensionExtMeshFeaturesFeatureId featureId = - meshFeatures.featureIds.emplace_back(); + FeatureId featureId = meshFeatures.featureIds.emplace_back(); featureId.texture = featureIdTexture; FeatureIdTextureView view(model, featureIdTexture); @@ -140,13 +136,12 @@ TEST_CASE( ExtensionExtMeshFeatures& meshFeatures = primitive.addExtension(); - ExtensionExtMeshFeaturesFeatureIdTexture featureIdTexture; + FeatureIdTexture featureIdTexture; featureIdTexture.index = 0; featureIdTexture.texCoord = -1; featureIdTexture.channels = {0}; - ExtensionExtMeshFeaturesFeatureId featureId = - meshFeatures.featureIds.emplace_back(); + FeatureId featureId = meshFeatures.featureIds.emplace_back(); featureId.texture = featureIdTexture; FeatureIdTextureView view(model, featureIdTexture); @@ -172,13 +167,12 @@ TEST_CASE( ExtensionExtMeshFeatures& meshFeatures = primitive.addExtension(); - ExtensionExtMeshFeaturesFeatureIdTexture featureIdTexture; + FeatureIdTexture featureIdTexture; featureIdTexture.index = 0; featureIdTexture.texCoord = 0; featureIdTexture.channels = {}; - ExtensionExtMeshFeaturesFeatureId featureId = - meshFeatures.featureIds.emplace_back(); + FeatureId featureId = meshFeatures.featureIds.emplace_back(); featureId.texture = featureIdTexture; FeatureIdTextureView view(model, featureIdTexture); @@ -202,13 +196,12 @@ TEST_CASE( ExtensionExtMeshFeatures& meshFeatures = primitive.addExtension(); - ExtensionExtMeshFeaturesFeatureIdTexture featureIdTexture; + FeatureIdTexture featureIdTexture; featureIdTexture.index = 0; featureIdTexture.texCoord = 0; featureIdTexture.channels = {0, 1, 2, 3, 3}; - ExtensionExtMeshFeaturesFeatureId featureId = - meshFeatures.featureIds.emplace_back(); + FeatureId featureId = meshFeatures.featureIds.emplace_back(); featureId.texture = featureIdTexture; FeatureIdTextureView view(model, featureIdTexture); @@ -232,13 +225,12 @@ TEST_CASE("Test FeatureIdTextureView on feature ID texture with out of range " ExtensionExtMeshFeatures& meshFeatures = primitive.addExtension(); - ExtensionExtMeshFeaturesFeatureIdTexture featureIdTexture; + FeatureIdTexture featureIdTexture; featureIdTexture.index = 0; featureIdTexture.texCoord = 0; featureIdTexture.channels = {4}; - ExtensionExtMeshFeaturesFeatureId featureId = - meshFeatures.featureIds.emplace_back(); + FeatureId featureId = meshFeatures.featureIds.emplace_back(); featureId.texture = featureIdTexture; FeatureIdTextureView view(model, featureIdTexture); diff --git a/CesiumGltfReader/generated/src/ClassJsonHandler.h b/CesiumGltfReader/generated/src/ClassJsonHandler.h deleted file mode 100644 index 580791944..000000000 --- a/CesiumGltfReader/generated/src/ClassJsonHandler.h +++ /dev/null @@ -1,41 +0,0 @@ -// This file was generated by generate-classes. -// DO NOT EDIT THIS FILE! -#pragma once - -#include "ClassPropertyJsonHandler.h" - -#include -#include -#include -#include - -namespace CesiumJsonReader { -class ExtensionReaderContext; -} - -namespace CesiumGltfReader { -class ClassJsonHandler : public CesiumJsonReader::ExtensibleObjectJsonHandler { -public: - using ValueType = CesiumGltf::Class; - - ClassJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; - void reset(IJsonHandler* pParentHandler, CesiumGltf::Class* pObject); - - virtual IJsonHandler* readObjectKey(const std::string_view& str) override; - -protected: - IJsonHandler* readObjectKeyClass( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::Class& o); - -private: - CesiumGltf::Class* _pObject = nullptr; - CesiumJsonReader::StringJsonHandler _name; - CesiumJsonReader::StringJsonHandler _description; - CesiumJsonReader:: - DictionaryJsonHandler - _properties; -}; -} // namespace CesiumGltfReader diff --git a/CesiumGltfReader/generated/src/EnumJsonHandler.h b/CesiumGltfReader/generated/src/EnumJsonHandler.h deleted file mode 100644 index 355e88790..000000000 --- a/CesiumGltfReader/generated/src/EnumJsonHandler.h +++ /dev/null @@ -1,42 +0,0 @@ -// This file was generated by generate-classes. -// DO NOT EDIT THIS FILE! -#pragma once - -#include "EnumValueJsonHandler.h" - -#include -#include -#include -#include - -namespace CesiumJsonReader { -class ExtensionReaderContext; -} - -namespace CesiumGltfReader { -class EnumJsonHandler : public CesiumJsonReader::ExtensibleObjectJsonHandler { -public: - using ValueType = CesiumGltf::Enum; - - EnumJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; - void reset(IJsonHandler* pParentHandler, CesiumGltf::Enum* pObject); - - virtual IJsonHandler* readObjectKey(const std::string_view& str) override; - -protected: - IJsonHandler* readObjectKeyEnum( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::Enum& o); - -private: - CesiumGltf::Enum* _pObject = nullptr; - CesiumJsonReader::StringJsonHandler _name; - CesiumJsonReader::StringJsonHandler _description; - CesiumJsonReader::StringJsonHandler _valueType; - CesiumJsonReader:: - ArrayJsonHandler - _values; -}; -} // namespace CesiumGltfReader diff --git a/CesiumGltfReader/generated/src/ExtensionExtFeatureMetadataClassJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionExtFeatureMetadataClassJsonHandler.h new file mode 100644 index 000000000..e8f8c7a2b --- /dev/null +++ b/CesiumGltfReader/generated/src/ExtensionExtFeatureMetadataClassJsonHandler.h @@ -0,0 +1,45 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "ExtensionExtFeatureMetadataClassPropertyJsonHandler.h" + +#include +#include +#include +#include + +namespace CesiumJsonReader { +class ExtensionReaderContext; +} + +namespace CesiumGltfReader { +class ExtensionExtFeatureMetadataClassJsonHandler + : public CesiumJsonReader::ExtensibleObjectJsonHandler { +public: + using ValueType = CesiumGltf::ExtensionExtFeatureMetadataClass; + + ExtensionExtFeatureMetadataClassJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + void reset( + IJsonHandler* pParentHandler, + CesiumGltf::ExtensionExtFeatureMetadataClass* pObject); + + virtual IJsonHandler* readObjectKey(const std::string_view& str) override; + +protected: + IJsonHandler* readObjectKeyExtensionExtFeatureMetadataClass( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::ExtensionExtFeatureMetadataClass& o); + +private: + CesiumGltf::ExtensionExtFeatureMetadataClass* _pObject = nullptr; + CesiumJsonReader::StringJsonHandler _name; + CesiumJsonReader::StringJsonHandler _description; + CesiumJsonReader::DictionaryJsonHandler< + CesiumGltf::ExtensionExtFeatureMetadataClassProperty, + ExtensionExtFeatureMetadataClassPropertyJsonHandler> + _properties; +}; +} // namespace CesiumGltfReader diff --git a/CesiumGltfReader/generated/src/ClassPropertyJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionExtFeatureMetadataClassPropertyJsonHandler.h similarity index 70% rename from CesiumGltfReader/generated/src/ClassPropertyJsonHandler.h rename to CesiumGltfReader/generated/src/ExtensionExtFeatureMetadataClassPropertyJsonHandler.h index aa176fe01..60b3f82cf 100644 --- a/CesiumGltfReader/generated/src/ClassPropertyJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionExtFeatureMetadataClassPropertyJsonHandler.h @@ -2,7 +2,7 @@ // DO NOT EDIT THIS FILE! #pragma once -#include +#include #include #include #include @@ -14,25 +14,27 @@ class ExtensionReaderContext; } namespace CesiumGltfReader { -class ClassPropertyJsonHandler +class ExtensionExtFeatureMetadataClassPropertyJsonHandler : public CesiumJsonReader::ExtensibleObjectJsonHandler { public: - using ValueType = CesiumGltf::ClassProperty; + using ValueType = CesiumGltf::ExtensionExtFeatureMetadataClassProperty; - ClassPropertyJsonHandler( + ExtensionExtFeatureMetadataClassPropertyJsonHandler( const CesiumJsonReader::ExtensionReaderContext& context) noexcept; - void reset(IJsonHandler* pParentHandler, CesiumGltf::ClassProperty* pObject); + void reset( + IJsonHandler* pParentHandler, + CesiumGltf::ExtensionExtFeatureMetadataClassProperty* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; protected: - IJsonHandler* readObjectKeyClassProperty( + IJsonHandler* readObjectKeyExtensionExtFeatureMetadataClassProperty( const std::string& objectType, const std::string_view& str, - CesiumGltf::ClassProperty& o); + CesiumGltf::ExtensionExtFeatureMetadataClassProperty& o); private: - CesiumGltf::ClassProperty* _pObject = nullptr; + CesiumGltf::ExtensionExtFeatureMetadataClassProperty* _pObject = nullptr; CesiumJsonReader::StringJsonHandler _name; CesiumJsonReader::StringJsonHandler _description; CesiumJsonReader::StringJsonHandler _type; diff --git a/CesiumGltfReader/generated/src/ClassStatisticsJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionExtFeatureMetadataClassStatisticsJsonHandler.h similarity index 50% rename from CesiumGltfReader/generated/src/ClassStatisticsJsonHandler.h rename to CesiumGltfReader/generated/src/ExtensionExtFeatureMetadataClassStatisticsJsonHandler.h index 840492669..02bd6d76a 100644 --- a/CesiumGltfReader/generated/src/ClassStatisticsJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionExtFeatureMetadataClassStatisticsJsonHandler.h @@ -2,9 +2,9 @@ // DO NOT EDIT THIS FILE! #pragma once -#include "PropertyStatisticsJsonHandler.h" +#include "ExtensionExtFeatureMetadataPropertyStatisticsJsonHandler.h" -#include +#include #include #include #include @@ -14,30 +14,31 @@ class ExtensionReaderContext; } namespace CesiumGltfReader { -class ClassStatisticsJsonHandler +class ExtensionExtFeatureMetadataClassStatisticsJsonHandler : public CesiumJsonReader::ExtensibleObjectJsonHandler { public: - using ValueType = CesiumGltf::ClassStatistics; + using ValueType = CesiumGltf::ExtensionExtFeatureMetadataClassStatistics; - ClassStatisticsJsonHandler( + ExtensionExtFeatureMetadataClassStatisticsJsonHandler( const CesiumJsonReader::ExtensionReaderContext& context) noexcept; - void - reset(IJsonHandler* pParentHandler, CesiumGltf::ClassStatistics* pObject); + void reset( + IJsonHandler* pParentHandler, + CesiumGltf::ExtensionExtFeatureMetadataClassStatistics* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; protected: - IJsonHandler* readObjectKeyClassStatistics( + IJsonHandler* readObjectKeyExtensionExtFeatureMetadataClassStatistics( const std::string& objectType, const std::string_view& str, - CesiumGltf::ClassStatistics& o); + CesiumGltf::ExtensionExtFeatureMetadataClassStatistics& o); private: - CesiumGltf::ClassStatistics* _pObject = nullptr; + CesiumGltf::ExtensionExtFeatureMetadataClassStatistics* _pObject = nullptr; CesiumJsonReader::IntegerJsonHandler _count; CesiumJsonReader::DictionaryJsonHandler< - CesiumGltf::PropertyStatistics, - PropertyStatisticsJsonHandler> + CesiumGltf::ExtensionExtFeatureMetadataPropertyStatistics, + ExtensionExtFeatureMetadataPropertyStatisticsJsonHandler> _properties; }; } // namespace CesiumGltfReader diff --git a/CesiumGltfReader/generated/src/ExtensionExtFeatureMetadataEnumJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionExtFeatureMetadataEnumJsonHandler.h new file mode 100644 index 000000000..e8a635833 --- /dev/null +++ b/CesiumGltfReader/generated/src/ExtensionExtFeatureMetadataEnumJsonHandler.h @@ -0,0 +1,46 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "ExtensionExtFeatureMetadataEnumValueJsonHandler.h" + +#include +#include +#include +#include + +namespace CesiumJsonReader { +class ExtensionReaderContext; +} + +namespace CesiumGltfReader { +class ExtensionExtFeatureMetadataEnumJsonHandler + : public CesiumJsonReader::ExtensibleObjectJsonHandler { +public: + using ValueType = CesiumGltf::ExtensionExtFeatureMetadataEnum; + + ExtensionExtFeatureMetadataEnumJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + void reset( + IJsonHandler* pParentHandler, + CesiumGltf::ExtensionExtFeatureMetadataEnum* pObject); + + virtual IJsonHandler* readObjectKey(const std::string_view& str) override; + +protected: + IJsonHandler* readObjectKeyExtensionExtFeatureMetadataEnum( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::ExtensionExtFeatureMetadataEnum& o); + +private: + CesiumGltf::ExtensionExtFeatureMetadataEnum* _pObject = nullptr; + CesiumJsonReader::StringJsonHandler _name; + CesiumJsonReader::StringJsonHandler _description; + CesiumJsonReader::StringJsonHandler _valueType; + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionExtFeatureMetadataEnumValue, + ExtensionExtFeatureMetadataEnumValueJsonHandler> + _values; +}; +} // namespace CesiumGltfReader diff --git a/CesiumGltfReader/generated/src/EnumValueJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionExtFeatureMetadataEnumValueJsonHandler.h similarity index 60% rename from CesiumGltfReader/generated/src/EnumValueJsonHandler.h rename to CesiumGltfReader/generated/src/ExtensionExtFeatureMetadataEnumValueJsonHandler.h index d56d90722..30ee048b4 100644 --- a/CesiumGltfReader/generated/src/EnumValueJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionExtFeatureMetadataEnumValueJsonHandler.h @@ -2,7 +2,7 @@ // DO NOT EDIT THIS FILE! #pragma once -#include +#include #include #include #include @@ -12,25 +12,27 @@ class ExtensionReaderContext; } namespace CesiumGltfReader { -class EnumValueJsonHandler +class ExtensionExtFeatureMetadataEnumValueJsonHandler : public CesiumJsonReader::ExtensibleObjectJsonHandler { public: - using ValueType = CesiumGltf::EnumValue; + using ValueType = CesiumGltf::ExtensionExtFeatureMetadataEnumValue; - EnumValueJsonHandler( + ExtensionExtFeatureMetadataEnumValueJsonHandler( const CesiumJsonReader::ExtensionReaderContext& context) noexcept; - void reset(IJsonHandler* pParentHandler, CesiumGltf::EnumValue* pObject); + void reset( + IJsonHandler* pParentHandler, + CesiumGltf::ExtensionExtFeatureMetadataEnumValue* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; protected: - IJsonHandler* readObjectKeyEnumValue( + IJsonHandler* readObjectKeyExtensionExtFeatureMetadataEnumValue( const std::string& objectType, const std::string_view& str, - CesiumGltf::EnumValue& o); + CesiumGltf::ExtensionExtFeatureMetadataEnumValue& o); private: - CesiumGltf::EnumValue* _pObject = nullptr; + CesiumGltf::ExtensionExtFeatureMetadataEnumValue* _pObject = nullptr; CesiumJsonReader::StringJsonHandler _name; CesiumJsonReader::StringJsonHandler _description; CesiumJsonReader::IntegerJsonHandler _value; diff --git a/CesiumGltfReader/generated/src/ExtensionExtFeatureMetadataFeatureIDAttributeJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionExtFeatureMetadataFeatureIDAttributeJsonHandler.h new file mode 100644 index 000000000..0cd99c005 --- /dev/null +++ b/CesiumGltfReader/generated/src/ExtensionExtFeatureMetadataFeatureIDAttributeJsonHandler.h @@ -0,0 +1,40 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "ExtensionExtFeatureMetadataFeatureIDsJsonHandler.h" + +#include +#include +#include + +namespace CesiumJsonReader { +class ExtensionReaderContext; +} + +namespace CesiumGltfReader { +class ExtensionExtFeatureMetadataFeatureIDAttributeJsonHandler + : public CesiumJsonReader::ExtensibleObjectJsonHandler { +public: + using ValueType = CesiumGltf::ExtensionExtFeatureMetadataFeatureIDAttribute; + + ExtensionExtFeatureMetadataFeatureIDAttributeJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + void reset( + IJsonHandler* pParentHandler, + CesiumGltf::ExtensionExtFeatureMetadataFeatureIDAttribute* pObject); + + virtual IJsonHandler* readObjectKey(const std::string_view& str) override; + +protected: + IJsonHandler* readObjectKeyExtensionExtFeatureMetadataFeatureIDAttribute( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::ExtensionExtFeatureMetadataFeatureIDAttribute& o); + +private: + CesiumGltf::ExtensionExtFeatureMetadataFeatureIDAttribute* _pObject = nullptr; + CesiumJsonReader::StringJsonHandler _featureTable; + ExtensionExtFeatureMetadataFeatureIDsJsonHandler _featureIds; +}; +} // namespace CesiumGltfReader diff --git a/CesiumGltfReader/generated/src/ExtensionExtFeatureMetadataFeatureIDTextureJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionExtFeatureMetadataFeatureIDTextureJsonHandler.h new file mode 100644 index 000000000..413396acf --- /dev/null +++ b/CesiumGltfReader/generated/src/ExtensionExtFeatureMetadataFeatureIDTextureJsonHandler.h @@ -0,0 +1,40 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "ExtensionExtFeatureMetadataTextureAccessorJsonHandler.h" + +#include +#include +#include + +namespace CesiumJsonReader { +class ExtensionReaderContext; +} + +namespace CesiumGltfReader { +class ExtensionExtFeatureMetadataFeatureIDTextureJsonHandler + : public CesiumJsonReader::ExtensibleObjectJsonHandler { +public: + using ValueType = CesiumGltf::ExtensionExtFeatureMetadataFeatureIDTexture; + + ExtensionExtFeatureMetadataFeatureIDTextureJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + void reset( + IJsonHandler* pParentHandler, + CesiumGltf::ExtensionExtFeatureMetadataFeatureIDTexture* pObject); + + virtual IJsonHandler* readObjectKey(const std::string_view& str) override; + +protected: + IJsonHandler* readObjectKeyExtensionExtFeatureMetadataFeatureIDTexture( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::ExtensionExtFeatureMetadataFeatureIDTexture& o); + +private: + CesiumGltf::ExtensionExtFeatureMetadataFeatureIDTexture* _pObject = nullptr; + CesiumJsonReader::StringJsonHandler _featureTable; + ExtensionExtFeatureMetadataTextureAccessorJsonHandler _featureIds; +}; +} // namespace CesiumGltfReader diff --git a/CesiumGltfReader/generated/src/FeatureIDsJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionExtFeatureMetadataFeatureIDsJsonHandler.h similarity index 60% rename from CesiumGltfReader/generated/src/FeatureIDsJsonHandler.h rename to CesiumGltfReader/generated/src/ExtensionExtFeatureMetadataFeatureIDsJsonHandler.h index 3501f6207..a8db907d5 100644 --- a/CesiumGltfReader/generated/src/FeatureIDsJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionExtFeatureMetadataFeatureIDsJsonHandler.h @@ -2,7 +2,7 @@ // DO NOT EDIT THIS FILE! #pragma once -#include +#include #include #include #include @@ -12,25 +12,27 @@ class ExtensionReaderContext; } namespace CesiumGltfReader { -class FeatureIDsJsonHandler +class ExtensionExtFeatureMetadataFeatureIDsJsonHandler : public CesiumJsonReader::ExtensibleObjectJsonHandler { public: - using ValueType = CesiumGltf::FeatureIDs; + using ValueType = CesiumGltf::ExtensionExtFeatureMetadataFeatureIDs; - FeatureIDsJsonHandler( + ExtensionExtFeatureMetadataFeatureIDsJsonHandler( const CesiumJsonReader::ExtensionReaderContext& context) noexcept; - void reset(IJsonHandler* pParentHandler, CesiumGltf::FeatureIDs* pObject); + void reset( + IJsonHandler* pParentHandler, + CesiumGltf::ExtensionExtFeatureMetadataFeatureIDs* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; protected: - IJsonHandler* readObjectKeyFeatureIDs( + IJsonHandler* readObjectKeyExtensionExtFeatureMetadataFeatureIDs( const std::string& objectType, const std::string_view& str, - CesiumGltf::FeatureIDs& o); + CesiumGltf::ExtensionExtFeatureMetadataFeatureIDs& o); private: - CesiumGltf::FeatureIDs* _pObject = nullptr; + CesiumGltf::ExtensionExtFeatureMetadataFeatureIDs* _pObject = nullptr; CesiumJsonReader::StringJsonHandler _attribute; CesiumJsonReader::IntegerJsonHandler _constant; CesiumJsonReader::IntegerJsonHandler _divisor; diff --git a/CesiumGltfReader/generated/src/FeatureTableJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionExtFeatureMetadataFeatureTableJsonHandler.h similarity index 53% rename from CesiumGltfReader/generated/src/FeatureTableJsonHandler.h rename to CesiumGltfReader/generated/src/ExtensionExtFeatureMetadataFeatureTableJsonHandler.h index f2d5f34ac..e49cb7a69 100644 --- a/CesiumGltfReader/generated/src/FeatureTableJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionExtFeatureMetadataFeatureTableJsonHandler.h @@ -2,9 +2,9 @@ // DO NOT EDIT THIS FILE! #pragma once -#include "FeatureTablePropertyJsonHandler.h" +#include "ExtensionExtFeatureMetadataFeatureTablePropertyJsonHandler.h" -#include +#include #include #include #include @@ -15,30 +15,32 @@ class ExtensionReaderContext; } namespace CesiumGltfReader { -class FeatureTableJsonHandler +class ExtensionExtFeatureMetadataFeatureTableJsonHandler : public CesiumJsonReader::ExtensibleObjectJsonHandler { public: - using ValueType = CesiumGltf::FeatureTable; + using ValueType = CesiumGltf::ExtensionExtFeatureMetadataFeatureTable; - FeatureTableJsonHandler( + ExtensionExtFeatureMetadataFeatureTableJsonHandler( const CesiumJsonReader::ExtensionReaderContext& context) noexcept; - void reset(IJsonHandler* pParentHandler, CesiumGltf::FeatureTable* pObject); + void reset( + IJsonHandler* pParentHandler, + CesiumGltf::ExtensionExtFeatureMetadataFeatureTable* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; protected: - IJsonHandler* readObjectKeyFeatureTable( + IJsonHandler* readObjectKeyExtensionExtFeatureMetadataFeatureTable( const std::string& objectType, const std::string_view& str, - CesiumGltf::FeatureTable& o); + CesiumGltf::ExtensionExtFeatureMetadataFeatureTable& o); private: - CesiumGltf::FeatureTable* _pObject = nullptr; + CesiumGltf::ExtensionExtFeatureMetadataFeatureTable* _pObject = nullptr; CesiumJsonReader::StringJsonHandler _classProperty; CesiumJsonReader::IntegerJsonHandler _count; CesiumJsonReader::DictionaryJsonHandler< - CesiumGltf::FeatureTableProperty, - FeatureTablePropertyJsonHandler> + CesiumGltf::ExtensionExtFeatureMetadataFeatureTableProperty, + ExtensionExtFeatureMetadataFeatureTablePropertyJsonHandler> _properties; }; } // namespace CesiumGltfReader diff --git a/CesiumGltfReader/generated/src/FeatureTablePropertyJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionExtFeatureMetadataFeatureTablePropertyJsonHandler.h similarity index 62% rename from CesiumGltfReader/generated/src/FeatureTablePropertyJsonHandler.h rename to CesiumGltfReader/generated/src/ExtensionExtFeatureMetadataFeatureTablePropertyJsonHandler.h index fab2ff647..943702750 100644 --- a/CesiumGltfReader/generated/src/FeatureTablePropertyJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionExtFeatureMetadataFeatureTablePropertyJsonHandler.h @@ -2,7 +2,7 @@ // DO NOT EDIT THIS FILE! #pragma once -#include +#include #include #include #include @@ -12,27 +12,28 @@ class ExtensionReaderContext; } namespace CesiumGltfReader { -class FeatureTablePropertyJsonHandler +class ExtensionExtFeatureMetadataFeatureTablePropertyJsonHandler : public CesiumJsonReader::ExtensibleObjectJsonHandler { public: - using ValueType = CesiumGltf::FeatureTableProperty; + using ValueType = CesiumGltf::ExtensionExtFeatureMetadataFeatureTableProperty; - FeatureTablePropertyJsonHandler( + ExtensionExtFeatureMetadataFeatureTablePropertyJsonHandler( const CesiumJsonReader::ExtensionReaderContext& context) noexcept; void reset( IJsonHandler* pParentHandler, - CesiumGltf::FeatureTableProperty* pObject); + CesiumGltf::ExtensionExtFeatureMetadataFeatureTableProperty* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; protected: - IJsonHandler* readObjectKeyFeatureTableProperty( + IJsonHandler* readObjectKeyExtensionExtFeatureMetadataFeatureTableProperty( const std::string& objectType, const std::string_view& str, - CesiumGltf::FeatureTableProperty& o); + CesiumGltf::ExtensionExtFeatureMetadataFeatureTableProperty& o); private: - CesiumGltf::FeatureTableProperty* _pObject = nullptr; + CesiumGltf::ExtensionExtFeatureMetadataFeatureTableProperty* _pObject = + nullptr; CesiumJsonReader::IntegerJsonHandler _bufferView; CesiumJsonReader::StringJsonHandler _offsetType; CesiumJsonReader::IntegerJsonHandler _arrayOffsetBufferView; diff --git a/CesiumGltfReader/generated/src/FeatureTextureJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionExtFeatureMetadataFeatureTextureJsonHandler.h similarity index 50% rename from CesiumGltfReader/generated/src/FeatureTextureJsonHandler.h rename to CesiumGltfReader/generated/src/ExtensionExtFeatureMetadataFeatureTextureJsonHandler.h index 0cf3ad846..ac9efd63f 100644 --- a/CesiumGltfReader/generated/src/FeatureTextureJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionExtFeatureMetadataFeatureTextureJsonHandler.h @@ -2,9 +2,9 @@ // DO NOT EDIT THIS FILE! #pragma once -#include "TextureAccessorJsonHandler.h" +#include "ExtensionExtFeatureMetadataTextureAccessorJsonHandler.h" -#include +#include #include #include #include @@ -14,29 +14,31 @@ class ExtensionReaderContext; } namespace CesiumGltfReader { -class FeatureTextureJsonHandler +class ExtensionExtFeatureMetadataFeatureTextureJsonHandler : public CesiumJsonReader::ExtensibleObjectJsonHandler { public: - using ValueType = CesiumGltf::FeatureTexture; + using ValueType = CesiumGltf::ExtensionExtFeatureMetadataFeatureTexture; - FeatureTextureJsonHandler( + ExtensionExtFeatureMetadataFeatureTextureJsonHandler( const CesiumJsonReader::ExtensionReaderContext& context) noexcept; - void reset(IJsonHandler* pParentHandler, CesiumGltf::FeatureTexture* pObject); + void reset( + IJsonHandler* pParentHandler, + CesiumGltf::ExtensionExtFeatureMetadataFeatureTexture* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; protected: - IJsonHandler* readObjectKeyFeatureTexture( + IJsonHandler* readObjectKeyExtensionExtFeatureMetadataFeatureTexture( const std::string& objectType, const std::string_view& str, - CesiumGltf::FeatureTexture& o); + CesiumGltf::ExtensionExtFeatureMetadataFeatureTexture& o); private: - CesiumGltf::FeatureTexture* _pObject = nullptr; + CesiumGltf::ExtensionExtFeatureMetadataFeatureTexture* _pObject = nullptr; CesiumJsonReader::StringJsonHandler _classProperty; CesiumJsonReader::DictionaryJsonHandler< - CesiumGltf::TextureAccessor, - TextureAccessorJsonHandler> + CesiumGltf::ExtensionExtFeatureMetadataTextureAccessor, + ExtensionExtFeatureMetadataTextureAccessorJsonHandler> _properties; }; } // namespace CesiumGltfReader diff --git a/CesiumGltfReader/generated/src/PropertyStatisticsJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionExtFeatureMetadataPropertyStatisticsJsonHandler.h similarity index 65% rename from CesiumGltfReader/generated/src/PropertyStatisticsJsonHandler.h rename to CesiumGltfReader/generated/src/ExtensionExtFeatureMetadataPropertyStatisticsJsonHandler.h index 6d28e4c5f..b2568c348 100644 --- a/CesiumGltfReader/generated/src/PropertyStatisticsJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionExtFeatureMetadataPropertyStatisticsJsonHandler.h @@ -2,7 +2,7 @@ // DO NOT EDIT THIS FILE! #pragma once -#include +#include #include #include #include @@ -12,26 +12,27 @@ class ExtensionReaderContext; } namespace CesiumGltfReader { -class PropertyStatisticsJsonHandler +class ExtensionExtFeatureMetadataPropertyStatisticsJsonHandler : public CesiumJsonReader::ExtensibleObjectJsonHandler { public: - using ValueType = CesiumGltf::PropertyStatistics; + using ValueType = CesiumGltf::ExtensionExtFeatureMetadataPropertyStatistics; - PropertyStatisticsJsonHandler( + ExtensionExtFeatureMetadataPropertyStatisticsJsonHandler( const CesiumJsonReader::ExtensionReaderContext& context) noexcept; - void - reset(IJsonHandler* pParentHandler, CesiumGltf::PropertyStatistics* pObject); + void reset( + IJsonHandler* pParentHandler, + CesiumGltf::ExtensionExtFeatureMetadataPropertyStatistics* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; protected: - IJsonHandler* readObjectKeyPropertyStatistics( + IJsonHandler* readObjectKeyExtensionExtFeatureMetadataPropertyStatistics( const std::string& objectType, const std::string_view& str, - CesiumGltf::PropertyStatistics& o); + CesiumGltf::ExtensionExtFeatureMetadataPropertyStatistics& o); private: - CesiumGltf::PropertyStatistics* _pObject = nullptr; + CesiumGltf::ExtensionExtFeatureMetadataPropertyStatistics* _pObject = nullptr; CesiumJsonReader::JsonObjectJsonHandler _min; CesiumJsonReader::JsonObjectJsonHandler _max; CesiumJsonReader::JsonObjectJsonHandler _mean; diff --git a/CesiumGltfReader/generated/src/ExtensionExtFeatureMetadataSchemaJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionExtFeatureMetadataSchemaJsonHandler.h new file mode 100644 index 000000000..190a3b5fc --- /dev/null +++ b/CesiumGltfReader/generated/src/ExtensionExtFeatureMetadataSchemaJsonHandler.h @@ -0,0 +1,51 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "ExtensionExtFeatureMetadataClassJsonHandler.h" +#include "ExtensionExtFeatureMetadataEnumJsonHandler.h" + +#include +#include +#include +#include + +namespace CesiumJsonReader { +class ExtensionReaderContext; +} + +namespace CesiumGltfReader { +class ExtensionExtFeatureMetadataSchemaJsonHandler + : public CesiumJsonReader::ExtensibleObjectJsonHandler { +public: + using ValueType = CesiumGltf::ExtensionExtFeatureMetadataSchema; + + ExtensionExtFeatureMetadataSchemaJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + void reset( + IJsonHandler* pParentHandler, + CesiumGltf::ExtensionExtFeatureMetadataSchema* pObject); + + virtual IJsonHandler* readObjectKey(const std::string_view& str) override; + +protected: + IJsonHandler* readObjectKeyExtensionExtFeatureMetadataSchema( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::ExtensionExtFeatureMetadataSchema& o); + +private: + CesiumGltf::ExtensionExtFeatureMetadataSchema* _pObject = nullptr; + CesiumJsonReader::StringJsonHandler _name; + CesiumJsonReader::StringJsonHandler _description; + CesiumJsonReader::StringJsonHandler _version; + CesiumJsonReader::DictionaryJsonHandler< + CesiumGltf::ExtensionExtFeatureMetadataClass, + ExtensionExtFeatureMetadataClassJsonHandler> + _classes; + CesiumJsonReader::DictionaryJsonHandler< + CesiumGltf::ExtensionExtFeatureMetadataEnum, + ExtensionExtFeatureMetadataEnumJsonHandler> + _enums; +}; +} // namespace CesiumGltfReader diff --git a/CesiumGltfReader/generated/src/ExtensionExtFeatureMetadataStatisticsJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionExtFeatureMetadataStatisticsJsonHandler.h new file mode 100644 index 000000000..62edc7334 --- /dev/null +++ b/CesiumGltfReader/generated/src/ExtensionExtFeatureMetadataStatisticsJsonHandler.h @@ -0,0 +1,42 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "ExtensionExtFeatureMetadataClassStatisticsJsonHandler.h" + +#include +#include +#include + +namespace CesiumJsonReader { +class ExtensionReaderContext; +} + +namespace CesiumGltfReader { +class ExtensionExtFeatureMetadataStatisticsJsonHandler + : public CesiumJsonReader::ExtensibleObjectJsonHandler { +public: + using ValueType = CesiumGltf::ExtensionExtFeatureMetadataStatistics; + + ExtensionExtFeatureMetadataStatisticsJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + void reset( + IJsonHandler* pParentHandler, + CesiumGltf::ExtensionExtFeatureMetadataStatistics* pObject); + + virtual IJsonHandler* readObjectKey(const std::string_view& str) override; + +protected: + IJsonHandler* readObjectKeyExtensionExtFeatureMetadataStatistics( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::ExtensionExtFeatureMetadataStatistics& o); + +private: + CesiumGltf::ExtensionExtFeatureMetadataStatistics* _pObject = nullptr; + CesiumJsonReader::DictionaryJsonHandler< + CesiumGltf::ExtensionExtFeatureMetadataClassStatistics, + ExtensionExtFeatureMetadataClassStatisticsJsonHandler> + _classes; +}; +} // namespace CesiumGltfReader diff --git a/CesiumGltfReader/generated/src/TextureAccessorJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionExtFeatureMetadataTextureAccessorJsonHandler.h similarity index 55% rename from CesiumGltfReader/generated/src/TextureAccessorJsonHandler.h rename to CesiumGltfReader/generated/src/ExtensionExtFeatureMetadataTextureAccessorJsonHandler.h index cb0d84e57..eec2bd6e9 100644 --- a/CesiumGltfReader/generated/src/TextureAccessorJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionExtFeatureMetadataTextureAccessorJsonHandler.h @@ -4,7 +4,7 @@ #include "TextureInfoJsonHandler.h" -#include +#include #include #include @@ -13,26 +13,27 @@ class ExtensionReaderContext; } namespace CesiumGltfReader { -class TextureAccessorJsonHandler +class ExtensionExtFeatureMetadataTextureAccessorJsonHandler : public CesiumJsonReader::ExtensibleObjectJsonHandler { public: - using ValueType = CesiumGltf::TextureAccessor; + using ValueType = CesiumGltf::ExtensionExtFeatureMetadataTextureAccessor; - TextureAccessorJsonHandler( + ExtensionExtFeatureMetadataTextureAccessorJsonHandler( const CesiumJsonReader::ExtensionReaderContext& context) noexcept; - void - reset(IJsonHandler* pParentHandler, CesiumGltf::TextureAccessor* pObject); + void reset( + IJsonHandler* pParentHandler, + CesiumGltf::ExtensionExtFeatureMetadataTextureAccessor* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; protected: - IJsonHandler* readObjectKeyTextureAccessor( + IJsonHandler* readObjectKeyExtensionExtFeatureMetadataTextureAccessor( const std::string& objectType, const std::string_view& str, - CesiumGltf::TextureAccessor& o); + CesiumGltf::ExtensionExtFeatureMetadataTextureAccessor& o); private: - CesiumGltf::TextureAccessor* _pObject = nullptr; + CesiumGltf::ExtensionExtFeatureMetadataTextureAccessor* _pObject = nullptr; CesiumJsonReader::StringJsonHandler _channels; TextureInfoJsonHandler _texture; }; diff --git a/CesiumGltfReader/generated/src/ExtensionExtMeshFeaturesJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionExtMeshFeaturesJsonHandler.h index e8609c68f..98c2aadf9 100644 --- a/CesiumGltfReader/generated/src/ExtensionExtMeshFeaturesJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionExtMeshFeaturesJsonHandler.h @@ -2,7 +2,7 @@ // DO NOT EDIT THIS FILE! #pragma once -#include "ExtensionExtMeshFeaturesFeatureIdJsonHandler.h" +#include "FeatureIdJsonHandler.h" #include #include @@ -87,9 +87,8 @@ class ExtensionExtMeshFeaturesJsonHandler private: CesiumGltf::ExtensionExtMeshFeatures* _pObject = nullptr; - CesiumJsonReader::ArrayJsonHandler< - CesiumGltf::ExtensionExtMeshFeaturesFeatureId, - ExtensionExtMeshFeaturesFeatureIdJsonHandler> - _featureIds; + CesiumJsonReader:: + ArrayJsonHandler + _featureIds; }; } // namespace CesiumGltfReader diff --git a/CesiumGltfReader/generated/src/ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler.h index 90f363195..ebb538825 100644 --- a/CesiumGltfReader/generated/src/ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler.h @@ -2,8 +2,8 @@ // DO NOT EDIT THIS FILE! #pragma once -#include "FeatureIDAttributeJsonHandler.h" -#include "FeatureIDTextureJsonHandler.h" +#include "ExtensionExtFeatureMetadataFeatureIDAttributeJsonHandler.h" +#include "ExtensionExtFeatureMetadataFeatureIDTextureJsonHandler.h" #include #include @@ -90,12 +90,12 @@ class ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler private: CesiumGltf::ExtensionMeshPrimitiveExtFeatureMetadata* _pObject = nullptr; CesiumJsonReader::ArrayJsonHandler< - CesiumGltf::FeatureIDAttribute, - FeatureIDAttributeJsonHandler> + CesiumGltf::ExtensionExtFeatureMetadataFeatureIDAttribute, + ExtensionExtFeatureMetadataFeatureIDAttributeJsonHandler> _featureIdAttributes; CesiumJsonReader::ArrayJsonHandler< - CesiumGltf::FeatureIDTexture, - FeatureIDTextureJsonHandler> + CesiumGltf::ExtensionExtFeatureMetadataFeatureIDTexture, + ExtensionExtFeatureMetadataFeatureIDTextureJsonHandler> _featureIdTextures; CesiumJsonReader:: ArrayJsonHandler diff --git a/CesiumGltfReader/generated/src/ExtensionModelExtFeatureMetadataJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionModelExtFeatureMetadataJsonHandler.h index 0b4b93203..964ea0008 100644 --- a/CesiumGltfReader/generated/src/ExtensionModelExtFeatureMetadataJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionModelExtFeatureMetadataJsonHandler.h @@ -2,10 +2,10 @@ // DO NOT EDIT THIS FILE! #pragma once -#include "FeatureTableJsonHandler.h" -#include "FeatureTextureJsonHandler.h" -#include "SchemaJsonHandler.h" -#include "StatisticsJsonHandler.h" +#include "ExtensionExtFeatureMetadataFeatureTableJsonHandler.h" +#include "ExtensionExtFeatureMetadataFeatureTextureJsonHandler.h" +#include "ExtensionExtFeatureMetadataSchemaJsonHandler.h" +#include "ExtensionExtFeatureMetadataStatisticsJsonHandler.h" #include #include @@ -91,15 +91,16 @@ class ExtensionModelExtFeatureMetadataJsonHandler private: CesiumGltf::ExtensionModelExtFeatureMetadata* _pObject = nullptr; - SchemaJsonHandler _schema; + ExtensionExtFeatureMetadataSchemaJsonHandler _schema; CesiumJsonReader::StringJsonHandler _schemaUri; - StatisticsJsonHandler _statistics; - CesiumJsonReader:: - DictionaryJsonHandler - _featureTables; + ExtensionExtFeatureMetadataStatisticsJsonHandler _statistics; CesiumJsonReader::DictionaryJsonHandler< - CesiumGltf::FeatureTexture, - FeatureTextureJsonHandler> + CesiumGltf::ExtensionExtFeatureMetadataFeatureTable, + ExtensionExtFeatureMetadataFeatureTableJsonHandler> + _featureTables; + CesiumJsonReader::DictionaryJsonHandler< + CesiumGltf::ExtensionExtFeatureMetadataFeatureTexture, + ExtensionExtFeatureMetadataFeatureTextureJsonHandler> _featureTextures; }; } // namespace CesiumGltfReader diff --git a/CesiumGltfReader/generated/src/FeatureIDAttributeJsonHandler.h b/CesiumGltfReader/generated/src/FeatureIDAttributeJsonHandler.h deleted file mode 100644 index 3241d8a22..000000000 --- a/CesiumGltfReader/generated/src/FeatureIDAttributeJsonHandler.h +++ /dev/null @@ -1,39 +0,0 @@ -// This file was generated by generate-classes. -// DO NOT EDIT THIS FILE! -#pragma once - -#include "FeatureIDsJsonHandler.h" - -#include -#include -#include - -namespace CesiumJsonReader { -class ExtensionReaderContext; -} - -namespace CesiumGltfReader { -class FeatureIDAttributeJsonHandler - : public CesiumJsonReader::ExtensibleObjectJsonHandler { -public: - using ValueType = CesiumGltf::FeatureIDAttribute; - - FeatureIDAttributeJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; - void - reset(IJsonHandler* pParentHandler, CesiumGltf::FeatureIDAttribute* pObject); - - virtual IJsonHandler* readObjectKey(const std::string_view& str) override; - -protected: - IJsonHandler* readObjectKeyFeatureIDAttribute( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::FeatureIDAttribute& o); - -private: - CesiumGltf::FeatureIDAttribute* _pObject = nullptr; - CesiumJsonReader::StringJsonHandler _featureTable; - FeatureIDsJsonHandler _featureIds; -}; -} // namespace CesiumGltfReader diff --git a/CesiumGltfReader/generated/src/FeatureIDTextureJsonHandler.h b/CesiumGltfReader/generated/src/FeatureIDTextureJsonHandler.h deleted file mode 100644 index fda07ea9c..000000000 --- a/CesiumGltfReader/generated/src/FeatureIDTextureJsonHandler.h +++ /dev/null @@ -1,39 +0,0 @@ -// This file was generated by generate-classes. -// DO NOT EDIT THIS FILE! -#pragma once - -#include "TextureAccessorJsonHandler.h" - -#include -#include -#include - -namespace CesiumJsonReader { -class ExtensionReaderContext; -} - -namespace CesiumGltfReader { -class FeatureIDTextureJsonHandler - : public CesiumJsonReader::ExtensibleObjectJsonHandler { -public: - using ValueType = CesiumGltf::FeatureIDTexture; - - FeatureIDTextureJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; - void - reset(IJsonHandler* pParentHandler, CesiumGltf::FeatureIDTexture* pObject); - - virtual IJsonHandler* readObjectKey(const std::string_view& str) override; - -protected: - IJsonHandler* readObjectKeyFeatureIDTexture( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::FeatureIDTexture& o); - -private: - CesiumGltf::FeatureIDTexture* _pObject = nullptr; - CesiumJsonReader::StringJsonHandler _featureTable; - TextureAccessorJsonHandler _featureIds; -}; -} // namespace CesiumGltfReader diff --git a/CesiumGltfReader/generated/src/ExtensionExtMeshFeaturesFeatureIdJsonHandler.h b/CesiumGltfReader/generated/src/FeatureIdJsonHandler.h similarity index 59% rename from CesiumGltfReader/generated/src/ExtensionExtMeshFeaturesFeatureIdJsonHandler.h rename to CesiumGltfReader/generated/src/FeatureIdJsonHandler.h index 9c8229e9a..0aa7db73b 100644 --- a/CesiumGltfReader/generated/src/ExtensionExtMeshFeaturesFeatureIdJsonHandler.h +++ b/CesiumGltfReader/generated/src/FeatureIdJsonHandler.h @@ -2,9 +2,9 @@ // DO NOT EDIT THIS FILE! #pragma once -#include "ExtensionExtMeshFeaturesFeatureIdTextureJsonHandler.h" +#include "FeatureIdTextureJsonHandler.h" -#include +#include #include #include #include @@ -14,32 +14,30 @@ class ExtensionReaderContext; } namespace CesiumGltfReader { -class ExtensionExtMeshFeaturesFeatureIdJsonHandler +class FeatureIdJsonHandler : public CesiumJsonReader::ExtensibleObjectJsonHandler { public: - using ValueType = CesiumGltf::ExtensionExtMeshFeaturesFeatureId; + using ValueType = CesiumGltf::FeatureId; - ExtensionExtMeshFeaturesFeatureIdJsonHandler( + FeatureIdJsonHandler( const CesiumJsonReader::ExtensionReaderContext& context) noexcept; - void reset( - IJsonHandler* pParentHandler, - CesiumGltf::ExtensionExtMeshFeaturesFeatureId* pObject); + void reset(IJsonHandler* pParentHandler, CesiumGltf::FeatureId* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; protected: - IJsonHandler* readObjectKeyExtensionExtMeshFeaturesFeatureId( + IJsonHandler* readObjectKeyFeatureId( const std::string& objectType, const std::string_view& str, - CesiumGltf::ExtensionExtMeshFeaturesFeatureId& o); + CesiumGltf::FeatureId& o); private: - CesiumGltf::ExtensionExtMeshFeaturesFeatureId* _pObject = nullptr; + CesiumGltf::FeatureId* _pObject = nullptr; CesiumJsonReader::IntegerJsonHandler _featureCount; CesiumJsonReader::IntegerJsonHandler _nullFeatureId; CesiumJsonReader::StringJsonHandler _label; CesiumJsonReader::IntegerJsonHandler _attribute; - ExtensionExtMeshFeaturesFeatureIdTextureJsonHandler _texture; + FeatureIdTextureJsonHandler _texture; CesiumJsonReader::IntegerJsonHandler _propertyTable; }; } // namespace CesiumGltfReader diff --git a/CesiumGltfReader/generated/src/ExtensionExtMeshFeaturesFeatureIdTextureJsonHandler.h b/CesiumGltfReader/generated/src/FeatureIdTextureJsonHandler.h similarity index 53% rename from CesiumGltfReader/generated/src/ExtensionExtMeshFeaturesFeatureIdTextureJsonHandler.h rename to CesiumGltfReader/generated/src/FeatureIdTextureJsonHandler.h index 667b85ab6..f20f16ef3 100644 --- a/CesiumGltfReader/generated/src/ExtensionExtMeshFeaturesFeatureIdTextureJsonHandler.h +++ b/CesiumGltfReader/generated/src/FeatureIdTextureJsonHandler.h @@ -4,7 +4,7 @@ #include "TextureInfoJsonHandler.h" -#include +#include #include #include @@ -13,27 +13,25 @@ class ExtensionReaderContext; } namespace CesiumGltfReader { -class ExtensionExtMeshFeaturesFeatureIdTextureJsonHandler - : public TextureInfoJsonHandler { +class FeatureIdTextureJsonHandler : public TextureInfoJsonHandler { public: - using ValueType = CesiumGltf::ExtensionExtMeshFeaturesFeatureIdTexture; + using ValueType = CesiumGltf::FeatureIdTexture; - ExtensionExtMeshFeaturesFeatureIdTextureJsonHandler( + FeatureIdTextureJsonHandler( const CesiumJsonReader::ExtensionReaderContext& context) noexcept; - void reset( - IJsonHandler* pParentHandler, - CesiumGltf::ExtensionExtMeshFeaturesFeatureIdTexture* pObject); + void + reset(IJsonHandler* pParentHandler, CesiumGltf::FeatureIdTexture* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; protected: - IJsonHandler* readObjectKeyExtensionExtMeshFeaturesFeatureIdTexture( + IJsonHandler* readObjectKeyFeatureIdTexture( const std::string& objectType, const std::string_view& str, - CesiumGltf::ExtensionExtMeshFeaturesFeatureIdTexture& o); + CesiumGltf::FeatureIdTexture& o); private: - CesiumGltf::ExtensionExtMeshFeaturesFeatureIdTexture* _pObject = nullptr; + CesiumGltf::FeatureIdTexture* _pObject = nullptr; CesiumJsonReader:: ArrayJsonHandler> _channels; diff --git a/CesiumGltfReader/generated/src/GeneratedJsonHandlers.cpp b/CesiumGltfReader/generated/src/GeneratedJsonHandlers.cpp index c305b3691..bde0586ff 100644 --- a/CesiumGltfReader/generated/src/GeneratedJsonHandlers.cpp +++ b/CesiumGltfReader/generated/src/GeneratedJsonHandlers.cpp @@ -9,30 +9,19 @@ namespace CesiumGltfReader { -ExtensionCesiumRTCJsonHandler::ExtensionCesiumRTCJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _center() {} +ExtensionCesiumRTCJsonHandler::ExtensionCesiumRTCJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _center() {} -void ExtensionCesiumRTCJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::ExtensionCesiumRTC* pObject) { +void ExtensionCesiumRTCJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionCesiumRTC* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -ExtensionCesiumRTCJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* ExtensionCesiumRTCJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionCesiumRTC( - CesiumGltf::ExtensionCesiumRTC::TypeName, - str, - *this->_pObject); + return this->readObjectKeyExtensionCesiumRTC(CesiumGltf::ExtensionCesiumRTC::TypeName, str, *this->_pObject); } -void ExtensionCesiumRTCJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumUtility::ExtensibleObject& o, - const std::string_view& extensionName) { +void ExtensionCesiumRTCJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) { std::any& value = o.extensions.emplace(extensionName, CesiumGltf::ExtensionCesiumRTC()) .first->second; @@ -41,15 +30,10 @@ void ExtensionCesiumRTCJsonHandler::reset( &std::any_cast(value)); } -CesiumJsonReader::IJsonHandler* -ExtensionCesiumRTCJsonHandler::readObjectKeyExtensionCesiumRTC( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::ExtensionCesiumRTC& o) { +CesiumJsonReader::IJsonHandler* ExtensionCesiumRTCJsonHandler::readObjectKeyExtensionCesiumRTC(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionCesiumRTC& o) { using namespace std::string_literals; - if ("center"s == str) - return property("center", this->_center, o.center); + if ("center"s == str) return property("center", this->_center, o.center); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -66,59 +50,34 @@ ExtensionCesiumRTCJsonHandler::readObjectKeyExtensionCesiumRTC( namespace CesiumGltfReader { -ExtensionCesiumTileEdgesJsonHandler::ExtensionCesiumTileEdgesJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _left(), - _bottom(), - _right(), - _top() {} - -void ExtensionCesiumTileEdgesJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::ExtensionCesiumTileEdges* pObject) { +ExtensionCesiumTileEdgesJsonHandler::ExtensionCesiumTileEdgesJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _left(), _bottom(), _right(), _top() {} + +void ExtensionCesiumTileEdgesJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionCesiumTileEdges* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -ExtensionCesiumTileEdgesJsonHandler::readObjectKey( - const std::string_view& str) { +CesiumJsonReader::IJsonHandler* ExtensionCesiumTileEdgesJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionCesiumTileEdges( - CesiumGltf::ExtensionCesiumTileEdges::TypeName, - str, - *this->_pObject); + return this->readObjectKeyExtensionCesiumTileEdges(CesiumGltf::ExtensionCesiumTileEdges::TypeName, str, *this->_pObject); } -void ExtensionCesiumTileEdgesJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumUtility::ExtensibleObject& o, - const std::string_view& extensionName) { +void ExtensionCesiumTileEdgesJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) { std::any& value = - o.extensions - .emplace(extensionName, CesiumGltf::ExtensionCesiumTileEdges()) + o.extensions.emplace(extensionName, CesiumGltf::ExtensionCesiumTileEdges()) .first->second; this->reset( pParentHandler, &std::any_cast(value)); } -CesiumJsonReader::IJsonHandler* -ExtensionCesiumTileEdgesJsonHandler::readObjectKeyExtensionCesiumTileEdges( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::ExtensionCesiumTileEdges& o) { +CesiumJsonReader::IJsonHandler* ExtensionCesiumTileEdgesJsonHandler::readObjectKeyExtensionCesiumTileEdges(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionCesiumTileEdges& o) { using namespace std::string_literals; - if ("left"s == str) - return property("left", this->_left, o.left); - if ("bottom"s == str) - return property("bottom", this->_bottom, o.bottom); - if ("right"s == str) - return property("right", this->_right, o.right); - if ("top"s == str) - return property("top", this->_top, o.top); + if ("left"s == str) return property("left", this->_left, o.left); + if ("bottom"s == str) return property("bottom", this->_bottom, o.bottom); + if ("right"s == str) return property("right", this->_right, o.right); + if ("top"s == str) return property("top", this->_top, o.top); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -135,67 +94,35 @@ ExtensionCesiumTileEdgesJsonHandler::readObjectKeyExtensionCesiumTileEdges( namespace CesiumGltfReader { -ExtensionModelExtFeatureMetadataJsonHandler:: - ExtensionModelExtFeatureMetadataJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _schema(context), - _schemaUri(), - _statistics(context), - _featureTables(context), - _featureTextures(context) {} - -void ExtensionModelExtFeatureMetadataJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::ExtensionModelExtFeatureMetadata* pObject) { +ExtensionModelExtFeatureMetadataJsonHandler::ExtensionModelExtFeatureMetadataJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _schema(context), _schemaUri(), _statistics(context), _featureTables(context), _featureTextures(context) {} + +void ExtensionModelExtFeatureMetadataJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionModelExtFeatureMetadata* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -ExtensionModelExtFeatureMetadataJsonHandler::readObjectKey( - const std::string_view& str) { +CesiumJsonReader::IJsonHandler* ExtensionModelExtFeatureMetadataJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionModelExtFeatureMetadata( - CesiumGltf::ExtensionModelExtFeatureMetadata::TypeName, - str, - *this->_pObject); -} - -void ExtensionModelExtFeatureMetadataJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumUtility::ExtensibleObject& o, - const std::string_view& extensionName) { - std::any& value = o.extensions - .emplace( - extensionName, - CesiumGltf::ExtensionModelExtFeatureMetadata()) - .first->second; + return this->readObjectKeyExtensionModelExtFeatureMetadata(CesiumGltf::ExtensionModelExtFeatureMetadata::TypeName, str, *this->_pObject); +} + +void ExtensionModelExtFeatureMetadataJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) { + std::any& value = + o.extensions.emplace(extensionName, CesiumGltf::ExtensionModelExtFeatureMetadata()) + .first->second; this->reset( pParentHandler, &std::any_cast(value)); } -CesiumJsonReader::IJsonHandler* ExtensionModelExtFeatureMetadataJsonHandler:: - readObjectKeyExtensionModelExtFeatureMetadata( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::ExtensionModelExtFeatureMetadata& o) { +CesiumJsonReader::IJsonHandler* ExtensionModelExtFeatureMetadataJsonHandler::readObjectKeyExtensionModelExtFeatureMetadata(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionModelExtFeatureMetadata& o) { using namespace std::string_literals; - if ("schema"s == str) - return property("schema", this->_schema, o.schema); - if ("schemaUri"s == str) - return property("schemaUri", this->_schemaUri, o.schemaUri); - if ("statistics"s == str) - return property("statistics", this->_statistics, o.statistics); - if ("featureTables"s == str) - return property("featureTables", this->_featureTables, o.featureTables); - if ("featureTextures"s == str) - return property( - "featureTextures", - this->_featureTextures, - o.featureTextures); + if ("schema"s == str) return property("schema", this->_schema, o.schema); + if ("schemaUri"s == str) return property("schemaUri", this->_schemaUri, o.schemaUri); + if ("statistics"s == str) return property("statistics", this->_statistics, o.statistics); + if ("featureTables"s == str) return property("featureTables", this->_featureTables, o.featureTables); + if ("featureTextures"s == str) return property("featureTextures", this->_featureTextures, o.featureTextures); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -212,70 +139,33 @@ CesiumJsonReader::IJsonHandler* ExtensionModelExtFeatureMetadataJsonHandler:: namespace CesiumGltfReader { -ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler:: - ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _featureIdAttributes(context), - _featureIdTextures(context), - _featureTextures() {} - -void ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::ExtensionMeshPrimitiveExtFeatureMetadata* pObject) { +ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler::ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _featureIdAttributes(context), _featureIdTextures(context), _featureTextures() {} + +void ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionMeshPrimitiveExtFeatureMetadata* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler::readObjectKey( - const std::string_view& str) { +CesiumJsonReader::IJsonHandler* ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionMeshPrimitiveExtFeatureMetadata( - CesiumGltf::ExtensionMeshPrimitiveExtFeatureMetadata::TypeName, - str, - *this->_pObject); + return this->readObjectKeyExtensionMeshPrimitiveExtFeatureMetadata(CesiumGltf::ExtensionMeshPrimitiveExtFeatureMetadata::TypeName, str, *this->_pObject); } -void ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumUtility::ExtensibleObject& o, - const std::string_view& extensionName) { +void ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) { std::any& value = - o.extensions - .emplace( - extensionName, - CesiumGltf::ExtensionMeshPrimitiveExtFeatureMetadata()) + o.extensions.emplace(extensionName, CesiumGltf::ExtensionMeshPrimitiveExtFeatureMetadata()) .first->second; this->reset( pParentHandler, - &std::any_cast( - value)); + &std::any_cast(value)); } -CesiumJsonReader::IJsonHandler* -ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler:: - readObjectKeyExtensionMeshPrimitiveExtFeatureMetadata( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::ExtensionMeshPrimitiveExtFeatureMetadata& o) { +CesiumJsonReader::IJsonHandler* ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler::readObjectKeyExtensionMeshPrimitiveExtFeatureMetadata(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionMeshPrimitiveExtFeatureMetadata& o) { using namespace std::string_literals; - if ("featureIdAttributes"s == str) - return property( - "featureIdAttributes", - this->_featureIdAttributes, - o.featureIdAttributes); - if ("featureIdTextures"s == str) - return property( - "featureIdTextures", - this->_featureIdTextures, - o.featureIdTextures); - if ("featureTextures"s == str) - return property( - "featureTextures", - this->_featureTextures, - o.featureTextures); + if ("featureIdAttributes"s == str) return property("featureIdAttributes", this->_featureIdAttributes, o.featureIdAttributes); + if ("featureIdTextures"s == str) return property("featureIdTextures", this->_featureIdTextures, o.featureIdTextures); + if ("featureTextures"s == str) return property("featureTextures", this->_featureTextures, o.featureTextures); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -292,51 +182,31 @@ ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler:: namespace CesiumGltfReader { -ExtensionExtInstanceFeaturesJsonHandler:: - ExtensionExtInstanceFeaturesJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _featureIds(context) {} +ExtensionExtInstanceFeaturesJsonHandler::ExtensionExtInstanceFeaturesJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _featureIds(context) {} -void ExtensionExtInstanceFeaturesJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::ExtensionExtInstanceFeatures* pObject) { +void ExtensionExtInstanceFeaturesJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtInstanceFeatures* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -ExtensionExtInstanceFeaturesJsonHandler::readObjectKey( - const std::string_view& str) { +CesiumJsonReader::IJsonHandler* ExtensionExtInstanceFeaturesJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionExtInstanceFeatures( - CesiumGltf::ExtensionExtInstanceFeatures::TypeName, - str, - *this->_pObject); + return this->readObjectKeyExtensionExtInstanceFeatures(CesiumGltf::ExtensionExtInstanceFeatures::TypeName, str, *this->_pObject); } -void ExtensionExtInstanceFeaturesJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumUtility::ExtensibleObject& o, - const std::string_view& extensionName) { +void ExtensionExtInstanceFeaturesJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) { std::any& value = - o.extensions - .emplace(extensionName, CesiumGltf::ExtensionExtInstanceFeatures()) + o.extensions.emplace(extensionName, CesiumGltf::ExtensionExtInstanceFeatures()) .first->second; this->reset( pParentHandler, &std::any_cast(value)); } -CesiumJsonReader::IJsonHandler* ExtensionExtInstanceFeaturesJsonHandler:: - readObjectKeyExtensionExtInstanceFeatures( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::ExtensionExtInstanceFeatures& o) { +CesiumJsonReader::IJsonHandler* ExtensionExtInstanceFeaturesJsonHandler::readObjectKeyExtensionExtInstanceFeatures(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtInstanceFeatures& o) { using namespace std::string_literals; - if ("featureIds"s == str) - return property("featureIds", this->_featureIds, o.featureIds); + if ("featureIds"s == str) return property("featureIds", this->_featureIds, o.featureIds); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -353,50 +223,31 @@ CesiumJsonReader::IJsonHandler* ExtensionExtInstanceFeaturesJsonHandler:: namespace CesiumGltfReader { -ExtensionExtMeshFeaturesJsonHandler::ExtensionExtMeshFeaturesJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _featureIds(context) {} +ExtensionExtMeshFeaturesJsonHandler::ExtensionExtMeshFeaturesJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _featureIds(context) {} -void ExtensionExtMeshFeaturesJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::ExtensionExtMeshFeatures* pObject) { +void ExtensionExtMeshFeaturesJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtMeshFeatures* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -ExtensionExtMeshFeaturesJsonHandler::readObjectKey( - const std::string_view& str) { +CesiumJsonReader::IJsonHandler* ExtensionExtMeshFeaturesJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionExtMeshFeatures( - CesiumGltf::ExtensionExtMeshFeatures::TypeName, - str, - *this->_pObject); + return this->readObjectKeyExtensionExtMeshFeatures(CesiumGltf::ExtensionExtMeshFeatures::TypeName, str, *this->_pObject); } -void ExtensionExtMeshFeaturesJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumUtility::ExtensibleObject& o, - const std::string_view& extensionName) { +void ExtensionExtMeshFeaturesJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) { std::any& value = - o.extensions - .emplace(extensionName, CesiumGltf::ExtensionExtMeshFeatures()) + o.extensions.emplace(extensionName, CesiumGltf::ExtensionExtMeshFeatures()) .first->second; this->reset( pParentHandler, &std::any_cast(value)); } -CesiumJsonReader::IJsonHandler* -ExtensionExtMeshFeaturesJsonHandler::readObjectKeyExtensionExtMeshFeatures( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::ExtensionExtMeshFeatures& o) { +CesiumJsonReader::IJsonHandler* ExtensionExtMeshFeaturesJsonHandler::readObjectKeyExtensionExtMeshFeatures(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtMeshFeatures& o) { using namespace std::string_literals; - if ("featureIds"s == str) - return property("featureIds", this->_featureIds, o.featureIds); + if ("featureIds"s == str) return property("featureIds", this->_featureIds, o.featureIds); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -413,50 +264,31 @@ ExtensionExtMeshFeaturesJsonHandler::readObjectKeyExtensionExtMeshFeatures( namespace CesiumGltfReader { -ExtensionExtMeshGpuInstancingJsonHandler:: - ExtensionExtMeshGpuInstancingJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _attributes() {} +ExtensionExtMeshGpuInstancingJsonHandler::ExtensionExtMeshGpuInstancingJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _attributes() {} -void ExtensionExtMeshGpuInstancingJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::ExtensionExtMeshGpuInstancing* pObject) { +void ExtensionExtMeshGpuInstancingJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtMeshGpuInstancing* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -ExtensionExtMeshGpuInstancingJsonHandler::readObjectKey( - const std::string_view& str) { +CesiumJsonReader::IJsonHandler* ExtensionExtMeshGpuInstancingJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionExtMeshGpuInstancing( - CesiumGltf::ExtensionExtMeshGpuInstancing::TypeName, - str, - *this->_pObject); + return this->readObjectKeyExtensionExtMeshGpuInstancing(CesiumGltf::ExtensionExtMeshGpuInstancing::TypeName, str, *this->_pObject); } -void ExtensionExtMeshGpuInstancingJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumUtility::ExtensibleObject& o, - const std::string_view& extensionName) { +void ExtensionExtMeshGpuInstancingJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) { std::any& value = - o.extensions - .emplace(extensionName, CesiumGltf::ExtensionExtMeshGpuInstancing()) + o.extensions.emplace(extensionName, CesiumGltf::ExtensionExtMeshGpuInstancing()) .first->second; this->reset( pParentHandler, &std::any_cast(value)); } -CesiumJsonReader::IJsonHandler* ExtensionExtMeshGpuInstancingJsonHandler:: - readObjectKeyExtensionExtMeshGpuInstancing( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::ExtensionExtMeshGpuInstancing& o) { +CesiumJsonReader::IJsonHandler* ExtensionExtMeshGpuInstancingJsonHandler::readObjectKeyExtensionExtMeshGpuInstancing(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtMeshGpuInstancing& o) { using namespace std::string_literals; - if ("attributes"s == str) - return property("attributes", this->_attributes, o.attributes); + if ("attributes"s == str) return property("attributes", this->_attributes, o.attributes); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -473,52 +305,31 @@ CesiumJsonReader::IJsonHandler* ExtensionExtMeshGpuInstancingJsonHandler:: namespace CesiumGltfReader { -ExtensionBufferExtMeshoptCompressionJsonHandler:: - ExtensionBufferExtMeshoptCompressionJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _fallback() {} +ExtensionBufferExtMeshoptCompressionJsonHandler::ExtensionBufferExtMeshoptCompressionJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _fallback() {} -void ExtensionBufferExtMeshoptCompressionJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::ExtensionBufferExtMeshoptCompression* pObject) { +void ExtensionBufferExtMeshoptCompressionJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionBufferExtMeshoptCompression* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -ExtensionBufferExtMeshoptCompressionJsonHandler::readObjectKey( - const std::string_view& str) { +CesiumJsonReader::IJsonHandler* ExtensionBufferExtMeshoptCompressionJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionBufferExtMeshoptCompression( - CesiumGltf::ExtensionBufferExtMeshoptCompression::TypeName, - str, - *this->_pObject); -} - -void ExtensionBufferExtMeshoptCompressionJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumUtility::ExtensibleObject& o, - const std::string_view& extensionName) { - std::any& value = o.extensions - .emplace( - extensionName, - CesiumGltf::ExtensionBufferExtMeshoptCompression()) - .first->second; + return this->readObjectKeyExtensionBufferExtMeshoptCompression(CesiumGltf::ExtensionBufferExtMeshoptCompression::TypeName, str, *this->_pObject); +} + +void ExtensionBufferExtMeshoptCompressionJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) { + std::any& value = + o.extensions.emplace(extensionName, CesiumGltf::ExtensionBufferExtMeshoptCompression()) + .first->second; this->reset( pParentHandler, &std::any_cast(value)); } -CesiumJsonReader::IJsonHandler* -ExtensionBufferExtMeshoptCompressionJsonHandler:: - readObjectKeyExtensionBufferExtMeshoptCompression( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::ExtensionBufferExtMeshoptCompression& o) { +CesiumJsonReader::IJsonHandler* ExtensionBufferExtMeshoptCompressionJsonHandler::readObjectKeyExtensionBufferExtMeshoptCompression(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionBufferExtMeshoptCompression& o) { using namespace std::string_literals; - if ("fallback"s == str) - return property("fallback", this->_fallback, o.fallback); + if ("fallback"s == str) return property("fallback", this->_fallback, o.fallback); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -535,73 +346,37 @@ ExtensionBufferExtMeshoptCompressionJsonHandler:: namespace CesiumGltfReader { -ExtensionBufferViewExtMeshoptCompressionJsonHandler:: - ExtensionBufferViewExtMeshoptCompressionJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _buffer(), - _byteOffset(), - _byteLength(), - _byteStride(), - _count(), - _mode(), - _filter() {} - -void ExtensionBufferViewExtMeshoptCompressionJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::ExtensionBufferViewExtMeshoptCompression* pObject) { +ExtensionBufferViewExtMeshoptCompressionJsonHandler::ExtensionBufferViewExtMeshoptCompressionJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _buffer(), _byteOffset(), _byteLength(), _byteStride(), _count(), _mode(), _filter() {} + +void ExtensionBufferViewExtMeshoptCompressionJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionBufferViewExtMeshoptCompression* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -ExtensionBufferViewExtMeshoptCompressionJsonHandler::readObjectKey( - const std::string_view& str) { +CesiumJsonReader::IJsonHandler* ExtensionBufferViewExtMeshoptCompressionJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionBufferViewExtMeshoptCompression( - CesiumGltf::ExtensionBufferViewExtMeshoptCompression::TypeName, - str, - *this->_pObject); + return this->readObjectKeyExtensionBufferViewExtMeshoptCompression(CesiumGltf::ExtensionBufferViewExtMeshoptCompression::TypeName, str, *this->_pObject); } -void ExtensionBufferViewExtMeshoptCompressionJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumUtility::ExtensibleObject& o, - const std::string_view& extensionName) { +void ExtensionBufferViewExtMeshoptCompressionJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) { std::any& value = - o.extensions - .emplace( - extensionName, - CesiumGltf::ExtensionBufferViewExtMeshoptCompression()) + o.extensions.emplace(extensionName, CesiumGltf::ExtensionBufferViewExtMeshoptCompression()) .first->second; this->reset( pParentHandler, - &std::any_cast( - value)); + &std::any_cast(value)); } -CesiumJsonReader::IJsonHandler* -ExtensionBufferViewExtMeshoptCompressionJsonHandler:: - readObjectKeyExtensionBufferViewExtMeshoptCompression( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::ExtensionBufferViewExtMeshoptCompression& o) { +CesiumJsonReader::IJsonHandler* ExtensionBufferViewExtMeshoptCompressionJsonHandler::readObjectKeyExtensionBufferViewExtMeshoptCompression(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionBufferViewExtMeshoptCompression& o) { using namespace std::string_literals; - if ("buffer"s == str) - return property("buffer", this->_buffer, o.buffer); - if ("byteOffset"s == str) - return property("byteOffset", this->_byteOffset, o.byteOffset); - if ("byteLength"s == str) - return property("byteLength", this->_byteLength, o.byteLength); - if ("byteStride"s == str) - return property("byteStride", this->_byteStride, o.byteStride); - if ("count"s == str) - return property("count", this->_count, o.count); - if ("mode"s == str) - return property("mode", this->_mode, o.mode); - if ("filter"s == str) - return property("filter", this->_filter, o.filter); + if ("buffer"s == str) return property("buffer", this->_buffer, o.buffer); + if ("byteOffset"s == str) return property("byteOffset", this->_byteOffset, o.byteOffset); + if ("byteLength"s == str) return property("byteLength", this->_byteLength, o.byteLength); + if ("byteStride"s == str) return property("byteStride", this->_byteStride, o.byteStride); + if ("count"s == str) return property("count", this->_count, o.count); + if ("mode"s == str) return property("mode", this->_mode, o.mode); + if ("filter"s == str) return property("filter", this->_filter, o.filter); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -618,70 +393,35 @@ ExtensionBufferViewExtMeshoptCompressionJsonHandler:: namespace CesiumGltfReader { -ExtensionModelExtStructuralMetadataJsonHandler:: - ExtensionModelExtStructuralMetadataJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _schema(context), - _schemaUri(), - _propertyTables(context), - _propertyTextures(context), - _propertyAttributes(context) {} - -void ExtensionModelExtStructuralMetadataJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::ExtensionModelExtStructuralMetadata* pObject) { +ExtensionModelExtStructuralMetadataJsonHandler::ExtensionModelExtStructuralMetadataJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _schema(context), _schemaUri(), _propertyTables(context), _propertyTextures(context), _propertyAttributes(context) {} + +void ExtensionModelExtStructuralMetadataJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionModelExtStructuralMetadata* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -ExtensionModelExtStructuralMetadataJsonHandler::readObjectKey( - const std::string_view& str) { +CesiumJsonReader::IJsonHandler* ExtensionModelExtStructuralMetadataJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionModelExtStructuralMetadata( - CesiumGltf::ExtensionModelExtStructuralMetadata::TypeName, - str, - *this->_pObject); -} - -void ExtensionModelExtStructuralMetadataJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumUtility::ExtensibleObject& o, - const std::string_view& extensionName) { - std::any& value = o.extensions - .emplace( - extensionName, - CesiumGltf::ExtensionModelExtStructuralMetadata()) - .first->second; + return this->readObjectKeyExtensionModelExtStructuralMetadata(CesiumGltf::ExtensionModelExtStructuralMetadata::TypeName, str, *this->_pObject); +} + +void ExtensionModelExtStructuralMetadataJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) { + std::any& value = + o.extensions.emplace(extensionName, CesiumGltf::ExtensionModelExtStructuralMetadata()) + .first->second; this->reset( pParentHandler, &std::any_cast(value)); } -CesiumJsonReader::IJsonHandler* ExtensionModelExtStructuralMetadataJsonHandler:: - readObjectKeyExtensionModelExtStructuralMetadata( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::ExtensionModelExtStructuralMetadata& o) { +CesiumJsonReader::IJsonHandler* ExtensionModelExtStructuralMetadataJsonHandler::readObjectKeyExtensionModelExtStructuralMetadata(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionModelExtStructuralMetadata& o) { using namespace std::string_literals; - if ("schema"s == str) - return property("schema", this->_schema, o.schema); - if ("schemaUri"s == str) - return property("schemaUri", this->_schemaUri, o.schemaUri); - if ("propertyTables"s == str) - return property("propertyTables", this->_propertyTables, o.propertyTables); - if ("propertyTextures"s == str) - return property( - "propertyTextures", - this->_propertyTextures, - o.propertyTextures); - if ("propertyAttributes"s == str) - return property( - "propertyAttributes", - this->_propertyAttributes, - o.propertyAttributes); + if ("schema"s == str) return property("schema", this->_schema, o.schema); + if ("schemaUri"s == str) return property("schemaUri", this->_schemaUri, o.schemaUri); + if ("propertyTables"s == str) return property("propertyTables", this->_propertyTables, o.propertyTables); + if ("propertyTextures"s == str) return property("propertyTextures", this->_propertyTextures, o.propertyTextures); + if ("propertyAttributes"s == str) return property("propertyAttributes", this->_propertyAttributes, o.propertyAttributes); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -698,64 +438,32 @@ CesiumJsonReader::IJsonHandler* ExtensionModelExtStructuralMetadataJsonHandler:: namespace CesiumGltfReader { -ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler:: - ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _propertyTextures(), - _propertyAttributes() {} +ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler::ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _propertyTextures(), _propertyAttributes() {} -void ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::ExtensionMeshPrimitiveExtStructuralMetadata* pObject) { +void ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionMeshPrimitiveExtStructuralMetadata* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler::readObjectKey( - const std::string_view& str) { +CesiumJsonReader::IJsonHandler* ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionMeshPrimitiveExtStructuralMetadata( - CesiumGltf::ExtensionMeshPrimitiveExtStructuralMetadata::TypeName, - str, - *this->_pObject); + return this->readObjectKeyExtensionMeshPrimitiveExtStructuralMetadata(CesiumGltf::ExtensionMeshPrimitiveExtStructuralMetadata::TypeName, str, *this->_pObject); } -void ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumUtility::ExtensibleObject& o, - const std::string_view& extensionName) { +void ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) { std::any& value = - o.extensions - .emplace( - extensionName, - CesiumGltf::ExtensionMeshPrimitiveExtStructuralMetadata()) + o.extensions.emplace(extensionName, CesiumGltf::ExtensionMeshPrimitiveExtStructuralMetadata()) .first->second; this->reset( pParentHandler, - &std::any_cast( - value)); + &std::any_cast(value)); } -CesiumJsonReader::IJsonHandler* -ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler:: - readObjectKeyExtensionMeshPrimitiveExtStructuralMetadata( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::ExtensionMeshPrimitiveExtStructuralMetadata& o) { +CesiumJsonReader::IJsonHandler* ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler::readObjectKeyExtensionMeshPrimitiveExtStructuralMetadata(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionMeshPrimitiveExtStructuralMetadata& o) { using namespace std::string_literals; - if ("propertyTextures"s == str) - return property( - "propertyTextures", - this->_propertyTextures, - o.propertyTextures); - if ("propertyAttributes"s == str) - return property( - "propertyAttributes", - this->_propertyAttributes, - o.propertyAttributes); + if ("propertyTextures"s == str) return property("propertyTextures", this->_propertyTextures, o.propertyTextures); + if ("propertyAttributes"s == str) return property("propertyAttributes", this->_propertyAttributes, o.propertyAttributes); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -772,55 +480,32 @@ ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler:: namespace CesiumGltfReader { -ExtensionKhrDracoMeshCompressionJsonHandler:: - ExtensionKhrDracoMeshCompressionJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _bufferView(), - _attributes() {} +ExtensionKhrDracoMeshCompressionJsonHandler::ExtensionKhrDracoMeshCompressionJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _bufferView(), _attributes() {} -void ExtensionKhrDracoMeshCompressionJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::ExtensionKhrDracoMeshCompression* pObject) { +void ExtensionKhrDracoMeshCompressionJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionKhrDracoMeshCompression* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -ExtensionKhrDracoMeshCompressionJsonHandler::readObjectKey( - const std::string_view& str) { +CesiumJsonReader::IJsonHandler* ExtensionKhrDracoMeshCompressionJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionKhrDracoMeshCompression( - CesiumGltf::ExtensionKhrDracoMeshCompression::TypeName, - str, - *this->_pObject); -} - -void ExtensionKhrDracoMeshCompressionJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumUtility::ExtensibleObject& o, - const std::string_view& extensionName) { - std::any& value = o.extensions - .emplace( - extensionName, - CesiumGltf::ExtensionKhrDracoMeshCompression()) - .first->second; + return this->readObjectKeyExtensionKhrDracoMeshCompression(CesiumGltf::ExtensionKhrDracoMeshCompression::TypeName, str, *this->_pObject); +} + +void ExtensionKhrDracoMeshCompressionJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) { + std::any& value = + o.extensions.emplace(extensionName, CesiumGltf::ExtensionKhrDracoMeshCompression()) + .first->second; this->reset( pParentHandler, &std::any_cast(value)); } -CesiumJsonReader::IJsonHandler* ExtensionKhrDracoMeshCompressionJsonHandler:: - readObjectKeyExtensionKhrDracoMeshCompression( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::ExtensionKhrDracoMeshCompression& o) { +CesiumJsonReader::IJsonHandler* ExtensionKhrDracoMeshCompressionJsonHandler::readObjectKeyExtensionKhrDracoMeshCompression(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionKhrDracoMeshCompression& o) { using namespace std::string_literals; - if ("bufferView"s == str) - return property("bufferView", this->_bufferView, o.bufferView); - if ("attributes"s == str) - return property("attributes", this->_attributes, o.attributes); + if ("bufferView"s == str) return property("bufferView", this->_bufferView, o.bufferView); + if ("attributes"s == str) return property("attributes", this->_attributes, o.attributes); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -837,45 +522,28 @@ CesiumJsonReader::IJsonHandler* ExtensionKhrDracoMeshCompressionJsonHandler:: namespace CesiumGltfReader { -ExtensionKhrMaterialsUnlitJsonHandler::ExtensionKhrMaterialsUnlitJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context) {} +ExtensionKhrMaterialsUnlitJsonHandler::ExtensionKhrMaterialsUnlitJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context) {} -void ExtensionKhrMaterialsUnlitJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::ExtensionKhrMaterialsUnlit* pObject) { +void ExtensionKhrMaterialsUnlitJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionKhrMaterialsUnlit* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -ExtensionKhrMaterialsUnlitJsonHandler::readObjectKey( - const std::string_view& str) { +CesiumJsonReader::IJsonHandler* ExtensionKhrMaterialsUnlitJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionKhrMaterialsUnlit( - CesiumGltf::ExtensionKhrMaterialsUnlit::TypeName, - str, - *this->_pObject); + return this->readObjectKeyExtensionKhrMaterialsUnlit(CesiumGltf::ExtensionKhrMaterialsUnlit::TypeName, str, *this->_pObject); } -void ExtensionKhrMaterialsUnlitJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumUtility::ExtensibleObject& o, - const std::string_view& extensionName) { +void ExtensionKhrMaterialsUnlitJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) { std::any& value = - o.extensions - .emplace(extensionName, CesiumGltf::ExtensionKhrMaterialsUnlit()) + o.extensions.emplace(extensionName, CesiumGltf::ExtensionKhrMaterialsUnlit()) .first->second; this->reset( pParentHandler, &std::any_cast(value)); } -CesiumJsonReader::IJsonHandler* -ExtensionKhrMaterialsUnlitJsonHandler::readObjectKeyExtensionKhrMaterialsUnlit( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::ExtensionKhrMaterialsUnlit& o) { +CesiumJsonReader::IJsonHandler* ExtensionKhrMaterialsUnlitJsonHandler::readObjectKeyExtensionKhrMaterialsUnlit(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionKhrMaterialsUnlit& o) { using namespace std::string_literals; (void)o; @@ -895,52 +563,31 @@ ExtensionKhrMaterialsUnlitJsonHandler::readObjectKeyExtensionKhrMaterialsUnlit( namespace CesiumGltfReader { -ExtensionModelKhrMaterialsVariantsJsonHandler:: - ExtensionModelKhrMaterialsVariantsJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _variants(context) {} +ExtensionModelKhrMaterialsVariantsJsonHandler::ExtensionModelKhrMaterialsVariantsJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _variants(context) {} -void ExtensionModelKhrMaterialsVariantsJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::ExtensionModelKhrMaterialsVariants* pObject) { +void ExtensionModelKhrMaterialsVariantsJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionModelKhrMaterialsVariants* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -ExtensionModelKhrMaterialsVariantsJsonHandler::readObjectKey( - const std::string_view& str) { +CesiumJsonReader::IJsonHandler* ExtensionModelKhrMaterialsVariantsJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionModelKhrMaterialsVariants( - CesiumGltf::ExtensionModelKhrMaterialsVariants::TypeName, - str, - *this->_pObject); -} - -void ExtensionModelKhrMaterialsVariantsJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumUtility::ExtensibleObject& o, - const std::string_view& extensionName) { - std::any& value = o.extensions - .emplace( - extensionName, - CesiumGltf::ExtensionModelKhrMaterialsVariants()) - .first->second; + return this->readObjectKeyExtensionModelKhrMaterialsVariants(CesiumGltf::ExtensionModelKhrMaterialsVariants::TypeName, str, *this->_pObject); +} + +void ExtensionModelKhrMaterialsVariantsJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) { + std::any& value = + o.extensions.emplace(extensionName, CesiumGltf::ExtensionModelKhrMaterialsVariants()) + .first->second; this->reset( pParentHandler, &std::any_cast(value)); } -CesiumJsonReader::IJsonHandler* ExtensionModelKhrMaterialsVariantsJsonHandler:: - readObjectKeyExtensionModelKhrMaterialsVariants( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::ExtensionModelKhrMaterialsVariants& o) { +CesiumJsonReader::IJsonHandler* ExtensionModelKhrMaterialsVariantsJsonHandler::readObjectKeyExtensionModelKhrMaterialsVariants(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionModelKhrMaterialsVariants& o) { using namespace std::string_literals; - if ("variants"s == str) - return property("variants", this->_variants, o.variants); + if ("variants"s == str) return property("variants", this->_variants, o.variants); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -957,55 +604,31 @@ CesiumJsonReader::IJsonHandler* ExtensionModelKhrMaterialsVariantsJsonHandler:: namespace CesiumGltfReader { -ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler:: - ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _mappings(context) {} +ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler::ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _mappings(context) {} -void ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::ExtensionMeshPrimitiveKhrMaterialsVariants* pObject) { +void ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionMeshPrimitiveKhrMaterialsVariants* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler::readObjectKey( - const std::string_view& str) { +CesiumJsonReader::IJsonHandler* ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionMeshPrimitiveKhrMaterialsVariants( - CesiumGltf::ExtensionMeshPrimitiveKhrMaterialsVariants::TypeName, - str, - *this->_pObject); + return this->readObjectKeyExtensionMeshPrimitiveKhrMaterialsVariants(CesiumGltf::ExtensionMeshPrimitiveKhrMaterialsVariants::TypeName, str, *this->_pObject); } -void ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumUtility::ExtensibleObject& o, - const std::string_view& extensionName) { +void ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) { std::any& value = - o.extensions - .emplace( - extensionName, - CesiumGltf::ExtensionMeshPrimitiveKhrMaterialsVariants()) + o.extensions.emplace(extensionName, CesiumGltf::ExtensionMeshPrimitiveKhrMaterialsVariants()) .first->second; this->reset( pParentHandler, - &std::any_cast( - value)); + &std::any_cast(value)); } -CesiumJsonReader::IJsonHandler* -ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler:: - readObjectKeyExtensionMeshPrimitiveKhrMaterialsVariants( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::ExtensionMeshPrimitiveKhrMaterialsVariants& o) { +CesiumJsonReader::IJsonHandler* ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler::readObjectKeyExtensionMeshPrimitiveKhrMaterialsVariants(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionMeshPrimitiveKhrMaterialsVariants& o) { using namespace std::string_literals; - if ("mappings"s == str) - return property("mappings", this->_mappings, o.mappings); + if ("mappings"s == str) return property("mappings", this->_mappings, o.mappings); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -1022,49 +645,31 @@ ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler:: namespace CesiumGltfReader { -ExtensionKhrTextureBasisuJsonHandler::ExtensionKhrTextureBasisuJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _source() {} +ExtensionKhrTextureBasisuJsonHandler::ExtensionKhrTextureBasisuJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _source() {} -void ExtensionKhrTextureBasisuJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::ExtensionKhrTextureBasisu* pObject) { +void ExtensionKhrTextureBasisuJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionKhrTextureBasisu* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -ExtensionKhrTextureBasisuJsonHandler::readObjectKey( - const std::string_view& str) { +CesiumJsonReader::IJsonHandler* ExtensionKhrTextureBasisuJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionKhrTextureBasisu( - CesiumGltf::ExtensionKhrTextureBasisu::TypeName, - str, - *this->_pObject); + return this->readObjectKeyExtensionKhrTextureBasisu(CesiumGltf::ExtensionKhrTextureBasisu::TypeName, str, *this->_pObject); } -void ExtensionKhrTextureBasisuJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumUtility::ExtensibleObject& o, - const std::string_view& extensionName) { +void ExtensionKhrTextureBasisuJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) { std::any& value = - o.extensions - .emplace(extensionName, CesiumGltf::ExtensionKhrTextureBasisu()) + o.extensions.emplace(extensionName, CesiumGltf::ExtensionKhrTextureBasisu()) .first->second; this->reset( pParentHandler, &std::any_cast(value)); } -CesiumJsonReader::IJsonHandler* -ExtensionKhrTextureBasisuJsonHandler::readObjectKeyExtensionKhrTextureBasisu( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::ExtensionKhrTextureBasisu& o) { +CesiumJsonReader::IJsonHandler* ExtensionKhrTextureBasisuJsonHandler::readObjectKeyExtensionKhrTextureBasisu(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionKhrTextureBasisu& o) { using namespace std::string_literals; - if ("source"s == str) - return property("source", this->_source, o.source); + if ("source"s == str) return property("source", this->_source, o.source); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -1081,54 +686,32 @@ ExtensionKhrTextureBasisuJsonHandler::readObjectKeyExtensionKhrTextureBasisu( namespace CesiumGltfReader { -ExtensionModelMaxarMeshVariantsJsonHandler:: - ExtensionModelMaxarMeshVariantsJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _defaultProperty(), - _variants(context) {} +ExtensionModelMaxarMeshVariantsJsonHandler::ExtensionModelMaxarMeshVariantsJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _defaultProperty(), _variants(context) {} -void ExtensionModelMaxarMeshVariantsJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::ExtensionModelMaxarMeshVariants* pObject) { +void ExtensionModelMaxarMeshVariantsJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionModelMaxarMeshVariants* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -ExtensionModelMaxarMeshVariantsJsonHandler::readObjectKey( - const std::string_view& str) { +CesiumJsonReader::IJsonHandler* ExtensionModelMaxarMeshVariantsJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionModelMaxarMeshVariants( - CesiumGltf::ExtensionModelMaxarMeshVariants::TypeName, - str, - *this->_pObject); + return this->readObjectKeyExtensionModelMaxarMeshVariants(CesiumGltf::ExtensionModelMaxarMeshVariants::TypeName, str, *this->_pObject); } -void ExtensionModelMaxarMeshVariantsJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumUtility::ExtensibleObject& o, - const std::string_view& extensionName) { +void ExtensionModelMaxarMeshVariantsJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) { std::any& value = - o.extensions - .emplace(extensionName, CesiumGltf::ExtensionModelMaxarMeshVariants()) + o.extensions.emplace(extensionName, CesiumGltf::ExtensionModelMaxarMeshVariants()) .first->second; this->reset( pParentHandler, &std::any_cast(value)); } -CesiumJsonReader::IJsonHandler* ExtensionModelMaxarMeshVariantsJsonHandler:: - readObjectKeyExtensionModelMaxarMeshVariants( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::ExtensionModelMaxarMeshVariants& o) { +CesiumJsonReader::IJsonHandler* ExtensionModelMaxarMeshVariantsJsonHandler::readObjectKeyExtensionModelMaxarMeshVariants(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionModelMaxarMeshVariants& o) { using namespace std::string_literals; - if ("default"s == str) - return property("default", this->_defaultProperty, o.defaultProperty); - if ("variants"s == str) - return property("variants", this->_variants, o.variants); + if ("default"s == str) return property("default", this->_defaultProperty, o.defaultProperty); + if ("variants"s == str) return property("variants", this->_variants, o.variants); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -1145,51 +728,31 @@ CesiumJsonReader::IJsonHandler* ExtensionModelMaxarMeshVariantsJsonHandler:: namespace CesiumGltfReader { -ExtensionNodeMaxarMeshVariantsJsonHandler:: - ExtensionNodeMaxarMeshVariantsJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _mappings(context) {} +ExtensionNodeMaxarMeshVariantsJsonHandler::ExtensionNodeMaxarMeshVariantsJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _mappings(context) {} -void ExtensionNodeMaxarMeshVariantsJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::ExtensionNodeMaxarMeshVariants* pObject) { +void ExtensionNodeMaxarMeshVariantsJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionNodeMaxarMeshVariants* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -ExtensionNodeMaxarMeshVariantsJsonHandler::readObjectKey( - const std::string_view& str) { +CesiumJsonReader::IJsonHandler* ExtensionNodeMaxarMeshVariantsJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionNodeMaxarMeshVariants( - CesiumGltf::ExtensionNodeMaxarMeshVariants::TypeName, - str, - *this->_pObject); + return this->readObjectKeyExtensionNodeMaxarMeshVariants(CesiumGltf::ExtensionNodeMaxarMeshVariants::TypeName, str, *this->_pObject); } -void ExtensionNodeMaxarMeshVariantsJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumUtility::ExtensibleObject& o, - const std::string_view& extensionName) { +void ExtensionNodeMaxarMeshVariantsJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) { std::any& value = - o.extensions - .emplace(extensionName, CesiumGltf::ExtensionNodeMaxarMeshVariants()) + o.extensions.emplace(extensionName, CesiumGltf::ExtensionNodeMaxarMeshVariants()) .first->second; this->reset( pParentHandler, &std::any_cast(value)); } -CesiumJsonReader::IJsonHandler* ExtensionNodeMaxarMeshVariantsJsonHandler:: - readObjectKeyExtensionNodeMaxarMeshVariants( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::ExtensionNodeMaxarMeshVariants& o) { +CesiumJsonReader::IJsonHandler* ExtensionNodeMaxarMeshVariantsJsonHandler::readObjectKeyExtensionNodeMaxarMeshVariants(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionNodeMaxarMeshVariants& o) { using namespace std::string_literals; - if ("mappings"s == str) - return property("mappings", this->_mappings, o.mappings); + if ("mappings"s == str) return property("mappings", this->_mappings, o.mappings); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -1206,60 +769,34 @@ CesiumJsonReader::IJsonHandler* ExtensionNodeMaxarMeshVariantsJsonHandler:: namespace CesiumGltfReader { -ExtensionKhrTextureTransformJsonHandler:: - ExtensionKhrTextureTransformJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _offset(), - _rotation(), - _scale(), - _texCoord() {} - -void ExtensionKhrTextureTransformJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::ExtensionKhrTextureTransform* pObject) { +ExtensionKhrTextureTransformJsonHandler::ExtensionKhrTextureTransformJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _offset(), _rotation(), _scale(), _texCoord() {} + +void ExtensionKhrTextureTransformJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionKhrTextureTransform* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -ExtensionKhrTextureTransformJsonHandler::readObjectKey( - const std::string_view& str) { +CesiumJsonReader::IJsonHandler* ExtensionKhrTextureTransformJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionKhrTextureTransform( - CesiumGltf::ExtensionKhrTextureTransform::TypeName, - str, - *this->_pObject); + return this->readObjectKeyExtensionKhrTextureTransform(CesiumGltf::ExtensionKhrTextureTransform::TypeName, str, *this->_pObject); } -void ExtensionKhrTextureTransformJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumUtility::ExtensibleObject& o, - const std::string_view& extensionName) { +void ExtensionKhrTextureTransformJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) { std::any& value = - o.extensions - .emplace(extensionName, CesiumGltf::ExtensionKhrTextureTransform()) + o.extensions.emplace(extensionName, CesiumGltf::ExtensionKhrTextureTransform()) .first->second; this->reset( pParentHandler, &std::any_cast(value)); } -CesiumJsonReader::IJsonHandler* ExtensionKhrTextureTransformJsonHandler:: - readObjectKeyExtensionKhrTextureTransform( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::ExtensionKhrTextureTransform& o) { +CesiumJsonReader::IJsonHandler* ExtensionKhrTextureTransformJsonHandler::readObjectKeyExtensionKhrTextureTransform(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionKhrTextureTransform& o) { using namespace std::string_literals; - if ("offset"s == str) - return property("offset", this->_offset, o.offset); - if ("rotation"s == str) - return property("rotation", this->_rotation, o.rotation); - if ("scale"s == str) - return property("scale", this->_scale, o.scale); - if ("texCoord"s == str) - return property("texCoord", this->_texCoord, o.texCoord); + if ("offset"s == str) return property("offset", this->_offset, o.offset); + if ("rotation"s == str) return property("rotation", this->_rotation, o.rotation); + if ("scale"s == str) return property("scale", this->_scale, o.scale); + if ("texCoord"s == str) return property("texCoord", this->_texCoord, o.texCoord); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -1276,30 +813,19 @@ CesiumJsonReader::IJsonHandler* ExtensionKhrTextureTransformJsonHandler:: namespace CesiumGltfReader { -ExtensionTextureWebpJsonHandler::ExtensionTextureWebpJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _source() {} +ExtensionTextureWebpJsonHandler::ExtensionTextureWebpJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _source() {} -void ExtensionTextureWebpJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::ExtensionTextureWebp* pObject) { +void ExtensionTextureWebpJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionTextureWebp* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -ExtensionTextureWebpJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* ExtensionTextureWebpJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionTextureWebp( - CesiumGltf::ExtensionTextureWebp::TypeName, - str, - *this->_pObject); + return this->readObjectKeyExtensionTextureWebp(CesiumGltf::ExtensionTextureWebp::TypeName, str, *this->_pObject); } -void ExtensionTextureWebpJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumUtility::ExtensibleObject& o, - const std::string_view& extensionName) { +void ExtensionTextureWebpJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) { std::any& value = o.extensions.emplace(extensionName, CesiumGltf::ExtensionTextureWebp()) .first->second; @@ -1308,15 +834,10 @@ void ExtensionTextureWebpJsonHandler::reset( &std::any_cast(value)); } -CesiumJsonReader::IJsonHandler* -ExtensionTextureWebpJsonHandler::readObjectKeyExtensionTextureWebp( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::ExtensionTextureWebp& o) { +CesiumJsonReader::IJsonHandler* ExtensionTextureWebpJsonHandler::readObjectKeyExtensionTextureWebp(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionTextureWebp& o) { using namespace std::string_literals; - if ("source"s == str) - return property("source", this->_source, o.source); + if ("source"s == str) return property("source", this->_source, o.source); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -1333,45 +854,24 @@ ExtensionTextureWebpJsonHandler::readObjectKeyExtensionTextureWebp( namespace CesiumGltfReader { -ExtensionNodeMaxarMeshVariantsMappingsValueJsonHandler:: - ExtensionNodeMaxarMeshVariantsMappingsValueJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _variants(), - _mesh(), - _name() {} - -void ExtensionNodeMaxarMeshVariantsMappingsValueJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::ExtensionNodeMaxarMeshVariantsMappingsValue* pObject) { +ExtensionNodeMaxarMeshVariantsMappingsValueJsonHandler::ExtensionNodeMaxarMeshVariantsMappingsValueJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _variants(), _mesh(), _name() {} + +void ExtensionNodeMaxarMeshVariantsMappingsValueJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionNodeMaxarMeshVariantsMappingsValue* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -ExtensionNodeMaxarMeshVariantsMappingsValueJsonHandler::readObjectKey( - const std::string_view& str) { +CesiumJsonReader::IJsonHandler* ExtensionNodeMaxarMeshVariantsMappingsValueJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionNodeMaxarMeshVariantsMappingsValue( - CesiumGltf::ExtensionNodeMaxarMeshVariantsMappingsValue::TypeName, - str, - *this->_pObject); -} - -CesiumJsonReader::IJsonHandler* -ExtensionNodeMaxarMeshVariantsMappingsValueJsonHandler:: - readObjectKeyExtensionNodeMaxarMeshVariantsMappingsValue( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::ExtensionNodeMaxarMeshVariantsMappingsValue& o) { + return this->readObjectKeyExtensionNodeMaxarMeshVariantsMappingsValue(CesiumGltf::ExtensionNodeMaxarMeshVariantsMappingsValue::TypeName, str, *this->_pObject); +} + +CesiumJsonReader::IJsonHandler* ExtensionNodeMaxarMeshVariantsMappingsValueJsonHandler::readObjectKeyExtensionNodeMaxarMeshVariantsMappingsValue(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionNodeMaxarMeshVariantsMappingsValue& o) { using namespace std::string_literals; - if ("variants"s == str) - return property("variants", this->_variants, o.variants); - if ("mesh"s == str) - return property("mesh", this->_mesh, o.mesh); - if ("name"s == str) - return property("name", this->_name, o.name); + if ("variants"s == str) return property("variants", this->_variants, o.variants); + if ("mesh"s == str) return property("mesh", this->_mesh, o.mesh); + if ("name"s == str) return property("name", this->_name, o.name); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -1388,38 +888,22 @@ ExtensionNodeMaxarMeshVariantsMappingsValueJsonHandler:: namespace CesiumGltfReader { -ExtensionModelMaxarMeshVariantsValueJsonHandler:: - ExtensionModelMaxarMeshVariantsValueJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumGltfReader::NamedObjectJsonHandler(context), _name() {} +ExtensionModelMaxarMeshVariantsValueJsonHandler::ExtensionModelMaxarMeshVariantsValueJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumGltfReader::NamedObjectJsonHandler(context), _name() {} -void ExtensionModelMaxarMeshVariantsValueJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::ExtensionModelMaxarMeshVariantsValue* pObject) { +void ExtensionModelMaxarMeshVariantsValueJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionModelMaxarMeshVariantsValue* pObject) { CesiumGltfReader::NamedObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -ExtensionModelMaxarMeshVariantsValueJsonHandler::readObjectKey( - const std::string_view& str) { +CesiumJsonReader::IJsonHandler* ExtensionModelMaxarMeshVariantsValueJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionModelMaxarMeshVariantsValue( - CesiumGltf::ExtensionModelMaxarMeshVariantsValue::TypeName, - str, - *this->_pObject); -} - -CesiumJsonReader::IJsonHandler* -ExtensionModelMaxarMeshVariantsValueJsonHandler:: - readObjectKeyExtensionModelMaxarMeshVariantsValue( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::ExtensionModelMaxarMeshVariantsValue& o) { + return this->readObjectKeyExtensionModelMaxarMeshVariantsValue(CesiumGltf::ExtensionModelMaxarMeshVariantsValue::TypeName, str, *this->_pObject); +} + +CesiumJsonReader::IJsonHandler* ExtensionModelMaxarMeshVariantsValueJsonHandler::readObjectKeyExtensionModelMaxarMeshVariantsValue(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionModelMaxarMeshVariantsValue& o) { using namespace std::string_literals; - if ("name"s == str) - return property("name", this->_name, o.name); + if ("name"s == str) return property("name", this->_name, o.name); return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } @@ -1436,49 +920,24 @@ ExtensionModelMaxarMeshVariantsValueJsonHandler:: namespace CesiumGltfReader { -ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueJsonHandler:: - ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _variants(), - _material(), - _name() {} - -void ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValue* - pObject) { +ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueJsonHandler::ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _variants(), _material(), _name() {} + +void ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValue* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueJsonHandler:: - readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this - ->readObjectKeyExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValue( - CesiumGltf::ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValue:: - TypeName, - str, - *this->_pObject); -} - -CesiumJsonReader::IJsonHandler* -ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueJsonHandler:: - readObjectKeyExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValue( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValue& - o) { + return this->readObjectKeyExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValue(CesiumGltf::ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValue::TypeName, str, *this->_pObject); +} + +CesiumJsonReader::IJsonHandler* ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueJsonHandler::readObjectKeyExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValue(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValue& o) { using namespace std::string_literals; - if ("variants"s == str) - return property("variants", this->_variants, o.variants); - if ("material"s == str) - return property("material", this->_material, o.material); - if ("name"s == str) - return property("name", this->_name, o.name); + if ("variants"s == str) return property("variants", this->_variants, o.variants); + if ("material"s == str) return property("material", this->_material, o.material); + if ("name"s == str) return property("name", this->_name, o.name); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -1495,38 +954,22 @@ ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueJsonHandler:: namespace CesiumGltfReader { -ExtensionModelKhrMaterialsVariantsValueJsonHandler:: - ExtensionModelKhrMaterialsVariantsValueJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumGltfReader::NamedObjectJsonHandler(context), _name() {} +ExtensionModelKhrMaterialsVariantsValueJsonHandler::ExtensionModelKhrMaterialsVariantsValueJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumGltfReader::NamedObjectJsonHandler(context), _name() {} -void ExtensionModelKhrMaterialsVariantsValueJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::ExtensionModelKhrMaterialsVariantsValue* pObject) { +void ExtensionModelKhrMaterialsVariantsValueJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionModelKhrMaterialsVariantsValue* pObject) { CesiumGltfReader::NamedObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -ExtensionModelKhrMaterialsVariantsValueJsonHandler::readObjectKey( - const std::string_view& str) { +CesiumJsonReader::IJsonHandler* ExtensionModelKhrMaterialsVariantsValueJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionModelKhrMaterialsVariantsValue( - CesiumGltf::ExtensionModelKhrMaterialsVariantsValue::TypeName, - str, - *this->_pObject); -} - -CesiumJsonReader::IJsonHandler* -ExtensionModelKhrMaterialsVariantsValueJsonHandler:: - readObjectKeyExtensionModelKhrMaterialsVariantsValue( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::ExtensionModelKhrMaterialsVariantsValue& o) { + return this->readObjectKeyExtensionModelKhrMaterialsVariantsValue(CesiumGltf::ExtensionModelKhrMaterialsVariantsValue::TypeName, str, *this->_pObject); +} + +CesiumJsonReader::IJsonHandler* ExtensionModelKhrMaterialsVariantsValueJsonHandler::readObjectKeyExtensionModelKhrMaterialsVariantsValue(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionModelKhrMaterialsVariantsValue& o) { using namespace std::string_literals; - if ("name"s == str) - return property("name", this->_name, o.name); + if ("name"s == str) return property("name", this->_name, o.name); return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } @@ -1543,45 +986,24 @@ ExtensionModelKhrMaterialsVariantsValueJsonHandler:: namespace CesiumGltfReader { -ExtensionExtStructuralMetadataPropertyAttributeJsonHandler:: - ExtensionExtStructuralMetadataPropertyAttributeJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _name(), - _classProperty(), - _properties(context) {} - -void ExtensionExtStructuralMetadataPropertyAttributeJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::ExtensionExtStructuralMetadataPropertyAttribute* pObject) { +ExtensionExtStructuralMetadataPropertyAttributeJsonHandler::ExtensionExtStructuralMetadataPropertyAttributeJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _name(), _classProperty(), _properties(context) {} + +void ExtensionExtStructuralMetadataPropertyAttributeJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtStructuralMetadataPropertyAttribute* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -ExtensionExtStructuralMetadataPropertyAttributeJsonHandler::readObjectKey( - const std::string_view& str) { +CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataPropertyAttributeJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionExtStructuralMetadataPropertyAttribute( - CesiumGltf::ExtensionExtStructuralMetadataPropertyAttribute::TypeName, - str, - *this->_pObject); -} - -CesiumJsonReader::IJsonHandler* -ExtensionExtStructuralMetadataPropertyAttributeJsonHandler:: - readObjectKeyExtensionExtStructuralMetadataPropertyAttribute( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::ExtensionExtStructuralMetadataPropertyAttribute& o) { + return this->readObjectKeyExtensionExtStructuralMetadataPropertyAttribute(CesiumGltf::ExtensionExtStructuralMetadataPropertyAttribute::TypeName, str, *this->_pObject); +} + +CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataPropertyAttributeJsonHandler::readObjectKeyExtensionExtStructuralMetadataPropertyAttribute(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtStructuralMetadataPropertyAttribute& o) { using namespace std::string_literals; - if ("name"s == str) - return property("name", this->_name, o.name); - if ("class"s == str) - return property("class", this->_classProperty, o.classProperty); - if ("properties"s == str) - return property("properties", this->_properties, o.properties); + if ("name"s == str) return property("name", this->_name, o.name); + if ("class"s == str) return property("class", this->_classProperty, o.classProperty); + if ("properties"s == str) return property("properties", this->_properties, o.properties); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -1598,55 +1020,26 @@ ExtensionExtStructuralMetadataPropertyAttributeJsonHandler:: namespace CesiumGltfReader { -ExtensionExtStructuralMetadataPropertyAttributePropertyJsonHandler:: - ExtensionExtStructuralMetadataPropertyAttributePropertyJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _attribute(), - _offset(), - _scale(), - _max(), - _min() {} - -void ExtensionExtStructuralMetadataPropertyAttributePropertyJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::ExtensionExtStructuralMetadataPropertyAttributeProperty* - pObject) { +ExtensionExtStructuralMetadataPropertyAttributePropertyJsonHandler::ExtensionExtStructuralMetadataPropertyAttributePropertyJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _attribute(), _offset(), _scale(), _max(), _min() {} + +void ExtensionExtStructuralMetadataPropertyAttributePropertyJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtStructuralMetadataPropertyAttributeProperty* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -ExtensionExtStructuralMetadataPropertyAttributePropertyJsonHandler:: - readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataPropertyAttributePropertyJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this - ->readObjectKeyExtensionExtStructuralMetadataPropertyAttributeProperty( - CesiumGltf::ExtensionExtStructuralMetadataPropertyAttributeProperty:: - TypeName, - str, - *this->_pObject); -} - -CesiumJsonReader::IJsonHandler* -ExtensionExtStructuralMetadataPropertyAttributePropertyJsonHandler:: - readObjectKeyExtensionExtStructuralMetadataPropertyAttributeProperty( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::ExtensionExtStructuralMetadataPropertyAttributeProperty& - o) { + return this->readObjectKeyExtensionExtStructuralMetadataPropertyAttributeProperty(CesiumGltf::ExtensionExtStructuralMetadataPropertyAttributeProperty::TypeName, str, *this->_pObject); +} + +CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataPropertyAttributePropertyJsonHandler::readObjectKeyExtensionExtStructuralMetadataPropertyAttributeProperty(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtStructuralMetadataPropertyAttributeProperty& o) { using namespace std::string_literals; - if ("attribute"s == str) - return property("attribute", this->_attribute, o.attribute); - if ("offset"s == str) - return property("offset", this->_offset, o.offset); - if ("scale"s == str) - return property("scale", this->_scale, o.scale); - if ("max"s == str) - return property("max", this->_max, o.max); - if ("min"s == str) - return property("min", this->_min, o.min); + if ("attribute"s == str) return property("attribute", this->_attribute, o.attribute); + if ("offset"s == str) return property("offset", this->_offset, o.offset); + if ("scale"s == str) return property("scale", this->_scale, o.scale); + if ("max"s == str) return property("max", this->_max, o.max); + if ("min"s == str) return property("min", this->_min, o.min); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -1663,45 +1056,24 @@ ExtensionExtStructuralMetadataPropertyAttributePropertyJsonHandler:: namespace CesiumGltfReader { -ExtensionExtStructuralMetadataPropertyTextureJsonHandler:: - ExtensionExtStructuralMetadataPropertyTextureJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _name(), - _classProperty(), - _properties(context) {} - -void ExtensionExtStructuralMetadataPropertyTextureJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::ExtensionExtStructuralMetadataPropertyTexture* pObject) { +ExtensionExtStructuralMetadataPropertyTextureJsonHandler::ExtensionExtStructuralMetadataPropertyTextureJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _name(), _classProperty(), _properties(context) {} + +void ExtensionExtStructuralMetadataPropertyTextureJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtStructuralMetadataPropertyTexture* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -ExtensionExtStructuralMetadataPropertyTextureJsonHandler::readObjectKey( - const std::string_view& str) { +CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataPropertyTextureJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionExtStructuralMetadataPropertyTexture( - CesiumGltf::ExtensionExtStructuralMetadataPropertyTexture::TypeName, - str, - *this->_pObject); -} - -CesiumJsonReader::IJsonHandler* -ExtensionExtStructuralMetadataPropertyTextureJsonHandler:: - readObjectKeyExtensionExtStructuralMetadataPropertyTexture( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::ExtensionExtStructuralMetadataPropertyTexture& o) { + return this->readObjectKeyExtensionExtStructuralMetadataPropertyTexture(CesiumGltf::ExtensionExtStructuralMetadataPropertyTexture::TypeName, str, *this->_pObject); +} + +CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataPropertyTextureJsonHandler::readObjectKeyExtensionExtStructuralMetadataPropertyTexture(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtStructuralMetadataPropertyTexture& o) { using namespace std::string_literals; - if ("name"s == str) - return property("name", this->_name, o.name); - if ("class"s == str) - return property("class", this->_classProperty, o.classProperty); - if ("properties"s == str) - return property("properties", this->_properties, o.properties); + if ("name"s == str) return property("name", this->_name, o.name); + if ("class"s == str) return property("class", this->_classProperty, o.classProperty); + if ("properties"s == str) return property("properties", this->_properties, o.properties); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -1718,54 +1090,26 @@ ExtensionExtStructuralMetadataPropertyTextureJsonHandler:: namespace CesiumGltfReader { -ExtensionExtStructuralMetadataPropertyTexturePropertyJsonHandler:: - ExtensionExtStructuralMetadataPropertyTexturePropertyJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : TextureInfoJsonHandler(context), - _channels(), - _offset(), - _scale(), - _max(), - _min() {} - -void ExtensionExtStructuralMetadataPropertyTexturePropertyJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::ExtensionExtStructuralMetadataPropertyTextureProperty* - pObject) { +ExtensionExtStructuralMetadataPropertyTexturePropertyJsonHandler::ExtensionExtStructuralMetadataPropertyTexturePropertyJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : TextureInfoJsonHandler(context), _channels(), _offset(), _scale(), _max(), _min() {} + +void ExtensionExtStructuralMetadataPropertyTexturePropertyJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtStructuralMetadataPropertyTextureProperty* pObject) { TextureInfoJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -ExtensionExtStructuralMetadataPropertyTexturePropertyJsonHandler::readObjectKey( - const std::string_view& str) { +CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataPropertyTexturePropertyJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this - ->readObjectKeyExtensionExtStructuralMetadataPropertyTextureProperty( - CesiumGltf::ExtensionExtStructuralMetadataPropertyTextureProperty:: - TypeName, - str, - *this->_pObject); -} - -CesiumJsonReader::IJsonHandler* -ExtensionExtStructuralMetadataPropertyTexturePropertyJsonHandler:: - readObjectKeyExtensionExtStructuralMetadataPropertyTextureProperty( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::ExtensionExtStructuralMetadataPropertyTextureProperty& o) { + return this->readObjectKeyExtensionExtStructuralMetadataPropertyTextureProperty(CesiumGltf::ExtensionExtStructuralMetadataPropertyTextureProperty::TypeName, str, *this->_pObject); +} + +CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataPropertyTexturePropertyJsonHandler::readObjectKeyExtensionExtStructuralMetadataPropertyTextureProperty(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtStructuralMetadataPropertyTextureProperty& o) { using namespace std::string_literals; - if ("channels"s == str) - return property("channels", this->_channels, o.channels); - if ("offset"s == str) - return property("offset", this->_offset, o.offset); - if ("scale"s == str) - return property("scale", this->_scale, o.scale); - if ("max"s == str) - return property("max", this->_max, o.max); - if ("min"s == str) - return property("min", this->_min, o.min); + if ("channels"s == str) return property("channels", this->_channels, o.channels); + if ("offset"s == str) return property("offset", this->_offset, o.offset); + if ("scale"s == str) return property("scale", this->_scale, o.scale); + if ("max"s == str) return property("max", this->_max, o.max); + if ("min"s == str) return property("min", this->_min, o.min); return this->readObjectKeyTextureInfo(objectType, str, *this->_pObject); } @@ -1782,39 +1126,23 @@ ExtensionExtStructuralMetadataPropertyTexturePropertyJsonHandler:: namespace CesiumGltfReader { -TextureInfoJsonHandler::TextureInfoJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _index(), - _texCoord() {} +TextureInfoJsonHandler::TextureInfoJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _index(), _texCoord() {} -void TextureInfoJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::TextureInfo* pObject) { +void TextureInfoJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::TextureInfo* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -TextureInfoJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* TextureInfoJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyTextureInfo( - CesiumGltf::TextureInfo::TypeName, - str, - *this->_pObject); + return this->readObjectKeyTextureInfo(CesiumGltf::TextureInfo::TypeName, str, *this->_pObject); } -CesiumJsonReader::IJsonHandler* -TextureInfoJsonHandler::readObjectKeyTextureInfo( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::TextureInfo& o) { +CesiumJsonReader::IJsonHandler* TextureInfoJsonHandler::readObjectKeyTextureInfo(const std::string& objectType, const std::string_view& str, CesiumGltf::TextureInfo& o) { using namespace std::string_literals; - if ("index"s == str) - return property("index", this->_index, o.index); - if ("texCoord"s == str) - return property("texCoord", this->_texCoord, o.texCoord); + if ("index"s == str) return property("index", this->_index, o.index); + if ("texCoord"s == str) return property("texCoord", this->_texCoord, o.texCoord); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -1831,48 +1159,25 @@ TextureInfoJsonHandler::readObjectKeyTextureInfo( namespace CesiumGltfReader { -ExtensionExtStructuralMetadataPropertyTableJsonHandler:: - ExtensionExtStructuralMetadataPropertyTableJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _name(), - _classProperty(), - _count(), - _properties(context) {} - -void ExtensionExtStructuralMetadataPropertyTableJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::ExtensionExtStructuralMetadataPropertyTable* pObject) { +ExtensionExtStructuralMetadataPropertyTableJsonHandler::ExtensionExtStructuralMetadataPropertyTableJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _name(), _classProperty(), _count(), _properties(context) {} + +void ExtensionExtStructuralMetadataPropertyTableJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtStructuralMetadataPropertyTable* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -ExtensionExtStructuralMetadataPropertyTableJsonHandler::readObjectKey( - const std::string_view& str) { +CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataPropertyTableJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionExtStructuralMetadataPropertyTable( - CesiumGltf::ExtensionExtStructuralMetadataPropertyTable::TypeName, - str, - *this->_pObject); -} - -CesiumJsonReader::IJsonHandler* -ExtensionExtStructuralMetadataPropertyTableJsonHandler:: - readObjectKeyExtensionExtStructuralMetadataPropertyTable( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::ExtensionExtStructuralMetadataPropertyTable& o) { + return this->readObjectKeyExtensionExtStructuralMetadataPropertyTable(CesiumGltf::ExtensionExtStructuralMetadataPropertyTable::TypeName, str, *this->_pObject); +} + +CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataPropertyTableJsonHandler::readObjectKeyExtensionExtStructuralMetadataPropertyTable(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtStructuralMetadataPropertyTable& o) { using namespace std::string_literals; - if ("name"s == str) - return property("name", this->_name, o.name); - if ("class"s == str) - return property("class", this->_classProperty, o.classProperty); - if ("count"s == str) - return property("count", this->_count, o.count); - if ("properties"s == str) - return property("properties", this->_properties, o.properties); + if ("name"s == str) return property("name", this->_name, o.name); + if ("class"s == str) return property("class", this->_classProperty, o.classProperty); + if ("count"s == str) return property("count", this->_count, o.count); + if ("properties"s == str) return property("properties", this->_properties, o.properties); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -1889,69 +1194,30 @@ ExtensionExtStructuralMetadataPropertyTableJsonHandler:: namespace CesiumGltfReader { -ExtensionExtStructuralMetadataPropertyTablePropertyJsonHandler:: - ExtensionExtStructuralMetadataPropertyTablePropertyJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _values(), - _arrayOffsets(), - _stringOffsets(), - _arrayOffsetType(), - _stringOffsetType(), - _offset(), - _scale(), - _max(), - _min() {} - -void ExtensionExtStructuralMetadataPropertyTablePropertyJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::ExtensionExtStructuralMetadataPropertyTableProperty* pObject) { +ExtensionExtStructuralMetadataPropertyTablePropertyJsonHandler::ExtensionExtStructuralMetadataPropertyTablePropertyJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _values(), _arrayOffsets(), _stringOffsets(), _arrayOffsetType(), _stringOffsetType(), _offset(), _scale(), _max(), _min() {} + +void ExtensionExtStructuralMetadataPropertyTablePropertyJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtStructuralMetadataPropertyTableProperty* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -ExtensionExtStructuralMetadataPropertyTablePropertyJsonHandler::readObjectKey( - const std::string_view& str) { +CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataPropertyTablePropertyJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionExtStructuralMetadataPropertyTableProperty( - CesiumGltf::ExtensionExtStructuralMetadataPropertyTableProperty::TypeName, - str, - *this->_pObject); -} - -CesiumJsonReader::IJsonHandler* -ExtensionExtStructuralMetadataPropertyTablePropertyJsonHandler:: - readObjectKeyExtensionExtStructuralMetadataPropertyTableProperty( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::ExtensionExtStructuralMetadataPropertyTableProperty& o) { + return this->readObjectKeyExtensionExtStructuralMetadataPropertyTableProperty(CesiumGltf::ExtensionExtStructuralMetadataPropertyTableProperty::TypeName, str, *this->_pObject); +} + +CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataPropertyTablePropertyJsonHandler::readObjectKeyExtensionExtStructuralMetadataPropertyTableProperty(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtStructuralMetadataPropertyTableProperty& o) { using namespace std::string_literals; - if ("values"s == str) - return property("values", this->_values, o.values); - if ("arrayOffsets"s == str) - return property("arrayOffsets", this->_arrayOffsets, o.arrayOffsets); - if ("stringOffsets"s == str) - return property("stringOffsets", this->_stringOffsets, o.stringOffsets); - if ("arrayOffsetType"s == str) - return property( - "arrayOffsetType", - this->_arrayOffsetType, - o.arrayOffsetType); - if ("stringOffsetType"s == str) - return property( - "stringOffsetType", - this->_stringOffsetType, - o.stringOffsetType); - if ("offset"s == str) - return property("offset", this->_offset, o.offset); - if ("scale"s == str) - return property("scale", this->_scale, o.scale); - if ("max"s == str) - return property("max", this->_max, o.max); - if ("min"s == str) - return property("min", this->_min, o.min); + if ("values"s == str) return property("values", this->_values, o.values); + if ("arrayOffsets"s == str) return property("arrayOffsets", this->_arrayOffsets, o.arrayOffsets); + if ("stringOffsets"s == str) return property("stringOffsets", this->_stringOffsets, o.stringOffsets); + if ("arrayOffsetType"s == str) return property("arrayOffsetType", this->_arrayOffsetType, o.arrayOffsetType); + if ("stringOffsetType"s == str) return property("stringOffsetType", this->_stringOffsetType, o.stringOffsetType); + if ("offset"s == str) return property("offset", this->_offset, o.offset); + if ("scale"s == str) return property("scale", this->_scale, o.scale); + if ("max"s == str) return property("max", this->_max, o.max); + if ("min"s == str) return property("min", this->_min, o.min); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -1968,54 +1234,27 @@ ExtensionExtStructuralMetadataPropertyTablePropertyJsonHandler:: namespace CesiumGltfReader { -ExtensionExtStructuralMetadataSchemaJsonHandler:: - ExtensionExtStructuralMetadataSchemaJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _id(), - _name(), - _description(), - _version(), - _classes(context), - _enums(context) {} - -void ExtensionExtStructuralMetadataSchemaJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::ExtensionExtStructuralMetadataSchema* pObject) { +ExtensionExtStructuralMetadataSchemaJsonHandler::ExtensionExtStructuralMetadataSchemaJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _id(), _name(), _description(), _version(), _classes(context), _enums(context) {} + +void ExtensionExtStructuralMetadataSchemaJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtStructuralMetadataSchema* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -ExtensionExtStructuralMetadataSchemaJsonHandler::readObjectKey( - const std::string_view& str) { +CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataSchemaJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionExtStructuralMetadataSchema( - CesiumGltf::ExtensionExtStructuralMetadataSchema::TypeName, - str, - *this->_pObject); -} - -CesiumJsonReader::IJsonHandler* -ExtensionExtStructuralMetadataSchemaJsonHandler:: - readObjectKeyExtensionExtStructuralMetadataSchema( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::ExtensionExtStructuralMetadataSchema& o) { + return this->readObjectKeyExtensionExtStructuralMetadataSchema(CesiumGltf::ExtensionExtStructuralMetadataSchema::TypeName, str, *this->_pObject); +} + +CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataSchemaJsonHandler::readObjectKeyExtensionExtStructuralMetadataSchema(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtStructuralMetadataSchema& o) { using namespace std::string_literals; - if ("id"s == str) - return property("id", this->_id, o.id); - if ("name"s == str) - return property("name", this->_name, o.name); - if ("description"s == str) - return property("description", this->_description, o.description); - if ("version"s == str) - return property("version", this->_version, o.version); - if ("classes"s == str) - return property("classes", this->_classes, o.classes); - if ("enums"s == str) - return property("enums", this->_enums, o.enums); + if ("id"s == str) return property("id", this->_id, o.id); + if ("name"s == str) return property("name", this->_name, o.name); + if ("description"s == str) return property("description", this->_description, o.description); + if ("version"s == str) return property("version", this->_version, o.version); + if ("classes"s == str) return property("classes", this->_classes, o.classes); + if ("enums"s == str) return property("enums", this->_enums, o.enums); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -2032,47 +1271,25 @@ ExtensionExtStructuralMetadataSchemaJsonHandler:: namespace CesiumGltfReader { -ExtensionExtStructuralMetadataEnumJsonHandler:: - ExtensionExtStructuralMetadataEnumJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _name(), - _description(), - _valueType(), - _values(context) {} - -void ExtensionExtStructuralMetadataEnumJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::ExtensionExtStructuralMetadataEnum* pObject) { +ExtensionExtStructuralMetadataEnumJsonHandler::ExtensionExtStructuralMetadataEnumJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _name(), _description(), _valueType(), _values(context) {} + +void ExtensionExtStructuralMetadataEnumJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtStructuralMetadataEnum* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -ExtensionExtStructuralMetadataEnumJsonHandler::readObjectKey( - const std::string_view& str) { +CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataEnumJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionExtStructuralMetadataEnum( - CesiumGltf::ExtensionExtStructuralMetadataEnum::TypeName, - str, - *this->_pObject); + return this->readObjectKeyExtensionExtStructuralMetadataEnum(CesiumGltf::ExtensionExtStructuralMetadataEnum::TypeName, str, *this->_pObject); } -CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataEnumJsonHandler:: - readObjectKeyExtensionExtStructuralMetadataEnum( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::ExtensionExtStructuralMetadataEnum& o) { +CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataEnumJsonHandler::readObjectKeyExtensionExtStructuralMetadataEnum(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtStructuralMetadataEnum& o) { using namespace std::string_literals; - if ("name"s == str) - return property("name", this->_name, o.name); - if ("description"s == str) - return property("description", this->_description, o.description); - if ("valueType"s == str) - return property("valueType", this->_valueType, o.valueType); - if ("values"s == str) - return property("values", this->_values, o.values); + if ("name"s == str) return property("name", this->_name, o.name); + if ("description"s == str) return property("description", this->_description, o.description); + if ("valueType"s == str) return property("valueType", this->_valueType, o.valueType); + if ("values"s == str) return property("values", this->_values, o.values); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -2089,45 +1306,24 @@ CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataEnumJsonHandler:: namespace CesiumGltfReader { -ExtensionExtStructuralMetadataEnumValueJsonHandler:: - ExtensionExtStructuralMetadataEnumValueJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _name(), - _description(), - _value() {} - -void ExtensionExtStructuralMetadataEnumValueJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::ExtensionExtStructuralMetadataEnumValue* pObject) { +ExtensionExtStructuralMetadataEnumValueJsonHandler::ExtensionExtStructuralMetadataEnumValueJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _name(), _description(), _value() {} + +void ExtensionExtStructuralMetadataEnumValueJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtStructuralMetadataEnumValue* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -ExtensionExtStructuralMetadataEnumValueJsonHandler::readObjectKey( - const std::string_view& str) { +CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataEnumValueJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionExtStructuralMetadataEnumValue( - CesiumGltf::ExtensionExtStructuralMetadataEnumValue::TypeName, - str, - *this->_pObject); -} - -CesiumJsonReader::IJsonHandler* -ExtensionExtStructuralMetadataEnumValueJsonHandler:: - readObjectKeyExtensionExtStructuralMetadataEnumValue( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::ExtensionExtStructuralMetadataEnumValue& o) { + return this->readObjectKeyExtensionExtStructuralMetadataEnumValue(CesiumGltf::ExtensionExtStructuralMetadataEnumValue::TypeName, str, *this->_pObject); +} + +CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataEnumValueJsonHandler::readObjectKeyExtensionExtStructuralMetadataEnumValue(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtStructuralMetadataEnumValue& o) { using namespace std::string_literals; - if ("name"s == str) - return property("name", this->_name, o.name); - if ("description"s == str) - return property("description", this->_description, o.description); - if ("value"s == str) - return property("value", this->_value, o.value); + if ("name"s == str) return property("name", this->_name, o.name); + if ("description"s == str) return property("description", this->_description, o.description); + if ("value"s == str) return property("value", this->_value, o.value); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -2144,44 +1340,24 @@ ExtensionExtStructuralMetadataEnumValueJsonHandler:: namespace CesiumGltfReader { -ExtensionExtStructuralMetadataClassJsonHandler:: - ExtensionExtStructuralMetadataClassJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _name(), - _description(), - _properties(context) {} - -void ExtensionExtStructuralMetadataClassJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::ExtensionExtStructuralMetadataClass* pObject) { +ExtensionExtStructuralMetadataClassJsonHandler::ExtensionExtStructuralMetadataClassJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _name(), _description(), _properties(context) {} + +void ExtensionExtStructuralMetadataClassJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtStructuralMetadataClass* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -ExtensionExtStructuralMetadataClassJsonHandler::readObjectKey( - const std::string_view& str) { +CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataClassJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionExtStructuralMetadataClass( - CesiumGltf::ExtensionExtStructuralMetadataClass::TypeName, - str, - *this->_pObject); + return this->readObjectKeyExtensionExtStructuralMetadataClass(CesiumGltf::ExtensionExtStructuralMetadataClass::TypeName, str, *this->_pObject); } -CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataClassJsonHandler:: - readObjectKeyExtensionExtStructuralMetadataClass( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::ExtensionExtStructuralMetadataClass& o) { +CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataClassJsonHandler::readObjectKeyExtensionExtStructuralMetadataClass(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtStructuralMetadataClass& o) { using namespace std::string_literals; - if ("name"s == str) - return property("name", this->_name, o.name); - if ("description"s == str) - return property("description", this->_description, o.description); - if ("properties"s == str) - return property("properties", this->_properties, o.properties); + if ("name"s == str) return property("name", this->_name, o.name); + if ("description"s == str) return property("description", this->_description, o.description); + if ("properties"s == str) return property("properties", this->_properties, o.properties); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -2198,84 +1374,37 @@ CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataClassJsonHandler:: namespace CesiumGltfReader { -ExtensionExtStructuralMetadataClassPropertyJsonHandler:: - ExtensionExtStructuralMetadataClassPropertyJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _name(), - _description(), - _type(), - _componentType(), - _enumType(), - _array(), - _count(), - _normalized(), - _offset(), - _scale(), - _max(), - _min(), - _required(), - _noData(), - _defaultProperty(), - _semantic() {} - -void ExtensionExtStructuralMetadataClassPropertyJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::ExtensionExtStructuralMetadataClassProperty* pObject) { +ExtensionExtStructuralMetadataClassPropertyJsonHandler::ExtensionExtStructuralMetadataClassPropertyJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _name(), _description(), _type(), _componentType(), _enumType(), _array(), _count(), _normalized(), _offset(), _scale(), _max(), _min(), _required(), _noData(), _defaultProperty(), _semantic() {} + +void ExtensionExtStructuralMetadataClassPropertyJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtStructuralMetadataClassProperty* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -ExtensionExtStructuralMetadataClassPropertyJsonHandler::readObjectKey( - const std::string_view& str) { +CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataClassPropertyJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionExtStructuralMetadataClassProperty( - CesiumGltf::ExtensionExtStructuralMetadataClassProperty::TypeName, - str, - *this->_pObject); -} - -CesiumJsonReader::IJsonHandler* -ExtensionExtStructuralMetadataClassPropertyJsonHandler:: - readObjectKeyExtensionExtStructuralMetadataClassProperty( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::ExtensionExtStructuralMetadataClassProperty& o) { + return this->readObjectKeyExtensionExtStructuralMetadataClassProperty(CesiumGltf::ExtensionExtStructuralMetadataClassProperty::TypeName, str, *this->_pObject); +} + +CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataClassPropertyJsonHandler::readObjectKeyExtensionExtStructuralMetadataClassProperty(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtStructuralMetadataClassProperty& o) { using namespace std::string_literals; - if ("name"s == str) - return property("name", this->_name, o.name); - if ("description"s == str) - return property("description", this->_description, o.description); - if ("type"s == str) - return property("type", this->_type, o.type); - if ("componentType"s == str) - return property("componentType", this->_componentType, o.componentType); - if ("enumType"s == str) - return property("enumType", this->_enumType, o.enumType); - if ("array"s == str) - return property("array", this->_array, o.array); - if ("count"s == str) - return property("count", this->_count, o.count); - if ("normalized"s == str) - return property("normalized", this->_normalized, o.normalized); - if ("offset"s == str) - return property("offset", this->_offset, o.offset); - if ("scale"s == str) - return property("scale", this->_scale, o.scale); - if ("max"s == str) - return property("max", this->_max, o.max); - if ("min"s == str) - return property("min", this->_min, o.min); - if ("required"s == str) - return property("required", this->_required, o.required); - if ("noData"s == str) - return property("noData", this->_noData, o.noData); - if ("default"s == str) - return property("default", this->_defaultProperty, o.defaultProperty); - if ("semantic"s == str) - return property("semantic", this->_semantic, o.semantic); + if ("name"s == str) return property("name", this->_name, o.name); + if ("description"s == str) return property("description", this->_description, o.description); + if ("type"s == str) return property("type", this->_type, o.type); + if ("componentType"s == str) return property("componentType", this->_componentType, o.componentType); + if ("enumType"s == str) return property("enumType", this->_enumType, o.enumType); + if ("array"s == str) return property("array", this->_array, o.array); + if ("count"s == str) return property("count", this->_count, o.count); + if ("normalized"s == str) return property("normalized", this->_normalized, o.normalized); + if ("offset"s == str) return property("offset", this->_offset, o.offset); + if ("scale"s == str) return property("scale", this->_scale, o.scale); + if ("max"s == str) return property("max", this->_max, o.max); + if ("min"s == str) return property("min", this->_min, o.min); + if ("required"s == str) return property("required", this->_required, o.required); + if ("noData"s == str) return property("noData", this->_noData, o.noData); + if ("default"s == str) return property("default", this->_defaultProperty, o.defaultProperty); + if ("semantic"s == str) return property("semantic", this->_semantic, o.semantic); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -2283,62 +1412,36 @@ ExtensionExtStructuralMetadataClassPropertyJsonHandler:: } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! -#include "ExtensionExtMeshFeaturesFeatureIdJsonHandler.h" +#include "FeatureIdJsonHandler.h" -#include +#include #include #include namespace CesiumGltfReader { -ExtensionExtMeshFeaturesFeatureIdJsonHandler:: - ExtensionExtMeshFeaturesFeatureIdJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _featureCount(), - _nullFeatureId(), - _label(), - _attribute(), - _texture(context), - _propertyTable() {} - -void ExtensionExtMeshFeaturesFeatureIdJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::ExtensionExtMeshFeaturesFeatureId* pObject) { +FeatureIdJsonHandler::FeatureIdJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _featureCount(), _nullFeatureId(), _label(), _attribute(), _texture(context), _propertyTable() {} + +void FeatureIdJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::FeatureId* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -ExtensionExtMeshFeaturesFeatureIdJsonHandler::readObjectKey( - const std::string_view& str) { +CesiumJsonReader::IJsonHandler* FeatureIdJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionExtMeshFeaturesFeatureId( - CesiumGltf::ExtensionExtMeshFeaturesFeatureId::TypeName, - str, - *this->_pObject); + return this->readObjectKeyFeatureId(CesiumGltf::FeatureId::TypeName, str, *this->_pObject); } -CesiumJsonReader::IJsonHandler* ExtensionExtMeshFeaturesFeatureIdJsonHandler:: - readObjectKeyExtensionExtMeshFeaturesFeatureId( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::ExtensionExtMeshFeaturesFeatureId& o) { +CesiumJsonReader::IJsonHandler* FeatureIdJsonHandler::readObjectKeyFeatureId(const std::string& objectType, const std::string_view& str, CesiumGltf::FeatureId& o) { using namespace std::string_literals; - if ("featureCount"s == str) - return property("featureCount", this->_featureCount, o.featureCount); - if ("nullFeatureId"s == str) - return property("nullFeatureId", this->_nullFeatureId, o.nullFeatureId); - if ("label"s == str) - return property("label", this->_label, o.label); - if ("attribute"s == str) - return property("attribute", this->_attribute, o.attribute); - if ("texture"s == str) - return property("texture", this->_texture, o.texture); - if ("propertyTable"s == str) - return property("propertyTable", this->_propertyTable, o.propertyTable); + if ("featureCount"s == str) return property("featureCount", this->_featureCount, o.featureCount); + if ("nullFeatureId"s == str) return property("nullFeatureId", this->_nullFeatureId, o.nullFeatureId); + if ("label"s == str) return property("label", this->_label, o.label); + if ("attribute"s == str) return property("attribute", this->_attribute, o.attribute); + if ("texture"s == str) return property("texture", this->_texture, o.texture); + if ("propertyTable"s == str) return property("propertyTable", this->_propertyTable, o.propertyTable); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -2346,47 +1449,31 @@ CesiumJsonReader::IJsonHandler* ExtensionExtMeshFeaturesFeatureIdJsonHandler:: } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! -#include "ExtensionExtMeshFeaturesFeatureIdTextureJsonHandler.h" +#include "FeatureIdTextureJsonHandler.h" -#include +#include #include #include namespace CesiumGltfReader { -ExtensionExtMeshFeaturesFeatureIdTextureJsonHandler:: - ExtensionExtMeshFeaturesFeatureIdTextureJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : TextureInfoJsonHandler(context), _channels() {} +FeatureIdTextureJsonHandler::FeatureIdTextureJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : TextureInfoJsonHandler(context), _channels() {} -void ExtensionExtMeshFeaturesFeatureIdTextureJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::ExtensionExtMeshFeaturesFeatureIdTexture* pObject) { +void FeatureIdTextureJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::FeatureIdTexture* pObject) { TextureInfoJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -ExtensionExtMeshFeaturesFeatureIdTextureJsonHandler::readObjectKey( - const std::string_view& str) { +CesiumJsonReader::IJsonHandler* FeatureIdTextureJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionExtMeshFeaturesFeatureIdTexture( - CesiumGltf::ExtensionExtMeshFeaturesFeatureIdTexture::TypeName, - str, - *this->_pObject); -} - -CesiumJsonReader::IJsonHandler* -ExtensionExtMeshFeaturesFeatureIdTextureJsonHandler:: - readObjectKeyExtensionExtMeshFeaturesFeatureIdTexture( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::ExtensionExtMeshFeaturesFeatureIdTexture& o) { + return this->readObjectKeyFeatureIdTexture(CesiumGltf::FeatureIdTexture::TypeName, str, *this->_pObject); +} + +CesiumJsonReader::IJsonHandler* FeatureIdTextureJsonHandler::readObjectKeyFeatureIdTexture(const std::string& objectType, const std::string_view& str, CesiumGltf::FeatureIdTexture& o) { using namespace std::string_literals; - if ("channels"s == str) - return property("channels", this->_channels, o.channels); + if ("channels"s == str) return property("channels", this->_channels, o.channels); return this->readObjectKeyTextureInfo(objectType, str, *this->_pObject); } @@ -2403,51 +1490,26 @@ ExtensionExtMeshFeaturesFeatureIdTextureJsonHandler:: namespace CesiumGltfReader { -ExtensionExtInstanceFeaturesFeatureIdJsonHandler:: - ExtensionExtInstanceFeaturesFeatureIdJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _featureCount(), - _nullFeatureId(), - _label(), - _attribute(), - _propertyTable() {} - -void ExtensionExtInstanceFeaturesFeatureIdJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::ExtensionExtInstanceFeaturesFeatureId* pObject) { +ExtensionExtInstanceFeaturesFeatureIdJsonHandler::ExtensionExtInstanceFeaturesFeatureIdJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _featureCount(), _nullFeatureId(), _label(), _attribute(), _propertyTable() {} + +void ExtensionExtInstanceFeaturesFeatureIdJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtInstanceFeaturesFeatureId* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -ExtensionExtInstanceFeaturesFeatureIdJsonHandler::readObjectKey( - const std::string_view& str) { +CesiumJsonReader::IJsonHandler* ExtensionExtInstanceFeaturesFeatureIdJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionExtInstanceFeaturesFeatureId( - CesiumGltf::ExtensionExtInstanceFeaturesFeatureId::TypeName, - str, - *this->_pObject); -} - -CesiumJsonReader::IJsonHandler* -ExtensionExtInstanceFeaturesFeatureIdJsonHandler:: - readObjectKeyExtensionExtInstanceFeaturesFeatureId( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::ExtensionExtInstanceFeaturesFeatureId& o) { + return this->readObjectKeyExtensionExtInstanceFeaturesFeatureId(CesiumGltf::ExtensionExtInstanceFeaturesFeatureId::TypeName, str, *this->_pObject); +} + +CesiumJsonReader::IJsonHandler* ExtensionExtInstanceFeaturesFeatureIdJsonHandler::readObjectKeyExtensionExtInstanceFeaturesFeatureId(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtInstanceFeaturesFeatureId& o) { using namespace std::string_literals; - if ("featureCount"s == str) - return property("featureCount", this->_featureCount, o.featureCount); - if ("nullFeatureId"s == str) - return property("nullFeatureId", this->_nullFeatureId, o.nullFeatureId); - if ("label"s == str) - return property("label", this->_label, o.label); - if ("attribute"s == str) - return property("attribute", this->_attribute, o.attribute); - if ("propertyTable"s == str) - return property("propertyTable", this->_propertyTable, o.propertyTable); + if ("featureCount"s == str) return property("featureCount", this->_featureCount, o.featureCount); + if ("nullFeatureId"s == str) return property("nullFeatureId", this->_nullFeatureId, o.nullFeatureId); + if ("label"s == str) return property("label", this->_label, o.label); + if ("attribute"s == str) return property("attribute", this->_attribute, o.attribute); + if ("propertyTable"s == str) return property("propertyTable", this->_propertyTable, o.propertyTable); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -2455,48 +1517,32 @@ ExtensionExtInstanceFeaturesFeatureIdJsonHandler:: } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! -#include "FeatureIDTextureJsonHandler.h" +#include "ExtensionExtFeatureMetadataFeatureIDTextureJsonHandler.h" -#include +#include #include #include namespace CesiumGltfReader { -FeatureIDTextureJsonHandler::FeatureIDTextureJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _featureTable(), - _featureIds(context) {} +ExtensionExtFeatureMetadataFeatureIDTextureJsonHandler::ExtensionExtFeatureMetadataFeatureIDTextureJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _featureTable(), _featureIds(context) {} -void FeatureIDTextureJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::FeatureIDTexture* pObject) { +void ExtensionExtFeatureMetadataFeatureIDTextureJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtFeatureMetadataFeatureIDTexture* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -FeatureIDTextureJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataFeatureIDTextureJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyFeatureIDTexture( - CesiumGltf::FeatureIDTexture::TypeName, - str, - *this->_pObject); + return this->readObjectKeyExtensionExtFeatureMetadataFeatureIDTexture(CesiumGltf::ExtensionExtFeatureMetadataFeatureIDTexture::TypeName, str, *this->_pObject); } -CesiumJsonReader::IJsonHandler* -FeatureIDTextureJsonHandler::readObjectKeyFeatureIDTexture( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::FeatureIDTexture& o) { +CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataFeatureIDTextureJsonHandler::readObjectKeyExtensionExtFeatureMetadataFeatureIDTexture(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtFeatureMetadataFeatureIDTexture& o) { using namespace std::string_literals; - if ("featureTable"s == str) - return property("featureTable", this->_featureTable, o.featureTable); - if ("featureIds"s == str) - return property("featureIds", this->_featureIds, o.featureIds); + if ("featureTable"s == str) return property("featureTable", this->_featureTable, o.featureTable); + if ("featureIds"s == str) return property("featureIds", this->_featureIds, o.featureIds); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -2504,48 +1550,32 @@ FeatureIDTextureJsonHandler::readObjectKeyFeatureIDTexture( } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! -#include "TextureAccessorJsonHandler.h" +#include "ExtensionExtFeatureMetadataTextureAccessorJsonHandler.h" -#include +#include #include #include namespace CesiumGltfReader { -TextureAccessorJsonHandler::TextureAccessorJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _channels(), - _texture(context) {} +ExtensionExtFeatureMetadataTextureAccessorJsonHandler::ExtensionExtFeatureMetadataTextureAccessorJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _channels(), _texture(context) {} -void TextureAccessorJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::TextureAccessor* pObject) { +void ExtensionExtFeatureMetadataTextureAccessorJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtFeatureMetadataTextureAccessor* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -TextureAccessorJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataTextureAccessorJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyTextureAccessor( - CesiumGltf::TextureAccessor::TypeName, - str, - *this->_pObject); + return this->readObjectKeyExtensionExtFeatureMetadataTextureAccessor(CesiumGltf::ExtensionExtFeatureMetadataTextureAccessor::TypeName, str, *this->_pObject); } -CesiumJsonReader::IJsonHandler* -TextureAccessorJsonHandler::readObjectKeyTextureAccessor( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::TextureAccessor& o) { +CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataTextureAccessorJsonHandler::readObjectKeyExtensionExtFeatureMetadataTextureAccessor(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtFeatureMetadataTextureAccessor& o) { using namespace std::string_literals; - if ("channels"s == str) - return property("channels", this->_channels, o.channels); - if ("texture"s == str) - return property("texture", this->_texture, o.texture); + if ("channels"s == str) return property("channels", this->_channels, o.channels); + if ("texture"s == str) return property("texture", this->_texture, o.texture); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -2553,48 +1583,32 @@ TextureAccessorJsonHandler::readObjectKeyTextureAccessor( } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! -#include "FeatureIDAttributeJsonHandler.h" +#include "ExtensionExtFeatureMetadataFeatureIDAttributeJsonHandler.h" -#include +#include #include #include namespace CesiumGltfReader { -FeatureIDAttributeJsonHandler::FeatureIDAttributeJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _featureTable(), - _featureIds(context) {} +ExtensionExtFeatureMetadataFeatureIDAttributeJsonHandler::ExtensionExtFeatureMetadataFeatureIDAttributeJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _featureTable(), _featureIds(context) {} -void FeatureIDAttributeJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::FeatureIDAttribute* pObject) { +void ExtensionExtFeatureMetadataFeatureIDAttributeJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtFeatureMetadataFeatureIDAttribute* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -FeatureIDAttributeJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataFeatureIDAttributeJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyFeatureIDAttribute( - CesiumGltf::FeatureIDAttribute::TypeName, - str, - *this->_pObject); + return this->readObjectKeyExtensionExtFeatureMetadataFeatureIDAttribute(CesiumGltf::ExtensionExtFeatureMetadataFeatureIDAttribute::TypeName, str, *this->_pObject); } -CesiumJsonReader::IJsonHandler* -FeatureIDAttributeJsonHandler::readObjectKeyFeatureIDAttribute( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::FeatureIDAttribute& o) { +CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataFeatureIDAttributeJsonHandler::readObjectKeyExtensionExtFeatureMetadataFeatureIDAttribute(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtFeatureMetadataFeatureIDAttribute& o) { using namespace std::string_literals; - if ("featureTable"s == str) - return property("featureTable", this->_featureTable, o.featureTable); - if ("featureIds"s == str) - return property("featureIds", this->_featureIds, o.featureIds); + if ("featureTable"s == str) return property("featureTable", this->_featureTable, o.featureTable); + if ("featureIds"s == str) return property("featureIds", this->_featureIds, o.featureIds); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -2602,50 +1616,33 @@ FeatureIDAttributeJsonHandler::readObjectKeyFeatureIDAttribute( } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! -#include "FeatureIDsJsonHandler.h" +#include "ExtensionExtFeatureMetadataFeatureIDsJsonHandler.h" -#include +#include #include #include namespace CesiumGltfReader { -FeatureIDsJsonHandler::FeatureIDsJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _attribute(), - _constant(), - _divisor() {} +ExtensionExtFeatureMetadataFeatureIDsJsonHandler::ExtensionExtFeatureMetadataFeatureIDsJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _attribute(), _constant(), _divisor() {} -void FeatureIDsJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::FeatureIDs* pObject) { +void ExtensionExtFeatureMetadataFeatureIDsJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtFeatureMetadataFeatureIDs* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -FeatureIDsJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataFeatureIDsJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyFeatureIDs( - CesiumGltf::FeatureIDs::TypeName, - str, - *this->_pObject); + return this->readObjectKeyExtensionExtFeatureMetadataFeatureIDs(CesiumGltf::ExtensionExtFeatureMetadataFeatureIDs::TypeName, str, *this->_pObject); } -CesiumJsonReader::IJsonHandler* FeatureIDsJsonHandler::readObjectKeyFeatureIDs( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::FeatureIDs& o) { +CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataFeatureIDsJsonHandler::readObjectKeyExtensionExtFeatureMetadataFeatureIDs(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtFeatureMetadataFeatureIDs& o) { using namespace std::string_literals; - if ("attribute"s == str) - return property("attribute", this->_attribute, o.attribute); - if ("constant"s == str) - return property("constant", this->_constant, o.constant); - if ("divisor"s == str) - return property("divisor", this->_divisor, o.divisor); + if ("attribute"s == str) return property("attribute", this->_attribute, o.attribute); + if ("constant"s == str) return property("constant", this->_constant, o.constant); + if ("divisor"s == str) return property("divisor", this->_divisor, o.divisor); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -2653,48 +1650,32 @@ CesiumJsonReader::IJsonHandler* FeatureIDsJsonHandler::readObjectKeyFeatureIDs( } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! -#include "FeatureTextureJsonHandler.h" +#include "ExtensionExtFeatureMetadataFeatureTextureJsonHandler.h" -#include +#include #include #include namespace CesiumGltfReader { -FeatureTextureJsonHandler::FeatureTextureJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _classProperty(), - _properties(context) {} +ExtensionExtFeatureMetadataFeatureTextureJsonHandler::ExtensionExtFeatureMetadataFeatureTextureJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _classProperty(), _properties(context) {} -void FeatureTextureJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::FeatureTexture* pObject) { +void ExtensionExtFeatureMetadataFeatureTextureJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtFeatureMetadataFeatureTexture* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -FeatureTextureJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataFeatureTextureJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyFeatureTexture( - CesiumGltf::FeatureTexture::TypeName, - str, - *this->_pObject); + return this->readObjectKeyExtensionExtFeatureMetadataFeatureTexture(CesiumGltf::ExtensionExtFeatureMetadataFeatureTexture::TypeName, str, *this->_pObject); } -CesiumJsonReader::IJsonHandler* -FeatureTextureJsonHandler::readObjectKeyFeatureTexture( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::FeatureTexture& o) { +CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataFeatureTextureJsonHandler::readObjectKeyExtensionExtFeatureMetadataFeatureTexture(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtFeatureMetadataFeatureTexture& o) { using namespace std::string_literals; - if ("class"s == str) - return property("class", this->_classProperty, o.classProperty); - if ("properties"s == str) - return property("properties", this->_properties, o.properties); + if ("class"s == str) return property("class", this->_classProperty, o.classProperty); + if ("properties"s == str) return property("properties", this->_properties, o.properties); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -2702,51 +1683,33 @@ FeatureTextureJsonHandler::readObjectKeyFeatureTexture( } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! -#include "FeatureTableJsonHandler.h" +#include "ExtensionExtFeatureMetadataFeatureTableJsonHandler.h" -#include +#include #include #include namespace CesiumGltfReader { -FeatureTableJsonHandler::FeatureTableJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _classProperty(), - _count(), - _properties(context) {} +ExtensionExtFeatureMetadataFeatureTableJsonHandler::ExtensionExtFeatureMetadataFeatureTableJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _classProperty(), _count(), _properties(context) {} -void FeatureTableJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::FeatureTable* pObject) { +void ExtensionExtFeatureMetadataFeatureTableJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtFeatureMetadataFeatureTable* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -FeatureTableJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataFeatureTableJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyFeatureTable( - CesiumGltf::FeatureTable::TypeName, - str, - *this->_pObject); + return this->readObjectKeyExtensionExtFeatureMetadataFeatureTable(CesiumGltf::ExtensionExtFeatureMetadataFeatureTable::TypeName, str, *this->_pObject); } -CesiumJsonReader::IJsonHandler* -FeatureTableJsonHandler::readObjectKeyFeatureTable( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::FeatureTable& o) { +CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataFeatureTableJsonHandler::readObjectKeyExtensionExtFeatureMetadataFeatureTable(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtFeatureMetadataFeatureTable& o) { using namespace std::string_literals; - if ("class"s == str) - return property("class", this->_classProperty, o.classProperty); - if ("count"s == str) - return property("count", this->_count, o.count); - if ("properties"s == str) - return property("properties", this->_properties, o.properties); + if ("class"s == str) return property("class", this->_classProperty, o.classProperty); + if ("count"s == str) return property("count", this->_count, o.count); + if ("properties"s == str) return property("properties", this->_properties, o.properties); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -2754,60 +1717,34 @@ FeatureTableJsonHandler::readObjectKeyFeatureTable( } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! -#include "FeatureTablePropertyJsonHandler.h" +#include "ExtensionExtFeatureMetadataFeatureTablePropertyJsonHandler.h" -#include +#include #include #include namespace CesiumGltfReader { -FeatureTablePropertyJsonHandler::FeatureTablePropertyJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _bufferView(), - _offsetType(), - _arrayOffsetBufferView(), - _stringOffsetBufferView() {} - -void FeatureTablePropertyJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::FeatureTableProperty* pObject) { +ExtensionExtFeatureMetadataFeatureTablePropertyJsonHandler::ExtensionExtFeatureMetadataFeatureTablePropertyJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _bufferView(), _offsetType(), _arrayOffsetBufferView(), _stringOffsetBufferView() {} + +void ExtensionExtFeatureMetadataFeatureTablePropertyJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtFeatureMetadataFeatureTableProperty* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -FeatureTablePropertyJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataFeatureTablePropertyJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyFeatureTableProperty( - CesiumGltf::FeatureTableProperty::TypeName, - str, - *this->_pObject); + return this->readObjectKeyExtensionExtFeatureMetadataFeatureTableProperty(CesiumGltf::ExtensionExtFeatureMetadataFeatureTableProperty::TypeName, str, *this->_pObject); } -CesiumJsonReader::IJsonHandler* -FeatureTablePropertyJsonHandler::readObjectKeyFeatureTableProperty( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::FeatureTableProperty& o) { +CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataFeatureTablePropertyJsonHandler::readObjectKeyExtensionExtFeatureMetadataFeatureTableProperty(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtFeatureMetadataFeatureTableProperty& o) { using namespace std::string_literals; - if ("bufferView"s == str) - return property("bufferView", this->_bufferView, o.bufferView); - if ("offsetType"s == str) - return property("offsetType", this->_offsetType, o.offsetType); - if ("arrayOffsetBufferView"s == str) - return property( - "arrayOffsetBufferView", - this->_arrayOffsetBufferView, - o.arrayOffsetBufferView); - if ("stringOffsetBufferView"s == str) - return property( - "stringOffsetBufferView", - this->_stringOffsetBufferView, - o.stringOffsetBufferView); + if ("bufferView"s == str) return property("bufferView", this->_bufferView, o.bufferView); + if ("offsetType"s == str) return property("offsetType", this->_offsetType, o.offsetType); + if ("arrayOffsetBufferView"s == str) return property("arrayOffsetBufferView", this->_arrayOffsetBufferView, o.arrayOffsetBufferView); + if ("stringOffsetBufferView"s == str) return property("stringOffsetBufferView", this->_stringOffsetBufferView, o.stringOffsetBufferView); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -2815,44 +1752,31 @@ FeatureTablePropertyJsonHandler::readObjectKeyFeatureTableProperty( } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! -#include "StatisticsJsonHandler.h" +#include "ExtensionExtFeatureMetadataStatisticsJsonHandler.h" -#include +#include #include #include namespace CesiumGltfReader { -StatisticsJsonHandler::StatisticsJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _classes(context) {} +ExtensionExtFeatureMetadataStatisticsJsonHandler::ExtensionExtFeatureMetadataStatisticsJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _classes(context) {} -void StatisticsJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::Statistics* pObject) { +void ExtensionExtFeatureMetadataStatisticsJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtFeatureMetadataStatistics* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -StatisticsJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataStatisticsJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyStatistics( - CesiumGltf::Statistics::TypeName, - str, - *this->_pObject); + return this->readObjectKeyExtensionExtFeatureMetadataStatistics(CesiumGltf::ExtensionExtFeatureMetadataStatistics::TypeName, str, *this->_pObject); } -CesiumJsonReader::IJsonHandler* StatisticsJsonHandler::readObjectKeyStatistics( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::Statistics& o) { +CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataStatisticsJsonHandler::readObjectKeyExtensionExtFeatureMetadataStatistics(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtFeatureMetadataStatistics& o) { using namespace std::string_literals; - if ("classes"s == str) - return property("classes", this->_classes, o.classes); + if ("classes"s == str) return property("classes", this->_classes, o.classes); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -2860,48 +1784,32 @@ CesiumJsonReader::IJsonHandler* StatisticsJsonHandler::readObjectKeyStatistics( } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! -#include "ClassStatisticsJsonHandler.h" +#include "ExtensionExtFeatureMetadataClassStatisticsJsonHandler.h" -#include +#include #include #include namespace CesiumGltfReader { -ClassStatisticsJsonHandler::ClassStatisticsJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _count(), - _properties(context) {} +ExtensionExtFeatureMetadataClassStatisticsJsonHandler::ExtensionExtFeatureMetadataClassStatisticsJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _count(), _properties(context) {} -void ClassStatisticsJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::ClassStatistics* pObject) { +void ExtensionExtFeatureMetadataClassStatisticsJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtFeatureMetadataClassStatistics* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -ClassStatisticsJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataClassStatisticsJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyClassStatistics( - CesiumGltf::ClassStatistics::TypeName, - str, - *this->_pObject); + return this->readObjectKeyExtensionExtFeatureMetadataClassStatistics(CesiumGltf::ExtensionExtFeatureMetadataClassStatistics::TypeName, str, *this->_pObject); } -CesiumJsonReader::IJsonHandler* -ClassStatisticsJsonHandler::readObjectKeyClassStatistics( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::ClassStatistics& o) { +CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataClassStatisticsJsonHandler::readObjectKeyExtensionExtFeatureMetadataClassStatistics(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtFeatureMetadataClassStatistics& o) { using namespace std::string_literals; - if ("count"s == str) - return property("count", this->_count, o.count); - if ("properties"s == str) - return property("properties", this->_properties, o.properties); + if ("count"s == str) return property("count", this->_count, o.count); + if ("properties"s == str) return property("properties", this->_properties, o.properties); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -2909,69 +1817,38 @@ ClassStatisticsJsonHandler::readObjectKeyClassStatistics( } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! -#include "PropertyStatisticsJsonHandler.h" +#include "ExtensionExtFeatureMetadataPropertyStatisticsJsonHandler.h" -#include +#include #include #include namespace CesiumGltfReader { -PropertyStatisticsJsonHandler::PropertyStatisticsJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _min(), - _max(), - _mean(), - _median(), - _standardDeviation(), - _variance(), - _sum(), - _occurrences() {} - -void PropertyStatisticsJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::PropertyStatistics* pObject) { +ExtensionExtFeatureMetadataPropertyStatisticsJsonHandler::ExtensionExtFeatureMetadataPropertyStatisticsJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _min(), _max(), _mean(), _median(), _standardDeviation(), _variance(), _sum(), _occurrences() {} + +void ExtensionExtFeatureMetadataPropertyStatisticsJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtFeatureMetadataPropertyStatistics* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -PropertyStatisticsJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataPropertyStatisticsJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyPropertyStatistics( - CesiumGltf::PropertyStatistics::TypeName, - str, - *this->_pObject); + return this->readObjectKeyExtensionExtFeatureMetadataPropertyStatistics(CesiumGltf::ExtensionExtFeatureMetadataPropertyStatistics::TypeName, str, *this->_pObject); } -CesiumJsonReader::IJsonHandler* -PropertyStatisticsJsonHandler::readObjectKeyPropertyStatistics( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::PropertyStatistics& o) { +CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataPropertyStatisticsJsonHandler::readObjectKeyExtensionExtFeatureMetadataPropertyStatistics(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtFeatureMetadataPropertyStatistics& o) { using namespace std::string_literals; - if ("min"s == str) - return property("min", this->_min, o.min); - if ("max"s == str) - return property("max", this->_max, o.max); - if ("mean"s == str) - return property("mean", this->_mean, o.mean); - if ("median"s == str) - return property("median", this->_median, o.median); - if ("standardDeviation"s == str) - return property( - "standardDeviation", - this->_standardDeviation, - o.standardDeviation); - if ("variance"s == str) - return property("variance", this->_variance, o.variance); - if ("sum"s == str) - return property("sum", this->_sum, o.sum); - if ("occurrences"s == str) - return property("occurrences", this->_occurrences, o.occurrences); + if ("min"s == str) return property("min", this->_min, o.min); + if ("max"s == str) return property("max", this->_max, o.max); + if ("mean"s == str) return property("mean", this->_mean, o.mean); + if ("median"s == str) return property("median", this->_median, o.median); + if ("standardDeviation"s == str) return property("standardDeviation", this->_standardDeviation, o.standardDeviation); + if ("variance"s == str) return property("variance", this->_variance, o.variance); + if ("sum"s == str) return property("sum", this->_sum, o.sum); + if ("occurrences"s == str) return property("occurrences", this->_occurrences, o.occurrences); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -2979,56 +1856,35 @@ PropertyStatisticsJsonHandler::readObjectKeyPropertyStatistics( } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! -#include "SchemaJsonHandler.h" +#include "ExtensionExtFeatureMetadataSchemaJsonHandler.h" -#include +#include #include #include namespace CesiumGltfReader { -SchemaJsonHandler::SchemaJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _name(), - _description(), - _version(), - _classes(context), - _enums(context) {} - -void SchemaJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::Schema* pObject) { +ExtensionExtFeatureMetadataSchemaJsonHandler::ExtensionExtFeatureMetadataSchemaJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _name(), _description(), _version(), _classes(context), _enums(context) {} + +void ExtensionExtFeatureMetadataSchemaJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtFeatureMetadataSchema* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -SchemaJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataSchemaJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeySchema( - CesiumGltf::Schema::TypeName, - str, - *this->_pObject); + return this->readObjectKeyExtensionExtFeatureMetadataSchema(CesiumGltf::ExtensionExtFeatureMetadataSchema::TypeName, str, *this->_pObject); } -CesiumJsonReader::IJsonHandler* SchemaJsonHandler::readObjectKeySchema( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::Schema& o) { +CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataSchemaJsonHandler::readObjectKeyExtensionExtFeatureMetadataSchema(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtFeatureMetadataSchema& o) { using namespace std::string_literals; - if ("name"s == str) - return property("name", this->_name, o.name); - if ("description"s == str) - return property("description", this->_description, o.description); - if ("version"s == str) - return property("version", this->_version, o.version); - if ("classes"s == str) - return property("classes", this->_classes, o.classes); - if ("enums"s == str) - return property("enums", this->_enums, o.enums); + if ("name"s == str) return property("name", this->_name, o.name); + if ("description"s == str) return property("description", this->_description, o.description); + if ("version"s == str) return property("version", this->_version, o.version); + if ("classes"s == str) return property("classes", this->_classes, o.classes); + if ("enums"s == str) return property("enums", this->_enums, o.enums); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -3036,53 +1892,34 @@ CesiumJsonReader::IJsonHandler* SchemaJsonHandler::readObjectKeySchema( } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! -#include "EnumJsonHandler.h" +#include "ExtensionExtFeatureMetadataEnumJsonHandler.h" -#include +#include #include #include namespace CesiumGltfReader { -EnumJsonHandler::EnumJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _name(), - _description(), - _valueType(), - _values(context) {} - -void EnumJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::Enum* pObject) { +ExtensionExtFeatureMetadataEnumJsonHandler::ExtensionExtFeatureMetadataEnumJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _name(), _description(), _valueType(), _values(context) {} + +void ExtensionExtFeatureMetadataEnumJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtFeatureMetadataEnum* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -EnumJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataEnumJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyEnum( - CesiumGltf::Enum::TypeName, - str, - *this->_pObject); + return this->readObjectKeyExtensionExtFeatureMetadataEnum(CesiumGltf::ExtensionExtFeatureMetadataEnum::TypeName, str, *this->_pObject); } -CesiumJsonReader::IJsonHandler* EnumJsonHandler::readObjectKeyEnum( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::Enum& o) { +CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataEnumJsonHandler::readObjectKeyExtensionExtFeatureMetadataEnum(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtFeatureMetadataEnum& o) { using namespace std::string_literals; - if ("name"s == str) - return property("name", this->_name, o.name); - if ("description"s == str) - return property("description", this->_description, o.description); - if ("valueType"s == str) - return property("valueType", this->_valueType, o.valueType); - if ("values"s == str) - return property("values", this->_values, o.values); + if ("name"s == str) return property("name", this->_name, o.name); + if ("description"s == str) return property("description", this->_description, o.description); + if ("valueType"s == str) return property("valueType", this->_valueType, o.valueType); + if ("values"s == str) return property("values", this->_values, o.values); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -3090,50 +1927,33 @@ CesiumJsonReader::IJsonHandler* EnumJsonHandler::readObjectKeyEnum( } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! -#include "EnumValueJsonHandler.h" +#include "ExtensionExtFeatureMetadataEnumValueJsonHandler.h" -#include +#include #include #include namespace CesiumGltfReader { -EnumValueJsonHandler::EnumValueJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _name(), - _description(), - _value() {} +ExtensionExtFeatureMetadataEnumValueJsonHandler::ExtensionExtFeatureMetadataEnumValueJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _name(), _description(), _value() {} -void EnumValueJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::EnumValue* pObject) { +void ExtensionExtFeatureMetadataEnumValueJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtFeatureMetadataEnumValue* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -EnumValueJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataEnumValueJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyEnumValue( - CesiumGltf::EnumValue::TypeName, - str, - *this->_pObject); + return this->readObjectKeyExtensionExtFeatureMetadataEnumValue(CesiumGltf::ExtensionExtFeatureMetadataEnumValue::TypeName, str, *this->_pObject); } -CesiumJsonReader::IJsonHandler* EnumValueJsonHandler::readObjectKeyEnumValue( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::EnumValue& o) { +CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataEnumValueJsonHandler::readObjectKeyExtensionExtFeatureMetadataEnumValue(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtFeatureMetadataEnumValue& o) { using namespace std::string_literals; - if ("name"s == str) - return property("name", this->_name, o.name); - if ("description"s == str) - return property("description", this->_description, o.description); - if ("value"s == str) - return property("value", this->_value, o.value); + if ("name"s == str) return property("name", this->_name, o.name); + if ("description"s == str) return property("description", this->_description, o.description); + if ("value"s == str) return property("value", this->_value, o.value); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -3141,50 +1961,33 @@ CesiumJsonReader::IJsonHandler* EnumValueJsonHandler::readObjectKeyEnumValue( } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! -#include "ClassJsonHandler.h" +#include "ExtensionExtFeatureMetadataClassJsonHandler.h" -#include +#include #include #include namespace CesiumGltfReader { -ClassJsonHandler::ClassJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _name(), - _description(), - _properties(context) {} +ExtensionExtFeatureMetadataClassJsonHandler::ExtensionExtFeatureMetadataClassJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _name(), _description(), _properties(context) {} -void ClassJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::Class* pObject) { +void ExtensionExtFeatureMetadataClassJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtFeatureMetadataClass* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -ClassJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataClassJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyClass( - CesiumGltf::Class::TypeName, - str, - *this->_pObject); + return this->readObjectKeyExtensionExtFeatureMetadataClass(CesiumGltf::ExtensionExtFeatureMetadataClass::TypeName, str, *this->_pObject); } -CesiumJsonReader::IJsonHandler* ClassJsonHandler::readObjectKeyClass( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::Class& o) { +CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataClassJsonHandler::readObjectKeyExtensionExtFeatureMetadataClass(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtFeatureMetadataClass& o) { using namespace std::string_literals; - if ("name"s == str) - return property("name", this->_name, o.name); - if ("description"s == str) - return property("description", this->_description, o.description); - if ("properties"s == str) - return property("properties", this->_properties, o.properties); + if ("name"s == str) return property("name", this->_name, o.name); + if ("description"s == str) return property("description", this->_description, o.description); + if ("properties"s == str) return property("properties", this->_properties, o.properties); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -3192,78 +1995,42 @@ CesiumJsonReader::IJsonHandler* ClassJsonHandler::readObjectKeyClass( } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! -#include "ClassPropertyJsonHandler.h" +#include "ExtensionExtFeatureMetadataClassPropertyJsonHandler.h" -#include +#include #include #include namespace CesiumGltfReader { -ClassPropertyJsonHandler::ClassPropertyJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _name(), - _description(), - _type(), - _enumType(), - _componentType(), - _componentCount(), - _normalized(), - _max(), - _min(), - _defaultProperty(), - _optional(), - _semantic() {} - -void ClassPropertyJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::ClassProperty* pObject) { +ExtensionExtFeatureMetadataClassPropertyJsonHandler::ExtensionExtFeatureMetadataClassPropertyJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _name(), _description(), _type(), _enumType(), _componentType(), _componentCount(), _normalized(), _max(), _min(), _defaultProperty(), _optional(), _semantic() {} + +void ExtensionExtFeatureMetadataClassPropertyJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtFeatureMetadataClassProperty* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -ClassPropertyJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataClassPropertyJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyClassProperty( - CesiumGltf::ClassProperty::TypeName, - str, - *this->_pObject); + return this->readObjectKeyExtensionExtFeatureMetadataClassProperty(CesiumGltf::ExtensionExtFeatureMetadataClassProperty::TypeName, str, *this->_pObject); } -CesiumJsonReader::IJsonHandler* -ClassPropertyJsonHandler::readObjectKeyClassProperty( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::ClassProperty& o) { +CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataClassPropertyJsonHandler::readObjectKeyExtensionExtFeatureMetadataClassProperty(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtFeatureMetadataClassProperty& o) { using namespace std::string_literals; - if ("name"s == str) - return property("name", this->_name, o.name); - if ("description"s == str) - return property("description", this->_description, o.description); - if ("type"s == str) - return property("type", this->_type, o.type); - if ("enumType"s == str) - return property("enumType", this->_enumType, o.enumType); - if ("componentType"s == str) - return property("componentType", this->_componentType, o.componentType); - if ("componentCount"s == str) - return property("componentCount", this->_componentCount, o.componentCount); - if ("normalized"s == str) - return property("normalized", this->_normalized, o.normalized); - if ("max"s == str) - return property("max", this->_max, o.max); - if ("min"s == str) - return property("min", this->_min, o.min); - if ("default"s == str) - return property("default", this->_defaultProperty, o.defaultProperty); - if ("optional"s == str) - return property("optional", this->_optional, o.optional); - if ("semantic"s == str) - return property("semantic", this->_semantic, o.semantic); + if ("name"s == str) return property("name", this->_name, o.name); + if ("description"s == str) return property("description", this->_description, o.description); + if ("type"s == str) return property("type", this->_type, o.type); + if ("enumType"s == str) return property("enumType", this->_enumType, o.enumType); + if ("componentType"s == str) return property("componentType", this->_componentType, o.componentType); + if ("componentCount"s == str) return property("componentCount", this->_componentCount, o.componentCount); + if ("normalized"s == str) return property("normalized", this->_normalized, o.normalized); + if ("max"s == str) return property("max", this->_max, o.max); + if ("min"s == str) return property("min", this->_min, o.min); + if ("default"s == str) return property("default", this->_defaultProperty, o.defaultProperty); + if ("optional"s == str) return property("optional", this->_optional, o.optional); + if ("semantic"s == str) return property("semantic", this->_semantic, o.semantic); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -3280,86 +2047,38 @@ ClassPropertyJsonHandler::readObjectKeyClassProperty( namespace CesiumGltfReader { -ModelJsonHandler::ModelJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _extensionsUsed(), - _extensionsRequired(), - _accessors(context), - _animations(context), - _asset(context), - _buffers(context), - _bufferViews(context), - _cameras(context), - _images(context), - _materials(context), - _meshes(context), - _nodes(context), - _samplers(context), - _scene(), - _scenes(context), - _skins(context), - _textures(context) {} - -void ModelJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::Model* pObject) { +ModelJsonHandler::ModelJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _extensionsUsed(), _extensionsRequired(), _accessors(context), _animations(context), _asset(context), _buffers(context), _bufferViews(context), _cameras(context), _images(context), _materials(context), _meshes(context), _nodes(context), _samplers(context), _scene(), _scenes(context), _skins(context), _textures(context) {} + +void ModelJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::Model* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -ModelJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* ModelJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyModel( - CesiumGltf::Model::TypeName, - str, - *this->_pObject); + return this->readObjectKeyModel(CesiumGltf::Model::TypeName, str, *this->_pObject); } -CesiumJsonReader::IJsonHandler* ModelJsonHandler::readObjectKeyModel( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::Model& o) { +CesiumJsonReader::IJsonHandler* ModelJsonHandler::readObjectKeyModel(const std::string& objectType, const std::string_view& str, CesiumGltf::Model& o) { using namespace std::string_literals; - if ("extensionsUsed"s == str) - return property("extensionsUsed", this->_extensionsUsed, o.extensionsUsed); - if ("extensionsRequired"s == str) - return property( - "extensionsRequired", - this->_extensionsRequired, - o.extensionsRequired); - if ("accessors"s == str) - return property("accessors", this->_accessors, o.accessors); - if ("animations"s == str) - return property("animations", this->_animations, o.animations); - if ("asset"s == str) - return property("asset", this->_asset, o.asset); - if ("buffers"s == str) - return property("buffers", this->_buffers, o.buffers); - if ("bufferViews"s == str) - return property("bufferViews", this->_bufferViews, o.bufferViews); - if ("cameras"s == str) - return property("cameras", this->_cameras, o.cameras); - if ("images"s == str) - return property("images", this->_images, o.images); - if ("materials"s == str) - return property("materials", this->_materials, o.materials); - if ("meshes"s == str) - return property("meshes", this->_meshes, o.meshes); - if ("nodes"s == str) - return property("nodes", this->_nodes, o.nodes); - if ("samplers"s == str) - return property("samplers", this->_samplers, o.samplers); - if ("scene"s == str) - return property("scene", this->_scene, o.scene); - if ("scenes"s == str) - return property("scenes", this->_scenes, o.scenes); - if ("skins"s == str) - return property("skins", this->_skins, o.skins); - if ("textures"s == str) - return property("textures", this->_textures, o.textures); + if ("extensionsUsed"s == str) return property("extensionsUsed", this->_extensionsUsed, o.extensionsUsed); + if ("extensionsRequired"s == str) return property("extensionsRequired", this->_extensionsRequired, o.extensionsRequired); + if ("accessors"s == str) return property("accessors", this->_accessors, o.accessors); + if ("animations"s == str) return property("animations", this->_animations, o.animations); + if ("asset"s == str) return property("asset", this->_asset, o.asset); + if ("buffers"s == str) return property("buffers", this->_buffers, o.buffers); + if ("bufferViews"s == str) return property("bufferViews", this->_bufferViews, o.bufferViews); + if ("cameras"s == str) return property("cameras", this->_cameras, o.cameras); + if ("images"s == str) return property("images", this->_images, o.images); + if ("materials"s == str) return property("materials", this->_materials, o.materials); + if ("meshes"s == str) return property("meshes", this->_meshes, o.meshes); + if ("nodes"s == str) return property("nodes", this->_nodes, o.nodes); + if ("samplers"s == str) return property("samplers", this->_samplers, o.samplers); + if ("scene"s == str) return property("scene", this->_scene, o.scene); + if ("scenes"s == str) return property("scenes", this->_scenes, o.scenes); + if ("skins"s == str) return property("skins", this->_skins, o.skins); + if ("textures"s == str) return property("textures", this->_textures, o.textures); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -3376,38 +2095,23 @@ CesiumJsonReader::IJsonHandler* ModelJsonHandler::readObjectKeyModel( namespace CesiumGltfReader { -TextureJsonHandler::TextureJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumGltfReader::NamedObjectJsonHandler(context), - _sampler(), - _source() {} +TextureJsonHandler::TextureJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumGltfReader::NamedObjectJsonHandler(context), _sampler(), _source() {} -void TextureJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::Texture* pObject) { +void TextureJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::Texture* pObject) { CesiumGltfReader::NamedObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -TextureJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* TextureJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyTexture( - CesiumGltf::Texture::TypeName, - str, - *this->_pObject); + return this->readObjectKeyTexture(CesiumGltf::Texture::TypeName, str, *this->_pObject); } -CesiumJsonReader::IJsonHandler* TextureJsonHandler::readObjectKeyTexture( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::Texture& o) { +CesiumJsonReader::IJsonHandler* TextureJsonHandler::readObjectKeyTexture(const std::string& objectType, const std::string_view& str, CesiumGltf::Texture& o) { using namespace std::string_literals; - if ("sampler"s == str) - return property("sampler", this->_sampler, o.sampler); - if ("source"s == str) - return property("source", this->_source, o.source); + if ("sampler"s == str) return property("sampler", this->_sampler, o.sampler); + if ("source"s == str) return property("source", this->_source, o.source); return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } @@ -3424,44 +2128,24 @@ CesiumJsonReader::IJsonHandler* TextureJsonHandler::readObjectKeyTexture( namespace CesiumGltfReader { -SkinJsonHandler::SkinJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumGltfReader::NamedObjectJsonHandler(context), - _inverseBindMatrices(), - _skeleton(), - _joints() {} +SkinJsonHandler::SkinJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumGltfReader::NamedObjectJsonHandler(context), _inverseBindMatrices(), _skeleton(), _joints() {} -void SkinJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::Skin* pObject) { +void SkinJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::Skin* pObject) { CesiumGltfReader::NamedObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -SkinJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* SkinJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeySkin( - CesiumGltf::Skin::TypeName, - str, - *this->_pObject); + return this->readObjectKeySkin(CesiumGltf::Skin::TypeName, str, *this->_pObject); } -CesiumJsonReader::IJsonHandler* SkinJsonHandler::readObjectKeySkin( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::Skin& o) { +CesiumJsonReader::IJsonHandler* SkinJsonHandler::readObjectKeySkin(const std::string& objectType, const std::string_view& str, CesiumGltf::Skin& o) { using namespace std::string_literals; - if ("inverseBindMatrices"s == str) - return property( - "inverseBindMatrices", - this->_inverseBindMatrices, - o.inverseBindMatrices); - if ("skeleton"s == str) - return property("skeleton", this->_skeleton, o.skeleton); - if ("joints"s == str) - return property("joints", this->_joints, o.joints); + if ("inverseBindMatrices"s == str) return property("inverseBindMatrices", this->_inverseBindMatrices, o.inverseBindMatrices); + if ("skeleton"s == str) return property("skeleton", this->_skeleton, o.skeleton); + if ("joints"s == str) return property("joints", this->_joints, o.joints); return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } @@ -3478,34 +2162,22 @@ CesiumJsonReader::IJsonHandler* SkinJsonHandler::readObjectKeySkin( namespace CesiumGltfReader { -SceneJsonHandler::SceneJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumGltfReader::NamedObjectJsonHandler(context), _nodes() {} +SceneJsonHandler::SceneJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumGltfReader::NamedObjectJsonHandler(context), _nodes() {} -void SceneJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::Scene* pObject) { +void SceneJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::Scene* pObject) { CesiumGltfReader::NamedObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -SceneJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* SceneJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyScene( - CesiumGltf::Scene::TypeName, - str, - *this->_pObject); + return this->readObjectKeyScene(CesiumGltf::Scene::TypeName, str, *this->_pObject); } -CesiumJsonReader::IJsonHandler* SceneJsonHandler::readObjectKeyScene( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::Scene& o) { +CesiumJsonReader::IJsonHandler* SceneJsonHandler::readObjectKeyScene(const std::string& objectType, const std::string_view& str, CesiumGltf::Scene& o) { using namespace std::string_literals; - if ("nodes"s == str) - return property("nodes", this->_nodes, o.nodes); + if ("nodes"s == str) return property("nodes", this->_nodes, o.nodes); return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } @@ -3522,44 +2194,25 @@ CesiumJsonReader::IJsonHandler* SceneJsonHandler::readObjectKeyScene( namespace CesiumGltfReader { -SamplerJsonHandler::SamplerJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumGltfReader::NamedObjectJsonHandler(context), - _magFilter(), - _minFilter(), - _wrapS(), - _wrapT() {} - -void SamplerJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::Sampler* pObject) { +SamplerJsonHandler::SamplerJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumGltfReader::NamedObjectJsonHandler(context), _magFilter(), _minFilter(), _wrapS(), _wrapT() {} + +void SamplerJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::Sampler* pObject) { CesiumGltfReader::NamedObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -SamplerJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* SamplerJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeySampler( - CesiumGltf::Sampler::TypeName, - str, - *this->_pObject); + return this->readObjectKeySampler(CesiumGltf::Sampler::TypeName, str, *this->_pObject); } -CesiumJsonReader::IJsonHandler* SamplerJsonHandler::readObjectKeySampler( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::Sampler& o) { +CesiumJsonReader::IJsonHandler* SamplerJsonHandler::readObjectKeySampler(const std::string& objectType, const std::string_view& str, CesiumGltf::Sampler& o) { using namespace std::string_literals; - if ("magFilter"s == str) - return property("magFilter", this->_magFilter, o.magFilter); - if ("minFilter"s == str) - return property("minFilter", this->_minFilter, o.minFilter); - if ("wrapS"s == str) - return property("wrapS", this->_wrapS, o.wrapS); - if ("wrapT"s == str) - return property("wrapT", this->_wrapT, o.wrapT); + if ("magFilter"s == str) return property("magFilter", this->_magFilter, o.magFilter); + if ("minFilter"s == str) return property("minFilter", this->_minFilter, o.minFilter); + if ("wrapS"s == str) return property("wrapS", this->_wrapS, o.wrapS); + if ("wrapT"s == str) return property("wrapT", this->_wrapT, o.wrapT); return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } @@ -3576,59 +2229,30 @@ CesiumJsonReader::IJsonHandler* SamplerJsonHandler::readObjectKeySampler( namespace CesiumGltfReader { -NodeJsonHandler::NodeJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumGltfReader::NamedObjectJsonHandler(context), - _camera(), - _children(), - _skin(), - _matrix(), - _mesh(), - _rotation(), - _scale(), - _translation(), - _weights() {} - -void NodeJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::Node* pObject) { +NodeJsonHandler::NodeJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumGltfReader::NamedObjectJsonHandler(context), _camera(), _children(), _skin(), _matrix(), _mesh(), _rotation(), _scale(), _translation(), _weights() {} + +void NodeJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::Node* pObject) { CesiumGltfReader::NamedObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -NodeJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* NodeJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyNode( - CesiumGltf::Node::TypeName, - str, - *this->_pObject); + return this->readObjectKeyNode(CesiumGltf::Node::TypeName, str, *this->_pObject); } -CesiumJsonReader::IJsonHandler* NodeJsonHandler::readObjectKeyNode( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::Node& o) { +CesiumJsonReader::IJsonHandler* NodeJsonHandler::readObjectKeyNode(const std::string& objectType, const std::string_view& str, CesiumGltf::Node& o) { using namespace std::string_literals; - if ("camera"s == str) - return property("camera", this->_camera, o.camera); - if ("children"s == str) - return property("children", this->_children, o.children); - if ("skin"s == str) - return property("skin", this->_skin, o.skin); - if ("matrix"s == str) - return property("matrix", this->_matrix, o.matrix); - if ("mesh"s == str) - return property("mesh", this->_mesh, o.mesh); - if ("rotation"s == str) - return property("rotation", this->_rotation, o.rotation); - if ("scale"s == str) - return property("scale", this->_scale, o.scale); - if ("translation"s == str) - return property("translation", this->_translation, o.translation); - if ("weights"s == str) - return property("weights", this->_weights, o.weights); + if ("camera"s == str) return property("camera", this->_camera, o.camera); + if ("children"s == str) return property("children", this->_children, o.children); + if ("skin"s == str) return property("skin", this->_skin, o.skin); + if ("matrix"s == str) return property("matrix", this->_matrix, o.matrix); + if ("mesh"s == str) return property("mesh", this->_mesh, o.mesh); + if ("rotation"s == str) return property("rotation", this->_rotation, o.rotation); + if ("scale"s == str) return property("scale", this->_scale, o.scale); + if ("translation"s == str) return property("translation", this->_translation, o.translation); + if ("weights"s == str) return property("weights", this->_weights, o.weights); return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } @@ -3645,38 +2269,23 @@ CesiumJsonReader::IJsonHandler* NodeJsonHandler::readObjectKeyNode( namespace CesiumGltfReader { -MeshJsonHandler::MeshJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumGltfReader::NamedObjectJsonHandler(context), - _primitives(context), - _weights() {} +MeshJsonHandler::MeshJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumGltfReader::NamedObjectJsonHandler(context), _primitives(context), _weights() {} -void MeshJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::Mesh* pObject) { +void MeshJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::Mesh* pObject) { CesiumGltfReader::NamedObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -MeshJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* MeshJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyMesh( - CesiumGltf::Mesh::TypeName, - str, - *this->_pObject); + return this->readObjectKeyMesh(CesiumGltf::Mesh::TypeName, str, *this->_pObject); } -CesiumJsonReader::IJsonHandler* MeshJsonHandler::readObjectKeyMesh( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::Mesh& o) { +CesiumJsonReader::IJsonHandler* MeshJsonHandler::readObjectKeyMesh(const std::string& objectType, const std::string_view& str, CesiumGltf::Mesh& o) { using namespace std::string_literals; - if ("primitives"s == str) - return property("primitives", this->_primitives, o.primitives); - if ("weights"s == str) - return property("weights", this->_weights, o.weights); + if ("primitives"s == str) return property("primitives", this->_primitives, o.primitives); + if ("weights"s == str) return property("weights", this->_weights, o.weights); return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } @@ -3693,48 +2302,26 @@ CesiumJsonReader::IJsonHandler* MeshJsonHandler::readObjectKeyMesh( namespace CesiumGltfReader { -MeshPrimitiveJsonHandler::MeshPrimitiveJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _attributes(), - _indices(), - _material(), - _mode(), - _targets() {} - -void MeshPrimitiveJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::MeshPrimitive* pObject) { +MeshPrimitiveJsonHandler::MeshPrimitiveJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _attributes(), _indices(), _material(), _mode(), _targets() {} + +void MeshPrimitiveJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::MeshPrimitive* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -MeshPrimitiveJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* MeshPrimitiveJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyMeshPrimitive( - CesiumGltf::MeshPrimitive::TypeName, - str, - *this->_pObject); + return this->readObjectKeyMeshPrimitive(CesiumGltf::MeshPrimitive::TypeName, str, *this->_pObject); } -CesiumJsonReader::IJsonHandler* -MeshPrimitiveJsonHandler::readObjectKeyMeshPrimitive( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::MeshPrimitive& o) { +CesiumJsonReader::IJsonHandler* MeshPrimitiveJsonHandler::readObjectKeyMeshPrimitive(const std::string& objectType, const std::string_view& str, CesiumGltf::MeshPrimitive& o) { using namespace std::string_literals; - if ("attributes"s == str) - return property("attributes", this->_attributes, o.attributes); - if ("indices"s == str) - return property("indices", this->_indices, o.indices); - if ("material"s == str) - return property("material", this->_material, o.material); - if ("mode"s == str) - return property("mode", this->_mode, o.mode); - if ("targets"s == str) - return property("targets", this->_targets, o.targets); + if ("attributes"s == str) return property("attributes", this->_attributes, o.attributes); + if ("indices"s == str) return property("indices", this->_indices, o.indices); + if ("material"s == str) return property("material", this->_material, o.material); + if ("mode"s == str) return property("mode", this->_mode, o.mode); + if ("targets"s == str) return property("targets", this->_targets, o.targets); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -3751,65 +2338,29 @@ MeshPrimitiveJsonHandler::readObjectKeyMeshPrimitive( namespace CesiumGltfReader { -MaterialJsonHandler::MaterialJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumGltfReader::NamedObjectJsonHandler(context), - _pbrMetallicRoughness(context), - _normalTexture(context), - _occlusionTexture(context), - _emissiveTexture(context), - _emissiveFactor(), - _alphaMode(), - _alphaCutoff(), - _doubleSided() {} - -void MaterialJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::Material* pObject) { +MaterialJsonHandler::MaterialJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumGltfReader::NamedObjectJsonHandler(context), _pbrMetallicRoughness(context), _normalTexture(context), _occlusionTexture(context), _emissiveTexture(context), _emissiveFactor(), _alphaMode(), _alphaCutoff(), _doubleSided() {} + +void MaterialJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::Material* pObject) { CesiumGltfReader::NamedObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -MaterialJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* MaterialJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyMaterial( - CesiumGltf::Material::TypeName, - str, - *this->_pObject); + return this->readObjectKeyMaterial(CesiumGltf::Material::TypeName, str, *this->_pObject); } -CesiumJsonReader::IJsonHandler* MaterialJsonHandler::readObjectKeyMaterial( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::Material& o) { +CesiumJsonReader::IJsonHandler* MaterialJsonHandler::readObjectKeyMaterial(const std::string& objectType, const std::string_view& str, CesiumGltf::Material& o) { using namespace std::string_literals; - if ("pbrMetallicRoughness"s == str) - return property( - "pbrMetallicRoughness", - this->_pbrMetallicRoughness, - o.pbrMetallicRoughness); - if ("normalTexture"s == str) - return property("normalTexture", this->_normalTexture, o.normalTexture); - if ("occlusionTexture"s == str) - return property( - "occlusionTexture", - this->_occlusionTexture, - o.occlusionTexture); - if ("emissiveTexture"s == str) - return property( - "emissiveTexture", - this->_emissiveTexture, - o.emissiveTexture); - if ("emissiveFactor"s == str) - return property("emissiveFactor", this->_emissiveFactor, o.emissiveFactor); - if ("alphaMode"s == str) - return property("alphaMode", this->_alphaMode, o.alphaMode); - if ("alphaCutoff"s == str) - return property("alphaCutoff", this->_alphaCutoff, o.alphaCutoff); - if ("doubleSided"s == str) - return property("doubleSided", this->_doubleSided, o.doubleSided); + if ("pbrMetallicRoughness"s == str) return property("pbrMetallicRoughness", this->_pbrMetallicRoughness, o.pbrMetallicRoughness); + if ("normalTexture"s == str) return property("normalTexture", this->_normalTexture, o.normalTexture); + if ("occlusionTexture"s == str) return property("occlusionTexture", this->_occlusionTexture, o.occlusionTexture); + if ("emissiveTexture"s == str) return property("emissiveTexture", this->_emissiveTexture, o.emissiveTexture); + if ("emissiveFactor"s == str) return property("emissiveFactor", this->_emissiveFactor, o.emissiveFactor); + if ("alphaMode"s == str) return property("alphaMode", this->_alphaMode, o.alphaMode); + if ("alphaCutoff"s == str) return property("alphaCutoff", this->_alphaCutoff, o.alphaCutoff); + if ("doubleSided"s == str) return property("doubleSided", this->_doubleSided, o.doubleSided); return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } @@ -3826,37 +2377,22 @@ CesiumJsonReader::IJsonHandler* MaterialJsonHandler::readObjectKeyMaterial( namespace CesiumGltfReader { -MaterialOcclusionTextureInfoJsonHandler:: - MaterialOcclusionTextureInfoJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : TextureInfoJsonHandler(context), _strength() {} +MaterialOcclusionTextureInfoJsonHandler::MaterialOcclusionTextureInfoJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : TextureInfoJsonHandler(context), _strength() {} -void MaterialOcclusionTextureInfoJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::MaterialOcclusionTextureInfo* pObject) { +void MaterialOcclusionTextureInfoJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::MaterialOcclusionTextureInfo* pObject) { TextureInfoJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -MaterialOcclusionTextureInfoJsonHandler::readObjectKey( - const std::string_view& str) { +CesiumJsonReader::IJsonHandler* MaterialOcclusionTextureInfoJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyMaterialOcclusionTextureInfo( - CesiumGltf::MaterialOcclusionTextureInfo::TypeName, - str, - *this->_pObject); + return this->readObjectKeyMaterialOcclusionTextureInfo(CesiumGltf::MaterialOcclusionTextureInfo::TypeName, str, *this->_pObject); } -CesiumJsonReader::IJsonHandler* MaterialOcclusionTextureInfoJsonHandler:: - readObjectKeyMaterialOcclusionTextureInfo( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::MaterialOcclusionTextureInfo& o) { +CesiumJsonReader::IJsonHandler* MaterialOcclusionTextureInfoJsonHandler::readObjectKeyMaterialOcclusionTextureInfo(const std::string& objectType, const std::string_view& str, CesiumGltf::MaterialOcclusionTextureInfo& o) { using namespace std::string_literals; - if ("strength"s == str) - return property("strength", this->_strength, o.strength); + if ("strength"s == str) return property("strength", this->_strength, o.strength); return this->readObjectKeyTextureInfo(objectType, str, *this->_pObject); } @@ -3873,36 +2409,22 @@ CesiumJsonReader::IJsonHandler* MaterialOcclusionTextureInfoJsonHandler:: namespace CesiumGltfReader { -MaterialNormalTextureInfoJsonHandler::MaterialNormalTextureInfoJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : TextureInfoJsonHandler(context), _scale() {} +MaterialNormalTextureInfoJsonHandler::MaterialNormalTextureInfoJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : TextureInfoJsonHandler(context), _scale() {} -void MaterialNormalTextureInfoJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::MaterialNormalTextureInfo* pObject) { +void MaterialNormalTextureInfoJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::MaterialNormalTextureInfo* pObject) { TextureInfoJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -MaterialNormalTextureInfoJsonHandler::readObjectKey( - const std::string_view& str) { +CesiumJsonReader::IJsonHandler* MaterialNormalTextureInfoJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyMaterialNormalTextureInfo( - CesiumGltf::MaterialNormalTextureInfo::TypeName, - str, - *this->_pObject); + return this->readObjectKeyMaterialNormalTextureInfo(CesiumGltf::MaterialNormalTextureInfo::TypeName, str, *this->_pObject); } -CesiumJsonReader::IJsonHandler* -MaterialNormalTextureInfoJsonHandler::readObjectKeyMaterialNormalTextureInfo( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::MaterialNormalTextureInfo& o) { +CesiumJsonReader::IJsonHandler* MaterialNormalTextureInfoJsonHandler::readObjectKeyMaterialNormalTextureInfo(const std::string& objectType, const std::string_view& str, CesiumGltf::MaterialNormalTextureInfo& o) { using namespace std::string_literals; - if ("scale"s == str) - return property("scale", this->_scale, o.scale); + if ("scale"s == str) return property("scale", this->_scale, o.scale); return this->readObjectKeyTextureInfo(objectType, str, *this->_pObject); } @@ -3919,62 +2441,26 @@ MaterialNormalTextureInfoJsonHandler::readObjectKeyMaterialNormalTextureInfo( namespace CesiumGltfReader { -MaterialPBRMetallicRoughnessJsonHandler:: - MaterialPBRMetallicRoughnessJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _baseColorFactor(), - _baseColorTexture(context), - _metallicFactor(), - _roughnessFactor(), - _metallicRoughnessTexture(context) {} - -void MaterialPBRMetallicRoughnessJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::MaterialPBRMetallicRoughness* pObject) { +MaterialPBRMetallicRoughnessJsonHandler::MaterialPBRMetallicRoughnessJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _baseColorFactor(), _baseColorTexture(context), _metallicFactor(), _roughnessFactor(), _metallicRoughnessTexture(context) {} + +void MaterialPBRMetallicRoughnessJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::MaterialPBRMetallicRoughness* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -MaterialPBRMetallicRoughnessJsonHandler::readObjectKey( - const std::string_view& str) { +CesiumJsonReader::IJsonHandler* MaterialPBRMetallicRoughnessJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyMaterialPBRMetallicRoughness( - CesiumGltf::MaterialPBRMetallicRoughness::TypeName, - str, - *this->_pObject); + return this->readObjectKeyMaterialPBRMetallicRoughness(CesiumGltf::MaterialPBRMetallicRoughness::TypeName, str, *this->_pObject); } -CesiumJsonReader::IJsonHandler* MaterialPBRMetallicRoughnessJsonHandler:: - readObjectKeyMaterialPBRMetallicRoughness( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::MaterialPBRMetallicRoughness& o) { +CesiumJsonReader::IJsonHandler* MaterialPBRMetallicRoughnessJsonHandler::readObjectKeyMaterialPBRMetallicRoughness(const std::string& objectType, const std::string_view& str, CesiumGltf::MaterialPBRMetallicRoughness& o) { using namespace std::string_literals; - if ("baseColorFactor"s == str) - return property( - "baseColorFactor", - this->_baseColorFactor, - o.baseColorFactor); - if ("baseColorTexture"s == str) - return property( - "baseColorTexture", - this->_baseColorTexture, - o.baseColorTexture); - if ("metallicFactor"s == str) - return property("metallicFactor", this->_metallicFactor, o.metallicFactor); - if ("roughnessFactor"s == str) - return property( - "roughnessFactor", - this->_roughnessFactor, - o.roughnessFactor); - if ("metallicRoughnessTexture"s == str) - return property( - "metallicRoughnessTexture", - this->_metallicRoughnessTexture, - o.metallicRoughnessTexture); + if ("baseColorFactor"s == str) return property("baseColorFactor", this->_baseColorFactor, o.baseColorFactor); + if ("baseColorTexture"s == str) return property("baseColorTexture", this->_baseColorTexture, o.baseColorTexture); + if ("metallicFactor"s == str) return property("metallicFactor", this->_metallicFactor, o.metallicFactor); + if ("roughnessFactor"s == str) return property("roughnessFactor", this->_roughnessFactor, o.roughnessFactor); + if ("metallicRoughnessTexture"s == str) return property("metallicRoughnessTexture", this->_metallicRoughnessTexture, o.metallicRoughnessTexture); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -3991,41 +2477,24 @@ CesiumJsonReader::IJsonHandler* MaterialPBRMetallicRoughnessJsonHandler:: namespace CesiumGltfReader { -ImageJsonHandler::ImageJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumGltfReader::NamedObjectJsonHandler(context), - _uri(), - _mimeType(), - _bufferView() {} +ImageJsonHandler::ImageJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumGltfReader::NamedObjectJsonHandler(context), _uri(), _mimeType(), _bufferView() {} -void ImageJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::Image* pObject) { +void ImageJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::Image* pObject) { CesiumGltfReader::NamedObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -ImageJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* ImageJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyImage( - CesiumGltf::Image::TypeName, - str, - *this->_pObject); + return this->readObjectKeyImage(CesiumGltf::Image::TypeName, str, *this->_pObject); } -CesiumJsonReader::IJsonHandler* ImageJsonHandler::readObjectKeyImage( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::Image& o) { +CesiumJsonReader::IJsonHandler* ImageJsonHandler::readObjectKeyImage(const std::string& objectType, const std::string_view& str, CesiumGltf::Image& o) { using namespace std::string_literals; - if ("uri"s == str) - return property("uri", this->_uri, o.uri); - if ("mimeType"s == str) - return property("mimeType", this->_mimeType, o.mimeType); - if ("bufferView"s == str) - return property("bufferView", this->_bufferView, o.bufferView); + if ("uri"s == str) return property("uri", this->_uri, o.uri); + if ("mimeType"s == str) return property("mimeType", this->_mimeType, o.mimeType); + if ("bufferView"s == str) return property("bufferView", this->_bufferView, o.bufferView); return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } @@ -4042,41 +2511,24 @@ CesiumJsonReader::IJsonHandler* ImageJsonHandler::readObjectKeyImage( namespace CesiumGltfReader { -CameraJsonHandler::CameraJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumGltfReader::NamedObjectJsonHandler(context), - _orthographic(context), - _perspective(context), - _type() {} +CameraJsonHandler::CameraJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumGltfReader::NamedObjectJsonHandler(context), _orthographic(context), _perspective(context), _type() {} -void CameraJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::Camera* pObject) { +void CameraJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::Camera* pObject) { CesiumGltfReader::NamedObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -CameraJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* CameraJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyCamera( - CesiumGltf::Camera::TypeName, - str, - *this->_pObject); + return this->readObjectKeyCamera(CesiumGltf::Camera::TypeName, str, *this->_pObject); } -CesiumJsonReader::IJsonHandler* CameraJsonHandler::readObjectKeyCamera( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::Camera& o) { +CesiumJsonReader::IJsonHandler* CameraJsonHandler::readObjectKeyCamera(const std::string& objectType, const std::string_view& str, CesiumGltf::Camera& o) { using namespace std::string_literals; - if ("orthographic"s == str) - return property("orthographic", this->_orthographic, o.orthographic); - if ("perspective"s == str) - return property("perspective", this->_perspective, o.perspective); - if ("type"s == str) - return property("type", this->_type, o.type); + if ("orthographic"s == str) return property("orthographic", this->_orthographic, o.orthographic); + if ("perspective"s == str) return property("perspective", this->_perspective, o.perspective); + if ("type"s == str) return property("type", this->_type, o.type); return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } @@ -4093,45 +2545,25 @@ CesiumJsonReader::IJsonHandler* CameraJsonHandler::readObjectKeyCamera( namespace CesiumGltfReader { -CameraPerspectiveJsonHandler::CameraPerspectiveJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _aspectRatio(), - _yfov(), - _zfar(), - _znear() {} - -void CameraPerspectiveJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::CameraPerspective* pObject) { +CameraPerspectiveJsonHandler::CameraPerspectiveJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _aspectRatio(), _yfov(), _zfar(), _znear() {} + +void CameraPerspectiveJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::CameraPerspective* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -CameraPerspectiveJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* CameraPerspectiveJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyCameraPerspective( - CesiumGltf::CameraPerspective::TypeName, - str, - *this->_pObject); + return this->readObjectKeyCameraPerspective(CesiumGltf::CameraPerspective::TypeName, str, *this->_pObject); } -CesiumJsonReader::IJsonHandler* -CameraPerspectiveJsonHandler::readObjectKeyCameraPerspective( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::CameraPerspective& o) { +CesiumJsonReader::IJsonHandler* CameraPerspectiveJsonHandler::readObjectKeyCameraPerspective(const std::string& objectType, const std::string_view& str, CesiumGltf::CameraPerspective& o) { using namespace std::string_literals; - if ("aspectRatio"s == str) - return property("aspectRatio", this->_aspectRatio, o.aspectRatio); - if ("yfov"s == str) - return property("yfov", this->_yfov, o.yfov); - if ("zfar"s == str) - return property("zfar", this->_zfar, o.zfar); - if ("znear"s == str) - return property("znear", this->_znear, o.znear); + if ("aspectRatio"s == str) return property("aspectRatio", this->_aspectRatio, o.aspectRatio); + if ("yfov"s == str) return property("yfov", this->_yfov, o.yfov); + if ("zfar"s == str) return property("zfar", this->_zfar, o.zfar); + if ("znear"s == str) return property("znear", this->_znear, o.znear); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -4148,45 +2580,25 @@ CameraPerspectiveJsonHandler::readObjectKeyCameraPerspective( namespace CesiumGltfReader { -CameraOrthographicJsonHandler::CameraOrthographicJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _xmag(), - _ymag(), - _zfar(), - _znear() {} - -void CameraOrthographicJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::CameraOrthographic* pObject) { +CameraOrthographicJsonHandler::CameraOrthographicJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _xmag(), _ymag(), _zfar(), _znear() {} + +void CameraOrthographicJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::CameraOrthographic* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -CameraOrthographicJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* CameraOrthographicJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyCameraOrthographic( - CesiumGltf::CameraOrthographic::TypeName, - str, - *this->_pObject); + return this->readObjectKeyCameraOrthographic(CesiumGltf::CameraOrthographic::TypeName, str, *this->_pObject); } -CesiumJsonReader::IJsonHandler* -CameraOrthographicJsonHandler::readObjectKeyCameraOrthographic( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::CameraOrthographic& o) { +CesiumJsonReader::IJsonHandler* CameraOrthographicJsonHandler::readObjectKeyCameraOrthographic(const std::string& objectType, const std::string_view& str, CesiumGltf::CameraOrthographic& o) { using namespace std::string_literals; - if ("xmag"s == str) - return property("xmag", this->_xmag, o.xmag); - if ("ymag"s == str) - return property("ymag", this->_ymag, o.ymag); - if ("zfar"s == str) - return property("zfar", this->_zfar, o.zfar); - if ("znear"s == str) - return property("znear", this->_znear, o.znear); + if ("xmag"s == str) return property("xmag", this->_xmag, o.xmag); + if ("ymag"s == str) return property("ymag", this->_ymag, o.ymag); + if ("zfar"s == str) return property("zfar", this->_zfar, o.zfar); + if ("znear"s == str) return property("znear", this->_znear, o.znear); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -4203,47 +2615,26 @@ CameraOrthographicJsonHandler::readObjectKeyCameraOrthographic( namespace CesiumGltfReader { -BufferViewJsonHandler::BufferViewJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumGltfReader::NamedObjectJsonHandler(context), - _buffer(), - _byteOffset(), - _byteLength(), - _byteStride(), - _target() {} - -void BufferViewJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::BufferView* pObject) { +BufferViewJsonHandler::BufferViewJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumGltfReader::NamedObjectJsonHandler(context), _buffer(), _byteOffset(), _byteLength(), _byteStride(), _target() {} + +void BufferViewJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::BufferView* pObject) { CesiumGltfReader::NamedObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -BufferViewJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* BufferViewJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyBufferView( - CesiumGltf::BufferView::TypeName, - str, - *this->_pObject); + return this->readObjectKeyBufferView(CesiumGltf::BufferView::TypeName, str, *this->_pObject); } -CesiumJsonReader::IJsonHandler* BufferViewJsonHandler::readObjectKeyBufferView( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::BufferView& o) { +CesiumJsonReader::IJsonHandler* BufferViewJsonHandler::readObjectKeyBufferView(const std::string& objectType, const std::string_view& str, CesiumGltf::BufferView& o) { using namespace std::string_literals; - if ("buffer"s == str) - return property("buffer", this->_buffer, o.buffer); - if ("byteOffset"s == str) - return property("byteOffset", this->_byteOffset, o.byteOffset); - if ("byteLength"s == str) - return property("byteLength", this->_byteLength, o.byteLength); - if ("byteStride"s == str) - return property("byteStride", this->_byteStride, o.byteStride); - if ("target"s == str) - return property("target", this->_target, o.target); + if ("buffer"s == str) return property("buffer", this->_buffer, o.buffer); + if ("byteOffset"s == str) return property("byteOffset", this->_byteOffset, o.byteOffset); + if ("byteLength"s == str) return property("byteLength", this->_byteLength, o.byteLength); + if ("byteStride"s == str) return property("byteStride", this->_byteStride, o.byteStride); + if ("target"s == str) return property("target", this->_target, o.target); return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } @@ -4260,38 +2651,23 @@ CesiumJsonReader::IJsonHandler* BufferViewJsonHandler::readObjectKeyBufferView( namespace CesiumGltfReader { -BufferJsonHandler::BufferJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumGltfReader::NamedObjectJsonHandler(context), - _uri(), - _byteLength() {} +BufferJsonHandler::BufferJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumGltfReader::NamedObjectJsonHandler(context), _uri(), _byteLength() {} -void BufferJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::Buffer* pObject) { +void BufferJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::Buffer* pObject) { CesiumGltfReader::NamedObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -BufferJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* BufferJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyBuffer( - CesiumGltf::Buffer::TypeName, - str, - *this->_pObject); + return this->readObjectKeyBuffer(CesiumGltf::Buffer::TypeName, str, *this->_pObject); } -CesiumJsonReader::IJsonHandler* BufferJsonHandler::readObjectKeyBuffer( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::Buffer& o) { +CesiumJsonReader::IJsonHandler* BufferJsonHandler::readObjectKeyBuffer(const std::string& objectType, const std::string_view& str, CesiumGltf::Buffer& o) { using namespace std::string_literals; - if ("uri"s == str) - return property("uri", this->_uri, o.uri); - if ("byteLength"s == str) - return property("byteLength", this->_byteLength, o.byteLength); + if ("uri"s == str) return property("uri", this->_uri, o.uri); + if ("byteLength"s == str) return property("byteLength", this->_byteLength, o.byteLength); return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } @@ -4308,44 +2684,25 @@ CesiumJsonReader::IJsonHandler* BufferJsonHandler::readObjectKeyBuffer( namespace CesiumGltfReader { -AssetJsonHandler::AssetJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _copyright(), - _generator(), - _version(), - _minVersion() {} - -void AssetJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::Asset* pObject) { +AssetJsonHandler::AssetJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _copyright(), _generator(), _version(), _minVersion() {} + +void AssetJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::Asset* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -AssetJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* AssetJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyAsset( - CesiumGltf::Asset::TypeName, - str, - *this->_pObject); + return this->readObjectKeyAsset(CesiumGltf::Asset::TypeName, str, *this->_pObject); } -CesiumJsonReader::IJsonHandler* AssetJsonHandler::readObjectKeyAsset( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::Asset& o) { +CesiumJsonReader::IJsonHandler* AssetJsonHandler::readObjectKeyAsset(const std::string& objectType, const std::string_view& str, CesiumGltf::Asset& o) { using namespace std::string_literals; - if ("copyright"s == str) - return property("copyright", this->_copyright, o.copyright); - if ("generator"s == str) - return property("generator", this->_generator, o.generator); - if ("version"s == str) - return property("version", this->_version, o.version); - if ("minVersion"s == str) - return property("minVersion", this->_minVersion, o.minVersion); + if ("copyright"s == str) return property("copyright", this->_copyright, o.copyright); + if ("generator"s == str) return property("generator", this->_generator, o.generator); + if ("version"s == str) return property("version", this->_version, o.version); + if ("minVersion"s == str) return property("minVersion", this->_minVersion, o.minVersion); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -4362,38 +2719,23 @@ CesiumJsonReader::IJsonHandler* AssetJsonHandler::readObjectKeyAsset( namespace CesiumGltfReader { -AnimationJsonHandler::AnimationJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumGltfReader::NamedObjectJsonHandler(context), - _channels(context), - _samplers(context) {} +AnimationJsonHandler::AnimationJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumGltfReader::NamedObjectJsonHandler(context), _channels(context), _samplers(context) {} -void AnimationJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::Animation* pObject) { +void AnimationJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::Animation* pObject) { CesiumGltfReader::NamedObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -AnimationJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* AnimationJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyAnimation( - CesiumGltf::Animation::TypeName, - str, - *this->_pObject); + return this->readObjectKeyAnimation(CesiumGltf::Animation::TypeName, str, *this->_pObject); } -CesiumJsonReader::IJsonHandler* AnimationJsonHandler::readObjectKeyAnimation( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::Animation& o) { +CesiumJsonReader::IJsonHandler* AnimationJsonHandler::readObjectKeyAnimation(const std::string& objectType, const std::string_view& str, CesiumGltf::Animation& o) { using namespace std::string_literals; - if ("channels"s == str) - return property("channels", this->_channels, o.channels); - if ("samplers"s == str) - return property("samplers", this->_samplers, o.samplers); + if ("channels"s == str) return property("channels", this->_channels, o.channels); + if ("samplers"s == str) return property("samplers", this->_samplers, o.samplers); return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } @@ -4410,42 +2752,24 @@ CesiumJsonReader::IJsonHandler* AnimationJsonHandler::readObjectKeyAnimation( namespace CesiumGltfReader { -AnimationSamplerJsonHandler::AnimationSamplerJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _input(), - _interpolation(), - _output() {} +AnimationSamplerJsonHandler::AnimationSamplerJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _input(), _interpolation(), _output() {} -void AnimationSamplerJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::AnimationSampler* pObject) { +void AnimationSamplerJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::AnimationSampler* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -AnimationSamplerJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* AnimationSamplerJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyAnimationSampler( - CesiumGltf::AnimationSampler::TypeName, - str, - *this->_pObject); + return this->readObjectKeyAnimationSampler(CesiumGltf::AnimationSampler::TypeName, str, *this->_pObject); } -CesiumJsonReader::IJsonHandler* -AnimationSamplerJsonHandler::readObjectKeyAnimationSampler( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::AnimationSampler& o) { +CesiumJsonReader::IJsonHandler* AnimationSamplerJsonHandler::readObjectKeyAnimationSampler(const std::string& objectType, const std::string_view& str, CesiumGltf::AnimationSampler& o) { using namespace std::string_literals; - if ("input"s == str) - return property("input", this->_input, o.input); - if ("interpolation"s == str) - return property("interpolation", this->_interpolation, o.interpolation); - if ("output"s == str) - return property("output", this->_output, o.output); + if ("input"s == str) return property("input", this->_input, o.input); + if ("interpolation"s == str) return property("interpolation", this->_interpolation, o.interpolation); + if ("output"s == str) return property("output", this->_output, o.output); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -4462,39 +2786,23 @@ AnimationSamplerJsonHandler::readObjectKeyAnimationSampler( namespace CesiumGltfReader { -AnimationChannelJsonHandler::AnimationChannelJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _sampler(), - _target(context) {} +AnimationChannelJsonHandler::AnimationChannelJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _sampler(), _target(context) {} -void AnimationChannelJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::AnimationChannel* pObject) { +void AnimationChannelJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::AnimationChannel* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -AnimationChannelJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* AnimationChannelJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyAnimationChannel( - CesiumGltf::AnimationChannel::TypeName, - str, - *this->_pObject); + return this->readObjectKeyAnimationChannel(CesiumGltf::AnimationChannel::TypeName, str, *this->_pObject); } -CesiumJsonReader::IJsonHandler* -AnimationChannelJsonHandler::readObjectKeyAnimationChannel( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::AnimationChannel& o) { +CesiumJsonReader::IJsonHandler* AnimationChannelJsonHandler::readObjectKeyAnimationChannel(const std::string& objectType, const std::string_view& str, CesiumGltf::AnimationChannel& o) { using namespace std::string_literals; - if ("sampler"s == str) - return property("sampler", this->_sampler, o.sampler); - if ("target"s == str) - return property("target", this->_target, o.target); + if ("sampler"s == str) return property("sampler", this->_sampler, o.sampler); + if ("target"s == str) return property("target", this->_target, o.target); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -4511,39 +2819,23 @@ AnimationChannelJsonHandler::readObjectKeyAnimationChannel( namespace CesiumGltfReader { -AnimationChannelTargetJsonHandler::AnimationChannelTargetJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _node(), - _path() {} +AnimationChannelTargetJsonHandler::AnimationChannelTargetJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _node(), _path() {} -void AnimationChannelTargetJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::AnimationChannelTarget* pObject) { +void AnimationChannelTargetJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::AnimationChannelTarget* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -AnimationChannelTargetJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* AnimationChannelTargetJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyAnimationChannelTarget( - CesiumGltf::AnimationChannelTarget::TypeName, - str, - *this->_pObject); + return this->readObjectKeyAnimationChannelTarget(CesiumGltf::AnimationChannelTarget::TypeName, str, *this->_pObject); } -CesiumJsonReader::IJsonHandler* -AnimationChannelTargetJsonHandler::readObjectKeyAnimationChannelTarget( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::AnimationChannelTarget& o) { +CesiumJsonReader::IJsonHandler* AnimationChannelTargetJsonHandler::readObjectKeyAnimationChannelTarget(const std::string& objectType, const std::string_view& str, CesiumGltf::AnimationChannelTarget& o) { using namespace std::string_literals; - if ("node"s == str) - return property("node", this->_node, o.node); - if ("path"s == str) - return property("path", this->_path, o.path); + if ("node"s == str) return property("node", this->_node, o.node); + if ("path"s == str) return property("path", this->_path, o.path); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -4560,59 +2852,30 @@ AnimationChannelTargetJsonHandler::readObjectKeyAnimationChannelTarget( namespace CesiumGltfReader { -AccessorJsonHandler::AccessorJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumGltfReader::NamedObjectJsonHandler(context), - _bufferView(), - _byteOffset(), - _componentType(), - _normalized(), - _count(), - _type(), - _max(), - _min(), - _sparse(context) {} - -void AccessorJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::Accessor* pObject) { +AccessorJsonHandler::AccessorJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumGltfReader::NamedObjectJsonHandler(context), _bufferView(), _byteOffset(), _componentType(), _normalized(), _count(), _type(), _max(), _min(), _sparse(context) {} + +void AccessorJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::Accessor* pObject) { CesiumGltfReader::NamedObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -AccessorJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* AccessorJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyAccessor( - CesiumGltf::Accessor::TypeName, - str, - *this->_pObject); + return this->readObjectKeyAccessor(CesiumGltf::Accessor::TypeName, str, *this->_pObject); } -CesiumJsonReader::IJsonHandler* AccessorJsonHandler::readObjectKeyAccessor( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::Accessor& o) { +CesiumJsonReader::IJsonHandler* AccessorJsonHandler::readObjectKeyAccessor(const std::string& objectType, const std::string_view& str, CesiumGltf::Accessor& o) { using namespace std::string_literals; - if ("bufferView"s == str) - return property("bufferView", this->_bufferView, o.bufferView); - if ("byteOffset"s == str) - return property("byteOffset", this->_byteOffset, o.byteOffset); - if ("componentType"s == str) - return property("componentType", this->_componentType, o.componentType); - if ("normalized"s == str) - return property("normalized", this->_normalized, o.normalized); - if ("count"s == str) - return property("count", this->_count, o.count); - if ("type"s == str) - return property("type", this->_type, o.type); - if ("max"s == str) - return property("max", this->_max, o.max); - if ("min"s == str) - return property("min", this->_min, o.min); - if ("sparse"s == str) - return property("sparse", this->_sparse, o.sparse); + if ("bufferView"s == str) return property("bufferView", this->_bufferView, o.bufferView); + if ("byteOffset"s == str) return property("byteOffset", this->_byteOffset, o.byteOffset); + if ("componentType"s == str) return property("componentType", this->_componentType, o.componentType); + if ("normalized"s == str) return property("normalized", this->_normalized, o.normalized); + if ("count"s == str) return property("count", this->_count, o.count); + if ("type"s == str) return property("type", this->_type, o.type); + if ("max"s == str) return property("max", this->_max, o.max); + if ("min"s == str) return property("min", this->_min, o.min); + if ("sparse"s == str) return property("sparse", this->_sparse, o.sparse); return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } @@ -4629,42 +2892,24 @@ CesiumJsonReader::IJsonHandler* AccessorJsonHandler::readObjectKeyAccessor( namespace CesiumGltfReader { -AccessorSparseJsonHandler::AccessorSparseJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _count(), - _indices(context), - _values(context) {} +AccessorSparseJsonHandler::AccessorSparseJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _count(), _indices(context), _values(context) {} -void AccessorSparseJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::AccessorSparse* pObject) { +void AccessorSparseJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::AccessorSparse* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -AccessorSparseJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* AccessorSparseJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyAccessorSparse( - CesiumGltf::AccessorSparse::TypeName, - str, - *this->_pObject); + return this->readObjectKeyAccessorSparse(CesiumGltf::AccessorSparse::TypeName, str, *this->_pObject); } -CesiumJsonReader::IJsonHandler* -AccessorSparseJsonHandler::readObjectKeyAccessorSparse( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::AccessorSparse& o) { +CesiumJsonReader::IJsonHandler* AccessorSparseJsonHandler::readObjectKeyAccessorSparse(const std::string& objectType, const std::string_view& str, CesiumGltf::AccessorSparse& o) { using namespace std::string_literals; - if ("count"s == str) - return property("count", this->_count, o.count); - if ("indices"s == str) - return property("indices", this->_indices, o.indices); - if ("values"s == str) - return property("values", this->_values, o.values); + if ("count"s == str) return property("count", this->_count, o.count); + if ("indices"s == str) return property("indices", this->_indices, o.indices); + if ("values"s == str) return property("values", this->_values, o.values); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -4681,39 +2926,23 @@ AccessorSparseJsonHandler::readObjectKeyAccessorSparse( namespace CesiumGltfReader { -AccessorSparseValuesJsonHandler::AccessorSparseValuesJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _bufferView(), - _byteOffset() {} +AccessorSparseValuesJsonHandler::AccessorSparseValuesJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _bufferView(), _byteOffset() {} -void AccessorSparseValuesJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::AccessorSparseValues* pObject) { +void AccessorSparseValuesJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::AccessorSparseValues* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -AccessorSparseValuesJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* AccessorSparseValuesJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyAccessorSparseValues( - CesiumGltf::AccessorSparseValues::TypeName, - str, - *this->_pObject); + return this->readObjectKeyAccessorSparseValues(CesiumGltf::AccessorSparseValues::TypeName, str, *this->_pObject); } -CesiumJsonReader::IJsonHandler* -AccessorSparseValuesJsonHandler::readObjectKeyAccessorSparseValues( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::AccessorSparseValues& o) { +CesiumJsonReader::IJsonHandler* AccessorSparseValuesJsonHandler::readObjectKeyAccessorSparseValues(const std::string& objectType, const std::string_view& str, CesiumGltf::AccessorSparseValues& o) { using namespace std::string_literals; - if ("bufferView"s == str) - return property("bufferView", this->_bufferView, o.bufferView); - if ("byteOffset"s == str) - return property("byteOffset", this->_byteOffset, o.byteOffset); + if ("bufferView"s == str) return property("bufferView", this->_bufferView, o.bufferView); + if ("byteOffset"s == str) return property("byteOffset", this->_byteOffset, o.byteOffset); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -4730,42 +2959,24 @@ AccessorSparseValuesJsonHandler::readObjectKeyAccessorSparseValues( namespace CesiumGltfReader { -AccessorSparseIndicesJsonHandler::AccessorSparseIndicesJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _bufferView(), - _byteOffset(), - _componentType() {} +AccessorSparseIndicesJsonHandler::AccessorSparseIndicesJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _bufferView(), _byteOffset(), _componentType() {} -void AccessorSparseIndicesJsonHandler::reset( - CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::AccessorSparseIndices* pObject) { +void AccessorSparseIndicesJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::AccessorSparseIndices* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* -AccessorSparseIndicesJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* AccessorSparseIndicesJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyAccessorSparseIndices( - CesiumGltf::AccessorSparseIndices::TypeName, - str, - *this->_pObject); + return this->readObjectKeyAccessorSparseIndices(CesiumGltf::AccessorSparseIndices::TypeName, str, *this->_pObject); } -CesiumJsonReader::IJsonHandler* -AccessorSparseIndicesJsonHandler::readObjectKeyAccessorSparseIndices( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::AccessorSparseIndices& o) { +CesiumJsonReader::IJsonHandler* AccessorSparseIndicesJsonHandler::readObjectKeyAccessorSparseIndices(const std::string& objectType, const std::string_view& str, CesiumGltf::AccessorSparseIndices& o) { using namespace std::string_literals; - if ("bufferView"s == str) - return property("bufferView", this->_bufferView, o.bufferView); - if ("byteOffset"s == str) - return property("byteOffset", this->_byteOffset, o.byteOffset); - if ("componentType"s == str) - return property("componentType", this->_componentType, o.componentType); + if ("bufferView"s == str) return property("bufferView", this->_bufferView, o.bufferView); + if ("byteOffset"s == str) return property("byteOffset", this->_byteOffset, o.byteOffset); + if ("componentType"s == str) return property("componentType", this->_componentType, o.componentType); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } diff --git a/CesiumGltfReader/generated/src/SchemaJsonHandler.h b/CesiumGltfReader/generated/src/SchemaJsonHandler.h deleted file mode 100644 index 496e45c14..000000000 --- a/CesiumGltfReader/generated/src/SchemaJsonHandler.h +++ /dev/null @@ -1,44 +0,0 @@ -// This file was generated by generate-classes. -// DO NOT EDIT THIS FILE! -#pragma once - -#include "ClassJsonHandler.h" -#include "EnumJsonHandler.h" - -#include -#include -#include -#include - -namespace CesiumJsonReader { -class ExtensionReaderContext; -} - -namespace CesiumGltfReader { -class SchemaJsonHandler : public CesiumJsonReader::ExtensibleObjectJsonHandler { -public: - using ValueType = CesiumGltf::Schema; - - SchemaJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; - void reset(IJsonHandler* pParentHandler, CesiumGltf::Schema* pObject); - - virtual IJsonHandler* readObjectKey(const std::string_view& str) override; - -protected: - IJsonHandler* readObjectKeySchema( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::Schema& o); - -private: - CesiumGltf::Schema* _pObject = nullptr; - CesiumJsonReader::StringJsonHandler _name; - CesiumJsonReader::StringJsonHandler _description; - CesiumJsonReader::StringJsonHandler _version; - CesiumJsonReader::DictionaryJsonHandler - _classes; - CesiumJsonReader::DictionaryJsonHandler - _enums; -}; -} // namespace CesiumGltfReader diff --git a/CesiumGltfReader/generated/src/StatisticsJsonHandler.h b/CesiumGltfReader/generated/src/StatisticsJsonHandler.h deleted file mode 100644 index 5e6eb875e..000000000 --- a/CesiumGltfReader/generated/src/StatisticsJsonHandler.h +++ /dev/null @@ -1,40 +0,0 @@ -// This file was generated by generate-classes. -// DO NOT EDIT THIS FILE! -#pragma once - -#include "ClassStatisticsJsonHandler.h" - -#include -#include -#include - -namespace CesiumJsonReader { -class ExtensionReaderContext; -} - -namespace CesiumGltfReader { -class StatisticsJsonHandler - : public CesiumJsonReader::ExtensibleObjectJsonHandler { -public: - using ValueType = CesiumGltf::Statistics; - - StatisticsJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; - void reset(IJsonHandler* pParentHandler, CesiumGltf::Statistics* pObject); - - virtual IJsonHandler* readObjectKey(const std::string_view& str) override; - -protected: - IJsonHandler* readObjectKeyStatistics( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::Statistics& o); - -private: - CesiumGltf::Statistics* _pObject = nullptr; - CesiumJsonReader::DictionaryJsonHandler< - CesiumGltf::ClassStatistics, - ClassStatisticsJsonHandler> - _classes; -}; -} // namespace CesiumGltfReader diff --git a/CesiumGltfReader/generated/src/registerExtensions.cpp b/CesiumGltfReader/generated/src/registerExtensions.cpp index 15deefecc..1c46f4ba9 100644 --- a/CesiumGltfReader/generated/src/registerExtensions.cpp +++ b/CesiumGltfReader/generated/src/registerExtensions.cpp @@ -3,98 +3,61 @@ #include "registerExtensions.h" -#include "ExtensionBufferExtMeshoptCompressionJsonHandler.h" -#include "ExtensionBufferViewExtMeshoptCompressionJsonHandler.h" +#include + +#include +#include +#include +#include +#include +#include +#include +#include + #include "ExtensionCesiumRTCJsonHandler.h" -#include "ExtensionCesiumTileEdgesJsonHandler.h" -#include "ExtensionExtInstanceFeaturesJsonHandler.h" -#include "ExtensionExtMeshFeaturesJsonHandler.h" -#include "ExtensionExtMeshGpuInstancingJsonHandler.h" -#include "ExtensionKhrDracoMeshCompressionJsonHandler.h" -#include "ExtensionKhrMaterialsUnlitJsonHandler.h" -#include "ExtensionKhrTextureBasisuJsonHandler.h" -#include "ExtensionKhrTextureTransformJsonHandler.h" -#include "ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler.h" -#include "ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler.h" -#include "ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler.h" #include "ExtensionModelExtFeatureMetadataJsonHandler.h" #include "ExtensionModelExtStructuralMetadataJsonHandler.h" #include "ExtensionModelKhrMaterialsVariantsJsonHandler.h" #include "ExtensionModelMaxarMeshVariantsJsonHandler.h" +#include "ExtensionCesiumTileEdgesJsonHandler.h" +#include "ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler.h" +#include "ExtensionExtMeshFeaturesJsonHandler.h" +#include "ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler.h" +#include "ExtensionKhrDracoMeshCompressionJsonHandler.h" +#include "ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler.h" +#include "ExtensionExtInstanceFeaturesJsonHandler.h" +#include "ExtensionExtMeshGpuInstancingJsonHandler.h" #include "ExtensionNodeMaxarMeshVariantsJsonHandler.h" +#include "ExtensionBufferExtMeshoptCompressionJsonHandler.h" +#include "ExtensionBufferViewExtMeshoptCompressionJsonHandler.h" +#include "ExtensionKhrMaterialsUnlitJsonHandler.h" +#include "ExtensionKhrTextureBasisuJsonHandler.h" #include "ExtensionTextureWebpJsonHandler.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "ExtensionKhrTextureTransformJsonHandler.h" namespace CesiumGltfReader { void registerExtensions(CesiumJsonReader::ExtensionReaderContext& context) { (void)context; context.registerExtension(); - context.registerExtension< - CesiumGltf::Model, - ExtensionModelExtFeatureMetadataJsonHandler>(); - context.registerExtension< - CesiumGltf::Model, - ExtensionModelExtStructuralMetadataJsonHandler>(); - context.registerExtension< - CesiumGltf::Model, - ExtensionModelKhrMaterialsVariantsJsonHandler>(); - context.registerExtension< - CesiumGltf::Model, - ExtensionModelMaxarMeshVariantsJsonHandler>(); - context.registerExtension< - CesiumGltf::MeshPrimitive, - ExtensionCesiumTileEdgesJsonHandler>(); - context.registerExtension< - CesiumGltf::MeshPrimitive, - ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler>(); - context.registerExtension< - CesiumGltf::MeshPrimitive, - ExtensionExtMeshFeaturesJsonHandler>(); - context.registerExtension< - CesiumGltf::MeshPrimitive, - ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler>(); - context.registerExtension< - CesiumGltf::MeshPrimitive, - ExtensionKhrDracoMeshCompressionJsonHandler>(); - context.registerExtension< - CesiumGltf::MeshPrimitive, - ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler>(); - context.registerExtension< - CesiumGltf::Node, - ExtensionExtInstanceFeaturesJsonHandler>(); - context.registerExtension< - CesiumGltf::Node, - ExtensionExtMeshGpuInstancingJsonHandler>(); - context.registerExtension< - CesiumGltf::Node, - ExtensionNodeMaxarMeshVariantsJsonHandler>(); - context.registerExtension< - CesiumGltf::Buffer, - ExtensionBufferExtMeshoptCompressionJsonHandler>(); - context.registerExtension< - CesiumGltf::BufferView, - ExtensionBufferViewExtMeshoptCompressionJsonHandler>(); - context.registerExtension< - CesiumGltf::Material, - ExtensionKhrMaterialsUnlitJsonHandler>(); - context.registerExtension< - CesiumGltf::Texture, - ExtensionKhrTextureBasisuJsonHandler>(); - context.registerExtension< - CesiumGltf::Texture, - ExtensionTextureWebpJsonHandler>(); - context.registerExtension< - CesiumGltf::TextureInfo, - ExtensionKhrTextureTransformJsonHandler>(); +context.registerExtension(); +context.registerExtension(); +context.registerExtension(); +context.registerExtension(); +context.registerExtension(); +context.registerExtension(); +context.registerExtension(); +context.registerExtension(); +context.registerExtension(); +context.registerExtension(); +context.registerExtension(); +context.registerExtension(); +context.registerExtension(); +context.registerExtension(); +context.registerExtension(); +context.registerExtension(); +context.registerExtension(); +context.registerExtension(); +context.registerExtension(); } } // namespace CesiumGltfReader diff --git a/CesiumGltfWriter/generated/src/ModelJsonWriter.cpp b/CesiumGltfWriter/generated/src/ModelJsonWriter.cpp index ec6644ff5..a008e3e3c 100644 --- a/CesiumGltfWriter/generated/src/ModelJsonWriter.cpp +++ b/CesiumGltfWriter/generated/src/ModelJsonWriter.cpp @@ -17,20 +17,28 @@ #include #include #include -#include -#include -#include -#include -#include #include #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include #include -#include -#include #include #include #include @@ -60,12 +68,8 @@ #include #include #include -#include -#include -#include -#include -#include -#include +#include +#include #include #include #include @@ -75,14 +79,10 @@ #include #include #include -#include #include #include -#include #include -#include #include -#include #include #include #include @@ -278,12 +278,12 @@ void writeJson( const CesiumJsonWriter::ExtensionWriterContext& context); void writeJson( - const CesiumGltf::ExtensionExtMeshFeaturesFeatureId& obj, + const CesiumGltf::FeatureId& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context); void writeJson( - const CesiumGltf::ExtensionExtMeshFeaturesFeatureIdTexture& obj, + const CesiumGltf::FeatureIdTexture& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context); @@ -293,77 +293,77 @@ void writeJson( const CesiumJsonWriter::ExtensionWriterContext& context); void writeJson( - const CesiumGltf::FeatureIDTexture& obj, + const CesiumGltf::ExtensionExtFeatureMetadataFeatureIDTexture& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context); void writeJson( - const CesiumGltf::TextureAccessor& obj, + const CesiumGltf::ExtensionExtFeatureMetadataTextureAccessor& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context); void writeJson( - const CesiumGltf::FeatureIDAttribute& obj, + const CesiumGltf::ExtensionExtFeatureMetadataFeatureIDAttribute& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context); void writeJson( - const CesiumGltf::FeatureIDs& obj, + const CesiumGltf::ExtensionExtFeatureMetadataFeatureIDs& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context); void writeJson( - const CesiumGltf::FeatureTexture& obj, + const CesiumGltf::ExtensionExtFeatureMetadataFeatureTexture& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context); void writeJson( - const CesiumGltf::FeatureTable& obj, + const CesiumGltf::ExtensionExtFeatureMetadataFeatureTable& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context); void writeJson( - const CesiumGltf::FeatureTableProperty& obj, + const CesiumGltf::ExtensionExtFeatureMetadataFeatureTableProperty& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context); void writeJson( - const CesiumGltf::Statistics& obj, + const CesiumGltf::ExtensionExtFeatureMetadataStatistics& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context); void writeJson( - const CesiumGltf::ClassStatistics& obj, + const CesiumGltf::ExtensionExtFeatureMetadataClassStatistics& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context); void writeJson( - const CesiumGltf::PropertyStatistics& obj, + const CesiumGltf::ExtensionExtFeatureMetadataPropertyStatistics& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context); void writeJson( - const CesiumGltf::Schema& obj, + const CesiumGltf::ExtensionExtFeatureMetadataSchema& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context); void writeJson( - const CesiumGltf::Enum& obj, + const CesiumGltf::ExtensionExtFeatureMetadataEnum& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context); void writeJson( - const CesiumGltf::EnumValue& obj, + const CesiumGltf::ExtensionExtFeatureMetadataEnumValue& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context); void writeJson( - const CesiumGltf::Class& obj, + const CesiumGltf::ExtensionExtFeatureMetadataClass& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context); void writeJson( - const CesiumGltf::ClassProperty& obj, + const CesiumGltf::ExtensionExtFeatureMetadataClassProperty& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context); @@ -1595,7 +1595,7 @@ void writeJson( } void writeJson( - const CesiumGltf::ExtensionExtMeshFeaturesFeatureId& obj, + const CesiumGltf::FeatureId& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context) { jsonWriter.StartObject(); @@ -1634,7 +1634,7 @@ void writeJson( } void writeJson( - const CesiumGltf::ExtensionExtMeshFeaturesFeatureIdTexture& obj, + const CesiumGltf::FeatureIdTexture& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context) { jsonWriter.StartObject(); @@ -1685,7 +1685,7 @@ void writeJson( } void writeJson( - const CesiumGltf::FeatureIDTexture& obj, + const CesiumGltf::ExtensionExtFeatureMetadataFeatureIDTexture& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context) { jsonWriter.StartObject(); @@ -1702,7 +1702,7 @@ void writeJson( } void writeJson( - const CesiumGltf::TextureAccessor& obj, + const CesiumGltf::ExtensionExtFeatureMetadataTextureAccessor& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context) { jsonWriter.StartObject(); @@ -1719,7 +1719,7 @@ void writeJson( } void writeJson( - const CesiumGltf::FeatureIDAttribute& obj, + const CesiumGltf::ExtensionExtFeatureMetadataFeatureIDAttribute& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context) { jsonWriter.StartObject(); @@ -1736,7 +1736,7 @@ void writeJson( } void writeJson( - const CesiumGltf::FeatureIDs& obj, + const CesiumGltf::ExtensionExtFeatureMetadataFeatureIDs& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context) { jsonWriter.StartObject(); @@ -1762,7 +1762,7 @@ void writeJson( } void writeJson( - const CesiumGltf::FeatureTexture& obj, + const CesiumGltf::ExtensionExtFeatureMetadataFeatureTexture& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context) { jsonWriter.StartObject(); @@ -1781,7 +1781,7 @@ void writeJson( } void writeJson( - const CesiumGltf::FeatureTable& obj, + const CesiumGltf::ExtensionExtFeatureMetadataFeatureTable& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context) { jsonWriter.StartObject(); @@ -1805,7 +1805,7 @@ void writeJson( } void writeJson( - const CesiumGltf::FeatureTableProperty& obj, + const CesiumGltf::ExtensionExtFeatureMetadataFeatureTableProperty& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context) { jsonWriter.StartObject(); @@ -1815,7 +1815,9 @@ void writeJson( writeJson(obj.bufferView, jsonWriter, context); } - if (obj.offsetType != CesiumGltf::FeatureTableProperty::OffsetType::UINT32) { + if (obj.offsetType != + CesiumGltf::ExtensionExtFeatureMetadataFeatureTableProperty::OffsetType:: + UINT32) { jsonWriter.Key("offsetType"); writeJson(obj.offsetType, jsonWriter, context); } @@ -1836,7 +1838,7 @@ void writeJson( } void writeJson( - const CesiumGltf::Statistics& obj, + const CesiumGltf::ExtensionExtFeatureMetadataStatistics& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context) { jsonWriter.StartObject(); @@ -1852,7 +1854,7 @@ void writeJson( } void writeJson( - const CesiumGltf::ClassStatistics& obj, + const CesiumGltf::ExtensionExtFeatureMetadataClassStatistics& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context) { jsonWriter.StartObject(); @@ -1873,7 +1875,7 @@ void writeJson( } void writeJson( - const CesiumGltf::PropertyStatistics& obj, + const CesiumGltf::ExtensionExtFeatureMetadataPropertyStatistics& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context) { jsonWriter.StartObject(); @@ -1924,7 +1926,7 @@ void writeJson( } void writeJson( - const CesiumGltf::Schema& obj, + const CesiumGltf::ExtensionExtFeatureMetadataSchema& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context) { jsonWriter.StartObject(); @@ -1960,7 +1962,7 @@ void writeJson( } void writeJson( - const CesiumGltf::Enum& obj, + const CesiumGltf::ExtensionExtFeatureMetadataEnum& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context) { jsonWriter.StartObject(); @@ -1975,7 +1977,8 @@ void writeJson( writeJson(obj.description, jsonWriter, context); } - if (obj.valueType != CesiumGltf::Enum::ValueType::UINT16) { + if (obj.valueType != + CesiumGltf::ExtensionExtFeatureMetadataEnum::ValueType::UINT16) { jsonWriter.Key("valueType"); writeJson(obj.valueType, jsonWriter, context); } @@ -1991,7 +1994,7 @@ void writeJson( } void writeJson( - const CesiumGltf::EnumValue& obj, + const CesiumGltf::ExtensionExtFeatureMetadataEnumValue& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context) { jsonWriter.StartObject(); @@ -2013,7 +2016,7 @@ void writeJson( } void writeJson( - const CesiumGltf::Class& obj, + const CesiumGltf::ExtensionExtFeatureMetadataClass& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context) { jsonWriter.StartObject(); @@ -2039,7 +2042,7 @@ void writeJson( } void writeJson( - const CesiumGltf::ClassProperty& obj, + const CesiumGltf::ExtensionExtFeatureMetadataClassProperty& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context) { jsonWriter.StartObject(); @@ -3174,15 +3177,15 @@ void ExtensionExtStructuralMetadataClassPropertyJsonWriter::write( writeJson(obj, jsonWriter, context); } -void ExtensionExtMeshFeaturesFeatureIdJsonWriter::write( - const CesiumGltf::ExtensionExtMeshFeaturesFeatureId& obj, +void FeatureIdJsonWriter::write( + const CesiumGltf::FeatureId& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context) { writeJson(obj, jsonWriter, context); } -void ExtensionExtMeshFeaturesFeatureIdTextureJsonWriter::write( - const CesiumGltf::ExtensionExtMeshFeaturesFeatureIdTexture& obj, +void FeatureIdTextureJsonWriter::write( + const CesiumGltf::FeatureIdTexture& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context) { writeJson(obj, jsonWriter, context); @@ -3195,106 +3198,106 @@ void ExtensionExtInstanceFeaturesFeatureIdJsonWriter::write( writeJson(obj, jsonWriter, context); } -void FeatureIDTextureJsonWriter::write( - const CesiumGltf::FeatureIDTexture& obj, +void ExtensionExtFeatureMetadataFeatureIDTextureJsonWriter::write( + const CesiumGltf::ExtensionExtFeatureMetadataFeatureIDTexture& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context) { writeJson(obj, jsonWriter, context); } -void TextureAccessorJsonWriter::write( - const CesiumGltf::TextureAccessor& obj, +void ExtensionExtFeatureMetadataTextureAccessorJsonWriter::write( + const CesiumGltf::ExtensionExtFeatureMetadataTextureAccessor& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context) { writeJson(obj, jsonWriter, context); } -void FeatureIDAttributeJsonWriter::write( - const CesiumGltf::FeatureIDAttribute& obj, +void ExtensionExtFeatureMetadataFeatureIDAttributeJsonWriter::write( + const CesiumGltf::ExtensionExtFeatureMetadataFeatureIDAttribute& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context) { writeJson(obj, jsonWriter, context); } -void FeatureIDsJsonWriter::write( - const CesiumGltf::FeatureIDs& obj, +void ExtensionExtFeatureMetadataFeatureIDsJsonWriter::write( + const CesiumGltf::ExtensionExtFeatureMetadataFeatureIDs& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context) { writeJson(obj, jsonWriter, context); } -void FeatureTextureJsonWriter::write( - const CesiumGltf::FeatureTexture& obj, +void ExtensionExtFeatureMetadataFeatureTextureJsonWriter::write( + const CesiumGltf::ExtensionExtFeatureMetadataFeatureTexture& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context) { writeJson(obj, jsonWriter, context); } -void FeatureTableJsonWriter::write( - const CesiumGltf::FeatureTable& obj, +void ExtensionExtFeatureMetadataFeatureTableJsonWriter::write( + const CesiumGltf::ExtensionExtFeatureMetadataFeatureTable& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context) { writeJson(obj, jsonWriter, context); } -void FeatureTablePropertyJsonWriter::write( - const CesiumGltf::FeatureTableProperty& obj, +void ExtensionExtFeatureMetadataFeatureTablePropertyJsonWriter::write( + const CesiumGltf::ExtensionExtFeatureMetadataFeatureTableProperty& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context) { writeJson(obj, jsonWriter, context); } -void StatisticsJsonWriter::write( - const CesiumGltf::Statistics& obj, +void ExtensionExtFeatureMetadataStatisticsJsonWriter::write( + const CesiumGltf::ExtensionExtFeatureMetadataStatistics& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context) { writeJson(obj, jsonWriter, context); } -void ClassStatisticsJsonWriter::write( - const CesiumGltf::ClassStatistics& obj, +void ExtensionExtFeatureMetadataClassStatisticsJsonWriter::write( + const CesiumGltf::ExtensionExtFeatureMetadataClassStatistics& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context) { writeJson(obj, jsonWriter, context); } -void PropertyStatisticsJsonWriter::write( - const CesiumGltf::PropertyStatistics& obj, +void ExtensionExtFeatureMetadataPropertyStatisticsJsonWriter::write( + const CesiumGltf::ExtensionExtFeatureMetadataPropertyStatistics& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context) { writeJson(obj, jsonWriter, context); } -void SchemaJsonWriter::write( - const CesiumGltf::Schema& obj, +void ExtensionExtFeatureMetadataSchemaJsonWriter::write( + const CesiumGltf::ExtensionExtFeatureMetadataSchema& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context) { writeJson(obj, jsonWriter, context); } -void EnumJsonWriter::write( - const CesiumGltf::Enum& obj, +void ExtensionExtFeatureMetadataEnumJsonWriter::write( + const CesiumGltf::ExtensionExtFeatureMetadataEnum& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context) { writeJson(obj, jsonWriter, context); } -void EnumValueJsonWriter::write( - const CesiumGltf::EnumValue& obj, +void ExtensionExtFeatureMetadataEnumValueJsonWriter::write( + const CesiumGltf::ExtensionExtFeatureMetadataEnumValue& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context) { writeJson(obj, jsonWriter, context); } -void ClassJsonWriter::write( - const CesiumGltf::Class& obj, +void ExtensionExtFeatureMetadataClassJsonWriter::write( + const CesiumGltf::ExtensionExtFeatureMetadataClass& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context) { writeJson(obj, jsonWriter, context); } -void ClassPropertyJsonWriter::write( - const CesiumGltf::ClassProperty& obj, +void ExtensionExtFeatureMetadataClassPropertyJsonWriter::write( + const CesiumGltf::ExtensionExtFeatureMetadataClassProperty& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context) { writeJson(obj, jsonWriter, context); diff --git a/CesiumGltfWriter/generated/src/ModelJsonWriter.h b/CesiumGltfWriter/generated/src/ModelJsonWriter.h index 0d67c79b5..6ea76be94 100644 --- a/CesiumGltfWriter/generated/src/ModelJsonWriter.h +++ b/CesiumGltfWriter/generated/src/ModelJsonWriter.h @@ -46,24 +46,24 @@ struct ExtensionExtStructuralMetadataEnum; struct ExtensionExtStructuralMetadataEnumValue; struct ExtensionExtStructuralMetadataClass; struct ExtensionExtStructuralMetadataClassProperty; -struct ExtensionExtMeshFeaturesFeatureId; -struct ExtensionExtMeshFeaturesFeatureIdTexture; +struct FeatureId; +struct FeatureIdTexture; struct ExtensionExtInstanceFeaturesFeatureId; -struct FeatureIDTexture; -struct TextureAccessor; -struct FeatureIDAttribute; -struct FeatureIDs; -struct FeatureTexture; -struct FeatureTable; -struct FeatureTableProperty; -struct Statistics; -struct ClassStatistics; -struct PropertyStatistics; -struct Schema; -struct Enum; -struct EnumValue; -struct Class; -struct ClassProperty; +struct ExtensionExtFeatureMetadataFeatureIDTexture; +struct ExtensionExtFeatureMetadataTextureAccessor; +struct ExtensionExtFeatureMetadataFeatureIDAttribute; +struct ExtensionExtFeatureMetadataFeatureIDs; +struct ExtensionExtFeatureMetadataFeatureTexture; +struct ExtensionExtFeatureMetadataFeatureTable; +struct ExtensionExtFeatureMetadataFeatureTableProperty; +struct ExtensionExtFeatureMetadataStatistics; +struct ExtensionExtFeatureMetadataClassStatistics; +struct ExtensionExtFeatureMetadataPropertyStatistics; +struct ExtensionExtFeatureMetadataSchema; +struct ExtensionExtFeatureMetadataEnum; +struct ExtensionExtFeatureMetadataEnumValue; +struct ExtensionExtFeatureMetadataClass; +struct ExtensionExtFeatureMetadataClassProperty; struct Model; struct Texture; struct Skin; @@ -468,20 +468,20 @@ struct ExtensionExtStructuralMetadataClassPropertyJsonWriter { const CesiumJsonWriter::ExtensionWriterContext& context); }; -struct ExtensionExtMeshFeaturesFeatureIdJsonWriter { - using ValueType = CesiumGltf::ExtensionExtMeshFeaturesFeatureId; +struct FeatureIdJsonWriter { + using ValueType = CesiumGltf::FeatureId; static void write( - const CesiumGltf::ExtensionExtMeshFeaturesFeatureId& obj, + const CesiumGltf::FeatureId& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context); }; -struct ExtensionExtMeshFeaturesFeatureIdTextureJsonWriter { - using ValueType = CesiumGltf::ExtensionExtMeshFeaturesFeatureIdTexture; +struct FeatureIdTextureJsonWriter { + using ValueType = CesiumGltf::FeatureIdTexture; static void write( - const CesiumGltf::ExtensionExtMeshFeaturesFeatureIdTexture& obj, + const CesiumGltf::FeatureIdTexture& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context); }; @@ -495,137 +495,137 @@ struct ExtensionExtInstanceFeaturesFeatureIdJsonWriter { const CesiumJsonWriter::ExtensionWriterContext& context); }; -struct FeatureIDTextureJsonWriter { - using ValueType = CesiumGltf::FeatureIDTexture; +struct ExtensionExtFeatureMetadataFeatureIDTextureJsonWriter { + using ValueType = CesiumGltf::ExtensionExtFeatureMetadataFeatureIDTexture; static void write( - const CesiumGltf::FeatureIDTexture& obj, + const CesiumGltf::ExtensionExtFeatureMetadataFeatureIDTexture& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context); }; -struct TextureAccessorJsonWriter { - using ValueType = CesiumGltf::TextureAccessor; +struct ExtensionExtFeatureMetadataTextureAccessorJsonWriter { + using ValueType = CesiumGltf::ExtensionExtFeatureMetadataTextureAccessor; static void write( - const CesiumGltf::TextureAccessor& obj, + const CesiumGltf::ExtensionExtFeatureMetadataTextureAccessor& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context); }; -struct FeatureIDAttributeJsonWriter { - using ValueType = CesiumGltf::FeatureIDAttribute; +struct ExtensionExtFeatureMetadataFeatureIDAttributeJsonWriter { + using ValueType = CesiumGltf::ExtensionExtFeatureMetadataFeatureIDAttribute; static void write( - const CesiumGltf::FeatureIDAttribute& obj, + const CesiumGltf::ExtensionExtFeatureMetadataFeatureIDAttribute& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context); }; -struct FeatureIDsJsonWriter { - using ValueType = CesiumGltf::FeatureIDs; +struct ExtensionExtFeatureMetadataFeatureIDsJsonWriter { + using ValueType = CesiumGltf::ExtensionExtFeatureMetadataFeatureIDs; static void write( - const CesiumGltf::FeatureIDs& obj, + const CesiumGltf::ExtensionExtFeatureMetadataFeatureIDs& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context); }; -struct FeatureTextureJsonWriter { - using ValueType = CesiumGltf::FeatureTexture; +struct ExtensionExtFeatureMetadataFeatureTextureJsonWriter { + using ValueType = CesiumGltf::ExtensionExtFeatureMetadataFeatureTexture; static void write( - const CesiumGltf::FeatureTexture& obj, + const CesiumGltf::ExtensionExtFeatureMetadataFeatureTexture& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context); }; -struct FeatureTableJsonWriter { - using ValueType = CesiumGltf::FeatureTable; +struct ExtensionExtFeatureMetadataFeatureTableJsonWriter { + using ValueType = CesiumGltf::ExtensionExtFeatureMetadataFeatureTable; static void write( - const CesiumGltf::FeatureTable& obj, + const CesiumGltf::ExtensionExtFeatureMetadataFeatureTable& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context); }; -struct FeatureTablePropertyJsonWriter { - using ValueType = CesiumGltf::FeatureTableProperty; +struct ExtensionExtFeatureMetadataFeatureTablePropertyJsonWriter { + using ValueType = CesiumGltf::ExtensionExtFeatureMetadataFeatureTableProperty; static void write( - const CesiumGltf::FeatureTableProperty& obj, + const CesiumGltf::ExtensionExtFeatureMetadataFeatureTableProperty& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context); }; -struct StatisticsJsonWriter { - using ValueType = CesiumGltf::Statistics; +struct ExtensionExtFeatureMetadataStatisticsJsonWriter { + using ValueType = CesiumGltf::ExtensionExtFeatureMetadataStatistics; static void write( - const CesiumGltf::Statistics& obj, + const CesiumGltf::ExtensionExtFeatureMetadataStatistics& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context); }; -struct ClassStatisticsJsonWriter { - using ValueType = CesiumGltf::ClassStatistics; +struct ExtensionExtFeatureMetadataClassStatisticsJsonWriter { + using ValueType = CesiumGltf::ExtensionExtFeatureMetadataClassStatistics; static void write( - const CesiumGltf::ClassStatistics& obj, + const CesiumGltf::ExtensionExtFeatureMetadataClassStatistics& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context); }; -struct PropertyStatisticsJsonWriter { - using ValueType = CesiumGltf::PropertyStatistics; +struct ExtensionExtFeatureMetadataPropertyStatisticsJsonWriter { + using ValueType = CesiumGltf::ExtensionExtFeatureMetadataPropertyStatistics; static void write( - const CesiumGltf::PropertyStatistics& obj, + const CesiumGltf::ExtensionExtFeatureMetadataPropertyStatistics& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context); }; -struct SchemaJsonWriter { - using ValueType = CesiumGltf::Schema; +struct ExtensionExtFeatureMetadataSchemaJsonWriter { + using ValueType = CesiumGltf::ExtensionExtFeatureMetadataSchema; static void write( - const CesiumGltf::Schema& obj, + const CesiumGltf::ExtensionExtFeatureMetadataSchema& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context); }; -struct EnumJsonWriter { - using ValueType = CesiumGltf::Enum; +struct ExtensionExtFeatureMetadataEnumJsonWriter { + using ValueType = CesiumGltf::ExtensionExtFeatureMetadataEnum; static void write( - const CesiumGltf::Enum& obj, + const CesiumGltf::ExtensionExtFeatureMetadataEnum& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context); }; -struct EnumValueJsonWriter { - using ValueType = CesiumGltf::EnumValue; +struct ExtensionExtFeatureMetadataEnumValueJsonWriter { + using ValueType = CesiumGltf::ExtensionExtFeatureMetadataEnumValue; static void write( - const CesiumGltf::EnumValue& obj, + const CesiumGltf::ExtensionExtFeatureMetadataEnumValue& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context); }; -struct ClassJsonWriter { - using ValueType = CesiumGltf::Class; +struct ExtensionExtFeatureMetadataClassJsonWriter { + using ValueType = CesiumGltf::ExtensionExtFeatureMetadataClass; static void write( - const CesiumGltf::Class& obj, + const CesiumGltf::ExtensionExtFeatureMetadataClass& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context); }; -struct ClassPropertyJsonWriter { - using ValueType = CesiumGltf::ClassProperty; +struct ExtensionExtFeatureMetadataClassPropertyJsonWriter { + using ValueType = CesiumGltf::ExtensionExtFeatureMetadataClassProperty; static void write( - const CesiumGltf::ClassProperty& obj, + const CesiumGltf::ExtensionExtFeatureMetadataClassProperty& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context); }; diff --git a/tools/generate-classes/glTF.json b/tools/generate-classes/glTF.json index daf36eedb..e6b7e699a 100644 --- a/tools/generate-classes/glTF.json +++ b/tools/generate-classes/glTF.json @@ -43,49 +43,49 @@ "overrideName": "ExtensionMeshPrimitiveExtFeatureMetadata" }, "Class Property in EXT_feature_metadata": { - "overrideName": "ClassProperty" + "overrideName": "ExtensionExtFeatureMetadataClassProperty" }, "Class in EXT_feature_metadata": { - "overrideName": "Class" + "overrideName": "ExtensionExtFeatureMetadataClass" }, "Enum in EXT_feature_metadata": { - "overrideName": "Enum" + "overrideName": "ExtensionExtFeatureMetadataEnum" }, "Enum Value in EXT_feature_metadata": { - "overrideName": "EnumValue" + "overrideName": "ExtensionExtFeatureMetadataEnumValue" }, "Feature IDs in EXT_feature_metadata": { - "overrideName": "FeatureIDs" + "overrideName": "ExtensionExtFeatureMetadataFeatureIDs" }, "Feature ID Attribute in EXT_feature_metadata": { - "overrideName": "FeatureIDAttribute" + "overrideName": "ExtensionExtFeatureMetadataFeatureIDAttribute" }, "Feature ID Texture in EXT_feature_metadata": { - "overrideName": "FeatureIDTexture" + "overrideName": "ExtensionExtFeatureMetadataFeatureIDTexture" }, "Feature Table Property in EXT_feature_metadata": { - "overrideName": "FeatureTableProperty" + "overrideName": "ExtensionExtFeatureMetadataFeatureTableProperty" }, "Feature Table in EXT_feature_metadata": { - "overrideName": "FeatureTable" + "overrideName": "ExtensionExtFeatureMetadataFeatureTable" }, "Feature Texture in EXT_feature_metadata": { - "overrideName": "FeatureTexture" + "overrideName": "ExtensionExtFeatureMetadataFeatureTexture" }, "Schema in EXT_feature_metadata": { - "overrideName": "Schema" + "overrideName": "ExtensionExtFeatureMetadataSchema" }, "Class Property Statistics in EXT_feature_metadata": { - "overrideName": "PropertyStatistics" + "overrideName": "ExtensionExtFeatureMetadataPropertyStatistics" }, "Class Statistics in EXT_feature_metadata": { - "overrideName": "ClassStatistics" + "overrideName": "ExtensionExtFeatureMetadataClassStatistics" }, "Statistics in EXT_feature_metadata": { - "overrideName": "Statistics" + "overrideName": "ExtensionExtFeatureMetadataStatistics" }, "Texture Accessor in EXT_feature_metadata": { - "overrideName": "TextureAccessor" + "overrideName": "ExtensionExtFeatureMetadataTextureAccessor" }, "EXT_instance_features glTF Node extension": { "overrideName": "ExtensionExtInstanceFeatures" @@ -97,10 +97,10 @@ "overrideName": "ExtensionExtMeshFeatures" }, "Feature ID in EXT_mesh_features": { - "overrideName": "ExtensionExtMeshFeaturesFeatureId" + "overrideName": "FeatureId" }, "Feature ID Texture in EXT_mesh_features": { - "overrideName": "ExtensionExtMeshFeaturesFeatureIdTexture" + "overrideName": "FeatureIdTexture" }, "EXT_mesh_gpu_instancing glTF extension": { "overrideName": "ExtensionExtMeshGpuInstancing" From f82528cc3f42bf4ef8cb0e06e99341fda35cdd6a Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Fri, 16 Jun 2023 15:43:02 -0400 Subject: [PATCH 076/421] Formatting, changelog entry, cleanup --- CHANGES.md | 10 +- .../BatchTableToGltfStructuralMetadata.cpp | 2 +- ...gradeBatchTableToExtStructuralMetadata.cpp | 2 +- .../generated/src/GeneratedJsonHandlers.cpp | 3535 +++++++++++++---- 4 files changed, 2689 insertions(+), 860 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 5343e5d35..25ce81708 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -4,12 +4,10 @@ ##### Breaking Changes :mega: -Many classes have been overhauled to support `EXT_mesh_features` and `EXT_structural_metadata`. This includes the following changes: - -- Replaced `FeatureIDTextureView` with `FeatureIdTextureView`, which views a `ExtensionExtMeshFeaturesFeatureIdTexture`. - - Replaced `FeatureIDTextureViewStatus` with `FeatureIdTextureViewStatus`. - - Replaced `getChannel` with `getChannels`. This retrieves the channels as a vector of integers, instead of a single integer. - - Renamed `getTextureCoordinateAttributeId` to `getTexCoordSetIndex`. +- In `CesiumGltf`, all generated classes related to `EXT_feature_metadata` are now prefixed with `ExtensionExtFeatureMetadata`. For example, `ClassProperty` becomes `ExtensionExtFeatureMetadataClassProperty`. This also extends to the glTF reader and writer. +- In `CesiumGltf`, `ExtensionExtMeshFeaturesFeatureId` and `ExtensionExtMeshFeaturesFeatureIdTexture` have been renamed to `FeatureId` and `FeatureIdTexture` respectively. +- Replaced `FeatureIDTextureView` with `FeatureIdTextureView`, which views a `FeatureIdTexture` in `EXT_mesh_features`. Feature ID textures from `EXT_feature_metadata` are no longer supported. +- Renamed `FeatureIDTextureViewStatus` to `FeatureIdTextureViewStatus` for consistency. - Replaced `MetadataFeatureTableView` with `PropertyTableView`, which views a `ExtensionExtStructuralMetadataPropertyTable`. - Added `PropertyTableViewStatus` to indicate whether or not `PropertyTableView` is valid. - Renamed `MetadataArrayView` to `PropertyArrayView`. diff --git a/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp b/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp index 93dce98d2..7010cc286 100644 --- a/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp +++ b/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp @@ -1630,8 +1630,8 @@ ErrorList BatchTableToGltfStructuralMetadata::convertFromB3dm( ExtensionExtMeshFeatures& extension = primitive.addExtension(); - FeatureId& featureID = extension.featureIds.emplace_back(); + // No fast way to count the unique feature IDs in this primitive, so // subtitute the batch table length. featureID.featureCount = batchLength; diff --git a/Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp b/Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp index 74c81c281..445416236 100644 --- a/Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp +++ b/Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp @@ -411,7 +411,7 @@ TEST_CASE("Converts JSON B3DM batch table to EXT_feature_metadata") { primitive.getExtension(); REQUIRE(pPrimitiveExtension); REQUIRE(pPrimitiveExtension->featureIds.size() == 1); - FeatureId& featureId = pPrimitiveExtension->featureIds[0]; + const FeatureId& featureId = pPrimitiveExtension->featureIds[0]; CHECK(featureId.featureCount == 10); CHECK(featureId.attribute == 0); CHECK(featureId.propertyTable == 0); diff --git a/CesiumGltfReader/generated/src/GeneratedJsonHandlers.cpp b/CesiumGltfReader/generated/src/GeneratedJsonHandlers.cpp index bde0586ff..b65df862c 100644 --- a/CesiumGltfReader/generated/src/GeneratedJsonHandlers.cpp +++ b/CesiumGltfReader/generated/src/GeneratedJsonHandlers.cpp @@ -9,19 +9,30 @@ namespace CesiumGltfReader { -ExtensionCesiumRTCJsonHandler::ExtensionCesiumRTCJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _center() {} +ExtensionCesiumRTCJsonHandler::ExtensionCesiumRTCJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _center() {} -void ExtensionCesiumRTCJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionCesiumRTC* pObject) { +void ExtensionCesiumRTCJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::ExtensionCesiumRTC* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* ExtensionCesiumRTCJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +ExtensionCesiumRTCJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionCesiumRTC(CesiumGltf::ExtensionCesiumRTC::TypeName, str, *this->_pObject); + return this->readObjectKeyExtensionCesiumRTC( + CesiumGltf::ExtensionCesiumRTC::TypeName, + str, + *this->_pObject); } -void ExtensionCesiumRTCJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) { +void ExtensionCesiumRTCJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumUtility::ExtensibleObject& o, + const std::string_view& extensionName) { std::any& value = o.extensions.emplace(extensionName, CesiumGltf::ExtensionCesiumRTC()) .first->second; @@ -30,10 +41,15 @@ void ExtensionCesiumRTCJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParen &std::any_cast(value)); } -CesiumJsonReader::IJsonHandler* ExtensionCesiumRTCJsonHandler::readObjectKeyExtensionCesiumRTC(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionCesiumRTC& o) { +CesiumJsonReader::IJsonHandler* +ExtensionCesiumRTCJsonHandler::readObjectKeyExtensionCesiumRTC( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::ExtensionCesiumRTC& o) { using namespace std::string_literals; - if ("center"s == str) return property("center", this->_center, o.center); + if ("center"s == str) + return property("center", this->_center, o.center); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -50,34 +66,59 @@ CesiumJsonReader::IJsonHandler* ExtensionCesiumRTCJsonHandler::readObjectKeyExte namespace CesiumGltfReader { -ExtensionCesiumTileEdgesJsonHandler::ExtensionCesiumTileEdgesJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _left(), _bottom(), _right(), _top() {} - -void ExtensionCesiumTileEdgesJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionCesiumTileEdges* pObject) { +ExtensionCesiumTileEdgesJsonHandler::ExtensionCesiumTileEdgesJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + _left(), + _bottom(), + _right(), + _top() {} + +void ExtensionCesiumTileEdgesJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::ExtensionCesiumTileEdges* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* ExtensionCesiumTileEdgesJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +ExtensionCesiumTileEdgesJsonHandler::readObjectKey( + const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionCesiumTileEdges(CesiumGltf::ExtensionCesiumTileEdges::TypeName, str, *this->_pObject); + return this->readObjectKeyExtensionCesiumTileEdges( + CesiumGltf::ExtensionCesiumTileEdges::TypeName, + str, + *this->_pObject); } -void ExtensionCesiumTileEdgesJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) { +void ExtensionCesiumTileEdgesJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumUtility::ExtensibleObject& o, + const std::string_view& extensionName) { std::any& value = - o.extensions.emplace(extensionName, CesiumGltf::ExtensionCesiumTileEdges()) + o.extensions + .emplace(extensionName, CesiumGltf::ExtensionCesiumTileEdges()) .first->second; this->reset( pParentHandler, &std::any_cast(value)); } -CesiumJsonReader::IJsonHandler* ExtensionCesiumTileEdgesJsonHandler::readObjectKeyExtensionCesiumTileEdges(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionCesiumTileEdges& o) { +CesiumJsonReader::IJsonHandler* +ExtensionCesiumTileEdgesJsonHandler::readObjectKeyExtensionCesiumTileEdges( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::ExtensionCesiumTileEdges& o) { using namespace std::string_literals; - if ("left"s == str) return property("left", this->_left, o.left); - if ("bottom"s == str) return property("bottom", this->_bottom, o.bottom); - if ("right"s == str) return property("right", this->_right, o.right); - if ("top"s == str) return property("top", this->_top, o.top); + if ("left"s == str) + return property("left", this->_left, o.left); + if ("bottom"s == str) + return property("bottom", this->_bottom, o.bottom); + if ("right"s == str) + return property("right", this->_right, o.right); + if ("top"s == str) + return property("top", this->_top, o.top); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -94,35 +135,67 @@ CesiumJsonReader::IJsonHandler* ExtensionCesiumTileEdgesJsonHandler::readObjectK namespace CesiumGltfReader { -ExtensionModelExtFeatureMetadataJsonHandler::ExtensionModelExtFeatureMetadataJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _schema(context), _schemaUri(), _statistics(context), _featureTables(context), _featureTextures(context) {} - -void ExtensionModelExtFeatureMetadataJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionModelExtFeatureMetadata* pObject) { +ExtensionModelExtFeatureMetadataJsonHandler:: + ExtensionModelExtFeatureMetadataJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + _schema(context), + _schemaUri(), + _statistics(context), + _featureTables(context), + _featureTextures(context) {} + +void ExtensionModelExtFeatureMetadataJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::ExtensionModelExtFeatureMetadata* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* ExtensionModelExtFeatureMetadataJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +ExtensionModelExtFeatureMetadataJsonHandler::readObjectKey( + const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionModelExtFeatureMetadata(CesiumGltf::ExtensionModelExtFeatureMetadata::TypeName, str, *this->_pObject); -} - -void ExtensionModelExtFeatureMetadataJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) { - std::any& value = - o.extensions.emplace(extensionName, CesiumGltf::ExtensionModelExtFeatureMetadata()) - .first->second; + return this->readObjectKeyExtensionModelExtFeatureMetadata( + CesiumGltf::ExtensionModelExtFeatureMetadata::TypeName, + str, + *this->_pObject); +} + +void ExtensionModelExtFeatureMetadataJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumUtility::ExtensibleObject& o, + const std::string_view& extensionName) { + std::any& value = o.extensions + .emplace( + extensionName, + CesiumGltf::ExtensionModelExtFeatureMetadata()) + .first->second; this->reset( pParentHandler, &std::any_cast(value)); } -CesiumJsonReader::IJsonHandler* ExtensionModelExtFeatureMetadataJsonHandler::readObjectKeyExtensionModelExtFeatureMetadata(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionModelExtFeatureMetadata& o) { +CesiumJsonReader::IJsonHandler* ExtensionModelExtFeatureMetadataJsonHandler:: + readObjectKeyExtensionModelExtFeatureMetadata( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::ExtensionModelExtFeatureMetadata& o) { using namespace std::string_literals; - if ("schema"s == str) return property("schema", this->_schema, o.schema); - if ("schemaUri"s == str) return property("schemaUri", this->_schemaUri, o.schemaUri); - if ("statistics"s == str) return property("statistics", this->_statistics, o.statistics); - if ("featureTables"s == str) return property("featureTables", this->_featureTables, o.featureTables); - if ("featureTextures"s == str) return property("featureTextures", this->_featureTextures, o.featureTextures); + if ("schema"s == str) + return property("schema", this->_schema, o.schema); + if ("schemaUri"s == str) + return property("schemaUri", this->_schemaUri, o.schemaUri); + if ("statistics"s == str) + return property("statistics", this->_statistics, o.statistics); + if ("featureTables"s == str) + return property("featureTables", this->_featureTables, o.featureTables); + if ("featureTextures"s == str) + return property( + "featureTextures", + this->_featureTextures, + o.featureTextures); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -139,33 +212,70 @@ CesiumJsonReader::IJsonHandler* ExtensionModelExtFeatureMetadataJsonHandler::rea namespace CesiumGltfReader { -ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler::ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _featureIdAttributes(context), _featureIdTextures(context), _featureTextures() {} - -void ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionMeshPrimitiveExtFeatureMetadata* pObject) { +ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler:: + ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + _featureIdAttributes(context), + _featureIdTextures(context), + _featureTextures() {} + +void ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::ExtensionMeshPrimitiveExtFeatureMetadata* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler::readObjectKey( + const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionMeshPrimitiveExtFeatureMetadata(CesiumGltf::ExtensionMeshPrimitiveExtFeatureMetadata::TypeName, str, *this->_pObject); + return this->readObjectKeyExtensionMeshPrimitiveExtFeatureMetadata( + CesiumGltf::ExtensionMeshPrimitiveExtFeatureMetadata::TypeName, + str, + *this->_pObject); } -void ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) { +void ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumUtility::ExtensibleObject& o, + const std::string_view& extensionName) { std::any& value = - o.extensions.emplace(extensionName, CesiumGltf::ExtensionMeshPrimitiveExtFeatureMetadata()) + o.extensions + .emplace( + extensionName, + CesiumGltf::ExtensionMeshPrimitiveExtFeatureMetadata()) .first->second; this->reset( pParentHandler, - &std::any_cast(value)); + &std::any_cast( + value)); } -CesiumJsonReader::IJsonHandler* ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler::readObjectKeyExtensionMeshPrimitiveExtFeatureMetadata(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionMeshPrimitiveExtFeatureMetadata& o) { +CesiumJsonReader::IJsonHandler* +ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler:: + readObjectKeyExtensionMeshPrimitiveExtFeatureMetadata( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::ExtensionMeshPrimitiveExtFeatureMetadata& o) { using namespace std::string_literals; - if ("featureIdAttributes"s == str) return property("featureIdAttributes", this->_featureIdAttributes, o.featureIdAttributes); - if ("featureIdTextures"s == str) return property("featureIdTextures", this->_featureIdTextures, o.featureIdTextures); - if ("featureTextures"s == str) return property("featureTextures", this->_featureTextures, o.featureTextures); + if ("featureIdAttributes"s == str) + return property( + "featureIdAttributes", + this->_featureIdAttributes, + o.featureIdAttributes); + if ("featureIdTextures"s == str) + return property( + "featureIdTextures", + this->_featureIdTextures, + o.featureIdTextures); + if ("featureTextures"s == str) + return property( + "featureTextures", + this->_featureTextures, + o.featureTextures); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -182,31 +292,51 @@ CesiumJsonReader::IJsonHandler* ExtensionMeshPrimitiveExtFeatureMetadataJsonHand namespace CesiumGltfReader { -ExtensionExtInstanceFeaturesJsonHandler::ExtensionExtInstanceFeaturesJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _featureIds(context) {} +ExtensionExtInstanceFeaturesJsonHandler:: + ExtensionExtInstanceFeaturesJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + _featureIds(context) {} -void ExtensionExtInstanceFeaturesJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtInstanceFeatures* pObject) { +void ExtensionExtInstanceFeaturesJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::ExtensionExtInstanceFeatures* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* ExtensionExtInstanceFeaturesJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +ExtensionExtInstanceFeaturesJsonHandler::readObjectKey( + const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionExtInstanceFeatures(CesiumGltf::ExtensionExtInstanceFeatures::TypeName, str, *this->_pObject); + return this->readObjectKeyExtensionExtInstanceFeatures( + CesiumGltf::ExtensionExtInstanceFeatures::TypeName, + str, + *this->_pObject); } -void ExtensionExtInstanceFeaturesJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) { +void ExtensionExtInstanceFeaturesJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumUtility::ExtensibleObject& o, + const std::string_view& extensionName) { std::any& value = - o.extensions.emplace(extensionName, CesiumGltf::ExtensionExtInstanceFeatures()) + o.extensions + .emplace(extensionName, CesiumGltf::ExtensionExtInstanceFeatures()) .first->second; this->reset( pParentHandler, &std::any_cast(value)); } -CesiumJsonReader::IJsonHandler* ExtensionExtInstanceFeaturesJsonHandler::readObjectKeyExtensionExtInstanceFeatures(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtInstanceFeatures& o) { +CesiumJsonReader::IJsonHandler* ExtensionExtInstanceFeaturesJsonHandler:: + readObjectKeyExtensionExtInstanceFeatures( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::ExtensionExtInstanceFeatures& o) { using namespace std::string_literals; - if ("featureIds"s == str) return property("featureIds", this->_featureIds, o.featureIds); + if ("featureIds"s == str) + return property("featureIds", this->_featureIds, o.featureIds); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -223,31 +353,50 @@ CesiumJsonReader::IJsonHandler* ExtensionExtInstanceFeaturesJsonHandler::readObj namespace CesiumGltfReader { -ExtensionExtMeshFeaturesJsonHandler::ExtensionExtMeshFeaturesJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _featureIds(context) {} +ExtensionExtMeshFeaturesJsonHandler::ExtensionExtMeshFeaturesJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + _featureIds(context) {} -void ExtensionExtMeshFeaturesJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtMeshFeatures* pObject) { +void ExtensionExtMeshFeaturesJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::ExtensionExtMeshFeatures* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* ExtensionExtMeshFeaturesJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +ExtensionExtMeshFeaturesJsonHandler::readObjectKey( + const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionExtMeshFeatures(CesiumGltf::ExtensionExtMeshFeatures::TypeName, str, *this->_pObject); + return this->readObjectKeyExtensionExtMeshFeatures( + CesiumGltf::ExtensionExtMeshFeatures::TypeName, + str, + *this->_pObject); } -void ExtensionExtMeshFeaturesJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) { +void ExtensionExtMeshFeaturesJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumUtility::ExtensibleObject& o, + const std::string_view& extensionName) { std::any& value = - o.extensions.emplace(extensionName, CesiumGltf::ExtensionExtMeshFeatures()) + o.extensions + .emplace(extensionName, CesiumGltf::ExtensionExtMeshFeatures()) .first->second; this->reset( pParentHandler, &std::any_cast(value)); } -CesiumJsonReader::IJsonHandler* ExtensionExtMeshFeaturesJsonHandler::readObjectKeyExtensionExtMeshFeatures(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtMeshFeatures& o) { +CesiumJsonReader::IJsonHandler* +ExtensionExtMeshFeaturesJsonHandler::readObjectKeyExtensionExtMeshFeatures( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::ExtensionExtMeshFeatures& o) { using namespace std::string_literals; - if ("featureIds"s == str) return property("featureIds", this->_featureIds, o.featureIds); + if ("featureIds"s == str) + return property("featureIds", this->_featureIds, o.featureIds); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -264,31 +413,50 @@ CesiumJsonReader::IJsonHandler* ExtensionExtMeshFeaturesJsonHandler::readObjectK namespace CesiumGltfReader { -ExtensionExtMeshGpuInstancingJsonHandler::ExtensionExtMeshGpuInstancingJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _attributes() {} +ExtensionExtMeshGpuInstancingJsonHandler:: + ExtensionExtMeshGpuInstancingJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _attributes() {} -void ExtensionExtMeshGpuInstancingJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtMeshGpuInstancing* pObject) { +void ExtensionExtMeshGpuInstancingJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::ExtensionExtMeshGpuInstancing* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* ExtensionExtMeshGpuInstancingJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +ExtensionExtMeshGpuInstancingJsonHandler::readObjectKey( + const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionExtMeshGpuInstancing(CesiumGltf::ExtensionExtMeshGpuInstancing::TypeName, str, *this->_pObject); + return this->readObjectKeyExtensionExtMeshGpuInstancing( + CesiumGltf::ExtensionExtMeshGpuInstancing::TypeName, + str, + *this->_pObject); } -void ExtensionExtMeshGpuInstancingJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) { +void ExtensionExtMeshGpuInstancingJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumUtility::ExtensibleObject& o, + const std::string_view& extensionName) { std::any& value = - o.extensions.emplace(extensionName, CesiumGltf::ExtensionExtMeshGpuInstancing()) + o.extensions + .emplace(extensionName, CesiumGltf::ExtensionExtMeshGpuInstancing()) .first->second; this->reset( pParentHandler, &std::any_cast(value)); } -CesiumJsonReader::IJsonHandler* ExtensionExtMeshGpuInstancingJsonHandler::readObjectKeyExtensionExtMeshGpuInstancing(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtMeshGpuInstancing& o) { +CesiumJsonReader::IJsonHandler* ExtensionExtMeshGpuInstancingJsonHandler:: + readObjectKeyExtensionExtMeshGpuInstancing( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::ExtensionExtMeshGpuInstancing& o) { using namespace std::string_literals; - if ("attributes"s == str) return property("attributes", this->_attributes, o.attributes); + if ("attributes"s == str) + return property("attributes", this->_attributes, o.attributes); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -305,31 +473,52 @@ CesiumJsonReader::IJsonHandler* ExtensionExtMeshGpuInstancingJsonHandler::readOb namespace CesiumGltfReader { -ExtensionBufferExtMeshoptCompressionJsonHandler::ExtensionBufferExtMeshoptCompressionJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _fallback() {} +ExtensionBufferExtMeshoptCompressionJsonHandler:: + ExtensionBufferExtMeshoptCompressionJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _fallback() {} -void ExtensionBufferExtMeshoptCompressionJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionBufferExtMeshoptCompression* pObject) { +void ExtensionBufferExtMeshoptCompressionJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::ExtensionBufferExtMeshoptCompression* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* ExtensionBufferExtMeshoptCompressionJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +ExtensionBufferExtMeshoptCompressionJsonHandler::readObjectKey( + const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionBufferExtMeshoptCompression(CesiumGltf::ExtensionBufferExtMeshoptCompression::TypeName, str, *this->_pObject); -} - -void ExtensionBufferExtMeshoptCompressionJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) { - std::any& value = - o.extensions.emplace(extensionName, CesiumGltf::ExtensionBufferExtMeshoptCompression()) - .first->second; + return this->readObjectKeyExtensionBufferExtMeshoptCompression( + CesiumGltf::ExtensionBufferExtMeshoptCompression::TypeName, + str, + *this->_pObject); +} + +void ExtensionBufferExtMeshoptCompressionJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumUtility::ExtensibleObject& o, + const std::string_view& extensionName) { + std::any& value = o.extensions + .emplace( + extensionName, + CesiumGltf::ExtensionBufferExtMeshoptCompression()) + .first->second; this->reset( pParentHandler, &std::any_cast(value)); } -CesiumJsonReader::IJsonHandler* ExtensionBufferExtMeshoptCompressionJsonHandler::readObjectKeyExtensionBufferExtMeshoptCompression(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionBufferExtMeshoptCompression& o) { +CesiumJsonReader::IJsonHandler* +ExtensionBufferExtMeshoptCompressionJsonHandler:: + readObjectKeyExtensionBufferExtMeshoptCompression( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::ExtensionBufferExtMeshoptCompression& o) { using namespace std::string_literals; - if ("fallback"s == str) return property("fallback", this->_fallback, o.fallback); + if ("fallback"s == str) + return property("fallback", this->_fallback, o.fallback); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -346,37 +535,73 @@ CesiumJsonReader::IJsonHandler* ExtensionBufferExtMeshoptCompressionJsonHandler: namespace CesiumGltfReader { -ExtensionBufferViewExtMeshoptCompressionJsonHandler::ExtensionBufferViewExtMeshoptCompressionJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _buffer(), _byteOffset(), _byteLength(), _byteStride(), _count(), _mode(), _filter() {} - -void ExtensionBufferViewExtMeshoptCompressionJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionBufferViewExtMeshoptCompression* pObject) { +ExtensionBufferViewExtMeshoptCompressionJsonHandler:: + ExtensionBufferViewExtMeshoptCompressionJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + _buffer(), + _byteOffset(), + _byteLength(), + _byteStride(), + _count(), + _mode(), + _filter() {} + +void ExtensionBufferViewExtMeshoptCompressionJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::ExtensionBufferViewExtMeshoptCompression* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* ExtensionBufferViewExtMeshoptCompressionJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +ExtensionBufferViewExtMeshoptCompressionJsonHandler::readObjectKey( + const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionBufferViewExtMeshoptCompression(CesiumGltf::ExtensionBufferViewExtMeshoptCompression::TypeName, str, *this->_pObject); + return this->readObjectKeyExtensionBufferViewExtMeshoptCompression( + CesiumGltf::ExtensionBufferViewExtMeshoptCompression::TypeName, + str, + *this->_pObject); } -void ExtensionBufferViewExtMeshoptCompressionJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) { +void ExtensionBufferViewExtMeshoptCompressionJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumUtility::ExtensibleObject& o, + const std::string_view& extensionName) { std::any& value = - o.extensions.emplace(extensionName, CesiumGltf::ExtensionBufferViewExtMeshoptCompression()) + o.extensions + .emplace( + extensionName, + CesiumGltf::ExtensionBufferViewExtMeshoptCompression()) .first->second; this->reset( pParentHandler, - &std::any_cast(value)); + &std::any_cast( + value)); } -CesiumJsonReader::IJsonHandler* ExtensionBufferViewExtMeshoptCompressionJsonHandler::readObjectKeyExtensionBufferViewExtMeshoptCompression(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionBufferViewExtMeshoptCompression& o) { +CesiumJsonReader::IJsonHandler* +ExtensionBufferViewExtMeshoptCompressionJsonHandler:: + readObjectKeyExtensionBufferViewExtMeshoptCompression( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::ExtensionBufferViewExtMeshoptCompression& o) { using namespace std::string_literals; - if ("buffer"s == str) return property("buffer", this->_buffer, o.buffer); - if ("byteOffset"s == str) return property("byteOffset", this->_byteOffset, o.byteOffset); - if ("byteLength"s == str) return property("byteLength", this->_byteLength, o.byteLength); - if ("byteStride"s == str) return property("byteStride", this->_byteStride, o.byteStride); - if ("count"s == str) return property("count", this->_count, o.count); - if ("mode"s == str) return property("mode", this->_mode, o.mode); - if ("filter"s == str) return property("filter", this->_filter, o.filter); + if ("buffer"s == str) + return property("buffer", this->_buffer, o.buffer); + if ("byteOffset"s == str) + return property("byteOffset", this->_byteOffset, o.byteOffset); + if ("byteLength"s == str) + return property("byteLength", this->_byteLength, o.byteLength); + if ("byteStride"s == str) + return property("byteStride", this->_byteStride, o.byteStride); + if ("count"s == str) + return property("count", this->_count, o.count); + if ("mode"s == str) + return property("mode", this->_mode, o.mode); + if ("filter"s == str) + return property("filter", this->_filter, o.filter); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -393,35 +618,70 @@ CesiumJsonReader::IJsonHandler* ExtensionBufferViewExtMeshoptCompressionJsonHand namespace CesiumGltfReader { -ExtensionModelExtStructuralMetadataJsonHandler::ExtensionModelExtStructuralMetadataJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _schema(context), _schemaUri(), _propertyTables(context), _propertyTextures(context), _propertyAttributes(context) {} - -void ExtensionModelExtStructuralMetadataJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionModelExtStructuralMetadata* pObject) { +ExtensionModelExtStructuralMetadataJsonHandler:: + ExtensionModelExtStructuralMetadataJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + _schema(context), + _schemaUri(), + _propertyTables(context), + _propertyTextures(context), + _propertyAttributes(context) {} + +void ExtensionModelExtStructuralMetadataJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::ExtensionModelExtStructuralMetadata* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* ExtensionModelExtStructuralMetadataJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +ExtensionModelExtStructuralMetadataJsonHandler::readObjectKey( + const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionModelExtStructuralMetadata(CesiumGltf::ExtensionModelExtStructuralMetadata::TypeName, str, *this->_pObject); -} - -void ExtensionModelExtStructuralMetadataJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) { - std::any& value = - o.extensions.emplace(extensionName, CesiumGltf::ExtensionModelExtStructuralMetadata()) - .first->second; + return this->readObjectKeyExtensionModelExtStructuralMetadata( + CesiumGltf::ExtensionModelExtStructuralMetadata::TypeName, + str, + *this->_pObject); +} + +void ExtensionModelExtStructuralMetadataJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumUtility::ExtensibleObject& o, + const std::string_view& extensionName) { + std::any& value = o.extensions + .emplace( + extensionName, + CesiumGltf::ExtensionModelExtStructuralMetadata()) + .first->second; this->reset( pParentHandler, &std::any_cast(value)); } -CesiumJsonReader::IJsonHandler* ExtensionModelExtStructuralMetadataJsonHandler::readObjectKeyExtensionModelExtStructuralMetadata(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionModelExtStructuralMetadata& o) { +CesiumJsonReader::IJsonHandler* ExtensionModelExtStructuralMetadataJsonHandler:: + readObjectKeyExtensionModelExtStructuralMetadata( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::ExtensionModelExtStructuralMetadata& o) { using namespace std::string_literals; - if ("schema"s == str) return property("schema", this->_schema, o.schema); - if ("schemaUri"s == str) return property("schemaUri", this->_schemaUri, o.schemaUri); - if ("propertyTables"s == str) return property("propertyTables", this->_propertyTables, o.propertyTables); - if ("propertyTextures"s == str) return property("propertyTextures", this->_propertyTextures, o.propertyTextures); - if ("propertyAttributes"s == str) return property("propertyAttributes", this->_propertyAttributes, o.propertyAttributes); + if ("schema"s == str) + return property("schema", this->_schema, o.schema); + if ("schemaUri"s == str) + return property("schemaUri", this->_schemaUri, o.schemaUri); + if ("propertyTables"s == str) + return property("propertyTables", this->_propertyTables, o.propertyTables); + if ("propertyTextures"s == str) + return property( + "propertyTextures", + this->_propertyTextures, + o.propertyTextures); + if ("propertyAttributes"s == str) + return property( + "propertyAttributes", + this->_propertyAttributes, + o.propertyAttributes); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -438,32 +698,64 @@ CesiumJsonReader::IJsonHandler* ExtensionModelExtStructuralMetadataJsonHandler:: namespace CesiumGltfReader { -ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler::ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _propertyTextures(), _propertyAttributes() {} +ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler:: + ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + _propertyTextures(), + _propertyAttributes() {} -void ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionMeshPrimitiveExtStructuralMetadata* pObject) { +void ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::ExtensionMeshPrimitiveExtStructuralMetadata* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler::readObjectKey( + const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionMeshPrimitiveExtStructuralMetadata(CesiumGltf::ExtensionMeshPrimitiveExtStructuralMetadata::TypeName, str, *this->_pObject); + return this->readObjectKeyExtensionMeshPrimitiveExtStructuralMetadata( + CesiumGltf::ExtensionMeshPrimitiveExtStructuralMetadata::TypeName, + str, + *this->_pObject); } -void ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) { +void ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumUtility::ExtensibleObject& o, + const std::string_view& extensionName) { std::any& value = - o.extensions.emplace(extensionName, CesiumGltf::ExtensionMeshPrimitiveExtStructuralMetadata()) + o.extensions + .emplace( + extensionName, + CesiumGltf::ExtensionMeshPrimitiveExtStructuralMetadata()) .first->second; this->reset( pParentHandler, - &std::any_cast(value)); + &std::any_cast( + value)); } -CesiumJsonReader::IJsonHandler* ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler::readObjectKeyExtensionMeshPrimitiveExtStructuralMetadata(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionMeshPrimitiveExtStructuralMetadata& o) { +CesiumJsonReader::IJsonHandler* +ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler:: + readObjectKeyExtensionMeshPrimitiveExtStructuralMetadata( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::ExtensionMeshPrimitiveExtStructuralMetadata& o) { using namespace std::string_literals; - if ("propertyTextures"s == str) return property("propertyTextures", this->_propertyTextures, o.propertyTextures); - if ("propertyAttributes"s == str) return property("propertyAttributes", this->_propertyAttributes, o.propertyAttributes); + if ("propertyTextures"s == str) + return property( + "propertyTextures", + this->_propertyTextures, + o.propertyTextures); + if ("propertyAttributes"s == str) + return property( + "propertyAttributes", + this->_propertyAttributes, + o.propertyAttributes); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -480,32 +772,55 @@ CesiumJsonReader::IJsonHandler* ExtensionMeshPrimitiveExtStructuralMetadataJsonH namespace CesiumGltfReader { -ExtensionKhrDracoMeshCompressionJsonHandler::ExtensionKhrDracoMeshCompressionJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _bufferView(), _attributes() {} +ExtensionKhrDracoMeshCompressionJsonHandler:: + ExtensionKhrDracoMeshCompressionJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + _bufferView(), + _attributes() {} -void ExtensionKhrDracoMeshCompressionJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionKhrDracoMeshCompression* pObject) { +void ExtensionKhrDracoMeshCompressionJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::ExtensionKhrDracoMeshCompression* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* ExtensionKhrDracoMeshCompressionJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +ExtensionKhrDracoMeshCompressionJsonHandler::readObjectKey( + const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionKhrDracoMeshCompression(CesiumGltf::ExtensionKhrDracoMeshCompression::TypeName, str, *this->_pObject); -} - -void ExtensionKhrDracoMeshCompressionJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) { - std::any& value = - o.extensions.emplace(extensionName, CesiumGltf::ExtensionKhrDracoMeshCompression()) - .first->second; + return this->readObjectKeyExtensionKhrDracoMeshCompression( + CesiumGltf::ExtensionKhrDracoMeshCompression::TypeName, + str, + *this->_pObject); +} + +void ExtensionKhrDracoMeshCompressionJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumUtility::ExtensibleObject& o, + const std::string_view& extensionName) { + std::any& value = o.extensions + .emplace( + extensionName, + CesiumGltf::ExtensionKhrDracoMeshCompression()) + .first->second; this->reset( pParentHandler, &std::any_cast(value)); } -CesiumJsonReader::IJsonHandler* ExtensionKhrDracoMeshCompressionJsonHandler::readObjectKeyExtensionKhrDracoMeshCompression(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionKhrDracoMeshCompression& o) { +CesiumJsonReader::IJsonHandler* ExtensionKhrDracoMeshCompressionJsonHandler:: + readObjectKeyExtensionKhrDracoMeshCompression( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::ExtensionKhrDracoMeshCompression& o) { using namespace std::string_literals; - if ("bufferView"s == str) return property("bufferView", this->_bufferView, o.bufferView); - if ("attributes"s == str) return property("attributes", this->_attributes, o.attributes); + if ("bufferView"s == str) + return property("bufferView", this->_bufferView, o.bufferView); + if ("attributes"s == str) + return property("attributes", this->_attributes, o.attributes); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -522,28 +837,45 @@ CesiumJsonReader::IJsonHandler* ExtensionKhrDracoMeshCompressionJsonHandler::rea namespace CesiumGltfReader { -ExtensionKhrMaterialsUnlitJsonHandler::ExtensionKhrMaterialsUnlitJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context) {} +ExtensionKhrMaterialsUnlitJsonHandler::ExtensionKhrMaterialsUnlitJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context) {} -void ExtensionKhrMaterialsUnlitJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionKhrMaterialsUnlit* pObject) { +void ExtensionKhrMaterialsUnlitJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::ExtensionKhrMaterialsUnlit* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* ExtensionKhrMaterialsUnlitJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +ExtensionKhrMaterialsUnlitJsonHandler::readObjectKey( + const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionKhrMaterialsUnlit(CesiumGltf::ExtensionKhrMaterialsUnlit::TypeName, str, *this->_pObject); + return this->readObjectKeyExtensionKhrMaterialsUnlit( + CesiumGltf::ExtensionKhrMaterialsUnlit::TypeName, + str, + *this->_pObject); } -void ExtensionKhrMaterialsUnlitJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) { +void ExtensionKhrMaterialsUnlitJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumUtility::ExtensibleObject& o, + const std::string_view& extensionName) { std::any& value = - o.extensions.emplace(extensionName, CesiumGltf::ExtensionKhrMaterialsUnlit()) + o.extensions + .emplace(extensionName, CesiumGltf::ExtensionKhrMaterialsUnlit()) .first->second; this->reset( pParentHandler, &std::any_cast(value)); } -CesiumJsonReader::IJsonHandler* ExtensionKhrMaterialsUnlitJsonHandler::readObjectKeyExtensionKhrMaterialsUnlit(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionKhrMaterialsUnlit& o) { +CesiumJsonReader::IJsonHandler* +ExtensionKhrMaterialsUnlitJsonHandler::readObjectKeyExtensionKhrMaterialsUnlit( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::ExtensionKhrMaterialsUnlit& o) { using namespace std::string_literals; (void)o; @@ -563,31 +895,52 @@ CesiumJsonReader::IJsonHandler* ExtensionKhrMaterialsUnlitJsonHandler::readObjec namespace CesiumGltfReader { -ExtensionModelKhrMaterialsVariantsJsonHandler::ExtensionModelKhrMaterialsVariantsJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _variants(context) {} +ExtensionModelKhrMaterialsVariantsJsonHandler:: + ExtensionModelKhrMaterialsVariantsJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + _variants(context) {} -void ExtensionModelKhrMaterialsVariantsJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionModelKhrMaterialsVariants* pObject) { +void ExtensionModelKhrMaterialsVariantsJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::ExtensionModelKhrMaterialsVariants* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* ExtensionModelKhrMaterialsVariantsJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +ExtensionModelKhrMaterialsVariantsJsonHandler::readObjectKey( + const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionModelKhrMaterialsVariants(CesiumGltf::ExtensionModelKhrMaterialsVariants::TypeName, str, *this->_pObject); -} - -void ExtensionModelKhrMaterialsVariantsJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) { - std::any& value = - o.extensions.emplace(extensionName, CesiumGltf::ExtensionModelKhrMaterialsVariants()) - .first->second; + return this->readObjectKeyExtensionModelKhrMaterialsVariants( + CesiumGltf::ExtensionModelKhrMaterialsVariants::TypeName, + str, + *this->_pObject); +} + +void ExtensionModelKhrMaterialsVariantsJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumUtility::ExtensibleObject& o, + const std::string_view& extensionName) { + std::any& value = o.extensions + .emplace( + extensionName, + CesiumGltf::ExtensionModelKhrMaterialsVariants()) + .first->second; this->reset( pParentHandler, &std::any_cast(value)); } -CesiumJsonReader::IJsonHandler* ExtensionModelKhrMaterialsVariantsJsonHandler::readObjectKeyExtensionModelKhrMaterialsVariants(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionModelKhrMaterialsVariants& o) { +CesiumJsonReader::IJsonHandler* ExtensionModelKhrMaterialsVariantsJsonHandler:: + readObjectKeyExtensionModelKhrMaterialsVariants( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::ExtensionModelKhrMaterialsVariants& o) { using namespace std::string_literals; - if ("variants"s == str) return property("variants", this->_variants, o.variants); + if ("variants"s == str) + return property("variants", this->_variants, o.variants); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -604,31 +957,55 @@ CesiumJsonReader::IJsonHandler* ExtensionModelKhrMaterialsVariantsJsonHandler::r namespace CesiumGltfReader { -ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler::ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _mappings(context) {} +ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler:: + ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + _mappings(context) {} -void ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionMeshPrimitiveKhrMaterialsVariants* pObject) { +void ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::ExtensionMeshPrimitiveKhrMaterialsVariants* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler::readObjectKey( + const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionMeshPrimitiveKhrMaterialsVariants(CesiumGltf::ExtensionMeshPrimitiveKhrMaterialsVariants::TypeName, str, *this->_pObject); + return this->readObjectKeyExtensionMeshPrimitiveKhrMaterialsVariants( + CesiumGltf::ExtensionMeshPrimitiveKhrMaterialsVariants::TypeName, + str, + *this->_pObject); } -void ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) { +void ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumUtility::ExtensibleObject& o, + const std::string_view& extensionName) { std::any& value = - o.extensions.emplace(extensionName, CesiumGltf::ExtensionMeshPrimitiveKhrMaterialsVariants()) + o.extensions + .emplace( + extensionName, + CesiumGltf::ExtensionMeshPrimitiveKhrMaterialsVariants()) .first->second; this->reset( pParentHandler, - &std::any_cast(value)); + &std::any_cast( + value)); } -CesiumJsonReader::IJsonHandler* ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler::readObjectKeyExtensionMeshPrimitiveKhrMaterialsVariants(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionMeshPrimitiveKhrMaterialsVariants& o) { +CesiumJsonReader::IJsonHandler* +ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler:: + readObjectKeyExtensionMeshPrimitiveKhrMaterialsVariants( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::ExtensionMeshPrimitiveKhrMaterialsVariants& o) { using namespace std::string_literals; - if ("mappings"s == str) return property("mappings", this->_mappings, o.mappings); + if ("mappings"s == str) + return property("mappings", this->_mappings, o.mappings); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -645,31 +1022,49 @@ CesiumJsonReader::IJsonHandler* ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHa namespace CesiumGltfReader { -ExtensionKhrTextureBasisuJsonHandler::ExtensionKhrTextureBasisuJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _source() {} +ExtensionKhrTextureBasisuJsonHandler::ExtensionKhrTextureBasisuJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _source() {} -void ExtensionKhrTextureBasisuJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionKhrTextureBasisu* pObject) { +void ExtensionKhrTextureBasisuJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::ExtensionKhrTextureBasisu* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* ExtensionKhrTextureBasisuJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +ExtensionKhrTextureBasisuJsonHandler::readObjectKey( + const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionKhrTextureBasisu(CesiumGltf::ExtensionKhrTextureBasisu::TypeName, str, *this->_pObject); + return this->readObjectKeyExtensionKhrTextureBasisu( + CesiumGltf::ExtensionKhrTextureBasisu::TypeName, + str, + *this->_pObject); } -void ExtensionKhrTextureBasisuJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) { +void ExtensionKhrTextureBasisuJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumUtility::ExtensibleObject& o, + const std::string_view& extensionName) { std::any& value = - o.extensions.emplace(extensionName, CesiumGltf::ExtensionKhrTextureBasisu()) + o.extensions + .emplace(extensionName, CesiumGltf::ExtensionKhrTextureBasisu()) .first->second; this->reset( pParentHandler, &std::any_cast(value)); } -CesiumJsonReader::IJsonHandler* ExtensionKhrTextureBasisuJsonHandler::readObjectKeyExtensionKhrTextureBasisu(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionKhrTextureBasisu& o) { +CesiumJsonReader::IJsonHandler* +ExtensionKhrTextureBasisuJsonHandler::readObjectKeyExtensionKhrTextureBasisu( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::ExtensionKhrTextureBasisu& o) { using namespace std::string_literals; - if ("source"s == str) return property("source", this->_source, o.source); + if ("source"s == str) + return property("source", this->_source, o.source); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -686,32 +1081,54 @@ CesiumJsonReader::IJsonHandler* ExtensionKhrTextureBasisuJsonHandler::readObject namespace CesiumGltfReader { -ExtensionModelMaxarMeshVariantsJsonHandler::ExtensionModelMaxarMeshVariantsJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _defaultProperty(), _variants(context) {} +ExtensionModelMaxarMeshVariantsJsonHandler:: + ExtensionModelMaxarMeshVariantsJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + _defaultProperty(), + _variants(context) {} -void ExtensionModelMaxarMeshVariantsJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionModelMaxarMeshVariants* pObject) { +void ExtensionModelMaxarMeshVariantsJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::ExtensionModelMaxarMeshVariants* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* ExtensionModelMaxarMeshVariantsJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +ExtensionModelMaxarMeshVariantsJsonHandler::readObjectKey( + const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionModelMaxarMeshVariants(CesiumGltf::ExtensionModelMaxarMeshVariants::TypeName, str, *this->_pObject); + return this->readObjectKeyExtensionModelMaxarMeshVariants( + CesiumGltf::ExtensionModelMaxarMeshVariants::TypeName, + str, + *this->_pObject); } -void ExtensionModelMaxarMeshVariantsJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) { +void ExtensionModelMaxarMeshVariantsJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumUtility::ExtensibleObject& o, + const std::string_view& extensionName) { std::any& value = - o.extensions.emplace(extensionName, CesiumGltf::ExtensionModelMaxarMeshVariants()) + o.extensions + .emplace(extensionName, CesiumGltf::ExtensionModelMaxarMeshVariants()) .first->second; this->reset( pParentHandler, &std::any_cast(value)); } -CesiumJsonReader::IJsonHandler* ExtensionModelMaxarMeshVariantsJsonHandler::readObjectKeyExtensionModelMaxarMeshVariants(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionModelMaxarMeshVariants& o) { +CesiumJsonReader::IJsonHandler* ExtensionModelMaxarMeshVariantsJsonHandler:: + readObjectKeyExtensionModelMaxarMeshVariants( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::ExtensionModelMaxarMeshVariants& o) { using namespace std::string_literals; - if ("default"s == str) return property("default", this->_defaultProperty, o.defaultProperty); - if ("variants"s == str) return property("variants", this->_variants, o.variants); + if ("default"s == str) + return property("default", this->_defaultProperty, o.defaultProperty); + if ("variants"s == str) + return property("variants", this->_variants, o.variants); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -728,31 +1145,51 @@ CesiumJsonReader::IJsonHandler* ExtensionModelMaxarMeshVariantsJsonHandler::read namespace CesiumGltfReader { -ExtensionNodeMaxarMeshVariantsJsonHandler::ExtensionNodeMaxarMeshVariantsJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _mappings(context) {} +ExtensionNodeMaxarMeshVariantsJsonHandler:: + ExtensionNodeMaxarMeshVariantsJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + _mappings(context) {} -void ExtensionNodeMaxarMeshVariantsJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionNodeMaxarMeshVariants* pObject) { +void ExtensionNodeMaxarMeshVariantsJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::ExtensionNodeMaxarMeshVariants* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* ExtensionNodeMaxarMeshVariantsJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +ExtensionNodeMaxarMeshVariantsJsonHandler::readObjectKey( + const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionNodeMaxarMeshVariants(CesiumGltf::ExtensionNodeMaxarMeshVariants::TypeName, str, *this->_pObject); + return this->readObjectKeyExtensionNodeMaxarMeshVariants( + CesiumGltf::ExtensionNodeMaxarMeshVariants::TypeName, + str, + *this->_pObject); } -void ExtensionNodeMaxarMeshVariantsJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) { +void ExtensionNodeMaxarMeshVariantsJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumUtility::ExtensibleObject& o, + const std::string_view& extensionName) { std::any& value = - o.extensions.emplace(extensionName, CesiumGltf::ExtensionNodeMaxarMeshVariants()) + o.extensions + .emplace(extensionName, CesiumGltf::ExtensionNodeMaxarMeshVariants()) .first->second; this->reset( pParentHandler, &std::any_cast(value)); } -CesiumJsonReader::IJsonHandler* ExtensionNodeMaxarMeshVariantsJsonHandler::readObjectKeyExtensionNodeMaxarMeshVariants(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionNodeMaxarMeshVariants& o) { +CesiumJsonReader::IJsonHandler* ExtensionNodeMaxarMeshVariantsJsonHandler:: + readObjectKeyExtensionNodeMaxarMeshVariants( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::ExtensionNodeMaxarMeshVariants& o) { using namespace std::string_literals; - if ("mappings"s == str) return property("mappings", this->_mappings, o.mappings); + if ("mappings"s == str) + return property("mappings", this->_mappings, o.mappings); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -769,34 +1206,60 @@ CesiumJsonReader::IJsonHandler* ExtensionNodeMaxarMeshVariantsJsonHandler::readO namespace CesiumGltfReader { -ExtensionKhrTextureTransformJsonHandler::ExtensionKhrTextureTransformJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _offset(), _rotation(), _scale(), _texCoord() {} - -void ExtensionKhrTextureTransformJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionKhrTextureTransform* pObject) { +ExtensionKhrTextureTransformJsonHandler:: + ExtensionKhrTextureTransformJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + _offset(), + _rotation(), + _scale(), + _texCoord() {} + +void ExtensionKhrTextureTransformJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::ExtensionKhrTextureTransform* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* ExtensionKhrTextureTransformJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +ExtensionKhrTextureTransformJsonHandler::readObjectKey( + const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionKhrTextureTransform(CesiumGltf::ExtensionKhrTextureTransform::TypeName, str, *this->_pObject); + return this->readObjectKeyExtensionKhrTextureTransform( + CesiumGltf::ExtensionKhrTextureTransform::TypeName, + str, + *this->_pObject); } -void ExtensionKhrTextureTransformJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) { +void ExtensionKhrTextureTransformJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumUtility::ExtensibleObject& o, + const std::string_view& extensionName) { std::any& value = - o.extensions.emplace(extensionName, CesiumGltf::ExtensionKhrTextureTransform()) + o.extensions + .emplace(extensionName, CesiumGltf::ExtensionKhrTextureTransform()) .first->second; this->reset( pParentHandler, &std::any_cast(value)); } -CesiumJsonReader::IJsonHandler* ExtensionKhrTextureTransformJsonHandler::readObjectKeyExtensionKhrTextureTransform(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionKhrTextureTransform& o) { +CesiumJsonReader::IJsonHandler* ExtensionKhrTextureTransformJsonHandler:: + readObjectKeyExtensionKhrTextureTransform( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::ExtensionKhrTextureTransform& o) { using namespace std::string_literals; - if ("offset"s == str) return property("offset", this->_offset, o.offset); - if ("rotation"s == str) return property("rotation", this->_rotation, o.rotation); - if ("scale"s == str) return property("scale", this->_scale, o.scale); - if ("texCoord"s == str) return property("texCoord", this->_texCoord, o.texCoord); + if ("offset"s == str) + return property("offset", this->_offset, o.offset); + if ("rotation"s == str) + return property("rotation", this->_rotation, o.rotation); + if ("scale"s == str) + return property("scale", this->_scale, o.scale); + if ("texCoord"s == str) + return property("texCoord", this->_texCoord, o.texCoord); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -813,19 +1276,30 @@ CesiumJsonReader::IJsonHandler* ExtensionKhrTextureTransformJsonHandler::readObj namespace CesiumGltfReader { -ExtensionTextureWebpJsonHandler::ExtensionTextureWebpJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _source() {} +ExtensionTextureWebpJsonHandler::ExtensionTextureWebpJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _source() {} -void ExtensionTextureWebpJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionTextureWebp* pObject) { +void ExtensionTextureWebpJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::ExtensionTextureWebp* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* ExtensionTextureWebpJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +ExtensionTextureWebpJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionTextureWebp(CesiumGltf::ExtensionTextureWebp::TypeName, str, *this->_pObject); + return this->readObjectKeyExtensionTextureWebp( + CesiumGltf::ExtensionTextureWebp::TypeName, + str, + *this->_pObject); } -void ExtensionTextureWebpJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) { +void ExtensionTextureWebpJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumUtility::ExtensibleObject& o, + const std::string_view& extensionName) { std::any& value = o.extensions.emplace(extensionName, CesiumGltf::ExtensionTextureWebp()) .first->second; @@ -834,10 +1308,15 @@ void ExtensionTextureWebpJsonHandler::reset(CesiumJsonReader::IJsonHandler* pPar &std::any_cast(value)); } -CesiumJsonReader::IJsonHandler* ExtensionTextureWebpJsonHandler::readObjectKeyExtensionTextureWebp(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionTextureWebp& o) { +CesiumJsonReader::IJsonHandler* +ExtensionTextureWebpJsonHandler::readObjectKeyExtensionTextureWebp( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::ExtensionTextureWebp& o) { using namespace std::string_literals; - if ("source"s == str) return property("source", this->_source, o.source); + if ("source"s == str) + return property("source", this->_source, o.source); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -854,24 +1333,45 @@ CesiumJsonReader::IJsonHandler* ExtensionTextureWebpJsonHandler::readObjectKeyEx namespace CesiumGltfReader { -ExtensionNodeMaxarMeshVariantsMappingsValueJsonHandler::ExtensionNodeMaxarMeshVariantsMappingsValueJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _variants(), _mesh(), _name() {} - -void ExtensionNodeMaxarMeshVariantsMappingsValueJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionNodeMaxarMeshVariantsMappingsValue* pObject) { +ExtensionNodeMaxarMeshVariantsMappingsValueJsonHandler:: + ExtensionNodeMaxarMeshVariantsMappingsValueJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + _variants(), + _mesh(), + _name() {} + +void ExtensionNodeMaxarMeshVariantsMappingsValueJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::ExtensionNodeMaxarMeshVariantsMappingsValue* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* ExtensionNodeMaxarMeshVariantsMappingsValueJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +ExtensionNodeMaxarMeshVariantsMappingsValueJsonHandler::readObjectKey( + const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionNodeMaxarMeshVariantsMappingsValue(CesiumGltf::ExtensionNodeMaxarMeshVariantsMappingsValue::TypeName, str, *this->_pObject); -} - -CesiumJsonReader::IJsonHandler* ExtensionNodeMaxarMeshVariantsMappingsValueJsonHandler::readObjectKeyExtensionNodeMaxarMeshVariantsMappingsValue(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionNodeMaxarMeshVariantsMappingsValue& o) { + return this->readObjectKeyExtensionNodeMaxarMeshVariantsMappingsValue( + CesiumGltf::ExtensionNodeMaxarMeshVariantsMappingsValue::TypeName, + str, + *this->_pObject); +} + +CesiumJsonReader::IJsonHandler* +ExtensionNodeMaxarMeshVariantsMappingsValueJsonHandler:: + readObjectKeyExtensionNodeMaxarMeshVariantsMappingsValue( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::ExtensionNodeMaxarMeshVariantsMappingsValue& o) { using namespace std::string_literals; - if ("variants"s == str) return property("variants", this->_variants, o.variants); - if ("mesh"s == str) return property("mesh", this->_mesh, o.mesh); - if ("name"s == str) return property("name", this->_name, o.name); + if ("variants"s == str) + return property("variants", this->_variants, o.variants); + if ("mesh"s == str) + return property("mesh", this->_mesh, o.mesh); + if ("name"s == str) + return property("name", this->_name, o.name); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -888,22 +1388,38 @@ CesiumJsonReader::IJsonHandler* ExtensionNodeMaxarMeshVariantsMappingsValueJsonH namespace CesiumGltfReader { -ExtensionModelMaxarMeshVariantsValueJsonHandler::ExtensionModelMaxarMeshVariantsValueJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumGltfReader::NamedObjectJsonHandler(context), _name() {} +ExtensionModelMaxarMeshVariantsValueJsonHandler:: + ExtensionModelMaxarMeshVariantsValueJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumGltfReader::NamedObjectJsonHandler(context), _name() {} -void ExtensionModelMaxarMeshVariantsValueJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionModelMaxarMeshVariantsValue* pObject) { +void ExtensionModelMaxarMeshVariantsValueJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::ExtensionModelMaxarMeshVariantsValue* pObject) { CesiumGltfReader::NamedObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* ExtensionModelMaxarMeshVariantsValueJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +ExtensionModelMaxarMeshVariantsValueJsonHandler::readObjectKey( + const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionModelMaxarMeshVariantsValue(CesiumGltf::ExtensionModelMaxarMeshVariantsValue::TypeName, str, *this->_pObject); -} - -CesiumJsonReader::IJsonHandler* ExtensionModelMaxarMeshVariantsValueJsonHandler::readObjectKeyExtensionModelMaxarMeshVariantsValue(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionModelMaxarMeshVariantsValue& o) { + return this->readObjectKeyExtensionModelMaxarMeshVariantsValue( + CesiumGltf::ExtensionModelMaxarMeshVariantsValue::TypeName, + str, + *this->_pObject); +} + +CesiumJsonReader::IJsonHandler* +ExtensionModelMaxarMeshVariantsValueJsonHandler:: + readObjectKeyExtensionModelMaxarMeshVariantsValue( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::ExtensionModelMaxarMeshVariantsValue& o) { using namespace std::string_literals; - if ("name"s == str) return property("name", this->_name, o.name); + if ("name"s == str) + return property("name", this->_name, o.name); return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } @@ -920,24 +1436,49 @@ CesiumJsonReader::IJsonHandler* ExtensionModelMaxarMeshVariantsValueJsonHandler: namespace CesiumGltfReader { -ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueJsonHandler::ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _variants(), _material(), _name() {} - -void ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValue* pObject) { +ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueJsonHandler:: + ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + _variants(), + _material(), + _name() {} + +void ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValue* + pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueJsonHandler:: + readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValue(CesiumGltf::ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValue::TypeName, str, *this->_pObject); -} - -CesiumJsonReader::IJsonHandler* ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueJsonHandler::readObjectKeyExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValue(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValue& o) { + return this + ->readObjectKeyExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValue( + CesiumGltf::ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValue:: + TypeName, + str, + *this->_pObject); +} + +CesiumJsonReader::IJsonHandler* +ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueJsonHandler:: + readObjectKeyExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValue( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValue& + o) { using namespace std::string_literals; - if ("variants"s == str) return property("variants", this->_variants, o.variants); - if ("material"s == str) return property("material", this->_material, o.material); - if ("name"s == str) return property("name", this->_name, o.name); + if ("variants"s == str) + return property("variants", this->_variants, o.variants); + if ("material"s == str) + return property("material", this->_material, o.material); + if ("name"s == str) + return property("name", this->_name, o.name); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -954,22 +1495,38 @@ CesiumJsonReader::IJsonHandler* ExtensionMeshPrimitiveKhrMaterialsVariantsMappin namespace CesiumGltfReader { -ExtensionModelKhrMaterialsVariantsValueJsonHandler::ExtensionModelKhrMaterialsVariantsValueJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumGltfReader::NamedObjectJsonHandler(context), _name() {} +ExtensionModelKhrMaterialsVariantsValueJsonHandler:: + ExtensionModelKhrMaterialsVariantsValueJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumGltfReader::NamedObjectJsonHandler(context), _name() {} -void ExtensionModelKhrMaterialsVariantsValueJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionModelKhrMaterialsVariantsValue* pObject) { +void ExtensionModelKhrMaterialsVariantsValueJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::ExtensionModelKhrMaterialsVariantsValue* pObject) { CesiumGltfReader::NamedObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* ExtensionModelKhrMaterialsVariantsValueJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +ExtensionModelKhrMaterialsVariantsValueJsonHandler::readObjectKey( + const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionModelKhrMaterialsVariantsValue(CesiumGltf::ExtensionModelKhrMaterialsVariantsValue::TypeName, str, *this->_pObject); -} - -CesiumJsonReader::IJsonHandler* ExtensionModelKhrMaterialsVariantsValueJsonHandler::readObjectKeyExtensionModelKhrMaterialsVariantsValue(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionModelKhrMaterialsVariantsValue& o) { + return this->readObjectKeyExtensionModelKhrMaterialsVariantsValue( + CesiumGltf::ExtensionModelKhrMaterialsVariantsValue::TypeName, + str, + *this->_pObject); +} + +CesiumJsonReader::IJsonHandler* +ExtensionModelKhrMaterialsVariantsValueJsonHandler:: + readObjectKeyExtensionModelKhrMaterialsVariantsValue( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::ExtensionModelKhrMaterialsVariantsValue& o) { using namespace std::string_literals; - if ("name"s == str) return property("name", this->_name, o.name); + if ("name"s == str) + return property("name", this->_name, o.name); return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } @@ -986,24 +1543,45 @@ CesiumJsonReader::IJsonHandler* ExtensionModelKhrMaterialsVariantsValueJsonHandl namespace CesiumGltfReader { -ExtensionExtStructuralMetadataPropertyAttributeJsonHandler::ExtensionExtStructuralMetadataPropertyAttributeJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _name(), _classProperty(), _properties(context) {} - -void ExtensionExtStructuralMetadataPropertyAttributeJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtStructuralMetadataPropertyAttribute* pObject) { +ExtensionExtStructuralMetadataPropertyAttributeJsonHandler:: + ExtensionExtStructuralMetadataPropertyAttributeJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + _name(), + _classProperty(), + _properties(context) {} + +void ExtensionExtStructuralMetadataPropertyAttributeJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::ExtensionExtStructuralMetadataPropertyAttribute* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataPropertyAttributeJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +ExtensionExtStructuralMetadataPropertyAttributeJsonHandler::readObjectKey( + const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionExtStructuralMetadataPropertyAttribute(CesiumGltf::ExtensionExtStructuralMetadataPropertyAttribute::TypeName, str, *this->_pObject); -} - -CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataPropertyAttributeJsonHandler::readObjectKeyExtensionExtStructuralMetadataPropertyAttribute(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtStructuralMetadataPropertyAttribute& o) { + return this->readObjectKeyExtensionExtStructuralMetadataPropertyAttribute( + CesiumGltf::ExtensionExtStructuralMetadataPropertyAttribute::TypeName, + str, + *this->_pObject); +} + +CesiumJsonReader::IJsonHandler* +ExtensionExtStructuralMetadataPropertyAttributeJsonHandler:: + readObjectKeyExtensionExtStructuralMetadataPropertyAttribute( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::ExtensionExtStructuralMetadataPropertyAttribute& o) { using namespace std::string_literals; - if ("name"s == str) return property("name", this->_name, o.name); - if ("class"s == str) return property("class", this->_classProperty, o.classProperty); - if ("properties"s == str) return property("properties", this->_properties, o.properties); + if ("name"s == str) + return property("name", this->_name, o.name); + if ("class"s == str) + return property("class", this->_classProperty, o.classProperty); + if ("properties"s == str) + return property("properties", this->_properties, o.properties); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -1020,26 +1598,55 @@ CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataPropertyAttributeJ namespace CesiumGltfReader { -ExtensionExtStructuralMetadataPropertyAttributePropertyJsonHandler::ExtensionExtStructuralMetadataPropertyAttributePropertyJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _attribute(), _offset(), _scale(), _max(), _min() {} - -void ExtensionExtStructuralMetadataPropertyAttributePropertyJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtStructuralMetadataPropertyAttributeProperty* pObject) { +ExtensionExtStructuralMetadataPropertyAttributePropertyJsonHandler:: + ExtensionExtStructuralMetadataPropertyAttributePropertyJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + _attribute(), + _offset(), + _scale(), + _max(), + _min() {} + +void ExtensionExtStructuralMetadataPropertyAttributePropertyJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::ExtensionExtStructuralMetadataPropertyAttributeProperty* + pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataPropertyAttributePropertyJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +ExtensionExtStructuralMetadataPropertyAttributePropertyJsonHandler:: + readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionExtStructuralMetadataPropertyAttributeProperty(CesiumGltf::ExtensionExtStructuralMetadataPropertyAttributeProperty::TypeName, str, *this->_pObject); -} - -CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataPropertyAttributePropertyJsonHandler::readObjectKeyExtensionExtStructuralMetadataPropertyAttributeProperty(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtStructuralMetadataPropertyAttributeProperty& o) { + return this + ->readObjectKeyExtensionExtStructuralMetadataPropertyAttributeProperty( + CesiumGltf::ExtensionExtStructuralMetadataPropertyAttributeProperty:: + TypeName, + str, + *this->_pObject); +} + +CesiumJsonReader::IJsonHandler* +ExtensionExtStructuralMetadataPropertyAttributePropertyJsonHandler:: + readObjectKeyExtensionExtStructuralMetadataPropertyAttributeProperty( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::ExtensionExtStructuralMetadataPropertyAttributeProperty& + o) { using namespace std::string_literals; - if ("attribute"s == str) return property("attribute", this->_attribute, o.attribute); - if ("offset"s == str) return property("offset", this->_offset, o.offset); - if ("scale"s == str) return property("scale", this->_scale, o.scale); - if ("max"s == str) return property("max", this->_max, o.max); - if ("min"s == str) return property("min", this->_min, o.min); + if ("attribute"s == str) + return property("attribute", this->_attribute, o.attribute); + if ("offset"s == str) + return property("offset", this->_offset, o.offset); + if ("scale"s == str) + return property("scale", this->_scale, o.scale); + if ("max"s == str) + return property("max", this->_max, o.max); + if ("min"s == str) + return property("min", this->_min, o.min); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -1056,24 +1663,45 @@ CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataPropertyAttributeP namespace CesiumGltfReader { -ExtensionExtStructuralMetadataPropertyTextureJsonHandler::ExtensionExtStructuralMetadataPropertyTextureJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _name(), _classProperty(), _properties(context) {} - -void ExtensionExtStructuralMetadataPropertyTextureJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtStructuralMetadataPropertyTexture* pObject) { +ExtensionExtStructuralMetadataPropertyTextureJsonHandler:: + ExtensionExtStructuralMetadataPropertyTextureJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + _name(), + _classProperty(), + _properties(context) {} + +void ExtensionExtStructuralMetadataPropertyTextureJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::ExtensionExtStructuralMetadataPropertyTexture* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataPropertyTextureJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +ExtensionExtStructuralMetadataPropertyTextureJsonHandler::readObjectKey( + const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionExtStructuralMetadataPropertyTexture(CesiumGltf::ExtensionExtStructuralMetadataPropertyTexture::TypeName, str, *this->_pObject); -} - -CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataPropertyTextureJsonHandler::readObjectKeyExtensionExtStructuralMetadataPropertyTexture(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtStructuralMetadataPropertyTexture& o) { + return this->readObjectKeyExtensionExtStructuralMetadataPropertyTexture( + CesiumGltf::ExtensionExtStructuralMetadataPropertyTexture::TypeName, + str, + *this->_pObject); +} + +CesiumJsonReader::IJsonHandler* +ExtensionExtStructuralMetadataPropertyTextureJsonHandler:: + readObjectKeyExtensionExtStructuralMetadataPropertyTexture( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::ExtensionExtStructuralMetadataPropertyTexture& o) { using namespace std::string_literals; - if ("name"s == str) return property("name", this->_name, o.name); - if ("class"s == str) return property("class", this->_classProperty, o.classProperty); - if ("properties"s == str) return property("properties", this->_properties, o.properties); + if ("name"s == str) + return property("name", this->_name, o.name); + if ("class"s == str) + return property("class", this->_classProperty, o.classProperty); + if ("properties"s == str) + return property("properties", this->_properties, o.properties); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -1090,26 +1718,54 @@ CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataPropertyTextureJso namespace CesiumGltfReader { -ExtensionExtStructuralMetadataPropertyTexturePropertyJsonHandler::ExtensionExtStructuralMetadataPropertyTexturePropertyJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : TextureInfoJsonHandler(context), _channels(), _offset(), _scale(), _max(), _min() {} - -void ExtensionExtStructuralMetadataPropertyTexturePropertyJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtStructuralMetadataPropertyTextureProperty* pObject) { +ExtensionExtStructuralMetadataPropertyTexturePropertyJsonHandler:: + ExtensionExtStructuralMetadataPropertyTexturePropertyJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : TextureInfoJsonHandler(context), + _channels(), + _offset(), + _scale(), + _max(), + _min() {} + +void ExtensionExtStructuralMetadataPropertyTexturePropertyJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::ExtensionExtStructuralMetadataPropertyTextureProperty* + pObject) { TextureInfoJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataPropertyTexturePropertyJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +ExtensionExtStructuralMetadataPropertyTexturePropertyJsonHandler::readObjectKey( + const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionExtStructuralMetadataPropertyTextureProperty(CesiumGltf::ExtensionExtStructuralMetadataPropertyTextureProperty::TypeName, str, *this->_pObject); -} - -CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataPropertyTexturePropertyJsonHandler::readObjectKeyExtensionExtStructuralMetadataPropertyTextureProperty(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtStructuralMetadataPropertyTextureProperty& o) { + return this + ->readObjectKeyExtensionExtStructuralMetadataPropertyTextureProperty( + CesiumGltf::ExtensionExtStructuralMetadataPropertyTextureProperty:: + TypeName, + str, + *this->_pObject); +} + +CesiumJsonReader::IJsonHandler* +ExtensionExtStructuralMetadataPropertyTexturePropertyJsonHandler:: + readObjectKeyExtensionExtStructuralMetadataPropertyTextureProperty( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::ExtensionExtStructuralMetadataPropertyTextureProperty& o) { using namespace std::string_literals; - if ("channels"s == str) return property("channels", this->_channels, o.channels); - if ("offset"s == str) return property("offset", this->_offset, o.offset); - if ("scale"s == str) return property("scale", this->_scale, o.scale); - if ("max"s == str) return property("max", this->_max, o.max); - if ("min"s == str) return property("min", this->_min, o.min); + if ("channels"s == str) + return property("channels", this->_channels, o.channels); + if ("offset"s == str) + return property("offset", this->_offset, o.offset); + if ("scale"s == str) + return property("scale", this->_scale, o.scale); + if ("max"s == str) + return property("max", this->_max, o.max); + if ("min"s == str) + return property("min", this->_min, o.min); return this->readObjectKeyTextureInfo(objectType, str, *this->_pObject); } @@ -1126,23 +1782,39 @@ CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataPropertyTexturePro namespace CesiumGltfReader { -TextureInfoJsonHandler::TextureInfoJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _index(), _texCoord() {} +TextureInfoJsonHandler::TextureInfoJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + _index(), + _texCoord() {} -void TextureInfoJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::TextureInfo* pObject) { +void TextureInfoJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::TextureInfo* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* TextureInfoJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +TextureInfoJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyTextureInfo(CesiumGltf::TextureInfo::TypeName, str, *this->_pObject); + return this->readObjectKeyTextureInfo( + CesiumGltf::TextureInfo::TypeName, + str, + *this->_pObject); } -CesiumJsonReader::IJsonHandler* TextureInfoJsonHandler::readObjectKeyTextureInfo(const std::string& objectType, const std::string_view& str, CesiumGltf::TextureInfo& o) { +CesiumJsonReader::IJsonHandler* +TextureInfoJsonHandler::readObjectKeyTextureInfo( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::TextureInfo& o) { using namespace std::string_literals; - if ("index"s == str) return property("index", this->_index, o.index); - if ("texCoord"s == str) return property("texCoord", this->_texCoord, o.texCoord); + if ("index"s == str) + return property("index", this->_index, o.index); + if ("texCoord"s == str) + return property("texCoord", this->_texCoord, o.texCoord); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -1159,25 +1831,48 @@ CesiumJsonReader::IJsonHandler* TextureInfoJsonHandler::readObjectKeyTextureInfo namespace CesiumGltfReader { -ExtensionExtStructuralMetadataPropertyTableJsonHandler::ExtensionExtStructuralMetadataPropertyTableJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _name(), _classProperty(), _count(), _properties(context) {} - -void ExtensionExtStructuralMetadataPropertyTableJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtStructuralMetadataPropertyTable* pObject) { +ExtensionExtStructuralMetadataPropertyTableJsonHandler:: + ExtensionExtStructuralMetadataPropertyTableJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + _name(), + _classProperty(), + _count(), + _properties(context) {} + +void ExtensionExtStructuralMetadataPropertyTableJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::ExtensionExtStructuralMetadataPropertyTable* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataPropertyTableJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +ExtensionExtStructuralMetadataPropertyTableJsonHandler::readObjectKey( + const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionExtStructuralMetadataPropertyTable(CesiumGltf::ExtensionExtStructuralMetadataPropertyTable::TypeName, str, *this->_pObject); -} - -CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataPropertyTableJsonHandler::readObjectKeyExtensionExtStructuralMetadataPropertyTable(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtStructuralMetadataPropertyTable& o) { + return this->readObjectKeyExtensionExtStructuralMetadataPropertyTable( + CesiumGltf::ExtensionExtStructuralMetadataPropertyTable::TypeName, + str, + *this->_pObject); +} + +CesiumJsonReader::IJsonHandler* +ExtensionExtStructuralMetadataPropertyTableJsonHandler:: + readObjectKeyExtensionExtStructuralMetadataPropertyTable( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::ExtensionExtStructuralMetadataPropertyTable& o) { using namespace std::string_literals; - if ("name"s == str) return property("name", this->_name, o.name); - if ("class"s == str) return property("class", this->_classProperty, o.classProperty); - if ("count"s == str) return property("count", this->_count, o.count); - if ("properties"s == str) return property("properties", this->_properties, o.properties); + if ("name"s == str) + return property("name", this->_name, o.name); + if ("class"s == str) + return property("class", this->_classProperty, o.classProperty); + if ("count"s == str) + return property("count", this->_count, o.count); + if ("properties"s == str) + return property("properties", this->_properties, o.properties); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -1194,30 +1889,69 @@ CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataPropertyTableJsonH namespace CesiumGltfReader { -ExtensionExtStructuralMetadataPropertyTablePropertyJsonHandler::ExtensionExtStructuralMetadataPropertyTablePropertyJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _values(), _arrayOffsets(), _stringOffsets(), _arrayOffsetType(), _stringOffsetType(), _offset(), _scale(), _max(), _min() {} - -void ExtensionExtStructuralMetadataPropertyTablePropertyJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtStructuralMetadataPropertyTableProperty* pObject) { +ExtensionExtStructuralMetadataPropertyTablePropertyJsonHandler:: + ExtensionExtStructuralMetadataPropertyTablePropertyJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + _values(), + _arrayOffsets(), + _stringOffsets(), + _arrayOffsetType(), + _stringOffsetType(), + _offset(), + _scale(), + _max(), + _min() {} + +void ExtensionExtStructuralMetadataPropertyTablePropertyJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::ExtensionExtStructuralMetadataPropertyTableProperty* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataPropertyTablePropertyJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +ExtensionExtStructuralMetadataPropertyTablePropertyJsonHandler::readObjectKey( + const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionExtStructuralMetadataPropertyTableProperty(CesiumGltf::ExtensionExtStructuralMetadataPropertyTableProperty::TypeName, str, *this->_pObject); -} - -CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataPropertyTablePropertyJsonHandler::readObjectKeyExtensionExtStructuralMetadataPropertyTableProperty(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtStructuralMetadataPropertyTableProperty& o) { + return this->readObjectKeyExtensionExtStructuralMetadataPropertyTableProperty( + CesiumGltf::ExtensionExtStructuralMetadataPropertyTableProperty::TypeName, + str, + *this->_pObject); +} + +CesiumJsonReader::IJsonHandler* +ExtensionExtStructuralMetadataPropertyTablePropertyJsonHandler:: + readObjectKeyExtensionExtStructuralMetadataPropertyTableProperty( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::ExtensionExtStructuralMetadataPropertyTableProperty& o) { using namespace std::string_literals; - if ("values"s == str) return property("values", this->_values, o.values); - if ("arrayOffsets"s == str) return property("arrayOffsets", this->_arrayOffsets, o.arrayOffsets); - if ("stringOffsets"s == str) return property("stringOffsets", this->_stringOffsets, o.stringOffsets); - if ("arrayOffsetType"s == str) return property("arrayOffsetType", this->_arrayOffsetType, o.arrayOffsetType); - if ("stringOffsetType"s == str) return property("stringOffsetType", this->_stringOffsetType, o.stringOffsetType); - if ("offset"s == str) return property("offset", this->_offset, o.offset); - if ("scale"s == str) return property("scale", this->_scale, o.scale); - if ("max"s == str) return property("max", this->_max, o.max); - if ("min"s == str) return property("min", this->_min, o.min); + if ("values"s == str) + return property("values", this->_values, o.values); + if ("arrayOffsets"s == str) + return property("arrayOffsets", this->_arrayOffsets, o.arrayOffsets); + if ("stringOffsets"s == str) + return property("stringOffsets", this->_stringOffsets, o.stringOffsets); + if ("arrayOffsetType"s == str) + return property( + "arrayOffsetType", + this->_arrayOffsetType, + o.arrayOffsetType); + if ("stringOffsetType"s == str) + return property( + "stringOffsetType", + this->_stringOffsetType, + o.stringOffsetType); + if ("offset"s == str) + return property("offset", this->_offset, o.offset); + if ("scale"s == str) + return property("scale", this->_scale, o.scale); + if ("max"s == str) + return property("max", this->_max, o.max); + if ("min"s == str) + return property("min", this->_min, o.min); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -1234,27 +1968,54 @@ CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataPropertyTablePrope namespace CesiumGltfReader { -ExtensionExtStructuralMetadataSchemaJsonHandler::ExtensionExtStructuralMetadataSchemaJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _id(), _name(), _description(), _version(), _classes(context), _enums(context) {} - -void ExtensionExtStructuralMetadataSchemaJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtStructuralMetadataSchema* pObject) { +ExtensionExtStructuralMetadataSchemaJsonHandler:: + ExtensionExtStructuralMetadataSchemaJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + _id(), + _name(), + _description(), + _version(), + _classes(context), + _enums(context) {} + +void ExtensionExtStructuralMetadataSchemaJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::ExtensionExtStructuralMetadataSchema* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataSchemaJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +ExtensionExtStructuralMetadataSchemaJsonHandler::readObjectKey( + const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionExtStructuralMetadataSchema(CesiumGltf::ExtensionExtStructuralMetadataSchema::TypeName, str, *this->_pObject); -} - -CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataSchemaJsonHandler::readObjectKeyExtensionExtStructuralMetadataSchema(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtStructuralMetadataSchema& o) { + return this->readObjectKeyExtensionExtStructuralMetadataSchema( + CesiumGltf::ExtensionExtStructuralMetadataSchema::TypeName, + str, + *this->_pObject); +} + +CesiumJsonReader::IJsonHandler* +ExtensionExtStructuralMetadataSchemaJsonHandler:: + readObjectKeyExtensionExtStructuralMetadataSchema( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::ExtensionExtStructuralMetadataSchema& o) { using namespace std::string_literals; - if ("id"s == str) return property("id", this->_id, o.id); - if ("name"s == str) return property("name", this->_name, o.name); - if ("description"s == str) return property("description", this->_description, o.description); - if ("version"s == str) return property("version", this->_version, o.version); - if ("classes"s == str) return property("classes", this->_classes, o.classes); - if ("enums"s == str) return property("enums", this->_enums, o.enums); + if ("id"s == str) + return property("id", this->_id, o.id); + if ("name"s == str) + return property("name", this->_name, o.name); + if ("description"s == str) + return property("description", this->_description, o.description); + if ("version"s == str) + return property("version", this->_version, o.version); + if ("classes"s == str) + return property("classes", this->_classes, o.classes); + if ("enums"s == str) + return property("enums", this->_enums, o.enums); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -1271,25 +2032,47 @@ CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataSchemaJsonHandler: namespace CesiumGltfReader { -ExtensionExtStructuralMetadataEnumJsonHandler::ExtensionExtStructuralMetadataEnumJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _name(), _description(), _valueType(), _values(context) {} - -void ExtensionExtStructuralMetadataEnumJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtStructuralMetadataEnum* pObject) { +ExtensionExtStructuralMetadataEnumJsonHandler:: + ExtensionExtStructuralMetadataEnumJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + _name(), + _description(), + _valueType(), + _values(context) {} + +void ExtensionExtStructuralMetadataEnumJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::ExtensionExtStructuralMetadataEnum* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataEnumJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +ExtensionExtStructuralMetadataEnumJsonHandler::readObjectKey( + const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionExtStructuralMetadataEnum(CesiumGltf::ExtensionExtStructuralMetadataEnum::TypeName, str, *this->_pObject); + return this->readObjectKeyExtensionExtStructuralMetadataEnum( + CesiumGltf::ExtensionExtStructuralMetadataEnum::TypeName, + str, + *this->_pObject); } -CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataEnumJsonHandler::readObjectKeyExtensionExtStructuralMetadataEnum(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtStructuralMetadataEnum& o) { +CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataEnumJsonHandler:: + readObjectKeyExtensionExtStructuralMetadataEnum( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::ExtensionExtStructuralMetadataEnum& o) { using namespace std::string_literals; - if ("name"s == str) return property("name", this->_name, o.name); - if ("description"s == str) return property("description", this->_description, o.description); - if ("valueType"s == str) return property("valueType", this->_valueType, o.valueType); - if ("values"s == str) return property("values", this->_values, o.values); + if ("name"s == str) + return property("name", this->_name, o.name); + if ("description"s == str) + return property("description", this->_description, o.description); + if ("valueType"s == str) + return property("valueType", this->_valueType, o.valueType); + if ("values"s == str) + return property("values", this->_values, o.values); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -1306,24 +2089,45 @@ CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataEnumJsonHandler::r namespace CesiumGltfReader { -ExtensionExtStructuralMetadataEnumValueJsonHandler::ExtensionExtStructuralMetadataEnumValueJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _name(), _description(), _value() {} - -void ExtensionExtStructuralMetadataEnumValueJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtStructuralMetadataEnumValue* pObject) { +ExtensionExtStructuralMetadataEnumValueJsonHandler:: + ExtensionExtStructuralMetadataEnumValueJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + _name(), + _description(), + _value() {} + +void ExtensionExtStructuralMetadataEnumValueJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::ExtensionExtStructuralMetadataEnumValue* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataEnumValueJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +ExtensionExtStructuralMetadataEnumValueJsonHandler::readObjectKey( + const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionExtStructuralMetadataEnumValue(CesiumGltf::ExtensionExtStructuralMetadataEnumValue::TypeName, str, *this->_pObject); -} - -CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataEnumValueJsonHandler::readObjectKeyExtensionExtStructuralMetadataEnumValue(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtStructuralMetadataEnumValue& o) { + return this->readObjectKeyExtensionExtStructuralMetadataEnumValue( + CesiumGltf::ExtensionExtStructuralMetadataEnumValue::TypeName, + str, + *this->_pObject); +} + +CesiumJsonReader::IJsonHandler* +ExtensionExtStructuralMetadataEnumValueJsonHandler:: + readObjectKeyExtensionExtStructuralMetadataEnumValue( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::ExtensionExtStructuralMetadataEnumValue& o) { using namespace std::string_literals; - if ("name"s == str) return property("name", this->_name, o.name); - if ("description"s == str) return property("description", this->_description, o.description); - if ("value"s == str) return property("value", this->_value, o.value); + if ("name"s == str) + return property("name", this->_name, o.name); + if ("description"s == str) + return property("description", this->_description, o.description); + if ("value"s == str) + return property("value", this->_value, o.value); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -1340,24 +2144,44 @@ CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataEnumValueJsonHandl namespace CesiumGltfReader { -ExtensionExtStructuralMetadataClassJsonHandler::ExtensionExtStructuralMetadataClassJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _name(), _description(), _properties(context) {} - -void ExtensionExtStructuralMetadataClassJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtStructuralMetadataClass* pObject) { +ExtensionExtStructuralMetadataClassJsonHandler:: + ExtensionExtStructuralMetadataClassJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + _name(), + _description(), + _properties(context) {} + +void ExtensionExtStructuralMetadataClassJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::ExtensionExtStructuralMetadataClass* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataClassJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +ExtensionExtStructuralMetadataClassJsonHandler::readObjectKey( + const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionExtStructuralMetadataClass(CesiumGltf::ExtensionExtStructuralMetadataClass::TypeName, str, *this->_pObject); + return this->readObjectKeyExtensionExtStructuralMetadataClass( + CesiumGltf::ExtensionExtStructuralMetadataClass::TypeName, + str, + *this->_pObject); } -CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataClassJsonHandler::readObjectKeyExtensionExtStructuralMetadataClass(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtStructuralMetadataClass& o) { +CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataClassJsonHandler:: + readObjectKeyExtensionExtStructuralMetadataClass( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::ExtensionExtStructuralMetadataClass& o) { using namespace std::string_literals; - if ("name"s == str) return property("name", this->_name, o.name); - if ("description"s == str) return property("description", this->_description, o.description); - if ("properties"s == str) return property("properties", this->_properties, o.properties); + if ("name"s == str) + return property("name", this->_name, o.name); + if ("description"s == str) + return property("description", this->_description, o.description); + if ("properties"s == str) + return property("properties", this->_properties, o.properties); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -1374,37 +2198,84 @@ CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataClassJsonHandler:: namespace CesiumGltfReader { -ExtensionExtStructuralMetadataClassPropertyJsonHandler::ExtensionExtStructuralMetadataClassPropertyJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _name(), _description(), _type(), _componentType(), _enumType(), _array(), _count(), _normalized(), _offset(), _scale(), _max(), _min(), _required(), _noData(), _defaultProperty(), _semantic() {} - -void ExtensionExtStructuralMetadataClassPropertyJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtStructuralMetadataClassProperty* pObject) { +ExtensionExtStructuralMetadataClassPropertyJsonHandler:: + ExtensionExtStructuralMetadataClassPropertyJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + _name(), + _description(), + _type(), + _componentType(), + _enumType(), + _array(), + _count(), + _normalized(), + _offset(), + _scale(), + _max(), + _min(), + _required(), + _noData(), + _defaultProperty(), + _semantic() {} + +void ExtensionExtStructuralMetadataClassPropertyJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::ExtensionExtStructuralMetadataClassProperty* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataClassPropertyJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +ExtensionExtStructuralMetadataClassPropertyJsonHandler::readObjectKey( + const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionExtStructuralMetadataClassProperty(CesiumGltf::ExtensionExtStructuralMetadataClassProperty::TypeName, str, *this->_pObject); -} - -CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataClassPropertyJsonHandler::readObjectKeyExtensionExtStructuralMetadataClassProperty(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtStructuralMetadataClassProperty& o) { + return this->readObjectKeyExtensionExtStructuralMetadataClassProperty( + CesiumGltf::ExtensionExtStructuralMetadataClassProperty::TypeName, + str, + *this->_pObject); +} + +CesiumJsonReader::IJsonHandler* +ExtensionExtStructuralMetadataClassPropertyJsonHandler:: + readObjectKeyExtensionExtStructuralMetadataClassProperty( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::ExtensionExtStructuralMetadataClassProperty& o) { using namespace std::string_literals; - if ("name"s == str) return property("name", this->_name, o.name); - if ("description"s == str) return property("description", this->_description, o.description); - if ("type"s == str) return property("type", this->_type, o.type); - if ("componentType"s == str) return property("componentType", this->_componentType, o.componentType); - if ("enumType"s == str) return property("enumType", this->_enumType, o.enumType); - if ("array"s == str) return property("array", this->_array, o.array); - if ("count"s == str) return property("count", this->_count, o.count); - if ("normalized"s == str) return property("normalized", this->_normalized, o.normalized); - if ("offset"s == str) return property("offset", this->_offset, o.offset); - if ("scale"s == str) return property("scale", this->_scale, o.scale); - if ("max"s == str) return property("max", this->_max, o.max); - if ("min"s == str) return property("min", this->_min, o.min); - if ("required"s == str) return property("required", this->_required, o.required); - if ("noData"s == str) return property("noData", this->_noData, o.noData); - if ("default"s == str) return property("default", this->_defaultProperty, o.defaultProperty); - if ("semantic"s == str) return property("semantic", this->_semantic, o.semantic); + if ("name"s == str) + return property("name", this->_name, o.name); + if ("description"s == str) + return property("description", this->_description, o.description); + if ("type"s == str) + return property("type", this->_type, o.type); + if ("componentType"s == str) + return property("componentType", this->_componentType, o.componentType); + if ("enumType"s == str) + return property("enumType", this->_enumType, o.enumType); + if ("array"s == str) + return property("array", this->_array, o.array); + if ("count"s == str) + return property("count", this->_count, o.count); + if ("normalized"s == str) + return property("normalized", this->_normalized, o.normalized); + if ("offset"s == str) + return property("offset", this->_offset, o.offset); + if ("scale"s == str) + return property("scale", this->_scale, o.scale); + if ("max"s == str) + return property("max", this->_max, o.max); + if ("min"s == str) + return property("min", this->_min, o.min); + if ("required"s == str) + return property("required", this->_required, o.required); + if ("noData"s == str) + return property("noData", this->_noData, o.noData); + if ("default"s == str) + return property("default", this->_defaultProperty, o.defaultProperty); + if ("semantic"s == str) + return property("semantic", this->_semantic, o.semantic); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -1421,27 +2292,50 @@ CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataClassPropertyJsonH namespace CesiumGltfReader { -FeatureIdJsonHandler::FeatureIdJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _featureCount(), _nullFeatureId(), _label(), _attribute(), _texture(context), _propertyTable() {} - -void FeatureIdJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::FeatureId* pObject) { +FeatureIdJsonHandler::FeatureIdJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + _featureCount(), + _nullFeatureId(), + _label(), + _attribute(), + _texture(context), + _propertyTable() {} + +void FeatureIdJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::FeatureId* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* FeatureIdJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +FeatureIdJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyFeatureId(CesiumGltf::FeatureId::TypeName, str, *this->_pObject); + return this->readObjectKeyFeatureId( + CesiumGltf::FeatureId::TypeName, + str, + *this->_pObject); } -CesiumJsonReader::IJsonHandler* FeatureIdJsonHandler::readObjectKeyFeatureId(const std::string& objectType, const std::string_view& str, CesiumGltf::FeatureId& o) { +CesiumJsonReader::IJsonHandler* FeatureIdJsonHandler::readObjectKeyFeatureId( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::FeatureId& o) { using namespace std::string_literals; - if ("featureCount"s == str) return property("featureCount", this->_featureCount, o.featureCount); - if ("nullFeatureId"s == str) return property("nullFeatureId", this->_nullFeatureId, o.nullFeatureId); - if ("label"s == str) return property("label", this->_label, o.label); - if ("attribute"s == str) return property("attribute", this->_attribute, o.attribute); - if ("texture"s == str) return property("texture", this->_texture, o.texture); - if ("propertyTable"s == str) return property("propertyTable", this->_propertyTable, o.propertyTable); + if ("featureCount"s == str) + return property("featureCount", this->_featureCount, o.featureCount); + if ("nullFeatureId"s == str) + return property("nullFeatureId", this->_nullFeatureId, o.nullFeatureId); + if ("label"s == str) + return property("label", this->_label, o.label); + if ("attribute"s == str) + return property("attribute", this->_attribute, o.attribute); + if ("texture"s == str) + return property("texture", this->_texture, o.texture); + if ("propertyTable"s == str) + return property("propertyTable", this->_propertyTable, o.propertyTable); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -1458,22 +2352,35 @@ CesiumJsonReader::IJsonHandler* FeatureIdJsonHandler::readObjectKeyFeatureId(con namespace CesiumGltfReader { -FeatureIdTextureJsonHandler::FeatureIdTextureJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : TextureInfoJsonHandler(context), _channels() {} +FeatureIdTextureJsonHandler::FeatureIdTextureJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : TextureInfoJsonHandler(context), _channels() {} -void FeatureIdTextureJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::FeatureIdTexture* pObject) { +void FeatureIdTextureJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::FeatureIdTexture* pObject) { TextureInfoJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* FeatureIdTextureJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +FeatureIdTextureJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyFeatureIdTexture(CesiumGltf::FeatureIdTexture::TypeName, str, *this->_pObject); + return this->readObjectKeyFeatureIdTexture( + CesiumGltf::FeatureIdTexture::TypeName, + str, + *this->_pObject); } -CesiumJsonReader::IJsonHandler* FeatureIdTextureJsonHandler::readObjectKeyFeatureIdTexture(const std::string& objectType, const std::string_view& str, CesiumGltf::FeatureIdTexture& o) { +CesiumJsonReader::IJsonHandler* +FeatureIdTextureJsonHandler::readObjectKeyFeatureIdTexture( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::FeatureIdTexture& o) { using namespace std::string_literals; - if ("channels"s == str) return property("channels", this->_channels, o.channels); + if ("channels"s == str) + return property("channels", this->_channels, o.channels); return this->readObjectKeyTextureInfo(objectType, str, *this->_pObject); } @@ -1490,26 +2397,51 @@ CesiumJsonReader::IJsonHandler* FeatureIdTextureJsonHandler::readObjectKeyFeatur namespace CesiumGltfReader { -ExtensionExtInstanceFeaturesFeatureIdJsonHandler::ExtensionExtInstanceFeaturesFeatureIdJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _featureCount(), _nullFeatureId(), _label(), _attribute(), _propertyTable() {} - -void ExtensionExtInstanceFeaturesFeatureIdJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtInstanceFeaturesFeatureId* pObject) { +ExtensionExtInstanceFeaturesFeatureIdJsonHandler:: + ExtensionExtInstanceFeaturesFeatureIdJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + _featureCount(), + _nullFeatureId(), + _label(), + _attribute(), + _propertyTable() {} + +void ExtensionExtInstanceFeaturesFeatureIdJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::ExtensionExtInstanceFeaturesFeatureId* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* ExtensionExtInstanceFeaturesFeatureIdJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +ExtensionExtInstanceFeaturesFeatureIdJsonHandler::readObjectKey( + const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionExtInstanceFeaturesFeatureId(CesiumGltf::ExtensionExtInstanceFeaturesFeatureId::TypeName, str, *this->_pObject); -} - -CesiumJsonReader::IJsonHandler* ExtensionExtInstanceFeaturesFeatureIdJsonHandler::readObjectKeyExtensionExtInstanceFeaturesFeatureId(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtInstanceFeaturesFeatureId& o) { + return this->readObjectKeyExtensionExtInstanceFeaturesFeatureId( + CesiumGltf::ExtensionExtInstanceFeaturesFeatureId::TypeName, + str, + *this->_pObject); +} + +CesiumJsonReader::IJsonHandler* +ExtensionExtInstanceFeaturesFeatureIdJsonHandler:: + readObjectKeyExtensionExtInstanceFeaturesFeatureId( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::ExtensionExtInstanceFeaturesFeatureId& o) { using namespace std::string_literals; - if ("featureCount"s == str) return property("featureCount", this->_featureCount, o.featureCount); - if ("nullFeatureId"s == str) return property("nullFeatureId", this->_nullFeatureId, o.nullFeatureId); - if ("label"s == str) return property("label", this->_label, o.label); - if ("attribute"s == str) return property("attribute", this->_attribute, o.attribute); - if ("propertyTable"s == str) return property("propertyTable", this->_propertyTable, o.propertyTable); + if ("featureCount"s == str) + return property("featureCount", this->_featureCount, o.featureCount); + if ("nullFeatureId"s == str) + return property("nullFeatureId", this->_nullFeatureId, o.nullFeatureId); + if ("label"s == str) + return property("label", this->_label, o.label); + if ("attribute"s == str) + return property("attribute", this->_attribute, o.attribute); + if ("propertyTable"s == str) + return property("propertyTable", this->_propertyTable, o.propertyTable); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -1526,23 +2458,42 @@ CesiumJsonReader::IJsonHandler* ExtensionExtInstanceFeaturesFeatureIdJsonHandler namespace CesiumGltfReader { -ExtensionExtFeatureMetadataFeatureIDTextureJsonHandler::ExtensionExtFeatureMetadataFeatureIDTextureJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _featureTable(), _featureIds(context) {} +ExtensionExtFeatureMetadataFeatureIDTextureJsonHandler:: + ExtensionExtFeatureMetadataFeatureIDTextureJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + _featureTable(), + _featureIds(context) {} -void ExtensionExtFeatureMetadataFeatureIDTextureJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtFeatureMetadataFeatureIDTexture* pObject) { +void ExtensionExtFeatureMetadataFeatureIDTextureJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::ExtensionExtFeatureMetadataFeatureIDTexture* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataFeatureIDTextureJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +ExtensionExtFeatureMetadataFeatureIDTextureJsonHandler::readObjectKey( + const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionExtFeatureMetadataFeatureIDTexture(CesiumGltf::ExtensionExtFeatureMetadataFeatureIDTexture::TypeName, str, *this->_pObject); -} - -CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataFeatureIDTextureJsonHandler::readObjectKeyExtensionExtFeatureMetadataFeatureIDTexture(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtFeatureMetadataFeatureIDTexture& o) { + return this->readObjectKeyExtensionExtFeatureMetadataFeatureIDTexture( + CesiumGltf::ExtensionExtFeatureMetadataFeatureIDTexture::TypeName, + str, + *this->_pObject); +} + +CesiumJsonReader::IJsonHandler* +ExtensionExtFeatureMetadataFeatureIDTextureJsonHandler:: + readObjectKeyExtensionExtFeatureMetadataFeatureIDTexture( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::ExtensionExtFeatureMetadataFeatureIDTexture& o) { using namespace std::string_literals; - if ("featureTable"s == str) return property("featureTable", this->_featureTable, o.featureTable); - if ("featureIds"s == str) return property("featureIds", this->_featureIds, o.featureIds); + if ("featureTable"s == str) + return property("featureTable", this->_featureTable, o.featureTable); + if ("featureIds"s == str) + return property("featureIds", this->_featureIds, o.featureIds); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -1559,23 +2510,42 @@ CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataFeatureIDTextureJsonH namespace CesiumGltfReader { -ExtensionExtFeatureMetadataTextureAccessorJsonHandler::ExtensionExtFeatureMetadataTextureAccessorJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _channels(), _texture(context) {} +ExtensionExtFeatureMetadataTextureAccessorJsonHandler:: + ExtensionExtFeatureMetadataTextureAccessorJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + _channels(), + _texture(context) {} -void ExtensionExtFeatureMetadataTextureAccessorJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtFeatureMetadataTextureAccessor* pObject) { +void ExtensionExtFeatureMetadataTextureAccessorJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::ExtensionExtFeatureMetadataTextureAccessor* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataTextureAccessorJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +ExtensionExtFeatureMetadataTextureAccessorJsonHandler::readObjectKey( + const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionExtFeatureMetadataTextureAccessor(CesiumGltf::ExtensionExtFeatureMetadataTextureAccessor::TypeName, str, *this->_pObject); -} - -CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataTextureAccessorJsonHandler::readObjectKeyExtensionExtFeatureMetadataTextureAccessor(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtFeatureMetadataTextureAccessor& o) { + return this->readObjectKeyExtensionExtFeatureMetadataTextureAccessor( + CesiumGltf::ExtensionExtFeatureMetadataTextureAccessor::TypeName, + str, + *this->_pObject); +} + +CesiumJsonReader::IJsonHandler* +ExtensionExtFeatureMetadataTextureAccessorJsonHandler:: + readObjectKeyExtensionExtFeatureMetadataTextureAccessor( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::ExtensionExtFeatureMetadataTextureAccessor& o) { using namespace std::string_literals; - if ("channels"s == str) return property("channels", this->_channels, o.channels); - if ("texture"s == str) return property("texture", this->_texture, o.texture); + if ("channels"s == str) + return property("channels", this->_channels, o.channels); + if ("texture"s == str) + return property("texture", this->_texture, o.texture); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -1592,23 +2562,42 @@ CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataTextureAccessorJsonHa namespace CesiumGltfReader { -ExtensionExtFeatureMetadataFeatureIDAttributeJsonHandler::ExtensionExtFeatureMetadataFeatureIDAttributeJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _featureTable(), _featureIds(context) {} +ExtensionExtFeatureMetadataFeatureIDAttributeJsonHandler:: + ExtensionExtFeatureMetadataFeatureIDAttributeJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + _featureTable(), + _featureIds(context) {} -void ExtensionExtFeatureMetadataFeatureIDAttributeJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtFeatureMetadataFeatureIDAttribute* pObject) { +void ExtensionExtFeatureMetadataFeatureIDAttributeJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::ExtensionExtFeatureMetadataFeatureIDAttribute* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataFeatureIDAttributeJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +ExtensionExtFeatureMetadataFeatureIDAttributeJsonHandler::readObjectKey( + const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionExtFeatureMetadataFeatureIDAttribute(CesiumGltf::ExtensionExtFeatureMetadataFeatureIDAttribute::TypeName, str, *this->_pObject); -} - -CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataFeatureIDAttributeJsonHandler::readObjectKeyExtensionExtFeatureMetadataFeatureIDAttribute(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtFeatureMetadataFeatureIDAttribute& o) { + return this->readObjectKeyExtensionExtFeatureMetadataFeatureIDAttribute( + CesiumGltf::ExtensionExtFeatureMetadataFeatureIDAttribute::TypeName, + str, + *this->_pObject); +} + +CesiumJsonReader::IJsonHandler* +ExtensionExtFeatureMetadataFeatureIDAttributeJsonHandler:: + readObjectKeyExtensionExtFeatureMetadataFeatureIDAttribute( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::ExtensionExtFeatureMetadataFeatureIDAttribute& o) { using namespace std::string_literals; - if ("featureTable"s == str) return property("featureTable", this->_featureTable, o.featureTable); - if ("featureIds"s == str) return property("featureIds", this->_featureIds, o.featureIds); + if ("featureTable"s == str) + return property("featureTable", this->_featureTable, o.featureTable); + if ("featureIds"s == str) + return property("featureIds", this->_featureIds, o.featureIds); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -1625,24 +2614,45 @@ CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataFeatureIDAttributeJso namespace CesiumGltfReader { -ExtensionExtFeatureMetadataFeatureIDsJsonHandler::ExtensionExtFeatureMetadataFeatureIDsJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _attribute(), _constant(), _divisor() {} - -void ExtensionExtFeatureMetadataFeatureIDsJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtFeatureMetadataFeatureIDs* pObject) { +ExtensionExtFeatureMetadataFeatureIDsJsonHandler:: + ExtensionExtFeatureMetadataFeatureIDsJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + _attribute(), + _constant(), + _divisor() {} + +void ExtensionExtFeatureMetadataFeatureIDsJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::ExtensionExtFeatureMetadataFeatureIDs* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataFeatureIDsJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +ExtensionExtFeatureMetadataFeatureIDsJsonHandler::readObjectKey( + const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionExtFeatureMetadataFeatureIDs(CesiumGltf::ExtensionExtFeatureMetadataFeatureIDs::TypeName, str, *this->_pObject); -} - -CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataFeatureIDsJsonHandler::readObjectKeyExtensionExtFeatureMetadataFeatureIDs(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtFeatureMetadataFeatureIDs& o) { + return this->readObjectKeyExtensionExtFeatureMetadataFeatureIDs( + CesiumGltf::ExtensionExtFeatureMetadataFeatureIDs::TypeName, + str, + *this->_pObject); +} + +CesiumJsonReader::IJsonHandler* +ExtensionExtFeatureMetadataFeatureIDsJsonHandler:: + readObjectKeyExtensionExtFeatureMetadataFeatureIDs( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::ExtensionExtFeatureMetadataFeatureIDs& o) { using namespace std::string_literals; - if ("attribute"s == str) return property("attribute", this->_attribute, o.attribute); - if ("constant"s == str) return property("constant", this->_constant, o.constant); - if ("divisor"s == str) return property("divisor", this->_divisor, o.divisor); + if ("attribute"s == str) + return property("attribute", this->_attribute, o.attribute); + if ("constant"s == str) + return property("constant", this->_constant, o.constant); + if ("divisor"s == str) + return property("divisor", this->_divisor, o.divisor); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -1659,23 +2669,42 @@ CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataFeatureIDsJsonHandler namespace CesiumGltfReader { -ExtensionExtFeatureMetadataFeatureTextureJsonHandler::ExtensionExtFeatureMetadataFeatureTextureJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _classProperty(), _properties(context) {} +ExtensionExtFeatureMetadataFeatureTextureJsonHandler:: + ExtensionExtFeatureMetadataFeatureTextureJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + _classProperty(), + _properties(context) {} -void ExtensionExtFeatureMetadataFeatureTextureJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtFeatureMetadataFeatureTexture* pObject) { +void ExtensionExtFeatureMetadataFeatureTextureJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::ExtensionExtFeatureMetadataFeatureTexture* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataFeatureTextureJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +ExtensionExtFeatureMetadataFeatureTextureJsonHandler::readObjectKey( + const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionExtFeatureMetadataFeatureTexture(CesiumGltf::ExtensionExtFeatureMetadataFeatureTexture::TypeName, str, *this->_pObject); -} - -CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataFeatureTextureJsonHandler::readObjectKeyExtensionExtFeatureMetadataFeatureTexture(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtFeatureMetadataFeatureTexture& o) { + return this->readObjectKeyExtensionExtFeatureMetadataFeatureTexture( + CesiumGltf::ExtensionExtFeatureMetadataFeatureTexture::TypeName, + str, + *this->_pObject); +} + +CesiumJsonReader::IJsonHandler* +ExtensionExtFeatureMetadataFeatureTextureJsonHandler:: + readObjectKeyExtensionExtFeatureMetadataFeatureTexture( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::ExtensionExtFeatureMetadataFeatureTexture& o) { using namespace std::string_literals; - if ("class"s == str) return property("class", this->_classProperty, o.classProperty); - if ("properties"s == str) return property("properties", this->_properties, o.properties); + if ("class"s == str) + return property("class", this->_classProperty, o.classProperty); + if ("properties"s == str) + return property("properties", this->_properties, o.properties); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -1692,24 +2721,45 @@ CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataFeatureTextureJsonHan namespace CesiumGltfReader { -ExtensionExtFeatureMetadataFeatureTableJsonHandler::ExtensionExtFeatureMetadataFeatureTableJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _classProperty(), _count(), _properties(context) {} - -void ExtensionExtFeatureMetadataFeatureTableJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtFeatureMetadataFeatureTable* pObject) { +ExtensionExtFeatureMetadataFeatureTableJsonHandler:: + ExtensionExtFeatureMetadataFeatureTableJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + _classProperty(), + _count(), + _properties(context) {} + +void ExtensionExtFeatureMetadataFeatureTableJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::ExtensionExtFeatureMetadataFeatureTable* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataFeatureTableJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +ExtensionExtFeatureMetadataFeatureTableJsonHandler::readObjectKey( + const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionExtFeatureMetadataFeatureTable(CesiumGltf::ExtensionExtFeatureMetadataFeatureTable::TypeName, str, *this->_pObject); -} - -CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataFeatureTableJsonHandler::readObjectKeyExtensionExtFeatureMetadataFeatureTable(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtFeatureMetadataFeatureTable& o) { + return this->readObjectKeyExtensionExtFeatureMetadataFeatureTable( + CesiumGltf::ExtensionExtFeatureMetadataFeatureTable::TypeName, + str, + *this->_pObject); +} + +CesiumJsonReader::IJsonHandler* +ExtensionExtFeatureMetadataFeatureTableJsonHandler:: + readObjectKeyExtensionExtFeatureMetadataFeatureTable( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::ExtensionExtFeatureMetadataFeatureTable& o) { using namespace std::string_literals; - if ("class"s == str) return property("class", this->_classProperty, o.classProperty); - if ("count"s == str) return property("count", this->_count, o.count); - if ("properties"s == str) return property("properties", this->_properties, o.properties); + if ("class"s == str) + return property("class", this->_classProperty, o.classProperty); + if ("count"s == str) + return property("count", this->_count, o.count); + if ("properties"s == str) + return property("properties", this->_properties, o.properties); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -1726,25 +2776,54 @@ CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataFeatureTableJsonHandl namespace CesiumGltfReader { -ExtensionExtFeatureMetadataFeatureTablePropertyJsonHandler::ExtensionExtFeatureMetadataFeatureTablePropertyJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _bufferView(), _offsetType(), _arrayOffsetBufferView(), _stringOffsetBufferView() {} - -void ExtensionExtFeatureMetadataFeatureTablePropertyJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtFeatureMetadataFeatureTableProperty* pObject) { +ExtensionExtFeatureMetadataFeatureTablePropertyJsonHandler:: + ExtensionExtFeatureMetadataFeatureTablePropertyJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + _bufferView(), + _offsetType(), + _arrayOffsetBufferView(), + _stringOffsetBufferView() {} + +void ExtensionExtFeatureMetadataFeatureTablePropertyJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::ExtensionExtFeatureMetadataFeatureTableProperty* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataFeatureTablePropertyJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +ExtensionExtFeatureMetadataFeatureTablePropertyJsonHandler::readObjectKey( + const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionExtFeatureMetadataFeatureTableProperty(CesiumGltf::ExtensionExtFeatureMetadataFeatureTableProperty::TypeName, str, *this->_pObject); -} - -CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataFeatureTablePropertyJsonHandler::readObjectKeyExtensionExtFeatureMetadataFeatureTableProperty(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtFeatureMetadataFeatureTableProperty& o) { + return this->readObjectKeyExtensionExtFeatureMetadataFeatureTableProperty( + CesiumGltf::ExtensionExtFeatureMetadataFeatureTableProperty::TypeName, + str, + *this->_pObject); +} + +CesiumJsonReader::IJsonHandler* +ExtensionExtFeatureMetadataFeatureTablePropertyJsonHandler:: + readObjectKeyExtensionExtFeatureMetadataFeatureTableProperty( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::ExtensionExtFeatureMetadataFeatureTableProperty& o) { using namespace std::string_literals; - if ("bufferView"s == str) return property("bufferView", this->_bufferView, o.bufferView); - if ("offsetType"s == str) return property("offsetType", this->_offsetType, o.offsetType); - if ("arrayOffsetBufferView"s == str) return property("arrayOffsetBufferView", this->_arrayOffsetBufferView, o.arrayOffsetBufferView); - if ("stringOffsetBufferView"s == str) return property("stringOffsetBufferView", this->_stringOffsetBufferView, o.stringOffsetBufferView); + if ("bufferView"s == str) + return property("bufferView", this->_bufferView, o.bufferView); + if ("offsetType"s == str) + return property("offsetType", this->_offsetType, o.offsetType); + if ("arrayOffsetBufferView"s == str) + return property( + "arrayOffsetBufferView", + this->_arrayOffsetBufferView, + o.arrayOffsetBufferView); + if ("stringOffsetBufferView"s == str) + return property( + "stringOffsetBufferView", + this->_stringOffsetBufferView, + o.stringOffsetBufferView); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -1761,22 +2840,39 @@ CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataFeatureTablePropertyJ namespace CesiumGltfReader { -ExtensionExtFeatureMetadataStatisticsJsonHandler::ExtensionExtFeatureMetadataStatisticsJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _classes(context) {} +ExtensionExtFeatureMetadataStatisticsJsonHandler:: + ExtensionExtFeatureMetadataStatisticsJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + _classes(context) {} -void ExtensionExtFeatureMetadataStatisticsJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtFeatureMetadataStatistics* pObject) { +void ExtensionExtFeatureMetadataStatisticsJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::ExtensionExtFeatureMetadataStatistics* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataStatisticsJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +ExtensionExtFeatureMetadataStatisticsJsonHandler::readObjectKey( + const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionExtFeatureMetadataStatistics(CesiumGltf::ExtensionExtFeatureMetadataStatistics::TypeName, str, *this->_pObject); -} - -CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataStatisticsJsonHandler::readObjectKeyExtensionExtFeatureMetadataStatistics(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtFeatureMetadataStatistics& o) { + return this->readObjectKeyExtensionExtFeatureMetadataStatistics( + CesiumGltf::ExtensionExtFeatureMetadataStatistics::TypeName, + str, + *this->_pObject); +} + +CesiumJsonReader::IJsonHandler* +ExtensionExtFeatureMetadataStatisticsJsonHandler:: + readObjectKeyExtensionExtFeatureMetadataStatistics( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::ExtensionExtFeatureMetadataStatistics& o) { using namespace std::string_literals; - if ("classes"s == str) return property("classes", this->_classes, o.classes); + if ("classes"s == str) + return property("classes", this->_classes, o.classes); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -1793,23 +2889,42 @@ CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataStatisticsJsonHandler namespace CesiumGltfReader { -ExtensionExtFeatureMetadataClassStatisticsJsonHandler::ExtensionExtFeatureMetadataClassStatisticsJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _count(), _properties(context) {} +ExtensionExtFeatureMetadataClassStatisticsJsonHandler:: + ExtensionExtFeatureMetadataClassStatisticsJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + _count(), + _properties(context) {} -void ExtensionExtFeatureMetadataClassStatisticsJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtFeatureMetadataClassStatistics* pObject) { +void ExtensionExtFeatureMetadataClassStatisticsJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::ExtensionExtFeatureMetadataClassStatistics* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataClassStatisticsJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +ExtensionExtFeatureMetadataClassStatisticsJsonHandler::readObjectKey( + const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionExtFeatureMetadataClassStatistics(CesiumGltf::ExtensionExtFeatureMetadataClassStatistics::TypeName, str, *this->_pObject); -} - -CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataClassStatisticsJsonHandler::readObjectKeyExtensionExtFeatureMetadataClassStatistics(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtFeatureMetadataClassStatistics& o) { + return this->readObjectKeyExtensionExtFeatureMetadataClassStatistics( + CesiumGltf::ExtensionExtFeatureMetadataClassStatistics::TypeName, + str, + *this->_pObject); +} + +CesiumJsonReader::IJsonHandler* +ExtensionExtFeatureMetadataClassStatisticsJsonHandler:: + readObjectKeyExtensionExtFeatureMetadataClassStatistics( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::ExtensionExtFeatureMetadataClassStatistics& o) { using namespace std::string_literals; - if ("count"s == str) return property("count", this->_count, o.count); - if ("properties"s == str) return property("properties", this->_properties, o.properties); + if ("count"s == str) + return property("count", this->_count, o.count); + if ("properties"s == str) + return property("properties", this->_properties, o.properties); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -1826,29 +2941,63 @@ CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataClassStatisticsJsonHa namespace CesiumGltfReader { -ExtensionExtFeatureMetadataPropertyStatisticsJsonHandler::ExtensionExtFeatureMetadataPropertyStatisticsJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _min(), _max(), _mean(), _median(), _standardDeviation(), _variance(), _sum(), _occurrences() {} - -void ExtensionExtFeatureMetadataPropertyStatisticsJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtFeatureMetadataPropertyStatistics* pObject) { +ExtensionExtFeatureMetadataPropertyStatisticsJsonHandler:: + ExtensionExtFeatureMetadataPropertyStatisticsJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + _min(), + _max(), + _mean(), + _median(), + _standardDeviation(), + _variance(), + _sum(), + _occurrences() {} + +void ExtensionExtFeatureMetadataPropertyStatisticsJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::ExtensionExtFeatureMetadataPropertyStatistics* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataPropertyStatisticsJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +ExtensionExtFeatureMetadataPropertyStatisticsJsonHandler::readObjectKey( + const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionExtFeatureMetadataPropertyStatistics(CesiumGltf::ExtensionExtFeatureMetadataPropertyStatistics::TypeName, str, *this->_pObject); -} - -CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataPropertyStatisticsJsonHandler::readObjectKeyExtensionExtFeatureMetadataPropertyStatistics(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtFeatureMetadataPropertyStatistics& o) { + return this->readObjectKeyExtensionExtFeatureMetadataPropertyStatistics( + CesiumGltf::ExtensionExtFeatureMetadataPropertyStatistics::TypeName, + str, + *this->_pObject); +} + +CesiumJsonReader::IJsonHandler* +ExtensionExtFeatureMetadataPropertyStatisticsJsonHandler:: + readObjectKeyExtensionExtFeatureMetadataPropertyStatistics( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::ExtensionExtFeatureMetadataPropertyStatistics& o) { using namespace std::string_literals; - if ("min"s == str) return property("min", this->_min, o.min); - if ("max"s == str) return property("max", this->_max, o.max); - if ("mean"s == str) return property("mean", this->_mean, o.mean); - if ("median"s == str) return property("median", this->_median, o.median); - if ("standardDeviation"s == str) return property("standardDeviation", this->_standardDeviation, o.standardDeviation); - if ("variance"s == str) return property("variance", this->_variance, o.variance); - if ("sum"s == str) return property("sum", this->_sum, o.sum); - if ("occurrences"s == str) return property("occurrences", this->_occurrences, o.occurrences); + if ("min"s == str) + return property("min", this->_min, o.min); + if ("max"s == str) + return property("max", this->_max, o.max); + if ("mean"s == str) + return property("mean", this->_mean, o.mean); + if ("median"s == str) + return property("median", this->_median, o.median); + if ("standardDeviation"s == str) + return property( + "standardDeviation", + this->_standardDeviation, + o.standardDeviation); + if ("variance"s == str) + return property("variance", this->_variance, o.variance); + if ("sum"s == str) + return property("sum", this->_sum, o.sum); + if ("occurrences"s == str) + return property("occurrences", this->_occurrences, o.occurrences); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -1865,26 +3014,50 @@ CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataPropertyStatisticsJso namespace CesiumGltfReader { -ExtensionExtFeatureMetadataSchemaJsonHandler::ExtensionExtFeatureMetadataSchemaJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _name(), _description(), _version(), _classes(context), _enums(context) {} - -void ExtensionExtFeatureMetadataSchemaJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtFeatureMetadataSchema* pObject) { +ExtensionExtFeatureMetadataSchemaJsonHandler:: + ExtensionExtFeatureMetadataSchemaJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + _name(), + _description(), + _version(), + _classes(context), + _enums(context) {} + +void ExtensionExtFeatureMetadataSchemaJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::ExtensionExtFeatureMetadataSchema* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataSchemaJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +ExtensionExtFeatureMetadataSchemaJsonHandler::readObjectKey( + const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionExtFeatureMetadataSchema(CesiumGltf::ExtensionExtFeatureMetadataSchema::TypeName, str, *this->_pObject); + return this->readObjectKeyExtensionExtFeatureMetadataSchema( + CesiumGltf::ExtensionExtFeatureMetadataSchema::TypeName, + str, + *this->_pObject); } -CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataSchemaJsonHandler::readObjectKeyExtensionExtFeatureMetadataSchema(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtFeatureMetadataSchema& o) { +CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataSchemaJsonHandler:: + readObjectKeyExtensionExtFeatureMetadataSchema( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::ExtensionExtFeatureMetadataSchema& o) { using namespace std::string_literals; - if ("name"s == str) return property("name", this->_name, o.name); - if ("description"s == str) return property("description", this->_description, o.description); - if ("version"s == str) return property("version", this->_version, o.version); - if ("classes"s == str) return property("classes", this->_classes, o.classes); - if ("enums"s == str) return property("enums", this->_enums, o.enums); + if ("name"s == str) + return property("name", this->_name, o.name); + if ("description"s == str) + return property("description", this->_description, o.description); + if ("version"s == str) + return property("version", this->_version, o.version); + if ("classes"s == str) + return property("classes", this->_classes, o.classes); + if ("enums"s == str) + return property("enums", this->_enums, o.enums); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -1901,25 +3074,47 @@ CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataSchemaJsonHandler::re namespace CesiumGltfReader { -ExtensionExtFeatureMetadataEnumJsonHandler::ExtensionExtFeatureMetadataEnumJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _name(), _description(), _valueType(), _values(context) {} - -void ExtensionExtFeatureMetadataEnumJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtFeatureMetadataEnum* pObject) { +ExtensionExtFeatureMetadataEnumJsonHandler:: + ExtensionExtFeatureMetadataEnumJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + _name(), + _description(), + _valueType(), + _values(context) {} + +void ExtensionExtFeatureMetadataEnumJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::ExtensionExtFeatureMetadataEnum* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataEnumJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +ExtensionExtFeatureMetadataEnumJsonHandler::readObjectKey( + const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionExtFeatureMetadataEnum(CesiumGltf::ExtensionExtFeatureMetadataEnum::TypeName, str, *this->_pObject); + return this->readObjectKeyExtensionExtFeatureMetadataEnum( + CesiumGltf::ExtensionExtFeatureMetadataEnum::TypeName, + str, + *this->_pObject); } -CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataEnumJsonHandler::readObjectKeyExtensionExtFeatureMetadataEnum(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtFeatureMetadataEnum& o) { +CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataEnumJsonHandler:: + readObjectKeyExtensionExtFeatureMetadataEnum( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::ExtensionExtFeatureMetadataEnum& o) { using namespace std::string_literals; - if ("name"s == str) return property("name", this->_name, o.name); - if ("description"s == str) return property("description", this->_description, o.description); - if ("valueType"s == str) return property("valueType", this->_valueType, o.valueType); - if ("values"s == str) return property("values", this->_values, o.values); + if ("name"s == str) + return property("name", this->_name, o.name); + if ("description"s == str) + return property("description", this->_description, o.description); + if ("valueType"s == str) + return property("valueType", this->_valueType, o.valueType); + if ("values"s == str) + return property("values", this->_values, o.values); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -1936,24 +3131,45 @@ CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataEnumJsonHandler::read namespace CesiumGltfReader { -ExtensionExtFeatureMetadataEnumValueJsonHandler::ExtensionExtFeatureMetadataEnumValueJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _name(), _description(), _value() {} - -void ExtensionExtFeatureMetadataEnumValueJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtFeatureMetadataEnumValue* pObject) { +ExtensionExtFeatureMetadataEnumValueJsonHandler:: + ExtensionExtFeatureMetadataEnumValueJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + _name(), + _description(), + _value() {} + +void ExtensionExtFeatureMetadataEnumValueJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::ExtensionExtFeatureMetadataEnumValue* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataEnumValueJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +ExtensionExtFeatureMetadataEnumValueJsonHandler::readObjectKey( + const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionExtFeatureMetadataEnumValue(CesiumGltf::ExtensionExtFeatureMetadataEnumValue::TypeName, str, *this->_pObject); -} - -CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataEnumValueJsonHandler::readObjectKeyExtensionExtFeatureMetadataEnumValue(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtFeatureMetadataEnumValue& o) { + return this->readObjectKeyExtensionExtFeatureMetadataEnumValue( + CesiumGltf::ExtensionExtFeatureMetadataEnumValue::TypeName, + str, + *this->_pObject); +} + +CesiumJsonReader::IJsonHandler* +ExtensionExtFeatureMetadataEnumValueJsonHandler:: + readObjectKeyExtensionExtFeatureMetadataEnumValue( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::ExtensionExtFeatureMetadataEnumValue& o) { using namespace std::string_literals; - if ("name"s == str) return property("name", this->_name, o.name); - if ("description"s == str) return property("description", this->_description, o.description); - if ("value"s == str) return property("value", this->_value, o.value); + if ("name"s == str) + return property("name", this->_name, o.name); + if ("description"s == str) + return property("description", this->_description, o.description); + if ("value"s == str) + return property("value", this->_value, o.value); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -1970,24 +3186,44 @@ CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataEnumValueJsonHandler: namespace CesiumGltfReader { -ExtensionExtFeatureMetadataClassJsonHandler::ExtensionExtFeatureMetadataClassJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _name(), _description(), _properties(context) {} - -void ExtensionExtFeatureMetadataClassJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtFeatureMetadataClass* pObject) { +ExtensionExtFeatureMetadataClassJsonHandler:: + ExtensionExtFeatureMetadataClassJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + _name(), + _description(), + _properties(context) {} + +void ExtensionExtFeatureMetadataClassJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::ExtensionExtFeatureMetadataClass* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataClassJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +ExtensionExtFeatureMetadataClassJsonHandler::readObjectKey( + const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionExtFeatureMetadataClass(CesiumGltf::ExtensionExtFeatureMetadataClass::TypeName, str, *this->_pObject); + return this->readObjectKeyExtensionExtFeatureMetadataClass( + CesiumGltf::ExtensionExtFeatureMetadataClass::TypeName, + str, + *this->_pObject); } -CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataClassJsonHandler::readObjectKeyExtensionExtFeatureMetadataClass(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtFeatureMetadataClass& o) { +CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataClassJsonHandler:: + readObjectKeyExtensionExtFeatureMetadataClass( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::ExtensionExtFeatureMetadataClass& o) { using namespace std::string_literals; - if ("name"s == str) return property("name", this->_name, o.name); - if ("description"s == str) return property("description", this->_description, o.description); - if ("properties"s == str) return property("properties", this->_properties, o.properties); + if ("name"s == str) + return property("name", this->_name, o.name); + if ("description"s == str) + return property("description", this->_description, o.description); + if ("properties"s == str) + return property("properties", this->_properties, o.properties); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -2004,33 +3240,72 @@ CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataClassJsonHandler::rea namespace CesiumGltfReader { -ExtensionExtFeatureMetadataClassPropertyJsonHandler::ExtensionExtFeatureMetadataClassPropertyJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _name(), _description(), _type(), _enumType(), _componentType(), _componentCount(), _normalized(), _max(), _min(), _defaultProperty(), _optional(), _semantic() {} - -void ExtensionExtFeatureMetadataClassPropertyJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtFeatureMetadataClassProperty* pObject) { +ExtensionExtFeatureMetadataClassPropertyJsonHandler:: + ExtensionExtFeatureMetadataClassPropertyJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + _name(), + _description(), + _type(), + _enumType(), + _componentType(), + _componentCount(), + _normalized(), + _max(), + _min(), + _defaultProperty(), + _optional(), + _semantic() {} + +void ExtensionExtFeatureMetadataClassPropertyJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::ExtensionExtFeatureMetadataClassProperty* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataClassPropertyJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +ExtensionExtFeatureMetadataClassPropertyJsonHandler::readObjectKey( + const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionExtFeatureMetadataClassProperty(CesiumGltf::ExtensionExtFeatureMetadataClassProperty::TypeName, str, *this->_pObject); -} - -CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataClassPropertyJsonHandler::readObjectKeyExtensionExtFeatureMetadataClassProperty(const std::string& objectType, const std::string_view& str, CesiumGltf::ExtensionExtFeatureMetadataClassProperty& o) { + return this->readObjectKeyExtensionExtFeatureMetadataClassProperty( + CesiumGltf::ExtensionExtFeatureMetadataClassProperty::TypeName, + str, + *this->_pObject); +} + +CesiumJsonReader::IJsonHandler* +ExtensionExtFeatureMetadataClassPropertyJsonHandler:: + readObjectKeyExtensionExtFeatureMetadataClassProperty( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::ExtensionExtFeatureMetadataClassProperty& o) { using namespace std::string_literals; - if ("name"s == str) return property("name", this->_name, o.name); - if ("description"s == str) return property("description", this->_description, o.description); - if ("type"s == str) return property("type", this->_type, o.type); - if ("enumType"s == str) return property("enumType", this->_enumType, o.enumType); - if ("componentType"s == str) return property("componentType", this->_componentType, o.componentType); - if ("componentCount"s == str) return property("componentCount", this->_componentCount, o.componentCount); - if ("normalized"s == str) return property("normalized", this->_normalized, o.normalized); - if ("max"s == str) return property("max", this->_max, o.max); - if ("min"s == str) return property("min", this->_min, o.min); - if ("default"s == str) return property("default", this->_defaultProperty, o.defaultProperty); - if ("optional"s == str) return property("optional", this->_optional, o.optional); - if ("semantic"s == str) return property("semantic", this->_semantic, o.semantic); + if ("name"s == str) + return property("name", this->_name, o.name); + if ("description"s == str) + return property("description", this->_description, o.description); + if ("type"s == str) + return property("type", this->_type, o.type); + if ("enumType"s == str) + return property("enumType", this->_enumType, o.enumType); + if ("componentType"s == str) + return property("componentType", this->_componentType, o.componentType); + if ("componentCount"s == str) + return property("componentCount", this->_componentCount, o.componentCount); + if ("normalized"s == str) + return property("normalized", this->_normalized, o.normalized); + if ("max"s == str) + return property("max", this->_max, o.max); + if ("min"s == str) + return property("min", this->_min, o.min); + if ("default"s == str) + return property("default", this->_defaultProperty, o.defaultProperty); + if ("optional"s == str) + return property("optional", this->_optional, o.optional); + if ("semantic"s == str) + return property("semantic", this->_semantic, o.semantic); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -2047,38 +3322,86 @@ CesiumJsonReader::IJsonHandler* ExtensionExtFeatureMetadataClassPropertyJsonHand namespace CesiumGltfReader { -ModelJsonHandler::ModelJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _extensionsUsed(), _extensionsRequired(), _accessors(context), _animations(context), _asset(context), _buffers(context), _bufferViews(context), _cameras(context), _images(context), _materials(context), _meshes(context), _nodes(context), _samplers(context), _scene(), _scenes(context), _skins(context), _textures(context) {} - -void ModelJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::Model* pObject) { +ModelJsonHandler::ModelJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + _extensionsUsed(), + _extensionsRequired(), + _accessors(context), + _animations(context), + _asset(context), + _buffers(context), + _bufferViews(context), + _cameras(context), + _images(context), + _materials(context), + _meshes(context), + _nodes(context), + _samplers(context), + _scene(), + _scenes(context), + _skins(context), + _textures(context) {} + +void ModelJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::Model* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* ModelJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +ModelJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyModel(CesiumGltf::Model::TypeName, str, *this->_pObject); + return this->readObjectKeyModel( + CesiumGltf::Model::TypeName, + str, + *this->_pObject); } -CesiumJsonReader::IJsonHandler* ModelJsonHandler::readObjectKeyModel(const std::string& objectType, const std::string_view& str, CesiumGltf::Model& o) { +CesiumJsonReader::IJsonHandler* ModelJsonHandler::readObjectKeyModel( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::Model& o) { using namespace std::string_literals; - if ("extensionsUsed"s == str) return property("extensionsUsed", this->_extensionsUsed, o.extensionsUsed); - if ("extensionsRequired"s == str) return property("extensionsRequired", this->_extensionsRequired, o.extensionsRequired); - if ("accessors"s == str) return property("accessors", this->_accessors, o.accessors); - if ("animations"s == str) return property("animations", this->_animations, o.animations); - if ("asset"s == str) return property("asset", this->_asset, o.asset); - if ("buffers"s == str) return property("buffers", this->_buffers, o.buffers); - if ("bufferViews"s == str) return property("bufferViews", this->_bufferViews, o.bufferViews); - if ("cameras"s == str) return property("cameras", this->_cameras, o.cameras); - if ("images"s == str) return property("images", this->_images, o.images); - if ("materials"s == str) return property("materials", this->_materials, o.materials); - if ("meshes"s == str) return property("meshes", this->_meshes, o.meshes); - if ("nodes"s == str) return property("nodes", this->_nodes, o.nodes); - if ("samplers"s == str) return property("samplers", this->_samplers, o.samplers); - if ("scene"s == str) return property("scene", this->_scene, o.scene); - if ("scenes"s == str) return property("scenes", this->_scenes, o.scenes); - if ("skins"s == str) return property("skins", this->_skins, o.skins); - if ("textures"s == str) return property("textures", this->_textures, o.textures); + if ("extensionsUsed"s == str) + return property("extensionsUsed", this->_extensionsUsed, o.extensionsUsed); + if ("extensionsRequired"s == str) + return property( + "extensionsRequired", + this->_extensionsRequired, + o.extensionsRequired); + if ("accessors"s == str) + return property("accessors", this->_accessors, o.accessors); + if ("animations"s == str) + return property("animations", this->_animations, o.animations); + if ("asset"s == str) + return property("asset", this->_asset, o.asset); + if ("buffers"s == str) + return property("buffers", this->_buffers, o.buffers); + if ("bufferViews"s == str) + return property("bufferViews", this->_bufferViews, o.bufferViews); + if ("cameras"s == str) + return property("cameras", this->_cameras, o.cameras); + if ("images"s == str) + return property("images", this->_images, o.images); + if ("materials"s == str) + return property("materials", this->_materials, o.materials); + if ("meshes"s == str) + return property("meshes", this->_meshes, o.meshes); + if ("nodes"s == str) + return property("nodes", this->_nodes, o.nodes); + if ("samplers"s == str) + return property("samplers", this->_samplers, o.samplers); + if ("scene"s == str) + return property("scene", this->_scene, o.scene); + if ("scenes"s == str) + return property("scenes", this->_scenes, o.scenes); + if ("skins"s == str) + return property("skins", this->_skins, o.skins); + if ("textures"s == str) + return property("textures", this->_textures, o.textures); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -2095,23 +3418,38 @@ CesiumJsonReader::IJsonHandler* ModelJsonHandler::readObjectKeyModel(const std:: namespace CesiumGltfReader { -TextureJsonHandler::TextureJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumGltfReader::NamedObjectJsonHandler(context), _sampler(), _source() {} +TextureJsonHandler::TextureJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumGltfReader::NamedObjectJsonHandler(context), + _sampler(), + _source() {} -void TextureJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::Texture* pObject) { +void TextureJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::Texture* pObject) { CesiumGltfReader::NamedObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* TextureJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +TextureJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyTexture(CesiumGltf::Texture::TypeName, str, *this->_pObject); + return this->readObjectKeyTexture( + CesiumGltf::Texture::TypeName, + str, + *this->_pObject); } -CesiumJsonReader::IJsonHandler* TextureJsonHandler::readObjectKeyTexture(const std::string& objectType, const std::string_view& str, CesiumGltf::Texture& o) { +CesiumJsonReader::IJsonHandler* TextureJsonHandler::readObjectKeyTexture( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::Texture& o) { using namespace std::string_literals; - if ("sampler"s == str) return property("sampler", this->_sampler, o.sampler); - if ("source"s == str) return property("source", this->_source, o.source); + if ("sampler"s == str) + return property("sampler", this->_sampler, o.sampler); + if ("source"s == str) + return property("source", this->_source, o.source); return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } @@ -2128,24 +3466,44 @@ CesiumJsonReader::IJsonHandler* TextureJsonHandler::readObjectKeyTexture(const s namespace CesiumGltfReader { -SkinJsonHandler::SkinJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumGltfReader::NamedObjectJsonHandler(context), _inverseBindMatrices(), _skeleton(), _joints() {} +SkinJsonHandler::SkinJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumGltfReader::NamedObjectJsonHandler(context), + _inverseBindMatrices(), + _skeleton(), + _joints() {} -void SkinJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::Skin* pObject) { +void SkinJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::Skin* pObject) { CesiumGltfReader::NamedObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* SkinJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +SkinJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeySkin(CesiumGltf::Skin::TypeName, str, *this->_pObject); + return this->readObjectKeySkin( + CesiumGltf::Skin::TypeName, + str, + *this->_pObject); } -CesiumJsonReader::IJsonHandler* SkinJsonHandler::readObjectKeySkin(const std::string& objectType, const std::string_view& str, CesiumGltf::Skin& o) { +CesiumJsonReader::IJsonHandler* SkinJsonHandler::readObjectKeySkin( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::Skin& o) { using namespace std::string_literals; - if ("inverseBindMatrices"s == str) return property("inverseBindMatrices", this->_inverseBindMatrices, o.inverseBindMatrices); - if ("skeleton"s == str) return property("skeleton", this->_skeleton, o.skeleton); - if ("joints"s == str) return property("joints", this->_joints, o.joints); + if ("inverseBindMatrices"s == str) + return property( + "inverseBindMatrices", + this->_inverseBindMatrices, + o.inverseBindMatrices); + if ("skeleton"s == str) + return property("skeleton", this->_skeleton, o.skeleton); + if ("joints"s == str) + return property("joints", this->_joints, o.joints); return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } @@ -2162,22 +3520,34 @@ CesiumJsonReader::IJsonHandler* SkinJsonHandler::readObjectKeySkin(const std::st namespace CesiumGltfReader { -SceneJsonHandler::SceneJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumGltfReader::NamedObjectJsonHandler(context), _nodes() {} +SceneJsonHandler::SceneJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumGltfReader::NamedObjectJsonHandler(context), _nodes() {} -void SceneJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::Scene* pObject) { +void SceneJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::Scene* pObject) { CesiumGltfReader::NamedObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* SceneJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +SceneJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyScene(CesiumGltf::Scene::TypeName, str, *this->_pObject); + return this->readObjectKeyScene( + CesiumGltf::Scene::TypeName, + str, + *this->_pObject); } -CesiumJsonReader::IJsonHandler* SceneJsonHandler::readObjectKeyScene(const std::string& objectType, const std::string_view& str, CesiumGltf::Scene& o) { +CesiumJsonReader::IJsonHandler* SceneJsonHandler::readObjectKeyScene( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::Scene& o) { using namespace std::string_literals; - if ("nodes"s == str) return property("nodes", this->_nodes, o.nodes); + if ("nodes"s == str) + return property("nodes", this->_nodes, o.nodes); return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } @@ -2194,25 +3564,44 @@ CesiumJsonReader::IJsonHandler* SceneJsonHandler::readObjectKeyScene(const std:: namespace CesiumGltfReader { -SamplerJsonHandler::SamplerJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumGltfReader::NamedObjectJsonHandler(context), _magFilter(), _minFilter(), _wrapS(), _wrapT() {} - -void SamplerJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::Sampler* pObject) { +SamplerJsonHandler::SamplerJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumGltfReader::NamedObjectJsonHandler(context), + _magFilter(), + _minFilter(), + _wrapS(), + _wrapT() {} + +void SamplerJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::Sampler* pObject) { CesiumGltfReader::NamedObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* SamplerJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +SamplerJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeySampler(CesiumGltf::Sampler::TypeName, str, *this->_pObject); + return this->readObjectKeySampler( + CesiumGltf::Sampler::TypeName, + str, + *this->_pObject); } -CesiumJsonReader::IJsonHandler* SamplerJsonHandler::readObjectKeySampler(const std::string& objectType, const std::string_view& str, CesiumGltf::Sampler& o) { +CesiumJsonReader::IJsonHandler* SamplerJsonHandler::readObjectKeySampler( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::Sampler& o) { using namespace std::string_literals; - if ("magFilter"s == str) return property("magFilter", this->_magFilter, o.magFilter); - if ("minFilter"s == str) return property("minFilter", this->_minFilter, o.minFilter); - if ("wrapS"s == str) return property("wrapS", this->_wrapS, o.wrapS); - if ("wrapT"s == str) return property("wrapT", this->_wrapT, o.wrapT); + if ("magFilter"s == str) + return property("magFilter", this->_magFilter, o.magFilter); + if ("minFilter"s == str) + return property("minFilter", this->_minFilter, o.minFilter); + if ("wrapS"s == str) + return property("wrapS", this->_wrapS, o.wrapS); + if ("wrapT"s == str) + return property("wrapT", this->_wrapT, o.wrapT); return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } @@ -2229,30 +3618,59 @@ CesiumJsonReader::IJsonHandler* SamplerJsonHandler::readObjectKeySampler(const s namespace CesiumGltfReader { -NodeJsonHandler::NodeJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumGltfReader::NamedObjectJsonHandler(context), _camera(), _children(), _skin(), _matrix(), _mesh(), _rotation(), _scale(), _translation(), _weights() {} - -void NodeJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::Node* pObject) { +NodeJsonHandler::NodeJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumGltfReader::NamedObjectJsonHandler(context), + _camera(), + _children(), + _skin(), + _matrix(), + _mesh(), + _rotation(), + _scale(), + _translation(), + _weights() {} + +void NodeJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::Node* pObject) { CesiumGltfReader::NamedObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* NodeJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +NodeJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyNode(CesiumGltf::Node::TypeName, str, *this->_pObject); + return this->readObjectKeyNode( + CesiumGltf::Node::TypeName, + str, + *this->_pObject); } -CesiumJsonReader::IJsonHandler* NodeJsonHandler::readObjectKeyNode(const std::string& objectType, const std::string_view& str, CesiumGltf::Node& o) { +CesiumJsonReader::IJsonHandler* NodeJsonHandler::readObjectKeyNode( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::Node& o) { using namespace std::string_literals; - if ("camera"s == str) return property("camera", this->_camera, o.camera); - if ("children"s == str) return property("children", this->_children, o.children); - if ("skin"s == str) return property("skin", this->_skin, o.skin); - if ("matrix"s == str) return property("matrix", this->_matrix, o.matrix); - if ("mesh"s == str) return property("mesh", this->_mesh, o.mesh); - if ("rotation"s == str) return property("rotation", this->_rotation, o.rotation); - if ("scale"s == str) return property("scale", this->_scale, o.scale); - if ("translation"s == str) return property("translation", this->_translation, o.translation); - if ("weights"s == str) return property("weights", this->_weights, o.weights); + if ("camera"s == str) + return property("camera", this->_camera, o.camera); + if ("children"s == str) + return property("children", this->_children, o.children); + if ("skin"s == str) + return property("skin", this->_skin, o.skin); + if ("matrix"s == str) + return property("matrix", this->_matrix, o.matrix); + if ("mesh"s == str) + return property("mesh", this->_mesh, o.mesh); + if ("rotation"s == str) + return property("rotation", this->_rotation, o.rotation); + if ("scale"s == str) + return property("scale", this->_scale, o.scale); + if ("translation"s == str) + return property("translation", this->_translation, o.translation); + if ("weights"s == str) + return property("weights", this->_weights, o.weights); return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } @@ -2269,23 +3687,38 @@ CesiumJsonReader::IJsonHandler* NodeJsonHandler::readObjectKeyNode(const std::st namespace CesiumGltfReader { -MeshJsonHandler::MeshJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumGltfReader::NamedObjectJsonHandler(context), _primitives(context), _weights() {} +MeshJsonHandler::MeshJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumGltfReader::NamedObjectJsonHandler(context), + _primitives(context), + _weights() {} -void MeshJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::Mesh* pObject) { +void MeshJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::Mesh* pObject) { CesiumGltfReader::NamedObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* MeshJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +MeshJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyMesh(CesiumGltf::Mesh::TypeName, str, *this->_pObject); + return this->readObjectKeyMesh( + CesiumGltf::Mesh::TypeName, + str, + *this->_pObject); } -CesiumJsonReader::IJsonHandler* MeshJsonHandler::readObjectKeyMesh(const std::string& objectType, const std::string_view& str, CesiumGltf::Mesh& o) { +CesiumJsonReader::IJsonHandler* MeshJsonHandler::readObjectKeyMesh( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::Mesh& o) { using namespace std::string_literals; - if ("primitives"s == str) return property("primitives", this->_primitives, o.primitives); - if ("weights"s == str) return property("weights", this->_weights, o.weights); + if ("primitives"s == str) + return property("primitives", this->_primitives, o.primitives); + if ("weights"s == str) + return property("weights", this->_weights, o.weights); return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } @@ -2302,26 +3735,48 @@ CesiumJsonReader::IJsonHandler* MeshJsonHandler::readObjectKeyMesh(const std::st namespace CesiumGltfReader { -MeshPrimitiveJsonHandler::MeshPrimitiveJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _attributes(), _indices(), _material(), _mode(), _targets() {} - -void MeshPrimitiveJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::MeshPrimitive* pObject) { +MeshPrimitiveJsonHandler::MeshPrimitiveJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + _attributes(), + _indices(), + _material(), + _mode(), + _targets() {} + +void MeshPrimitiveJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::MeshPrimitive* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* MeshPrimitiveJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +MeshPrimitiveJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyMeshPrimitive(CesiumGltf::MeshPrimitive::TypeName, str, *this->_pObject); + return this->readObjectKeyMeshPrimitive( + CesiumGltf::MeshPrimitive::TypeName, + str, + *this->_pObject); } -CesiumJsonReader::IJsonHandler* MeshPrimitiveJsonHandler::readObjectKeyMeshPrimitive(const std::string& objectType, const std::string_view& str, CesiumGltf::MeshPrimitive& o) { +CesiumJsonReader::IJsonHandler* +MeshPrimitiveJsonHandler::readObjectKeyMeshPrimitive( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::MeshPrimitive& o) { using namespace std::string_literals; - if ("attributes"s == str) return property("attributes", this->_attributes, o.attributes); - if ("indices"s == str) return property("indices", this->_indices, o.indices); - if ("material"s == str) return property("material", this->_material, o.material); - if ("mode"s == str) return property("mode", this->_mode, o.mode); - if ("targets"s == str) return property("targets", this->_targets, o.targets); + if ("attributes"s == str) + return property("attributes", this->_attributes, o.attributes); + if ("indices"s == str) + return property("indices", this->_indices, o.indices); + if ("material"s == str) + return property("material", this->_material, o.material); + if ("mode"s == str) + return property("mode", this->_mode, o.mode); + if ("targets"s == str) + return property("targets", this->_targets, o.targets); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -2338,29 +3793,65 @@ CesiumJsonReader::IJsonHandler* MeshPrimitiveJsonHandler::readObjectKeyMeshPrimi namespace CesiumGltfReader { -MaterialJsonHandler::MaterialJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumGltfReader::NamedObjectJsonHandler(context), _pbrMetallicRoughness(context), _normalTexture(context), _occlusionTexture(context), _emissiveTexture(context), _emissiveFactor(), _alphaMode(), _alphaCutoff(), _doubleSided() {} - -void MaterialJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::Material* pObject) { +MaterialJsonHandler::MaterialJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumGltfReader::NamedObjectJsonHandler(context), + _pbrMetallicRoughness(context), + _normalTexture(context), + _occlusionTexture(context), + _emissiveTexture(context), + _emissiveFactor(), + _alphaMode(), + _alphaCutoff(), + _doubleSided() {} + +void MaterialJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::Material* pObject) { CesiumGltfReader::NamedObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* MaterialJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +MaterialJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyMaterial(CesiumGltf::Material::TypeName, str, *this->_pObject); + return this->readObjectKeyMaterial( + CesiumGltf::Material::TypeName, + str, + *this->_pObject); } -CesiumJsonReader::IJsonHandler* MaterialJsonHandler::readObjectKeyMaterial(const std::string& objectType, const std::string_view& str, CesiumGltf::Material& o) { +CesiumJsonReader::IJsonHandler* MaterialJsonHandler::readObjectKeyMaterial( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::Material& o) { using namespace std::string_literals; - if ("pbrMetallicRoughness"s == str) return property("pbrMetallicRoughness", this->_pbrMetallicRoughness, o.pbrMetallicRoughness); - if ("normalTexture"s == str) return property("normalTexture", this->_normalTexture, o.normalTexture); - if ("occlusionTexture"s == str) return property("occlusionTexture", this->_occlusionTexture, o.occlusionTexture); - if ("emissiveTexture"s == str) return property("emissiveTexture", this->_emissiveTexture, o.emissiveTexture); - if ("emissiveFactor"s == str) return property("emissiveFactor", this->_emissiveFactor, o.emissiveFactor); - if ("alphaMode"s == str) return property("alphaMode", this->_alphaMode, o.alphaMode); - if ("alphaCutoff"s == str) return property("alphaCutoff", this->_alphaCutoff, o.alphaCutoff); - if ("doubleSided"s == str) return property("doubleSided", this->_doubleSided, o.doubleSided); + if ("pbrMetallicRoughness"s == str) + return property( + "pbrMetallicRoughness", + this->_pbrMetallicRoughness, + o.pbrMetallicRoughness); + if ("normalTexture"s == str) + return property("normalTexture", this->_normalTexture, o.normalTexture); + if ("occlusionTexture"s == str) + return property( + "occlusionTexture", + this->_occlusionTexture, + o.occlusionTexture); + if ("emissiveTexture"s == str) + return property( + "emissiveTexture", + this->_emissiveTexture, + o.emissiveTexture); + if ("emissiveFactor"s == str) + return property("emissiveFactor", this->_emissiveFactor, o.emissiveFactor); + if ("alphaMode"s == str) + return property("alphaMode", this->_alphaMode, o.alphaMode); + if ("alphaCutoff"s == str) + return property("alphaCutoff", this->_alphaCutoff, o.alphaCutoff); + if ("doubleSided"s == str) + return property("doubleSided", this->_doubleSided, o.doubleSided); return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } @@ -2377,22 +3868,37 @@ CesiumJsonReader::IJsonHandler* MaterialJsonHandler::readObjectKeyMaterial(const namespace CesiumGltfReader { -MaterialOcclusionTextureInfoJsonHandler::MaterialOcclusionTextureInfoJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : TextureInfoJsonHandler(context), _strength() {} +MaterialOcclusionTextureInfoJsonHandler:: + MaterialOcclusionTextureInfoJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : TextureInfoJsonHandler(context), _strength() {} -void MaterialOcclusionTextureInfoJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::MaterialOcclusionTextureInfo* pObject) { +void MaterialOcclusionTextureInfoJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::MaterialOcclusionTextureInfo* pObject) { TextureInfoJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* MaterialOcclusionTextureInfoJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +MaterialOcclusionTextureInfoJsonHandler::readObjectKey( + const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyMaterialOcclusionTextureInfo(CesiumGltf::MaterialOcclusionTextureInfo::TypeName, str, *this->_pObject); + return this->readObjectKeyMaterialOcclusionTextureInfo( + CesiumGltf::MaterialOcclusionTextureInfo::TypeName, + str, + *this->_pObject); } -CesiumJsonReader::IJsonHandler* MaterialOcclusionTextureInfoJsonHandler::readObjectKeyMaterialOcclusionTextureInfo(const std::string& objectType, const std::string_view& str, CesiumGltf::MaterialOcclusionTextureInfo& o) { +CesiumJsonReader::IJsonHandler* MaterialOcclusionTextureInfoJsonHandler:: + readObjectKeyMaterialOcclusionTextureInfo( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::MaterialOcclusionTextureInfo& o) { using namespace std::string_literals; - if ("strength"s == str) return property("strength", this->_strength, o.strength); + if ("strength"s == str) + return property("strength", this->_strength, o.strength); return this->readObjectKeyTextureInfo(objectType, str, *this->_pObject); } @@ -2409,22 +3915,36 @@ CesiumJsonReader::IJsonHandler* MaterialOcclusionTextureInfoJsonHandler::readObj namespace CesiumGltfReader { -MaterialNormalTextureInfoJsonHandler::MaterialNormalTextureInfoJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : TextureInfoJsonHandler(context), _scale() {} +MaterialNormalTextureInfoJsonHandler::MaterialNormalTextureInfoJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : TextureInfoJsonHandler(context), _scale() {} -void MaterialNormalTextureInfoJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::MaterialNormalTextureInfo* pObject) { +void MaterialNormalTextureInfoJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::MaterialNormalTextureInfo* pObject) { TextureInfoJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* MaterialNormalTextureInfoJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +MaterialNormalTextureInfoJsonHandler::readObjectKey( + const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyMaterialNormalTextureInfo(CesiumGltf::MaterialNormalTextureInfo::TypeName, str, *this->_pObject); + return this->readObjectKeyMaterialNormalTextureInfo( + CesiumGltf::MaterialNormalTextureInfo::TypeName, + str, + *this->_pObject); } -CesiumJsonReader::IJsonHandler* MaterialNormalTextureInfoJsonHandler::readObjectKeyMaterialNormalTextureInfo(const std::string& objectType, const std::string_view& str, CesiumGltf::MaterialNormalTextureInfo& o) { +CesiumJsonReader::IJsonHandler* +MaterialNormalTextureInfoJsonHandler::readObjectKeyMaterialNormalTextureInfo( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::MaterialNormalTextureInfo& o) { using namespace std::string_literals; - if ("scale"s == str) return property("scale", this->_scale, o.scale); + if ("scale"s == str) + return property("scale", this->_scale, o.scale); return this->readObjectKeyTextureInfo(objectType, str, *this->_pObject); } @@ -2441,26 +3961,62 @@ CesiumJsonReader::IJsonHandler* MaterialNormalTextureInfoJsonHandler::readObject namespace CesiumGltfReader { -MaterialPBRMetallicRoughnessJsonHandler::MaterialPBRMetallicRoughnessJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _baseColorFactor(), _baseColorTexture(context), _metallicFactor(), _roughnessFactor(), _metallicRoughnessTexture(context) {} - -void MaterialPBRMetallicRoughnessJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::MaterialPBRMetallicRoughness* pObject) { +MaterialPBRMetallicRoughnessJsonHandler:: + MaterialPBRMetallicRoughnessJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + _baseColorFactor(), + _baseColorTexture(context), + _metallicFactor(), + _roughnessFactor(), + _metallicRoughnessTexture(context) {} + +void MaterialPBRMetallicRoughnessJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::MaterialPBRMetallicRoughness* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* MaterialPBRMetallicRoughnessJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +MaterialPBRMetallicRoughnessJsonHandler::readObjectKey( + const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyMaterialPBRMetallicRoughness(CesiumGltf::MaterialPBRMetallicRoughness::TypeName, str, *this->_pObject); + return this->readObjectKeyMaterialPBRMetallicRoughness( + CesiumGltf::MaterialPBRMetallicRoughness::TypeName, + str, + *this->_pObject); } -CesiumJsonReader::IJsonHandler* MaterialPBRMetallicRoughnessJsonHandler::readObjectKeyMaterialPBRMetallicRoughness(const std::string& objectType, const std::string_view& str, CesiumGltf::MaterialPBRMetallicRoughness& o) { +CesiumJsonReader::IJsonHandler* MaterialPBRMetallicRoughnessJsonHandler:: + readObjectKeyMaterialPBRMetallicRoughness( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::MaterialPBRMetallicRoughness& o) { using namespace std::string_literals; - if ("baseColorFactor"s == str) return property("baseColorFactor", this->_baseColorFactor, o.baseColorFactor); - if ("baseColorTexture"s == str) return property("baseColorTexture", this->_baseColorTexture, o.baseColorTexture); - if ("metallicFactor"s == str) return property("metallicFactor", this->_metallicFactor, o.metallicFactor); - if ("roughnessFactor"s == str) return property("roughnessFactor", this->_roughnessFactor, o.roughnessFactor); - if ("metallicRoughnessTexture"s == str) return property("metallicRoughnessTexture", this->_metallicRoughnessTexture, o.metallicRoughnessTexture); + if ("baseColorFactor"s == str) + return property( + "baseColorFactor", + this->_baseColorFactor, + o.baseColorFactor); + if ("baseColorTexture"s == str) + return property( + "baseColorTexture", + this->_baseColorTexture, + o.baseColorTexture); + if ("metallicFactor"s == str) + return property("metallicFactor", this->_metallicFactor, o.metallicFactor); + if ("roughnessFactor"s == str) + return property( + "roughnessFactor", + this->_roughnessFactor, + o.roughnessFactor); + if ("metallicRoughnessTexture"s == str) + return property( + "metallicRoughnessTexture", + this->_metallicRoughnessTexture, + o.metallicRoughnessTexture); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -2477,24 +4033,41 @@ CesiumJsonReader::IJsonHandler* MaterialPBRMetallicRoughnessJsonHandler::readObj namespace CesiumGltfReader { -ImageJsonHandler::ImageJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumGltfReader::NamedObjectJsonHandler(context), _uri(), _mimeType(), _bufferView() {} +ImageJsonHandler::ImageJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumGltfReader::NamedObjectJsonHandler(context), + _uri(), + _mimeType(), + _bufferView() {} -void ImageJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::Image* pObject) { +void ImageJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::Image* pObject) { CesiumGltfReader::NamedObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* ImageJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +ImageJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyImage(CesiumGltf::Image::TypeName, str, *this->_pObject); + return this->readObjectKeyImage( + CesiumGltf::Image::TypeName, + str, + *this->_pObject); } -CesiumJsonReader::IJsonHandler* ImageJsonHandler::readObjectKeyImage(const std::string& objectType, const std::string_view& str, CesiumGltf::Image& o) { +CesiumJsonReader::IJsonHandler* ImageJsonHandler::readObjectKeyImage( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::Image& o) { using namespace std::string_literals; - if ("uri"s == str) return property("uri", this->_uri, o.uri); - if ("mimeType"s == str) return property("mimeType", this->_mimeType, o.mimeType); - if ("bufferView"s == str) return property("bufferView", this->_bufferView, o.bufferView); + if ("uri"s == str) + return property("uri", this->_uri, o.uri); + if ("mimeType"s == str) + return property("mimeType", this->_mimeType, o.mimeType); + if ("bufferView"s == str) + return property("bufferView", this->_bufferView, o.bufferView); return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } @@ -2511,24 +4084,41 @@ CesiumJsonReader::IJsonHandler* ImageJsonHandler::readObjectKeyImage(const std:: namespace CesiumGltfReader { -CameraJsonHandler::CameraJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumGltfReader::NamedObjectJsonHandler(context), _orthographic(context), _perspective(context), _type() {} +CameraJsonHandler::CameraJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumGltfReader::NamedObjectJsonHandler(context), + _orthographic(context), + _perspective(context), + _type() {} -void CameraJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::Camera* pObject) { +void CameraJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::Camera* pObject) { CesiumGltfReader::NamedObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* CameraJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +CameraJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyCamera(CesiumGltf::Camera::TypeName, str, *this->_pObject); + return this->readObjectKeyCamera( + CesiumGltf::Camera::TypeName, + str, + *this->_pObject); } -CesiumJsonReader::IJsonHandler* CameraJsonHandler::readObjectKeyCamera(const std::string& objectType, const std::string_view& str, CesiumGltf::Camera& o) { +CesiumJsonReader::IJsonHandler* CameraJsonHandler::readObjectKeyCamera( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::Camera& o) { using namespace std::string_literals; - if ("orthographic"s == str) return property("orthographic", this->_orthographic, o.orthographic); - if ("perspective"s == str) return property("perspective", this->_perspective, o.perspective); - if ("type"s == str) return property("type", this->_type, o.type); + if ("orthographic"s == str) + return property("orthographic", this->_orthographic, o.orthographic); + if ("perspective"s == str) + return property("perspective", this->_perspective, o.perspective); + if ("type"s == str) + return property("type", this->_type, o.type); return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } @@ -2545,25 +4135,45 @@ CesiumJsonReader::IJsonHandler* CameraJsonHandler::readObjectKeyCamera(const std namespace CesiumGltfReader { -CameraPerspectiveJsonHandler::CameraPerspectiveJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _aspectRatio(), _yfov(), _zfar(), _znear() {} - -void CameraPerspectiveJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::CameraPerspective* pObject) { +CameraPerspectiveJsonHandler::CameraPerspectiveJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + _aspectRatio(), + _yfov(), + _zfar(), + _znear() {} + +void CameraPerspectiveJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::CameraPerspective* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* CameraPerspectiveJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +CameraPerspectiveJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyCameraPerspective(CesiumGltf::CameraPerspective::TypeName, str, *this->_pObject); + return this->readObjectKeyCameraPerspective( + CesiumGltf::CameraPerspective::TypeName, + str, + *this->_pObject); } -CesiumJsonReader::IJsonHandler* CameraPerspectiveJsonHandler::readObjectKeyCameraPerspective(const std::string& objectType, const std::string_view& str, CesiumGltf::CameraPerspective& o) { +CesiumJsonReader::IJsonHandler* +CameraPerspectiveJsonHandler::readObjectKeyCameraPerspective( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::CameraPerspective& o) { using namespace std::string_literals; - if ("aspectRatio"s == str) return property("aspectRatio", this->_aspectRatio, o.aspectRatio); - if ("yfov"s == str) return property("yfov", this->_yfov, o.yfov); - if ("zfar"s == str) return property("zfar", this->_zfar, o.zfar); - if ("znear"s == str) return property("znear", this->_znear, o.znear); + if ("aspectRatio"s == str) + return property("aspectRatio", this->_aspectRatio, o.aspectRatio); + if ("yfov"s == str) + return property("yfov", this->_yfov, o.yfov); + if ("zfar"s == str) + return property("zfar", this->_zfar, o.zfar); + if ("znear"s == str) + return property("znear", this->_znear, o.znear); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -2580,25 +4190,45 @@ CesiumJsonReader::IJsonHandler* CameraPerspectiveJsonHandler::readObjectKeyCamer namespace CesiumGltfReader { -CameraOrthographicJsonHandler::CameraOrthographicJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _xmag(), _ymag(), _zfar(), _znear() {} - -void CameraOrthographicJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::CameraOrthographic* pObject) { +CameraOrthographicJsonHandler::CameraOrthographicJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + _xmag(), + _ymag(), + _zfar(), + _znear() {} + +void CameraOrthographicJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::CameraOrthographic* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* CameraOrthographicJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +CameraOrthographicJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyCameraOrthographic(CesiumGltf::CameraOrthographic::TypeName, str, *this->_pObject); + return this->readObjectKeyCameraOrthographic( + CesiumGltf::CameraOrthographic::TypeName, + str, + *this->_pObject); } -CesiumJsonReader::IJsonHandler* CameraOrthographicJsonHandler::readObjectKeyCameraOrthographic(const std::string& objectType, const std::string_view& str, CesiumGltf::CameraOrthographic& o) { +CesiumJsonReader::IJsonHandler* +CameraOrthographicJsonHandler::readObjectKeyCameraOrthographic( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::CameraOrthographic& o) { using namespace std::string_literals; - if ("xmag"s == str) return property("xmag", this->_xmag, o.xmag); - if ("ymag"s == str) return property("ymag", this->_ymag, o.ymag); - if ("zfar"s == str) return property("zfar", this->_zfar, o.zfar); - if ("znear"s == str) return property("znear", this->_znear, o.znear); + if ("xmag"s == str) + return property("xmag", this->_xmag, o.xmag); + if ("ymag"s == str) + return property("ymag", this->_ymag, o.ymag); + if ("zfar"s == str) + return property("zfar", this->_zfar, o.zfar); + if ("znear"s == str) + return property("znear", this->_znear, o.znear); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -2615,26 +4245,47 @@ CesiumJsonReader::IJsonHandler* CameraOrthographicJsonHandler::readObjectKeyCame namespace CesiumGltfReader { -BufferViewJsonHandler::BufferViewJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumGltfReader::NamedObjectJsonHandler(context), _buffer(), _byteOffset(), _byteLength(), _byteStride(), _target() {} - -void BufferViewJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::BufferView* pObject) { +BufferViewJsonHandler::BufferViewJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumGltfReader::NamedObjectJsonHandler(context), + _buffer(), + _byteOffset(), + _byteLength(), + _byteStride(), + _target() {} + +void BufferViewJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::BufferView* pObject) { CesiumGltfReader::NamedObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* BufferViewJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +BufferViewJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyBufferView(CesiumGltf::BufferView::TypeName, str, *this->_pObject); + return this->readObjectKeyBufferView( + CesiumGltf::BufferView::TypeName, + str, + *this->_pObject); } -CesiumJsonReader::IJsonHandler* BufferViewJsonHandler::readObjectKeyBufferView(const std::string& objectType, const std::string_view& str, CesiumGltf::BufferView& o) { +CesiumJsonReader::IJsonHandler* BufferViewJsonHandler::readObjectKeyBufferView( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::BufferView& o) { using namespace std::string_literals; - if ("buffer"s == str) return property("buffer", this->_buffer, o.buffer); - if ("byteOffset"s == str) return property("byteOffset", this->_byteOffset, o.byteOffset); - if ("byteLength"s == str) return property("byteLength", this->_byteLength, o.byteLength); - if ("byteStride"s == str) return property("byteStride", this->_byteStride, o.byteStride); - if ("target"s == str) return property("target", this->_target, o.target); + if ("buffer"s == str) + return property("buffer", this->_buffer, o.buffer); + if ("byteOffset"s == str) + return property("byteOffset", this->_byteOffset, o.byteOffset); + if ("byteLength"s == str) + return property("byteLength", this->_byteLength, o.byteLength); + if ("byteStride"s == str) + return property("byteStride", this->_byteStride, o.byteStride); + if ("target"s == str) + return property("target", this->_target, o.target); return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } @@ -2651,23 +4302,38 @@ CesiumJsonReader::IJsonHandler* BufferViewJsonHandler::readObjectKeyBufferView(c namespace CesiumGltfReader { -BufferJsonHandler::BufferJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumGltfReader::NamedObjectJsonHandler(context), _uri(), _byteLength() {} +BufferJsonHandler::BufferJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumGltfReader::NamedObjectJsonHandler(context), + _uri(), + _byteLength() {} -void BufferJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::Buffer* pObject) { +void BufferJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::Buffer* pObject) { CesiumGltfReader::NamedObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* BufferJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +BufferJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyBuffer(CesiumGltf::Buffer::TypeName, str, *this->_pObject); + return this->readObjectKeyBuffer( + CesiumGltf::Buffer::TypeName, + str, + *this->_pObject); } -CesiumJsonReader::IJsonHandler* BufferJsonHandler::readObjectKeyBuffer(const std::string& objectType, const std::string_view& str, CesiumGltf::Buffer& o) { +CesiumJsonReader::IJsonHandler* BufferJsonHandler::readObjectKeyBuffer( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::Buffer& o) { using namespace std::string_literals; - if ("uri"s == str) return property("uri", this->_uri, o.uri); - if ("byteLength"s == str) return property("byteLength", this->_byteLength, o.byteLength); + if ("uri"s == str) + return property("uri", this->_uri, o.uri); + if ("byteLength"s == str) + return property("byteLength", this->_byteLength, o.byteLength); return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } @@ -2684,25 +4350,44 @@ CesiumJsonReader::IJsonHandler* BufferJsonHandler::readObjectKeyBuffer(const std namespace CesiumGltfReader { -AssetJsonHandler::AssetJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _copyright(), _generator(), _version(), _minVersion() {} - -void AssetJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::Asset* pObject) { +AssetJsonHandler::AssetJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + _copyright(), + _generator(), + _version(), + _minVersion() {} + +void AssetJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::Asset* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* AssetJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +AssetJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyAsset(CesiumGltf::Asset::TypeName, str, *this->_pObject); + return this->readObjectKeyAsset( + CesiumGltf::Asset::TypeName, + str, + *this->_pObject); } -CesiumJsonReader::IJsonHandler* AssetJsonHandler::readObjectKeyAsset(const std::string& objectType, const std::string_view& str, CesiumGltf::Asset& o) { +CesiumJsonReader::IJsonHandler* AssetJsonHandler::readObjectKeyAsset( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::Asset& o) { using namespace std::string_literals; - if ("copyright"s == str) return property("copyright", this->_copyright, o.copyright); - if ("generator"s == str) return property("generator", this->_generator, o.generator); - if ("version"s == str) return property("version", this->_version, o.version); - if ("minVersion"s == str) return property("minVersion", this->_minVersion, o.minVersion); + if ("copyright"s == str) + return property("copyright", this->_copyright, o.copyright); + if ("generator"s == str) + return property("generator", this->_generator, o.generator); + if ("version"s == str) + return property("version", this->_version, o.version); + if ("minVersion"s == str) + return property("minVersion", this->_minVersion, o.minVersion); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -2719,23 +4404,38 @@ CesiumJsonReader::IJsonHandler* AssetJsonHandler::readObjectKeyAsset(const std:: namespace CesiumGltfReader { -AnimationJsonHandler::AnimationJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumGltfReader::NamedObjectJsonHandler(context), _channels(context), _samplers(context) {} +AnimationJsonHandler::AnimationJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumGltfReader::NamedObjectJsonHandler(context), + _channels(context), + _samplers(context) {} -void AnimationJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::Animation* pObject) { +void AnimationJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::Animation* pObject) { CesiumGltfReader::NamedObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* AnimationJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +AnimationJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyAnimation(CesiumGltf::Animation::TypeName, str, *this->_pObject); + return this->readObjectKeyAnimation( + CesiumGltf::Animation::TypeName, + str, + *this->_pObject); } -CesiumJsonReader::IJsonHandler* AnimationJsonHandler::readObjectKeyAnimation(const std::string& objectType, const std::string_view& str, CesiumGltf::Animation& o) { +CesiumJsonReader::IJsonHandler* AnimationJsonHandler::readObjectKeyAnimation( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::Animation& o) { using namespace std::string_literals; - if ("channels"s == str) return property("channels", this->_channels, o.channels); - if ("samplers"s == str) return property("samplers", this->_samplers, o.samplers); + if ("channels"s == str) + return property("channels", this->_channels, o.channels); + if ("samplers"s == str) + return property("samplers", this->_samplers, o.samplers); return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } @@ -2752,24 +4452,42 @@ CesiumJsonReader::IJsonHandler* AnimationJsonHandler::readObjectKeyAnimation(con namespace CesiumGltfReader { -AnimationSamplerJsonHandler::AnimationSamplerJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _input(), _interpolation(), _output() {} +AnimationSamplerJsonHandler::AnimationSamplerJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + _input(), + _interpolation(), + _output() {} -void AnimationSamplerJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::AnimationSampler* pObject) { +void AnimationSamplerJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::AnimationSampler* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* AnimationSamplerJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +AnimationSamplerJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyAnimationSampler(CesiumGltf::AnimationSampler::TypeName, str, *this->_pObject); + return this->readObjectKeyAnimationSampler( + CesiumGltf::AnimationSampler::TypeName, + str, + *this->_pObject); } -CesiumJsonReader::IJsonHandler* AnimationSamplerJsonHandler::readObjectKeyAnimationSampler(const std::string& objectType, const std::string_view& str, CesiumGltf::AnimationSampler& o) { +CesiumJsonReader::IJsonHandler* +AnimationSamplerJsonHandler::readObjectKeyAnimationSampler( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::AnimationSampler& o) { using namespace std::string_literals; - if ("input"s == str) return property("input", this->_input, o.input); - if ("interpolation"s == str) return property("interpolation", this->_interpolation, o.interpolation); - if ("output"s == str) return property("output", this->_output, o.output); + if ("input"s == str) + return property("input", this->_input, o.input); + if ("interpolation"s == str) + return property("interpolation", this->_interpolation, o.interpolation); + if ("output"s == str) + return property("output", this->_output, o.output); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -2786,23 +4504,39 @@ CesiumJsonReader::IJsonHandler* AnimationSamplerJsonHandler::readObjectKeyAnimat namespace CesiumGltfReader { -AnimationChannelJsonHandler::AnimationChannelJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _sampler(), _target(context) {} +AnimationChannelJsonHandler::AnimationChannelJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + _sampler(), + _target(context) {} -void AnimationChannelJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::AnimationChannel* pObject) { +void AnimationChannelJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::AnimationChannel* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* AnimationChannelJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +AnimationChannelJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyAnimationChannel(CesiumGltf::AnimationChannel::TypeName, str, *this->_pObject); + return this->readObjectKeyAnimationChannel( + CesiumGltf::AnimationChannel::TypeName, + str, + *this->_pObject); } -CesiumJsonReader::IJsonHandler* AnimationChannelJsonHandler::readObjectKeyAnimationChannel(const std::string& objectType, const std::string_view& str, CesiumGltf::AnimationChannel& o) { +CesiumJsonReader::IJsonHandler* +AnimationChannelJsonHandler::readObjectKeyAnimationChannel( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::AnimationChannel& o) { using namespace std::string_literals; - if ("sampler"s == str) return property("sampler", this->_sampler, o.sampler); - if ("target"s == str) return property("target", this->_target, o.target); + if ("sampler"s == str) + return property("sampler", this->_sampler, o.sampler); + if ("target"s == str) + return property("target", this->_target, o.target); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -2819,23 +4553,39 @@ CesiumJsonReader::IJsonHandler* AnimationChannelJsonHandler::readObjectKeyAnimat namespace CesiumGltfReader { -AnimationChannelTargetJsonHandler::AnimationChannelTargetJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _node(), _path() {} +AnimationChannelTargetJsonHandler::AnimationChannelTargetJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + _node(), + _path() {} -void AnimationChannelTargetJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::AnimationChannelTarget* pObject) { +void AnimationChannelTargetJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::AnimationChannelTarget* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* AnimationChannelTargetJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +AnimationChannelTargetJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyAnimationChannelTarget(CesiumGltf::AnimationChannelTarget::TypeName, str, *this->_pObject); + return this->readObjectKeyAnimationChannelTarget( + CesiumGltf::AnimationChannelTarget::TypeName, + str, + *this->_pObject); } -CesiumJsonReader::IJsonHandler* AnimationChannelTargetJsonHandler::readObjectKeyAnimationChannelTarget(const std::string& objectType, const std::string_view& str, CesiumGltf::AnimationChannelTarget& o) { +CesiumJsonReader::IJsonHandler* +AnimationChannelTargetJsonHandler::readObjectKeyAnimationChannelTarget( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::AnimationChannelTarget& o) { using namespace std::string_literals; - if ("node"s == str) return property("node", this->_node, o.node); - if ("path"s == str) return property("path", this->_path, o.path); + if ("node"s == str) + return property("node", this->_node, o.node); + if ("path"s == str) + return property("path", this->_path, o.path); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -2852,30 +4602,59 @@ CesiumJsonReader::IJsonHandler* AnimationChannelTargetJsonHandler::readObjectKey namespace CesiumGltfReader { -AccessorJsonHandler::AccessorJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumGltfReader::NamedObjectJsonHandler(context), _bufferView(), _byteOffset(), _componentType(), _normalized(), _count(), _type(), _max(), _min(), _sparse(context) {} - -void AccessorJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::Accessor* pObject) { +AccessorJsonHandler::AccessorJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumGltfReader::NamedObjectJsonHandler(context), + _bufferView(), + _byteOffset(), + _componentType(), + _normalized(), + _count(), + _type(), + _max(), + _min(), + _sparse(context) {} + +void AccessorJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::Accessor* pObject) { CesiumGltfReader::NamedObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* AccessorJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +AccessorJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyAccessor(CesiumGltf::Accessor::TypeName, str, *this->_pObject); + return this->readObjectKeyAccessor( + CesiumGltf::Accessor::TypeName, + str, + *this->_pObject); } -CesiumJsonReader::IJsonHandler* AccessorJsonHandler::readObjectKeyAccessor(const std::string& objectType, const std::string_view& str, CesiumGltf::Accessor& o) { +CesiumJsonReader::IJsonHandler* AccessorJsonHandler::readObjectKeyAccessor( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::Accessor& o) { using namespace std::string_literals; - if ("bufferView"s == str) return property("bufferView", this->_bufferView, o.bufferView); - if ("byteOffset"s == str) return property("byteOffset", this->_byteOffset, o.byteOffset); - if ("componentType"s == str) return property("componentType", this->_componentType, o.componentType); - if ("normalized"s == str) return property("normalized", this->_normalized, o.normalized); - if ("count"s == str) return property("count", this->_count, o.count); - if ("type"s == str) return property("type", this->_type, o.type); - if ("max"s == str) return property("max", this->_max, o.max); - if ("min"s == str) return property("min", this->_min, o.min); - if ("sparse"s == str) return property("sparse", this->_sparse, o.sparse); + if ("bufferView"s == str) + return property("bufferView", this->_bufferView, o.bufferView); + if ("byteOffset"s == str) + return property("byteOffset", this->_byteOffset, o.byteOffset); + if ("componentType"s == str) + return property("componentType", this->_componentType, o.componentType); + if ("normalized"s == str) + return property("normalized", this->_normalized, o.normalized); + if ("count"s == str) + return property("count", this->_count, o.count); + if ("type"s == str) + return property("type", this->_type, o.type); + if ("max"s == str) + return property("max", this->_max, o.max); + if ("min"s == str) + return property("min", this->_min, o.min); + if ("sparse"s == str) + return property("sparse", this->_sparse, o.sparse); return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } @@ -2892,24 +4671,42 @@ CesiumJsonReader::IJsonHandler* AccessorJsonHandler::readObjectKeyAccessor(const namespace CesiumGltfReader { -AccessorSparseJsonHandler::AccessorSparseJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _count(), _indices(context), _values(context) {} +AccessorSparseJsonHandler::AccessorSparseJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + _count(), + _indices(context), + _values(context) {} -void AccessorSparseJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::AccessorSparse* pObject) { +void AccessorSparseJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::AccessorSparse* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* AccessorSparseJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +AccessorSparseJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyAccessorSparse(CesiumGltf::AccessorSparse::TypeName, str, *this->_pObject); + return this->readObjectKeyAccessorSparse( + CesiumGltf::AccessorSparse::TypeName, + str, + *this->_pObject); } -CesiumJsonReader::IJsonHandler* AccessorSparseJsonHandler::readObjectKeyAccessorSparse(const std::string& objectType, const std::string_view& str, CesiumGltf::AccessorSparse& o) { +CesiumJsonReader::IJsonHandler* +AccessorSparseJsonHandler::readObjectKeyAccessorSparse( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::AccessorSparse& o) { using namespace std::string_literals; - if ("count"s == str) return property("count", this->_count, o.count); - if ("indices"s == str) return property("indices", this->_indices, o.indices); - if ("values"s == str) return property("values", this->_values, o.values); + if ("count"s == str) + return property("count", this->_count, o.count); + if ("indices"s == str) + return property("indices", this->_indices, o.indices); + if ("values"s == str) + return property("values", this->_values, o.values); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -2926,23 +4723,39 @@ CesiumJsonReader::IJsonHandler* AccessorSparseJsonHandler::readObjectKeyAccessor namespace CesiumGltfReader { -AccessorSparseValuesJsonHandler::AccessorSparseValuesJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _bufferView(), _byteOffset() {} +AccessorSparseValuesJsonHandler::AccessorSparseValuesJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + _bufferView(), + _byteOffset() {} -void AccessorSparseValuesJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::AccessorSparseValues* pObject) { +void AccessorSparseValuesJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::AccessorSparseValues* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* AccessorSparseValuesJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +AccessorSparseValuesJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyAccessorSparseValues(CesiumGltf::AccessorSparseValues::TypeName, str, *this->_pObject); + return this->readObjectKeyAccessorSparseValues( + CesiumGltf::AccessorSparseValues::TypeName, + str, + *this->_pObject); } -CesiumJsonReader::IJsonHandler* AccessorSparseValuesJsonHandler::readObjectKeyAccessorSparseValues(const std::string& objectType, const std::string_view& str, CesiumGltf::AccessorSparseValues& o) { +CesiumJsonReader::IJsonHandler* +AccessorSparseValuesJsonHandler::readObjectKeyAccessorSparseValues( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::AccessorSparseValues& o) { using namespace std::string_literals; - if ("bufferView"s == str) return property("bufferView", this->_bufferView, o.bufferView); - if ("byteOffset"s == str) return property("byteOffset", this->_byteOffset, o.byteOffset); + if ("bufferView"s == str) + return property("bufferView", this->_bufferView, o.bufferView); + if ("byteOffset"s == str) + return property("byteOffset", this->_byteOffset, o.byteOffset); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } @@ -2959,24 +4772,42 @@ CesiumJsonReader::IJsonHandler* AccessorSparseValuesJsonHandler::readObjectKeyAc namespace CesiumGltfReader { -AccessorSparseIndicesJsonHandler::AccessorSparseIndicesJsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _bufferView(), _byteOffset(), _componentType() {} +AccessorSparseIndicesJsonHandler::AccessorSparseIndicesJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + _bufferView(), + _byteOffset(), + _componentType() {} -void AccessorSparseIndicesJsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, CesiumGltf::AccessorSparseIndices* pObject) { +void AccessorSparseIndicesJsonHandler::reset( + CesiumJsonReader::IJsonHandler* pParentHandler, + CesiumGltf::AccessorSparseIndices* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } -CesiumJsonReader::IJsonHandler* AccessorSparseIndicesJsonHandler::readObjectKey(const std::string_view& str) { +CesiumJsonReader::IJsonHandler* +AccessorSparseIndicesJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyAccessorSparseIndices(CesiumGltf::AccessorSparseIndices::TypeName, str, *this->_pObject); + return this->readObjectKeyAccessorSparseIndices( + CesiumGltf::AccessorSparseIndices::TypeName, + str, + *this->_pObject); } -CesiumJsonReader::IJsonHandler* AccessorSparseIndicesJsonHandler::readObjectKeyAccessorSparseIndices(const std::string& objectType, const std::string_view& str, CesiumGltf::AccessorSparseIndices& o) { +CesiumJsonReader::IJsonHandler* +AccessorSparseIndicesJsonHandler::readObjectKeyAccessorSparseIndices( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::AccessorSparseIndices& o) { using namespace std::string_literals; - if ("bufferView"s == str) return property("bufferView", this->_bufferView, o.bufferView); - if ("byteOffset"s == str) return property("byteOffset", this->_byteOffset, o.byteOffset); - if ("componentType"s == str) return property("componentType", this->_componentType, o.componentType); + if ("bufferView"s == str) + return property("bufferView", this->_bufferView, o.bufferView); + if ("byteOffset"s == str) + return property("byteOffset", this->_byteOffset, o.byteOffset); + if ("componentType"s == str) + return property("componentType", this->_componentType, o.componentType); return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } From 55ed63a945fb554940ce84485595beee9f8ab7e6 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Fri, 16 Jun 2023 16:08:35 -0400 Subject: [PATCH 077/421] Regenerate EXT_structural_metadata classes without prefix --- ...onExtStructuralMetadataClass.h => Class.h} | 13 +- ...etadataClassProperty.h => ClassProperty.h} | 5 +- ...sionExtStructuralMetadataEnum.h => Enum.h} | 10 +- ...ucturalMetadataEnumValue.h => EnumValue.h} | 6 +- .../ExtensionModelExtStructuralMetadata.h | 19 +- ...ropertyAttribute.h => PropertyAttribute.h} | 11 +- ...Property.h => PropertyAttributeProperty.h} | 7 +- ...etadataPropertyTable.h => PropertyTable.h} | 12 +- ...ableProperty.h => PropertyTableProperty.h} | 5 +- ...ataPropertyTexture.h => PropertyTexture.h} | 11 +- ...reProperty.h => PropertyTextureProperty.h} | 6 +- ...ExtStructuralMetadataSchema.h => Schema.h} | 19 +- .../generated/src/ClassJsonHandler.h | 41 +++ ...onHandler.h => ClassPropertyJsonHandler.h} | 18 +- .../generated/src/EnumJsonHandler.h | 42 +++ ...ueJsonHandler.h => EnumValueJsonHandler.h} | 18 +- ...sionExtStructuralMetadataEnumJsonHandler.h | 46 --- ...uralMetadataPropertyAttributeJsonHandler.h | 46 --- ...onExtStructuralMetadataSchemaJsonHandler.h | 52 --- ...ionModelExtStructuralMetadataJsonHandler.h | 28 +- .../generated/src/GeneratedJsonHandlers.cpp | 337 ++++++++---------- ...ndler.h => PropertyAttributeJsonHandler.h} | 27 +- ...=> PropertyAttributePropertyJsonHandler.h} | 20 +- ...onHandler.h => PropertyTableJsonHandler.h} | 24 +- ...r.h => PropertyTablePropertyJsonHandler.h} | 19 +- ...Handler.h => PropertyTextureJsonHandler.h} | 25 +- ...h => PropertyTexturePropertyJsonHandler.h} | 21 +- .../generated/src/SchemaJsonHandler.h | 45 +++ .../generated/src/registerExtensions.cpp | 123 ++++--- .../generated/src/ModelJsonWriter.cpp | 125 +++---- .../generated/src/ModelJsonWriter.h | 94 +++-- tools/generate-classes/glTF.json | 22 +- 32 files changed, 603 insertions(+), 694 deletions(-) rename CesiumGltf/generated/include/CesiumGltf/{ExtensionExtStructuralMetadataClass.h => Class.h} (66%) rename CesiumGltf/generated/include/CesiumGltf/{ExtensionExtStructuralMetadataClassProperty.h => ClassProperty.h} (97%) rename CesiumGltf/generated/include/CesiumGltf/{ExtensionExtStructuralMetadataEnum.h => Enum.h} (79%) rename CesiumGltf/generated/include/CesiumGltf/{ExtensionExtStructuralMetadataEnumValue.h => EnumValue.h} (72%) rename CesiumGltf/generated/include/CesiumGltf/{ExtensionExtStructuralMetadataPropertyAttribute.h => PropertyAttribute.h} (72%) rename CesiumGltf/generated/include/CesiumGltf/{ExtensionExtStructuralMetadataPropertyAttributeProperty.h => PropertyAttributeProperty.h} (87%) rename CesiumGltf/generated/include/CesiumGltf/{ExtensionExtStructuralMetadataPropertyTable.h => PropertyTable.h} (74%) rename CesiumGltf/generated/include/CesiumGltf/{ExtensionExtStructuralMetadataPropertyTableProperty.h => PropertyTableProperty.h} (96%) rename CesiumGltf/generated/include/CesiumGltf/{ExtensionExtStructuralMetadataPropertyTexture.h => PropertyTexture.h} (73%) rename CesiumGltf/generated/include/CesiumGltf/{ExtensionExtStructuralMetadataPropertyTextureProperty.h => PropertyTextureProperty.h} (89%) rename CesiumGltf/generated/include/CesiumGltf/{ExtensionExtStructuralMetadataSchema.h => Schema.h} (70%) create mode 100644 CesiumGltfReader/generated/src/ClassJsonHandler.h rename CesiumGltfReader/generated/src/{ExtensionExtStructuralMetadataClassPropertyJsonHandler.h => ClassPropertyJsonHandler.h} (72%) create mode 100644 CesiumGltfReader/generated/src/EnumJsonHandler.h rename CesiumGltfReader/generated/src/{ExtensionExtStructuralMetadataEnumValueJsonHandler.h => EnumValueJsonHandler.h} (59%) delete mode 100644 CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataEnumJsonHandler.h delete mode 100644 CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataPropertyAttributeJsonHandler.h delete mode 100644 CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataSchemaJsonHandler.h rename CesiumGltfReader/generated/src/{ExtensionExtStructuralMetadataClassJsonHandler.h => PropertyAttributeJsonHandler.h} (50%) rename CesiumGltfReader/generated/src/{ExtensionExtStructuralMetadataPropertyAttributePropertyJsonHandler.h => PropertyAttributePropertyJsonHandler.h} (59%) rename CesiumGltfReader/generated/src/{ExtensionExtStructuralMetadataPropertyTableJsonHandler.h => PropertyTableJsonHandler.h} (53%) rename CesiumGltfReader/generated/src/{ExtensionExtStructuralMetadataPropertyTablePropertyJsonHandler.h => PropertyTablePropertyJsonHandler.h} (66%) rename CesiumGltfReader/generated/src/{ExtensionExtStructuralMetadataPropertyTextureJsonHandler.h => PropertyTextureJsonHandler.h} (50%) rename CesiumGltfReader/generated/src/{ExtensionExtStructuralMetadataPropertyTexturePropertyJsonHandler.h => PropertyTexturePropertyJsonHandler.h} (59%) create mode 100644 CesiumGltfReader/generated/src/SchemaJsonHandler.h diff --git a/CesiumGltf/generated/include/CesiumGltf/ExtensionExtStructuralMetadataClass.h b/CesiumGltf/generated/include/CesiumGltf/Class.h similarity index 66% rename from CesiumGltf/generated/include/CesiumGltf/ExtensionExtStructuralMetadataClass.h rename to CesiumGltf/generated/include/CesiumGltf/Class.h index caaed0ddf..ac548d150 100644 --- a/CesiumGltf/generated/include/CesiumGltf/ExtensionExtStructuralMetadataClass.h +++ b/CesiumGltf/generated/include/CesiumGltf/Class.h @@ -2,7 +2,7 @@ // DO NOT EDIT THIS FILE! #pragma once -#include "CesiumGltf/ExtensionExtStructuralMetadataClassProperty.h" +#include "CesiumGltf/ClassProperty.h" #include "CesiumGltf/Library.h" #include @@ -15,10 +15,8 @@ namespace CesiumGltf { /** * @brief A class containing a set of properties. */ -struct CESIUMGLTF_API ExtensionExtStructuralMetadataClass final - : public CesiumUtility::ExtensibleObject { - static inline constexpr const char* TypeName = - "ExtensionExtStructuralMetadataClass"; +struct CESIUMGLTF_API Class final : public CesiumUtility::ExtensibleObject { + static inline constexpr const char* TypeName = "Class"; /** * @brief The name of the class, e.g. for display purposes. @@ -35,9 +33,6 @@ struct CESIUMGLTF_API ExtensionExtStructuralMetadataClass final * object defining the property. Property IDs must be alphanumeric identifiers * matching the regular expression `^[a-zA-Z_][a-zA-Z0-9_]*$`. */ - std::unordered_map< - std::string, - CesiumGltf::ExtensionExtStructuralMetadataClassProperty> - properties; + std::unordered_map properties; }; } // namespace CesiumGltf diff --git a/CesiumGltf/generated/include/CesiumGltf/ExtensionExtStructuralMetadataClassProperty.h b/CesiumGltf/generated/include/CesiumGltf/ClassProperty.h similarity index 97% rename from CesiumGltf/generated/include/CesiumGltf/ExtensionExtStructuralMetadataClassProperty.h rename to CesiumGltf/generated/include/CesiumGltf/ClassProperty.h index 245866180..fe07dfca3 100644 --- a/CesiumGltf/generated/include/CesiumGltf/ExtensionExtStructuralMetadataClassProperty.h +++ b/CesiumGltf/generated/include/CesiumGltf/ClassProperty.h @@ -15,10 +15,9 @@ namespace CesiumGltf { /** * @brief A class property. */ -struct CESIUMGLTF_API ExtensionExtStructuralMetadataClassProperty final +struct CESIUMGLTF_API ClassProperty final : public CesiumUtility::ExtensibleObject { - static inline constexpr const char* TypeName = - "ExtensionExtStructuralMetadataClassProperty"; + static inline constexpr const char* TypeName = "ClassProperty"; /** * @brief Known values for The element type. diff --git a/CesiumGltf/generated/include/CesiumGltf/ExtensionExtStructuralMetadataEnum.h b/CesiumGltf/generated/include/CesiumGltf/Enum.h similarity index 79% rename from CesiumGltf/generated/include/CesiumGltf/ExtensionExtStructuralMetadataEnum.h rename to CesiumGltf/generated/include/CesiumGltf/Enum.h index a9c95a7df..2aac09e6a 100644 --- a/CesiumGltf/generated/include/CesiumGltf/ExtensionExtStructuralMetadataEnum.h +++ b/CesiumGltf/generated/include/CesiumGltf/Enum.h @@ -2,7 +2,7 @@ // DO NOT EDIT THIS FILE! #pragma once -#include "CesiumGltf/ExtensionExtStructuralMetadataEnumValue.h" +#include "CesiumGltf/EnumValue.h" #include "CesiumGltf/Library.h" #include @@ -15,10 +15,8 @@ namespace CesiumGltf { /** * @brief An object defining the values of an enum. */ -struct CESIUMGLTF_API ExtensionExtStructuralMetadataEnum final - : public CesiumUtility::ExtensibleObject { - static inline constexpr const char* TypeName = - "ExtensionExtStructuralMetadataEnum"; +struct CESIUMGLTF_API Enum final : public CesiumUtility::ExtensibleObject { + static inline constexpr const char* TypeName = "Enum"; /** * @brief Known values for The type of the integer enum value. @@ -63,6 +61,6 @@ struct CESIUMGLTF_API ExtensionExtStructuralMetadataEnum final * @brief An array of enum values. Duplicate names or duplicate integer values * are not allowed. */ - std::vector values; + std::vector values; }; } // namespace CesiumGltf diff --git a/CesiumGltf/generated/include/CesiumGltf/ExtensionExtStructuralMetadataEnumValue.h b/CesiumGltf/generated/include/CesiumGltf/EnumValue.h similarity index 72% rename from CesiumGltf/generated/include/CesiumGltf/ExtensionExtStructuralMetadataEnumValue.h rename to CesiumGltf/generated/include/CesiumGltf/EnumValue.h index 57bc28339..d1c505a4e 100644 --- a/CesiumGltf/generated/include/CesiumGltf/ExtensionExtStructuralMetadataEnumValue.h +++ b/CesiumGltf/generated/include/CesiumGltf/EnumValue.h @@ -14,10 +14,8 @@ namespace CesiumGltf { /** * @brief An enum value. */ -struct CESIUMGLTF_API ExtensionExtStructuralMetadataEnumValue final - : public CesiumUtility::ExtensibleObject { - static inline constexpr const char* TypeName = - "ExtensionExtStructuralMetadataEnumValue"; +struct CESIUMGLTF_API EnumValue final : public CesiumUtility::ExtensibleObject { + static inline constexpr const char* TypeName = "EnumValue"; /** * @brief The name of the enum value. diff --git a/CesiumGltf/generated/include/CesiumGltf/ExtensionModelExtStructuralMetadata.h b/CesiumGltf/generated/include/CesiumGltf/ExtensionModelExtStructuralMetadata.h index 270019b92..f78ace942 100644 --- a/CesiumGltf/generated/include/CesiumGltf/ExtensionModelExtStructuralMetadata.h +++ b/CesiumGltf/generated/include/CesiumGltf/ExtensionModelExtStructuralMetadata.h @@ -2,11 +2,11 @@ // DO NOT EDIT THIS FILE! #pragma once -#include "CesiumGltf/ExtensionExtStructuralMetadataPropertyAttribute.h" -#include "CesiumGltf/ExtensionExtStructuralMetadataPropertyTable.h" -#include "CesiumGltf/ExtensionExtStructuralMetadataPropertyTexture.h" -#include "CesiumGltf/ExtensionExtStructuralMetadataSchema.h" #include "CesiumGltf/Library.h" +#include "CesiumGltf/PropertyAttribute.h" +#include "CesiumGltf/PropertyTable.h" +#include "CesiumGltf/PropertyTexture.h" +#include "CesiumGltf/Schema.h" #include @@ -28,7 +28,7 @@ struct CESIUMGLTF_API ExtensionModelExtStructuralMetadata final /** * @brief An object defining classes and enums. */ - std::optional schema; + std::optional schema; /** * @brief The URI (or IRI) of the external schema file. @@ -39,21 +39,18 @@ struct CESIUMGLTF_API ExtensionModelExtStructuralMetadata final * @brief An array of property table definitions, which may be referenced by * index. */ - std::vector - propertyTables; + std::vector propertyTables; /** * @brief An array of property texture definitions, which may be referenced by * index. */ - std::vector - propertyTextures; + std::vector propertyTextures; /** * @brief An array of property attribute definitions, which may be referenced * by index. */ - std::vector - propertyAttributes; + std::vector propertyAttributes; }; } // namespace CesiumGltf diff --git a/CesiumGltf/generated/include/CesiumGltf/ExtensionExtStructuralMetadataPropertyAttribute.h b/CesiumGltf/generated/include/CesiumGltf/PropertyAttribute.h similarity index 72% rename from CesiumGltf/generated/include/CesiumGltf/ExtensionExtStructuralMetadataPropertyAttribute.h rename to CesiumGltf/generated/include/CesiumGltf/PropertyAttribute.h index 9222d9571..c7256721b 100644 --- a/CesiumGltf/generated/include/CesiumGltf/ExtensionExtStructuralMetadataPropertyAttribute.h +++ b/CesiumGltf/generated/include/CesiumGltf/PropertyAttribute.h @@ -2,8 +2,8 @@ // DO NOT EDIT THIS FILE! #pragma once -#include "CesiumGltf/ExtensionExtStructuralMetadataPropertyAttributeProperty.h" #include "CesiumGltf/Library.h" +#include "CesiumGltf/PropertyAttributeProperty.h" #include @@ -16,10 +16,9 @@ namespace CesiumGltf { * @brief Properties conforming to a class, organized as property values stored * in attributes. */ -struct CESIUMGLTF_API ExtensionExtStructuralMetadataPropertyAttribute final +struct CESIUMGLTF_API PropertyAttribute final : public CesiumUtility::ExtensibleObject { - static inline constexpr const char* TypeName = - "ExtensionExtStructuralMetadataPropertyAttribute"; + static inline constexpr const char* TypeName = "PropertyAttribute"; /** * @brief The name of the property attribute, e.g. for display purposes. @@ -38,9 +37,7 @@ struct CESIUMGLTF_API ExtensionExtStructuralMetadataPropertyAttribute final * property values are stored. Required properties must be included in this * dictionary. */ - std::unordered_map< - std::string, - CesiumGltf::ExtensionExtStructuralMetadataPropertyAttributeProperty> + std::unordered_map properties; }; } // namespace CesiumGltf diff --git a/CesiumGltf/generated/include/CesiumGltf/ExtensionExtStructuralMetadataPropertyAttributeProperty.h b/CesiumGltf/generated/include/CesiumGltf/PropertyAttributeProperty.h similarity index 87% rename from CesiumGltf/generated/include/CesiumGltf/ExtensionExtStructuralMetadataPropertyAttributeProperty.h rename to CesiumGltf/generated/include/CesiumGltf/PropertyAttributeProperty.h index 25405e9f3..25c6391ff 100644 --- a/CesiumGltf/generated/include/CesiumGltf/ExtensionExtStructuralMetadataPropertyAttributeProperty.h +++ b/CesiumGltf/generated/include/CesiumGltf/PropertyAttributeProperty.h @@ -14,10 +14,9 @@ namespace CesiumGltf { /** * @brief An attribute containing property values. */ -struct CESIUMGLTF_API ExtensionExtStructuralMetadataPropertyAttributeProperty - final : public CesiumUtility::ExtensibleObject { - static inline constexpr const char* TypeName = - "ExtensionExtStructuralMetadataPropertyAttributeProperty"; +struct CESIUMGLTF_API PropertyAttributeProperty final + : public CesiumUtility::ExtensibleObject { + static inline constexpr const char* TypeName = "PropertyAttributeProperty"; /** * @brief The name of the attribute containing property values. diff --git a/CesiumGltf/generated/include/CesiumGltf/ExtensionExtStructuralMetadataPropertyTable.h b/CesiumGltf/generated/include/CesiumGltf/PropertyTable.h similarity index 74% rename from CesiumGltf/generated/include/CesiumGltf/ExtensionExtStructuralMetadataPropertyTable.h rename to CesiumGltf/generated/include/CesiumGltf/PropertyTable.h index 004f3a206..4a4130cd6 100644 --- a/CesiumGltf/generated/include/CesiumGltf/ExtensionExtStructuralMetadataPropertyTable.h +++ b/CesiumGltf/generated/include/CesiumGltf/PropertyTable.h @@ -2,8 +2,8 @@ // DO NOT EDIT THIS FILE! #pragma once -#include "CesiumGltf/ExtensionExtStructuralMetadataPropertyTableProperty.h" #include "CesiumGltf/Library.h" +#include "CesiumGltf/PropertyTableProperty.h" #include @@ -17,10 +17,9 @@ namespace CesiumGltf { * @brief Properties conforming to a class, organized as property values stored * in binary columnar arrays. */ -struct CESIUMGLTF_API ExtensionExtStructuralMetadataPropertyTable final +struct CESIUMGLTF_API PropertyTable final : public CesiumUtility::ExtensibleObject { - static inline constexpr const char* TypeName = - "ExtensionExtStructuralMetadataPropertyTable"; + static inline constexpr const char* TypeName = "PropertyTable"; /** * @brief The name of the property table, e.g. for display purposes. @@ -44,9 +43,6 @@ struct CESIUMGLTF_API ExtensionExtStructuralMetadataPropertyTable final * property values are stored. Required properties must be included in this * dictionary. */ - std::unordered_map< - std::string, - CesiumGltf::ExtensionExtStructuralMetadataPropertyTableProperty> - properties; + std::unordered_map properties; }; } // namespace CesiumGltf diff --git a/CesiumGltf/generated/include/CesiumGltf/ExtensionExtStructuralMetadataPropertyTableProperty.h b/CesiumGltf/generated/include/CesiumGltf/PropertyTableProperty.h similarity index 96% rename from CesiumGltf/generated/include/CesiumGltf/ExtensionExtStructuralMetadataPropertyTableProperty.h rename to CesiumGltf/generated/include/CesiumGltf/PropertyTableProperty.h index 7d5751a4d..5b9cf839d 100644 --- a/CesiumGltf/generated/include/CesiumGltf/ExtensionExtStructuralMetadataPropertyTableProperty.h +++ b/CesiumGltf/generated/include/CesiumGltf/PropertyTableProperty.h @@ -15,10 +15,9 @@ namespace CesiumGltf { /** * @brief An array of binary property values. */ -struct CESIUMGLTF_API ExtensionExtStructuralMetadataPropertyTableProperty final +struct CESIUMGLTF_API PropertyTableProperty final : public CesiumUtility::ExtensibleObject { - static inline constexpr const char* TypeName = - "ExtensionExtStructuralMetadataPropertyTableProperty"; + static inline constexpr const char* TypeName = "PropertyTableProperty"; /** * @brief Known values for The type of values in `arrayOffsets`. diff --git a/CesiumGltf/generated/include/CesiumGltf/ExtensionExtStructuralMetadataPropertyTexture.h b/CesiumGltf/generated/include/CesiumGltf/PropertyTexture.h similarity index 73% rename from CesiumGltf/generated/include/CesiumGltf/ExtensionExtStructuralMetadataPropertyTexture.h rename to CesiumGltf/generated/include/CesiumGltf/PropertyTexture.h index 2b1ab5c32..6ea853c30 100644 --- a/CesiumGltf/generated/include/CesiumGltf/ExtensionExtStructuralMetadataPropertyTexture.h +++ b/CesiumGltf/generated/include/CesiumGltf/PropertyTexture.h @@ -2,8 +2,8 @@ // DO NOT EDIT THIS FILE! #pragma once -#include "CesiumGltf/ExtensionExtStructuralMetadataPropertyTextureProperty.h" #include "CesiumGltf/Library.h" +#include "CesiumGltf/PropertyTextureProperty.h" #include @@ -16,10 +16,9 @@ namespace CesiumGltf { * @brief Properties conforming to a class, organized as property values stored * in textures. */ -struct CESIUMGLTF_API ExtensionExtStructuralMetadataPropertyTexture final +struct CESIUMGLTF_API PropertyTexture final : public CesiumUtility::ExtensibleObject { - static inline constexpr const char* TypeName = - "ExtensionExtStructuralMetadataPropertyTexture"; + static inline constexpr const char* TypeName = "PropertyTexture"; /** * @brief The name of the property texture, e.g. for display purposes. @@ -38,9 +37,7 @@ struct CESIUMGLTF_API ExtensionExtStructuralMetadataPropertyTexture final * property values are stored. Required properties must be included in this * dictionary. */ - std::unordered_map< - std::string, - CesiumGltf::ExtensionExtStructuralMetadataPropertyTextureProperty> + std::unordered_map properties; }; } // namespace CesiumGltf diff --git a/CesiumGltf/generated/include/CesiumGltf/ExtensionExtStructuralMetadataPropertyTextureProperty.h b/CesiumGltf/generated/include/CesiumGltf/PropertyTextureProperty.h similarity index 89% rename from CesiumGltf/generated/include/CesiumGltf/ExtensionExtStructuralMetadataPropertyTextureProperty.h rename to CesiumGltf/generated/include/CesiumGltf/PropertyTextureProperty.h index faa50b189..04ec656fc 100644 --- a/CesiumGltf/generated/include/CesiumGltf/ExtensionExtStructuralMetadataPropertyTextureProperty.h +++ b/CesiumGltf/generated/include/CesiumGltf/PropertyTextureProperty.h @@ -15,10 +15,8 @@ namespace CesiumGltf { /** * @brief A texture containing property values. */ -struct CESIUMGLTF_API ExtensionExtStructuralMetadataPropertyTextureProperty - final : public TextureInfo { - static inline constexpr const char* TypeName = - "ExtensionExtStructuralMetadataPropertyTextureProperty"; +struct CESIUMGLTF_API PropertyTextureProperty final : public TextureInfo { + static inline constexpr const char* TypeName = "PropertyTextureProperty"; /** * @brief Texture channels containing property values, identified by index. diff --git a/CesiumGltf/generated/include/CesiumGltf/ExtensionExtStructuralMetadataSchema.h b/CesiumGltf/generated/include/CesiumGltf/Schema.h similarity index 70% rename from CesiumGltf/generated/include/CesiumGltf/ExtensionExtStructuralMetadataSchema.h rename to CesiumGltf/generated/include/CesiumGltf/Schema.h index 8b92f94af..7930d4f91 100644 --- a/CesiumGltf/generated/include/CesiumGltf/ExtensionExtStructuralMetadataSchema.h +++ b/CesiumGltf/generated/include/CesiumGltf/Schema.h @@ -2,8 +2,8 @@ // DO NOT EDIT THIS FILE! #pragma once -#include "CesiumGltf/ExtensionExtStructuralMetadataClass.h" -#include "CesiumGltf/ExtensionExtStructuralMetadataEnum.h" +#include "CesiumGltf/Class.h" +#include "CesiumGltf/Enum.h" #include "CesiumGltf/Library.h" #include @@ -16,10 +16,8 @@ namespace CesiumGltf { /** * @brief An object defining classes and enums. */ -struct CESIUMGLTF_API ExtensionExtStructuralMetadataSchema final - : public CesiumUtility::ExtensibleObject { - static inline constexpr const char* TypeName = - "ExtensionExtStructuralMetadataSchema"; +struct CESIUMGLTF_API Schema final : public CesiumUtility::ExtensibleObject { + static inline constexpr const char* TypeName = "Schema"; /** * @brief Unique identifier for the schema. Schema IDs must be alphanumeric @@ -47,18 +45,13 @@ struct CESIUMGLTF_API ExtensionExtStructuralMetadataSchema final * object defining the class. Class IDs must be alphanumeric identifiers * matching the regular expression `^[a-zA-Z_][a-zA-Z0-9_]*$`. */ - std::unordered_map< - std::string, - CesiumGltf::ExtensionExtStructuralMetadataClass> - classes; + std::unordered_map classes; /** * @brief A dictionary, where each key is an enum ID and each value is an * object defining the values for the enum. Enum IDs must be alphanumeric * identifiers matching the regular expression `^[a-zA-Z_][a-zA-Z0-9_]*$`. */ - std:: - unordered_map - enums; + std::unordered_map enums; }; } // namespace CesiumGltf diff --git a/CesiumGltfReader/generated/src/ClassJsonHandler.h b/CesiumGltfReader/generated/src/ClassJsonHandler.h new file mode 100644 index 000000000..580791944 --- /dev/null +++ b/CesiumGltfReader/generated/src/ClassJsonHandler.h @@ -0,0 +1,41 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "ClassPropertyJsonHandler.h" + +#include +#include +#include +#include + +namespace CesiumJsonReader { +class ExtensionReaderContext; +} + +namespace CesiumGltfReader { +class ClassJsonHandler : public CesiumJsonReader::ExtensibleObjectJsonHandler { +public: + using ValueType = CesiumGltf::Class; + + ClassJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + void reset(IJsonHandler* pParentHandler, CesiumGltf::Class* pObject); + + virtual IJsonHandler* readObjectKey(const std::string_view& str) override; + +protected: + IJsonHandler* readObjectKeyClass( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::Class& o); + +private: + CesiumGltf::Class* _pObject = nullptr; + CesiumJsonReader::StringJsonHandler _name; + CesiumJsonReader::StringJsonHandler _description; + CesiumJsonReader:: + DictionaryJsonHandler + _properties; +}; +} // namespace CesiumGltfReader diff --git a/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataClassPropertyJsonHandler.h b/CesiumGltfReader/generated/src/ClassPropertyJsonHandler.h similarity index 72% rename from CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataClassPropertyJsonHandler.h rename to CesiumGltfReader/generated/src/ClassPropertyJsonHandler.h index 5b4f30f29..8eab7a7e6 100644 --- a/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataClassPropertyJsonHandler.h +++ b/CesiumGltfReader/generated/src/ClassPropertyJsonHandler.h @@ -2,7 +2,7 @@ // DO NOT EDIT THIS FILE! #pragma once -#include +#include #include #include #include @@ -14,27 +14,25 @@ class ExtensionReaderContext; } namespace CesiumGltfReader { -class ExtensionExtStructuralMetadataClassPropertyJsonHandler +class ClassPropertyJsonHandler : public CesiumJsonReader::ExtensibleObjectJsonHandler { public: - using ValueType = CesiumGltf::ExtensionExtStructuralMetadataClassProperty; + using ValueType = CesiumGltf::ClassProperty; - ExtensionExtStructuralMetadataClassPropertyJsonHandler( + ClassPropertyJsonHandler( const CesiumJsonReader::ExtensionReaderContext& context) noexcept; - void reset( - IJsonHandler* pParentHandler, - CesiumGltf::ExtensionExtStructuralMetadataClassProperty* pObject); + void reset(IJsonHandler* pParentHandler, CesiumGltf::ClassProperty* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; protected: - IJsonHandler* readObjectKeyExtensionExtStructuralMetadataClassProperty( + IJsonHandler* readObjectKeyClassProperty( const std::string& objectType, const std::string_view& str, - CesiumGltf::ExtensionExtStructuralMetadataClassProperty& o); + CesiumGltf::ClassProperty& o); private: - CesiumGltf::ExtensionExtStructuralMetadataClassProperty* _pObject = nullptr; + CesiumGltf::ClassProperty* _pObject = nullptr; CesiumJsonReader::StringJsonHandler _name; CesiumJsonReader::StringJsonHandler _description; CesiumJsonReader::StringJsonHandler _type; diff --git a/CesiumGltfReader/generated/src/EnumJsonHandler.h b/CesiumGltfReader/generated/src/EnumJsonHandler.h new file mode 100644 index 000000000..355e88790 --- /dev/null +++ b/CesiumGltfReader/generated/src/EnumJsonHandler.h @@ -0,0 +1,42 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "EnumValueJsonHandler.h" + +#include +#include +#include +#include + +namespace CesiumJsonReader { +class ExtensionReaderContext; +} + +namespace CesiumGltfReader { +class EnumJsonHandler : public CesiumJsonReader::ExtensibleObjectJsonHandler { +public: + using ValueType = CesiumGltf::Enum; + + EnumJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + void reset(IJsonHandler* pParentHandler, CesiumGltf::Enum* pObject); + + virtual IJsonHandler* readObjectKey(const std::string_view& str) override; + +protected: + IJsonHandler* readObjectKeyEnum( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::Enum& o); + +private: + CesiumGltf::Enum* _pObject = nullptr; + CesiumJsonReader::StringJsonHandler _name; + CesiumJsonReader::StringJsonHandler _description; + CesiumJsonReader::StringJsonHandler _valueType; + CesiumJsonReader:: + ArrayJsonHandler + _values; +}; +} // namespace CesiumGltfReader diff --git a/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataEnumValueJsonHandler.h b/CesiumGltfReader/generated/src/EnumValueJsonHandler.h similarity index 59% rename from CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataEnumValueJsonHandler.h rename to CesiumGltfReader/generated/src/EnumValueJsonHandler.h index 477f0bd4b..d56d90722 100644 --- a/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataEnumValueJsonHandler.h +++ b/CesiumGltfReader/generated/src/EnumValueJsonHandler.h @@ -2,7 +2,7 @@ // DO NOT EDIT THIS FILE! #pragma once -#include +#include #include #include #include @@ -12,27 +12,25 @@ class ExtensionReaderContext; } namespace CesiumGltfReader { -class ExtensionExtStructuralMetadataEnumValueJsonHandler +class EnumValueJsonHandler : public CesiumJsonReader::ExtensibleObjectJsonHandler { public: - using ValueType = CesiumGltf::ExtensionExtStructuralMetadataEnumValue; + using ValueType = CesiumGltf::EnumValue; - ExtensionExtStructuralMetadataEnumValueJsonHandler( + EnumValueJsonHandler( const CesiumJsonReader::ExtensionReaderContext& context) noexcept; - void reset( - IJsonHandler* pParentHandler, - CesiumGltf::ExtensionExtStructuralMetadataEnumValue* pObject); + void reset(IJsonHandler* pParentHandler, CesiumGltf::EnumValue* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; protected: - IJsonHandler* readObjectKeyExtensionExtStructuralMetadataEnumValue( + IJsonHandler* readObjectKeyEnumValue( const std::string& objectType, const std::string_view& str, - CesiumGltf::ExtensionExtStructuralMetadataEnumValue& o); + CesiumGltf::EnumValue& o); private: - CesiumGltf::ExtensionExtStructuralMetadataEnumValue* _pObject = nullptr; + CesiumGltf::EnumValue* _pObject = nullptr; CesiumJsonReader::StringJsonHandler _name; CesiumJsonReader::StringJsonHandler _description; CesiumJsonReader::IntegerJsonHandler _value; diff --git a/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataEnumJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataEnumJsonHandler.h deleted file mode 100644 index dad293d8e..000000000 --- a/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataEnumJsonHandler.h +++ /dev/null @@ -1,46 +0,0 @@ -// This file was generated by generate-classes. -// DO NOT EDIT THIS FILE! -#pragma once - -#include "ExtensionExtStructuralMetadataEnumValueJsonHandler.h" - -#include -#include -#include -#include - -namespace CesiumJsonReader { -class ExtensionReaderContext; -} - -namespace CesiumGltfReader { -class ExtensionExtStructuralMetadataEnumJsonHandler - : public CesiumJsonReader::ExtensibleObjectJsonHandler { -public: - using ValueType = CesiumGltf::ExtensionExtStructuralMetadataEnum; - - ExtensionExtStructuralMetadataEnumJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; - void reset( - IJsonHandler* pParentHandler, - CesiumGltf::ExtensionExtStructuralMetadataEnum* pObject); - - virtual IJsonHandler* readObjectKey(const std::string_view& str) override; - -protected: - IJsonHandler* readObjectKeyExtensionExtStructuralMetadataEnum( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::ExtensionExtStructuralMetadataEnum& o); - -private: - CesiumGltf::ExtensionExtStructuralMetadataEnum* _pObject = nullptr; - CesiumJsonReader::StringJsonHandler _name; - CesiumJsonReader::StringJsonHandler _description; - CesiumJsonReader::StringJsonHandler _valueType; - CesiumJsonReader::ArrayJsonHandler< - CesiumGltf::ExtensionExtStructuralMetadataEnumValue, - ExtensionExtStructuralMetadataEnumValueJsonHandler> - _values; -}; -} // namespace CesiumGltfReader diff --git a/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataPropertyAttributeJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataPropertyAttributeJsonHandler.h deleted file mode 100644 index c195cae56..000000000 --- a/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataPropertyAttributeJsonHandler.h +++ /dev/null @@ -1,46 +0,0 @@ -// This file was generated by generate-classes. -// DO NOT EDIT THIS FILE! -#pragma once - -#include "ExtensionExtStructuralMetadataPropertyAttributePropertyJsonHandler.h" - -#include -#include -#include -#include - -namespace CesiumJsonReader { -class ExtensionReaderContext; -} - -namespace CesiumGltfReader { -class ExtensionExtStructuralMetadataPropertyAttributeJsonHandler - : public CesiumJsonReader::ExtensibleObjectJsonHandler { -public: - using ValueType = CesiumGltf::ExtensionExtStructuralMetadataPropertyAttribute; - - ExtensionExtStructuralMetadataPropertyAttributeJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; - void reset( - IJsonHandler* pParentHandler, - CesiumGltf::ExtensionExtStructuralMetadataPropertyAttribute* pObject); - - virtual IJsonHandler* readObjectKey(const std::string_view& str) override; - -protected: - IJsonHandler* readObjectKeyExtensionExtStructuralMetadataPropertyAttribute( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::ExtensionExtStructuralMetadataPropertyAttribute& o); - -private: - CesiumGltf::ExtensionExtStructuralMetadataPropertyAttribute* _pObject = - nullptr; - CesiumJsonReader::StringJsonHandler _name; - CesiumJsonReader::StringJsonHandler _classProperty; - CesiumJsonReader::DictionaryJsonHandler< - CesiumGltf::ExtensionExtStructuralMetadataPropertyAttributeProperty, - ExtensionExtStructuralMetadataPropertyAttributePropertyJsonHandler> - _properties; -}; -} // namespace CesiumGltfReader diff --git a/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataSchemaJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataSchemaJsonHandler.h deleted file mode 100644 index 78ba70d0c..000000000 --- a/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataSchemaJsonHandler.h +++ /dev/null @@ -1,52 +0,0 @@ -// This file was generated by generate-classes. -// DO NOT EDIT THIS FILE! -#pragma once - -#include "ExtensionExtStructuralMetadataClassJsonHandler.h" -#include "ExtensionExtStructuralMetadataEnumJsonHandler.h" - -#include -#include -#include -#include - -namespace CesiumJsonReader { -class ExtensionReaderContext; -} - -namespace CesiumGltfReader { -class ExtensionExtStructuralMetadataSchemaJsonHandler - : public CesiumJsonReader::ExtensibleObjectJsonHandler { -public: - using ValueType = CesiumGltf::ExtensionExtStructuralMetadataSchema; - - ExtensionExtStructuralMetadataSchemaJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; - void reset( - IJsonHandler* pParentHandler, - CesiumGltf::ExtensionExtStructuralMetadataSchema* pObject); - - virtual IJsonHandler* readObjectKey(const std::string_view& str) override; - -protected: - IJsonHandler* readObjectKeyExtensionExtStructuralMetadataSchema( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::ExtensionExtStructuralMetadataSchema& o); - -private: - CesiumGltf::ExtensionExtStructuralMetadataSchema* _pObject = nullptr; - CesiumJsonReader::StringJsonHandler _id; - CesiumJsonReader::StringJsonHandler _name; - CesiumJsonReader::StringJsonHandler _description; - CesiumJsonReader::StringJsonHandler _version; - CesiumJsonReader::DictionaryJsonHandler< - CesiumGltf::ExtensionExtStructuralMetadataClass, - ExtensionExtStructuralMetadataClassJsonHandler> - _classes; - CesiumJsonReader::DictionaryJsonHandler< - CesiumGltf::ExtensionExtStructuralMetadataEnum, - ExtensionExtStructuralMetadataEnumJsonHandler> - _enums; -}; -} // namespace CesiumGltfReader diff --git a/CesiumGltfReader/generated/src/ExtensionModelExtStructuralMetadataJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionModelExtStructuralMetadataJsonHandler.h index 4d4ce5e18..3def01e1a 100644 --- a/CesiumGltfReader/generated/src/ExtensionModelExtStructuralMetadataJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionModelExtStructuralMetadataJsonHandler.h @@ -2,10 +2,10 @@ // DO NOT EDIT THIS FILE! #pragma once -#include "ExtensionExtStructuralMetadataPropertyAttributeJsonHandler.h" -#include "ExtensionExtStructuralMetadataPropertyTableJsonHandler.h" -#include "ExtensionExtStructuralMetadataPropertyTextureJsonHandler.h" -#include "ExtensionExtStructuralMetadataSchemaJsonHandler.h" +#include "PropertyAttributeJsonHandler.h" +#include "PropertyTableJsonHandler.h" +#include "PropertyTextureJsonHandler.h" +#include "SchemaJsonHandler.h" #include #include @@ -91,19 +91,17 @@ class ExtensionModelExtStructuralMetadataJsonHandler private: CesiumGltf::ExtensionModelExtStructuralMetadata* _pObject = nullptr; - ExtensionExtStructuralMetadataSchemaJsonHandler _schema; + SchemaJsonHandler _schema; CesiumJsonReader::StringJsonHandler _schemaUri; + CesiumJsonReader:: + ArrayJsonHandler + _propertyTables; + CesiumJsonReader:: + ArrayJsonHandler + _propertyTextures; CesiumJsonReader::ArrayJsonHandler< - CesiumGltf::ExtensionExtStructuralMetadataPropertyTable, - ExtensionExtStructuralMetadataPropertyTableJsonHandler> - _propertyTables; - CesiumJsonReader::ArrayJsonHandler< - CesiumGltf::ExtensionExtStructuralMetadataPropertyTexture, - ExtensionExtStructuralMetadataPropertyTextureJsonHandler> - _propertyTextures; - CesiumJsonReader::ArrayJsonHandler< - CesiumGltf::ExtensionExtStructuralMetadataPropertyAttribute, - ExtensionExtStructuralMetadataPropertyAttributeJsonHandler> + CesiumGltf::PropertyAttribute, + PropertyAttributeJsonHandler> _propertyAttributes; }; } // namespace CesiumGltfReader diff --git a/CesiumGltfReader/generated/src/GeneratedJsonHandlers.cpp b/CesiumGltfReader/generated/src/GeneratedJsonHandlers.cpp index b65df862c..f7b8c5076 100644 --- a/CesiumGltfReader/generated/src/GeneratedJsonHandlers.cpp +++ b/CesiumGltfReader/generated/src/GeneratedJsonHandlers.cpp @@ -1534,46 +1534,43 @@ ExtensionModelKhrMaterialsVariantsValueJsonHandler:: } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! -#include "ExtensionExtStructuralMetadataPropertyAttributeJsonHandler.h" +#include "PropertyAttributeJsonHandler.h" -#include +#include #include #include namespace CesiumGltfReader { -ExtensionExtStructuralMetadataPropertyAttributeJsonHandler:: - ExtensionExtStructuralMetadataPropertyAttributeJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept +PropertyAttributeJsonHandler::PropertyAttributeJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _name(), _classProperty(), _properties(context) {} -void ExtensionExtStructuralMetadataPropertyAttributeJsonHandler::reset( +void PropertyAttributeJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::ExtensionExtStructuralMetadataPropertyAttribute* pObject) { + CesiumGltf::PropertyAttribute* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } CesiumJsonReader::IJsonHandler* -ExtensionExtStructuralMetadataPropertyAttributeJsonHandler::readObjectKey( - const std::string_view& str) { +PropertyAttributeJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionExtStructuralMetadataPropertyAttribute( - CesiumGltf::ExtensionExtStructuralMetadataPropertyAttribute::TypeName, + return this->readObjectKeyPropertyAttribute( + CesiumGltf::PropertyAttribute::TypeName, str, *this->_pObject); } CesiumJsonReader::IJsonHandler* -ExtensionExtStructuralMetadataPropertyAttributeJsonHandler:: - readObjectKeyExtensionExtStructuralMetadataPropertyAttribute( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::ExtensionExtStructuralMetadataPropertyAttribute& o) { +PropertyAttributeJsonHandler::readObjectKeyPropertyAttribute( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::PropertyAttribute& o) { using namespace std::string_literals; if ("name"s == str) @@ -1589,18 +1586,17 @@ ExtensionExtStructuralMetadataPropertyAttributeJsonHandler:: } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! -#include "ExtensionExtStructuralMetadataPropertyAttributePropertyJsonHandler.h" +#include "PropertyAttributePropertyJsonHandler.h" -#include +#include #include #include namespace CesiumGltfReader { -ExtensionExtStructuralMetadataPropertyAttributePropertyJsonHandler:: - ExtensionExtStructuralMetadataPropertyAttributePropertyJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept +PropertyAttributePropertyJsonHandler::PropertyAttributePropertyJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _attribute(), _offset(), @@ -1608,33 +1604,28 @@ ExtensionExtStructuralMetadataPropertyAttributePropertyJsonHandler:: _max(), _min() {} -void ExtensionExtStructuralMetadataPropertyAttributePropertyJsonHandler::reset( +void PropertyAttributePropertyJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::ExtensionExtStructuralMetadataPropertyAttributeProperty* - pObject) { + CesiumGltf::PropertyAttributeProperty* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } CesiumJsonReader::IJsonHandler* -ExtensionExtStructuralMetadataPropertyAttributePropertyJsonHandler:: - readObjectKey(const std::string_view& str) { +PropertyAttributePropertyJsonHandler::readObjectKey( + const std::string_view& str) { assert(this->_pObject); - return this - ->readObjectKeyExtensionExtStructuralMetadataPropertyAttributeProperty( - CesiumGltf::ExtensionExtStructuralMetadataPropertyAttributeProperty:: - TypeName, - str, - *this->_pObject); + return this->readObjectKeyPropertyAttributeProperty( + CesiumGltf::PropertyAttributeProperty::TypeName, + str, + *this->_pObject); } CesiumJsonReader::IJsonHandler* -ExtensionExtStructuralMetadataPropertyAttributePropertyJsonHandler:: - readObjectKeyExtensionExtStructuralMetadataPropertyAttributeProperty( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::ExtensionExtStructuralMetadataPropertyAttributeProperty& - o) { +PropertyAttributePropertyJsonHandler::readObjectKeyPropertyAttributeProperty( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::PropertyAttributeProperty& o) { using namespace std::string_literals; if ("attribute"s == str) @@ -1654,46 +1645,43 @@ ExtensionExtStructuralMetadataPropertyAttributePropertyJsonHandler:: } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! -#include "ExtensionExtStructuralMetadataPropertyTextureJsonHandler.h" +#include "PropertyTextureJsonHandler.h" -#include +#include #include #include namespace CesiumGltfReader { -ExtensionExtStructuralMetadataPropertyTextureJsonHandler:: - ExtensionExtStructuralMetadataPropertyTextureJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept +PropertyTextureJsonHandler::PropertyTextureJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _name(), _classProperty(), _properties(context) {} -void ExtensionExtStructuralMetadataPropertyTextureJsonHandler::reset( +void PropertyTextureJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::ExtensionExtStructuralMetadataPropertyTexture* pObject) { + CesiumGltf::PropertyTexture* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } CesiumJsonReader::IJsonHandler* -ExtensionExtStructuralMetadataPropertyTextureJsonHandler::readObjectKey( - const std::string_view& str) { +PropertyTextureJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionExtStructuralMetadataPropertyTexture( - CesiumGltf::ExtensionExtStructuralMetadataPropertyTexture::TypeName, + return this->readObjectKeyPropertyTexture( + CesiumGltf::PropertyTexture::TypeName, str, *this->_pObject); } CesiumJsonReader::IJsonHandler* -ExtensionExtStructuralMetadataPropertyTextureJsonHandler:: - readObjectKeyExtensionExtStructuralMetadataPropertyTexture( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::ExtensionExtStructuralMetadataPropertyTexture& o) { +PropertyTextureJsonHandler::readObjectKeyPropertyTexture( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::PropertyTexture& o) { using namespace std::string_literals; if ("name"s == str) @@ -1709,18 +1697,17 @@ ExtensionExtStructuralMetadataPropertyTextureJsonHandler:: } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! -#include "ExtensionExtStructuralMetadataPropertyTexturePropertyJsonHandler.h" +#include "PropertyTexturePropertyJsonHandler.h" -#include +#include #include #include namespace CesiumGltfReader { -ExtensionExtStructuralMetadataPropertyTexturePropertyJsonHandler:: - ExtensionExtStructuralMetadataPropertyTexturePropertyJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept +PropertyTexturePropertyJsonHandler::PropertyTexturePropertyJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept : TextureInfoJsonHandler(context), _channels(), _offset(), @@ -1728,32 +1715,27 @@ ExtensionExtStructuralMetadataPropertyTexturePropertyJsonHandler:: _max(), _min() {} -void ExtensionExtStructuralMetadataPropertyTexturePropertyJsonHandler::reset( +void PropertyTexturePropertyJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::ExtensionExtStructuralMetadataPropertyTextureProperty* - pObject) { + CesiumGltf::PropertyTextureProperty* pObject) { TextureInfoJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } CesiumJsonReader::IJsonHandler* -ExtensionExtStructuralMetadataPropertyTexturePropertyJsonHandler::readObjectKey( - const std::string_view& str) { +PropertyTexturePropertyJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this - ->readObjectKeyExtensionExtStructuralMetadataPropertyTextureProperty( - CesiumGltf::ExtensionExtStructuralMetadataPropertyTextureProperty:: - TypeName, - str, - *this->_pObject); + return this->readObjectKeyPropertyTextureProperty( + CesiumGltf::PropertyTextureProperty::TypeName, + str, + *this->_pObject); } CesiumJsonReader::IJsonHandler* -ExtensionExtStructuralMetadataPropertyTexturePropertyJsonHandler:: - readObjectKeyExtensionExtStructuralMetadataPropertyTextureProperty( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::ExtensionExtStructuralMetadataPropertyTextureProperty& o) { +PropertyTexturePropertyJsonHandler::readObjectKeyPropertyTextureProperty( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::PropertyTextureProperty& o) { using namespace std::string_literals; if ("channels"s == str) @@ -1822,47 +1804,44 @@ TextureInfoJsonHandler::readObjectKeyTextureInfo( } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! -#include "ExtensionExtStructuralMetadataPropertyTableJsonHandler.h" +#include "PropertyTableJsonHandler.h" -#include +#include #include #include namespace CesiumGltfReader { -ExtensionExtStructuralMetadataPropertyTableJsonHandler:: - ExtensionExtStructuralMetadataPropertyTableJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept +PropertyTableJsonHandler::PropertyTableJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _name(), _classProperty(), _count(), _properties(context) {} -void ExtensionExtStructuralMetadataPropertyTableJsonHandler::reset( +void PropertyTableJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::ExtensionExtStructuralMetadataPropertyTable* pObject) { + CesiumGltf::PropertyTable* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } CesiumJsonReader::IJsonHandler* -ExtensionExtStructuralMetadataPropertyTableJsonHandler::readObjectKey( - const std::string_view& str) { +PropertyTableJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionExtStructuralMetadataPropertyTable( - CesiumGltf::ExtensionExtStructuralMetadataPropertyTable::TypeName, + return this->readObjectKeyPropertyTable( + CesiumGltf::PropertyTable::TypeName, str, *this->_pObject); } CesiumJsonReader::IJsonHandler* -ExtensionExtStructuralMetadataPropertyTableJsonHandler:: - readObjectKeyExtensionExtStructuralMetadataPropertyTable( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::ExtensionExtStructuralMetadataPropertyTable& o) { +PropertyTableJsonHandler::readObjectKeyPropertyTable( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::PropertyTable& o) { using namespace std::string_literals; if ("name"s == str) @@ -1880,18 +1859,17 @@ ExtensionExtStructuralMetadataPropertyTableJsonHandler:: } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! -#include "ExtensionExtStructuralMetadataPropertyTablePropertyJsonHandler.h" +#include "PropertyTablePropertyJsonHandler.h" -#include +#include #include #include namespace CesiumGltfReader { -ExtensionExtStructuralMetadataPropertyTablePropertyJsonHandler:: - ExtensionExtStructuralMetadataPropertyTablePropertyJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept +PropertyTablePropertyJsonHandler::PropertyTablePropertyJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _values(), _arrayOffsets(), @@ -1903,29 +1881,27 @@ ExtensionExtStructuralMetadataPropertyTablePropertyJsonHandler:: _max(), _min() {} -void ExtensionExtStructuralMetadataPropertyTablePropertyJsonHandler::reset( +void PropertyTablePropertyJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::ExtensionExtStructuralMetadataPropertyTableProperty* pObject) { + CesiumGltf::PropertyTableProperty* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } CesiumJsonReader::IJsonHandler* -ExtensionExtStructuralMetadataPropertyTablePropertyJsonHandler::readObjectKey( - const std::string_view& str) { +PropertyTablePropertyJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionExtStructuralMetadataPropertyTableProperty( - CesiumGltf::ExtensionExtStructuralMetadataPropertyTableProperty::TypeName, + return this->readObjectKeyPropertyTableProperty( + CesiumGltf::PropertyTableProperty::TypeName, str, *this->_pObject); } CesiumJsonReader::IJsonHandler* -ExtensionExtStructuralMetadataPropertyTablePropertyJsonHandler:: - readObjectKeyExtensionExtStructuralMetadataPropertyTableProperty( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::ExtensionExtStructuralMetadataPropertyTableProperty& o) { +PropertyTablePropertyJsonHandler::readObjectKeyPropertyTableProperty( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::PropertyTableProperty& o) { using namespace std::string_literals; if ("values"s == str) @@ -1959,18 +1935,17 @@ ExtensionExtStructuralMetadataPropertyTablePropertyJsonHandler:: } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! -#include "ExtensionExtStructuralMetadataSchemaJsonHandler.h" +#include "SchemaJsonHandler.h" -#include +#include #include #include namespace CesiumGltfReader { -ExtensionExtStructuralMetadataSchemaJsonHandler:: - ExtensionExtStructuralMetadataSchemaJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept +SchemaJsonHandler::SchemaJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _id(), _name(), @@ -1979,29 +1954,26 @@ ExtensionExtStructuralMetadataSchemaJsonHandler:: _classes(context), _enums(context) {} -void ExtensionExtStructuralMetadataSchemaJsonHandler::reset( +void SchemaJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::ExtensionExtStructuralMetadataSchema* pObject) { + CesiumGltf::Schema* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } CesiumJsonReader::IJsonHandler* -ExtensionExtStructuralMetadataSchemaJsonHandler::readObjectKey( - const std::string_view& str) { +SchemaJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionExtStructuralMetadataSchema( - CesiumGltf::ExtensionExtStructuralMetadataSchema::TypeName, + return this->readObjectKeySchema( + CesiumGltf::Schema::TypeName, str, *this->_pObject); } -CesiumJsonReader::IJsonHandler* -ExtensionExtStructuralMetadataSchemaJsonHandler:: - readObjectKeyExtensionExtStructuralMetadataSchema( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::ExtensionExtStructuralMetadataSchema& o) { +CesiumJsonReader::IJsonHandler* SchemaJsonHandler::readObjectKeySchema( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::Schema& o) { using namespace std::string_literals; if ("id"s == str) @@ -2023,46 +1995,43 @@ ExtensionExtStructuralMetadataSchemaJsonHandler:: } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! -#include "ExtensionExtStructuralMetadataEnumJsonHandler.h" +#include "EnumJsonHandler.h" -#include +#include #include #include namespace CesiumGltfReader { -ExtensionExtStructuralMetadataEnumJsonHandler:: - ExtensionExtStructuralMetadataEnumJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept +EnumJsonHandler::EnumJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _name(), _description(), _valueType(), _values(context) {} -void ExtensionExtStructuralMetadataEnumJsonHandler::reset( +void EnumJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::ExtensionExtStructuralMetadataEnum* pObject) { + CesiumGltf::Enum* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } CesiumJsonReader::IJsonHandler* -ExtensionExtStructuralMetadataEnumJsonHandler::readObjectKey( - const std::string_view& str) { +EnumJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionExtStructuralMetadataEnum( - CesiumGltf::ExtensionExtStructuralMetadataEnum::TypeName, + return this->readObjectKeyEnum( + CesiumGltf::Enum::TypeName, str, *this->_pObject); } -CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataEnumJsonHandler:: - readObjectKeyExtensionExtStructuralMetadataEnum( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::ExtensionExtStructuralMetadataEnum& o) { +CesiumJsonReader::IJsonHandler* EnumJsonHandler::readObjectKeyEnum( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::Enum& o) { using namespace std::string_literals; if ("name"s == str) @@ -2080,46 +2049,42 @@ CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataEnumJsonHandler:: } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! -#include "ExtensionExtStructuralMetadataEnumValueJsonHandler.h" +#include "EnumValueJsonHandler.h" -#include +#include #include #include namespace CesiumGltfReader { -ExtensionExtStructuralMetadataEnumValueJsonHandler:: - ExtensionExtStructuralMetadataEnumValueJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept +EnumValueJsonHandler::EnumValueJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _name(), _description(), _value() {} -void ExtensionExtStructuralMetadataEnumValueJsonHandler::reset( +void EnumValueJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::ExtensionExtStructuralMetadataEnumValue* pObject) { + CesiumGltf::EnumValue* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } CesiumJsonReader::IJsonHandler* -ExtensionExtStructuralMetadataEnumValueJsonHandler::readObjectKey( - const std::string_view& str) { +EnumValueJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionExtStructuralMetadataEnumValue( - CesiumGltf::ExtensionExtStructuralMetadataEnumValue::TypeName, + return this->readObjectKeyEnumValue( + CesiumGltf::EnumValue::TypeName, str, *this->_pObject); } -CesiumJsonReader::IJsonHandler* -ExtensionExtStructuralMetadataEnumValueJsonHandler:: - readObjectKeyExtensionExtStructuralMetadataEnumValue( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::ExtensionExtStructuralMetadataEnumValue& o) { +CesiumJsonReader::IJsonHandler* EnumValueJsonHandler::readObjectKeyEnumValue( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::EnumValue& o) { using namespace std::string_literals; if ("name"s == str) @@ -2135,45 +2100,42 @@ ExtensionExtStructuralMetadataEnumValueJsonHandler:: } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! -#include "ExtensionExtStructuralMetadataClassJsonHandler.h" +#include "ClassJsonHandler.h" -#include +#include #include #include namespace CesiumGltfReader { -ExtensionExtStructuralMetadataClassJsonHandler:: - ExtensionExtStructuralMetadataClassJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept +ClassJsonHandler::ClassJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _name(), _description(), _properties(context) {} -void ExtensionExtStructuralMetadataClassJsonHandler::reset( +void ClassJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::ExtensionExtStructuralMetadataClass* pObject) { + CesiumGltf::Class* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } CesiumJsonReader::IJsonHandler* -ExtensionExtStructuralMetadataClassJsonHandler::readObjectKey( - const std::string_view& str) { +ClassJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionExtStructuralMetadataClass( - CesiumGltf::ExtensionExtStructuralMetadataClass::TypeName, + return this->readObjectKeyClass( + CesiumGltf::Class::TypeName, str, *this->_pObject); } -CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataClassJsonHandler:: - readObjectKeyExtensionExtStructuralMetadataClass( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::ExtensionExtStructuralMetadataClass& o) { +CesiumJsonReader::IJsonHandler* ClassJsonHandler::readObjectKeyClass( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::Class& o) { using namespace std::string_literals; if ("name"s == str) @@ -2189,18 +2151,17 @@ CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataClassJsonHandler:: } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! -#include "ExtensionExtStructuralMetadataClassPropertyJsonHandler.h" +#include "ClassPropertyJsonHandler.h" -#include +#include #include #include namespace CesiumGltfReader { -ExtensionExtStructuralMetadataClassPropertyJsonHandler:: - ExtensionExtStructuralMetadataClassPropertyJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept +ClassPropertyJsonHandler::ClassPropertyJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _name(), _description(), @@ -2219,29 +2180,27 @@ ExtensionExtStructuralMetadataClassPropertyJsonHandler:: _defaultProperty(), _semantic() {} -void ExtensionExtStructuralMetadataClassPropertyJsonHandler::reset( +void ClassPropertyJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, - CesiumGltf::ExtensionExtStructuralMetadataClassProperty* pObject) { + CesiumGltf::ClassProperty* pObject) { CesiumJsonReader::ExtensibleObjectJsonHandler::reset(pParentHandler, pObject); this->_pObject = pObject; } CesiumJsonReader::IJsonHandler* -ExtensionExtStructuralMetadataClassPropertyJsonHandler::readObjectKey( - const std::string_view& str) { +ClassPropertyJsonHandler::readObjectKey(const std::string_view& str) { assert(this->_pObject); - return this->readObjectKeyExtensionExtStructuralMetadataClassProperty( - CesiumGltf::ExtensionExtStructuralMetadataClassProperty::TypeName, + return this->readObjectKeyClassProperty( + CesiumGltf::ClassProperty::TypeName, str, *this->_pObject); } CesiumJsonReader::IJsonHandler* -ExtensionExtStructuralMetadataClassPropertyJsonHandler:: - readObjectKeyExtensionExtStructuralMetadataClassProperty( - const std::string& objectType, - const std::string_view& str, - CesiumGltf::ExtensionExtStructuralMetadataClassProperty& o) { +ClassPropertyJsonHandler::readObjectKeyClassProperty( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::ClassProperty& o) { using namespace std::string_literals; if ("name"s == str) diff --git a/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataClassJsonHandler.h b/CesiumGltfReader/generated/src/PropertyAttributeJsonHandler.h similarity index 50% rename from CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataClassJsonHandler.h rename to CesiumGltfReader/generated/src/PropertyAttributeJsonHandler.h index ba06f5973..8f870958c 100644 --- a/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataClassJsonHandler.h +++ b/CesiumGltfReader/generated/src/PropertyAttributeJsonHandler.h @@ -2,9 +2,9 @@ // DO NOT EDIT THIS FILE! #pragma once -#include "ExtensionExtStructuralMetadataClassPropertyJsonHandler.h" +#include "PropertyAttributePropertyJsonHandler.h" -#include +#include #include #include #include @@ -14,32 +14,31 @@ class ExtensionReaderContext; } namespace CesiumGltfReader { -class ExtensionExtStructuralMetadataClassJsonHandler +class PropertyAttributeJsonHandler : public CesiumJsonReader::ExtensibleObjectJsonHandler { public: - using ValueType = CesiumGltf::ExtensionExtStructuralMetadataClass; + using ValueType = CesiumGltf::PropertyAttribute; - ExtensionExtStructuralMetadataClassJsonHandler( + PropertyAttributeJsonHandler( const CesiumJsonReader::ExtensionReaderContext& context) noexcept; - void reset( - IJsonHandler* pParentHandler, - CesiumGltf::ExtensionExtStructuralMetadataClass* pObject); + void + reset(IJsonHandler* pParentHandler, CesiumGltf::PropertyAttribute* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; protected: - IJsonHandler* readObjectKeyExtensionExtStructuralMetadataClass( + IJsonHandler* readObjectKeyPropertyAttribute( const std::string& objectType, const std::string_view& str, - CesiumGltf::ExtensionExtStructuralMetadataClass& o); + CesiumGltf::PropertyAttribute& o); private: - CesiumGltf::ExtensionExtStructuralMetadataClass* _pObject = nullptr; + CesiumGltf::PropertyAttribute* _pObject = nullptr; CesiumJsonReader::StringJsonHandler _name; - CesiumJsonReader::StringJsonHandler _description; + CesiumJsonReader::StringJsonHandler _classProperty; CesiumJsonReader::DictionaryJsonHandler< - CesiumGltf::ExtensionExtStructuralMetadataClassProperty, - ExtensionExtStructuralMetadataClassPropertyJsonHandler> + CesiumGltf::PropertyAttributeProperty, + PropertyAttributePropertyJsonHandler> _properties; }; } // namespace CesiumGltfReader diff --git a/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataPropertyAttributePropertyJsonHandler.h b/CesiumGltfReader/generated/src/PropertyAttributePropertyJsonHandler.h similarity index 59% rename from CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataPropertyAttributePropertyJsonHandler.h rename to CesiumGltfReader/generated/src/PropertyAttributePropertyJsonHandler.h index 4a91ba0b8..8851411be 100644 --- a/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataPropertyAttributePropertyJsonHandler.h +++ b/CesiumGltfReader/generated/src/PropertyAttributePropertyJsonHandler.h @@ -2,7 +2,7 @@ // DO NOT EDIT THIS FILE! #pragma once -#include +#include #include #include #include @@ -12,31 +12,27 @@ class ExtensionReaderContext; } namespace CesiumGltfReader { -class ExtensionExtStructuralMetadataPropertyAttributePropertyJsonHandler +class PropertyAttributePropertyJsonHandler : public CesiumJsonReader::ExtensibleObjectJsonHandler { public: - using ValueType = - CesiumGltf::ExtensionExtStructuralMetadataPropertyAttributeProperty; + using ValueType = CesiumGltf::PropertyAttributeProperty; - ExtensionExtStructuralMetadataPropertyAttributePropertyJsonHandler( + PropertyAttributePropertyJsonHandler( const CesiumJsonReader::ExtensionReaderContext& context) noexcept; void reset( IJsonHandler* pParentHandler, - CesiumGltf::ExtensionExtStructuralMetadataPropertyAttributeProperty* - pObject); + CesiumGltf::PropertyAttributeProperty* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; protected: - IJsonHandler* - readObjectKeyExtensionExtStructuralMetadataPropertyAttributeProperty( + IJsonHandler* readObjectKeyPropertyAttributeProperty( const std::string& objectType, const std::string_view& str, - CesiumGltf::ExtensionExtStructuralMetadataPropertyAttributeProperty& o); + CesiumGltf::PropertyAttributeProperty& o); private: - CesiumGltf::ExtensionExtStructuralMetadataPropertyAttributeProperty* - _pObject = nullptr; + CesiumGltf::PropertyAttributeProperty* _pObject = nullptr; CesiumJsonReader::StringJsonHandler _attribute; CesiumJsonReader::JsonObjectJsonHandler _offset; CesiumJsonReader::JsonObjectJsonHandler _scale; diff --git a/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataPropertyTableJsonHandler.h b/CesiumGltfReader/generated/src/PropertyTableJsonHandler.h similarity index 53% rename from CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataPropertyTableJsonHandler.h rename to CesiumGltfReader/generated/src/PropertyTableJsonHandler.h index f4f3ee112..3fa49ed20 100644 --- a/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataPropertyTableJsonHandler.h +++ b/CesiumGltfReader/generated/src/PropertyTableJsonHandler.h @@ -2,9 +2,9 @@ // DO NOT EDIT THIS FILE! #pragma once -#include "ExtensionExtStructuralMetadataPropertyTablePropertyJsonHandler.h" +#include "PropertyTablePropertyJsonHandler.h" -#include +#include #include #include #include @@ -15,33 +15,31 @@ class ExtensionReaderContext; } namespace CesiumGltfReader { -class ExtensionExtStructuralMetadataPropertyTableJsonHandler +class PropertyTableJsonHandler : public CesiumJsonReader::ExtensibleObjectJsonHandler { public: - using ValueType = CesiumGltf::ExtensionExtStructuralMetadataPropertyTable; + using ValueType = CesiumGltf::PropertyTable; - ExtensionExtStructuralMetadataPropertyTableJsonHandler( + PropertyTableJsonHandler( const CesiumJsonReader::ExtensionReaderContext& context) noexcept; - void reset( - IJsonHandler* pParentHandler, - CesiumGltf::ExtensionExtStructuralMetadataPropertyTable* pObject); + void reset(IJsonHandler* pParentHandler, CesiumGltf::PropertyTable* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; protected: - IJsonHandler* readObjectKeyExtensionExtStructuralMetadataPropertyTable( + IJsonHandler* readObjectKeyPropertyTable( const std::string& objectType, const std::string_view& str, - CesiumGltf::ExtensionExtStructuralMetadataPropertyTable& o); + CesiumGltf::PropertyTable& o); private: - CesiumGltf::ExtensionExtStructuralMetadataPropertyTable* _pObject = nullptr; + CesiumGltf::PropertyTable* _pObject = nullptr; CesiumJsonReader::StringJsonHandler _name; CesiumJsonReader::StringJsonHandler _classProperty; CesiumJsonReader::IntegerJsonHandler _count; CesiumJsonReader::DictionaryJsonHandler< - CesiumGltf::ExtensionExtStructuralMetadataPropertyTableProperty, - ExtensionExtStructuralMetadataPropertyTablePropertyJsonHandler> + CesiumGltf::PropertyTableProperty, + PropertyTablePropertyJsonHandler> _properties; }; } // namespace CesiumGltfReader diff --git a/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataPropertyTablePropertyJsonHandler.h b/CesiumGltfReader/generated/src/PropertyTablePropertyJsonHandler.h similarity index 66% rename from CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataPropertyTablePropertyJsonHandler.h rename to CesiumGltfReader/generated/src/PropertyTablePropertyJsonHandler.h index 6051f6973..20951fbc1 100644 --- a/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataPropertyTablePropertyJsonHandler.h +++ b/CesiumGltfReader/generated/src/PropertyTablePropertyJsonHandler.h @@ -2,7 +2,7 @@ // DO NOT EDIT THIS FILE! #pragma once -#include +#include #include #include #include @@ -13,30 +13,27 @@ class ExtensionReaderContext; } namespace CesiumGltfReader { -class ExtensionExtStructuralMetadataPropertyTablePropertyJsonHandler +class PropertyTablePropertyJsonHandler : public CesiumJsonReader::ExtensibleObjectJsonHandler { public: - using ValueType = - CesiumGltf::ExtensionExtStructuralMetadataPropertyTableProperty; + using ValueType = CesiumGltf::PropertyTableProperty; - ExtensionExtStructuralMetadataPropertyTablePropertyJsonHandler( + PropertyTablePropertyJsonHandler( const CesiumJsonReader::ExtensionReaderContext& context) noexcept; void reset( IJsonHandler* pParentHandler, - CesiumGltf::ExtensionExtStructuralMetadataPropertyTableProperty* pObject); + CesiumGltf::PropertyTableProperty* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; protected: - IJsonHandler* - readObjectKeyExtensionExtStructuralMetadataPropertyTableProperty( + IJsonHandler* readObjectKeyPropertyTableProperty( const std::string& objectType, const std::string_view& str, - CesiumGltf::ExtensionExtStructuralMetadataPropertyTableProperty& o); + CesiumGltf::PropertyTableProperty& o); private: - CesiumGltf::ExtensionExtStructuralMetadataPropertyTableProperty* _pObject = - nullptr; + CesiumGltf::PropertyTableProperty* _pObject = nullptr; CesiumJsonReader::IntegerJsonHandler _values; CesiumJsonReader::IntegerJsonHandler _arrayOffsets; CesiumJsonReader::IntegerJsonHandler _stringOffsets; diff --git a/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataPropertyTextureJsonHandler.h b/CesiumGltfReader/generated/src/PropertyTextureJsonHandler.h similarity index 50% rename from CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataPropertyTextureJsonHandler.h rename to CesiumGltfReader/generated/src/PropertyTextureJsonHandler.h index aa8e3c513..16c7b9c45 100644 --- a/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataPropertyTextureJsonHandler.h +++ b/CesiumGltfReader/generated/src/PropertyTextureJsonHandler.h @@ -2,9 +2,9 @@ // DO NOT EDIT THIS FILE! #pragma once -#include "ExtensionExtStructuralMetadataPropertyTexturePropertyJsonHandler.h" +#include "PropertyTexturePropertyJsonHandler.h" -#include +#include #include #include #include @@ -14,32 +14,31 @@ class ExtensionReaderContext; } namespace CesiumGltfReader { -class ExtensionExtStructuralMetadataPropertyTextureJsonHandler +class PropertyTextureJsonHandler : public CesiumJsonReader::ExtensibleObjectJsonHandler { public: - using ValueType = CesiumGltf::ExtensionExtStructuralMetadataPropertyTexture; + using ValueType = CesiumGltf::PropertyTexture; - ExtensionExtStructuralMetadataPropertyTextureJsonHandler( + PropertyTextureJsonHandler( const CesiumJsonReader::ExtensionReaderContext& context) noexcept; - void reset( - IJsonHandler* pParentHandler, - CesiumGltf::ExtensionExtStructuralMetadataPropertyTexture* pObject); + void + reset(IJsonHandler* pParentHandler, CesiumGltf::PropertyTexture* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; protected: - IJsonHandler* readObjectKeyExtensionExtStructuralMetadataPropertyTexture( + IJsonHandler* readObjectKeyPropertyTexture( const std::string& objectType, const std::string_view& str, - CesiumGltf::ExtensionExtStructuralMetadataPropertyTexture& o); + CesiumGltf::PropertyTexture& o); private: - CesiumGltf::ExtensionExtStructuralMetadataPropertyTexture* _pObject = nullptr; + CesiumGltf::PropertyTexture* _pObject = nullptr; CesiumJsonReader::StringJsonHandler _name; CesiumJsonReader::StringJsonHandler _classProperty; CesiumJsonReader::DictionaryJsonHandler< - CesiumGltf::ExtensionExtStructuralMetadataPropertyTextureProperty, - ExtensionExtStructuralMetadataPropertyTexturePropertyJsonHandler> + CesiumGltf::PropertyTextureProperty, + PropertyTexturePropertyJsonHandler> _properties; }; } // namespace CesiumGltfReader diff --git a/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataPropertyTexturePropertyJsonHandler.h b/CesiumGltfReader/generated/src/PropertyTexturePropertyJsonHandler.h similarity index 59% rename from CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataPropertyTexturePropertyJsonHandler.h rename to CesiumGltfReader/generated/src/PropertyTexturePropertyJsonHandler.h index 2b2527250..1b30d241c 100644 --- a/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataPropertyTexturePropertyJsonHandler.h +++ b/CesiumGltfReader/generated/src/PropertyTexturePropertyJsonHandler.h @@ -4,7 +4,7 @@ #include "TextureInfoJsonHandler.h" -#include +#include #include #include #include @@ -14,31 +14,26 @@ class ExtensionReaderContext; } namespace CesiumGltfReader { -class ExtensionExtStructuralMetadataPropertyTexturePropertyJsonHandler - : public TextureInfoJsonHandler { +class PropertyTexturePropertyJsonHandler : public TextureInfoJsonHandler { public: - using ValueType = - CesiumGltf::ExtensionExtStructuralMetadataPropertyTextureProperty; + using ValueType = CesiumGltf::PropertyTextureProperty; - ExtensionExtStructuralMetadataPropertyTexturePropertyJsonHandler( + PropertyTexturePropertyJsonHandler( const CesiumJsonReader::ExtensionReaderContext& context) noexcept; void reset( IJsonHandler* pParentHandler, - CesiumGltf::ExtensionExtStructuralMetadataPropertyTextureProperty* - pObject); + CesiumGltf::PropertyTextureProperty* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; protected: - IJsonHandler* - readObjectKeyExtensionExtStructuralMetadataPropertyTextureProperty( + IJsonHandler* readObjectKeyPropertyTextureProperty( const std::string& objectType, const std::string_view& str, - CesiumGltf::ExtensionExtStructuralMetadataPropertyTextureProperty& o); + CesiumGltf::PropertyTextureProperty& o); private: - CesiumGltf::ExtensionExtStructuralMetadataPropertyTextureProperty* _pObject = - nullptr; + CesiumGltf::PropertyTextureProperty* _pObject = nullptr; CesiumJsonReader:: ArrayJsonHandler> _channels; diff --git a/CesiumGltfReader/generated/src/SchemaJsonHandler.h b/CesiumGltfReader/generated/src/SchemaJsonHandler.h new file mode 100644 index 000000000..ffc2ca7eb --- /dev/null +++ b/CesiumGltfReader/generated/src/SchemaJsonHandler.h @@ -0,0 +1,45 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "ClassJsonHandler.h" +#include "EnumJsonHandler.h" + +#include +#include +#include +#include + +namespace CesiumJsonReader { +class ExtensionReaderContext; +} + +namespace CesiumGltfReader { +class SchemaJsonHandler : public CesiumJsonReader::ExtensibleObjectJsonHandler { +public: + using ValueType = CesiumGltf::Schema; + + SchemaJsonHandler( + const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + void reset(IJsonHandler* pParentHandler, CesiumGltf::Schema* pObject); + + virtual IJsonHandler* readObjectKey(const std::string_view& str) override; + +protected: + IJsonHandler* readObjectKeySchema( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::Schema& o); + +private: + CesiumGltf::Schema* _pObject = nullptr; + CesiumJsonReader::StringJsonHandler _id; + CesiumJsonReader::StringJsonHandler _name; + CesiumJsonReader::StringJsonHandler _description; + CesiumJsonReader::StringJsonHandler _version; + CesiumJsonReader::DictionaryJsonHandler + _classes; + CesiumJsonReader::DictionaryJsonHandler + _enums; +}; +} // namespace CesiumGltfReader diff --git a/CesiumGltfReader/generated/src/registerExtensions.cpp b/CesiumGltfReader/generated/src/registerExtensions.cpp index 1c46f4ba9..15deefecc 100644 --- a/CesiumGltfReader/generated/src/registerExtensions.cpp +++ b/CesiumGltfReader/generated/src/registerExtensions.cpp @@ -3,61 +3,98 @@ #include "registerExtensions.h" -#include - -#include -#include -#include -#include -#include -#include -#include -#include - +#include "ExtensionBufferExtMeshoptCompressionJsonHandler.h" +#include "ExtensionBufferViewExtMeshoptCompressionJsonHandler.h" #include "ExtensionCesiumRTCJsonHandler.h" -#include "ExtensionModelExtFeatureMetadataJsonHandler.h" -#include "ExtensionModelExtStructuralMetadataJsonHandler.h" -#include "ExtensionModelKhrMaterialsVariantsJsonHandler.h" -#include "ExtensionModelMaxarMeshVariantsJsonHandler.h" #include "ExtensionCesiumTileEdgesJsonHandler.h" -#include "ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler.h" -#include "ExtensionExtMeshFeaturesJsonHandler.h" -#include "ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler.h" -#include "ExtensionKhrDracoMeshCompressionJsonHandler.h" -#include "ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler.h" #include "ExtensionExtInstanceFeaturesJsonHandler.h" +#include "ExtensionExtMeshFeaturesJsonHandler.h" #include "ExtensionExtMeshGpuInstancingJsonHandler.h" -#include "ExtensionNodeMaxarMeshVariantsJsonHandler.h" -#include "ExtensionBufferExtMeshoptCompressionJsonHandler.h" -#include "ExtensionBufferViewExtMeshoptCompressionJsonHandler.h" +#include "ExtensionKhrDracoMeshCompressionJsonHandler.h" #include "ExtensionKhrMaterialsUnlitJsonHandler.h" #include "ExtensionKhrTextureBasisuJsonHandler.h" +#include "ExtensionKhrTextureTransformJsonHandler.h" +#include "ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler.h" +#include "ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler.h" +#include "ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler.h" +#include "ExtensionModelExtFeatureMetadataJsonHandler.h" +#include "ExtensionModelExtStructuralMetadataJsonHandler.h" +#include "ExtensionModelKhrMaterialsVariantsJsonHandler.h" +#include "ExtensionModelMaxarMeshVariantsJsonHandler.h" +#include "ExtensionNodeMaxarMeshVariantsJsonHandler.h" #include "ExtensionTextureWebpJsonHandler.h" -#include "ExtensionKhrTextureTransformJsonHandler.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include namespace CesiumGltfReader { void registerExtensions(CesiumJsonReader::ExtensionReaderContext& context) { (void)context; context.registerExtension(); -context.registerExtension(); -context.registerExtension(); -context.registerExtension(); -context.registerExtension(); -context.registerExtension(); -context.registerExtension(); -context.registerExtension(); -context.registerExtension(); -context.registerExtension(); -context.registerExtension(); -context.registerExtension(); -context.registerExtension(); -context.registerExtension(); -context.registerExtension(); -context.registerExtension(); -context.registerExtension(); -context.registerExtension(); -context.registerExtension(); -context.registerExtension(); + context.registerExtension< + CesiumGltf::Model, + ExtensionModelExtFeatureMetadataJsonHandler>(); + context.registerExtension< + CesiumGltf::Model, + ExtensionModelExtStructuralMetadataJsonHandler>(); + context.registerExtension< + CesiumGltf::Model, + ExtensionModelKhrMaterialsVariantsJsonHandler>(); + context.registerExtension< + CesiumGltf::Model, + ExtensionModelMaxarMeshVariantsJsonHandler>(); + context.registerExtension< + CesiumGltf::MeshPrimitive, + ExtensionCesiumTileEdgesJsonHandler>(); + context.registerExtension< + CesiumGltf::MeshPrimitive, + ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler>(); + context.registerExtension< + CesiumGltf::MeshPrimitive, + ExtensionExtMeshFeaturesJsonHandler>(); + context.registerExtension< + CesiumGltf::MeshPrimitive, + ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler>(); + context.registerExtension< + CesiumGltf::MeshPrimitive, + ExtensionKhrDracoMeshCompressionJsonHandler>(); + context.registerExtension< + CesiumGltf::MeshPrimitive, + ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler>(); + context.registerExtension< + CesiumGltf::Node, + ExtensionExtInstanceFeaturesJsonHandler>(); + context.registerExtension< + CesiumGltf::Node, + ExtensionExtMeshGpuInstancingJsonHandler>(); + context.registerExtension< + CesiumGltf::Node, + ExtensionNodeMaxarMeshVariantsJsonHandler>(); + context.registerExtension< + CesiumGltf::Buffer, + ExtensionBufferExtMeshoptCompressionJsonHandler>(); + context.registerExtension< + CesiumGltf::BufferView, + ExtensionBufferViewExtMeshoptCompressionJsonHandler>(); + context.registerExtension< + CesiumGltf::Material, + ExtensionKhrMaterialsUnlitJsonHandler>(); + context.registerExtension< + CesiumGltf::Texture, + ExtensionKhrTextureBasisuJsonHandler>(); + context.registerExtension< + CesiumGltf::Texture, + ExtensionTextureWebpJsonHandler>(); + context.registerExtension< + CesiumGltf::TextureInfo, + ExtensionKhrTextureTransformJsonHandler>(); } } // namespace CesiumGltfReader diff --git a/CesiumGltfWriter/generated/src/ModelJsonWriter.cpp b/CesiumGltfWriter/generated/src/ModelJsonWriter.cpp index a008e3e3c..2d2e38fc2 100644 --- a/CesiumGltfWriter/generated/src/ModelJsonWriter.cpp +++ b/CesiumGltfWriter/generated/src/ModelJsonWriter.cpp @@ -17,6 +17,10 @@ #include #include #include +#include +#include +#include +#include #include #include #include @@ -40,17 +44,6 @@ #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include #include #include @@ -79,8 +72,15 @@ #include #include #include +#include +#include +#include +#include +#include +#include #include #include +#include #include #include #include @@ -216,24 +216,22 @@ void writeJson( const CesiumJsonWriter::ExtensionWriterContext& context); void writeJson( - const CesiumGltf::ExtensionExtStructuralMetadataPropertyAttribute& obj, + const CesiumGltf::PropertyAttribute& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context); void writeJson( - const CesiumGltf::ExtensionExtStructuralMetadataPropertyAttributeProperty& - obj, + const CesiumGltf::PropertyAttributeProperty& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context); void writeJson( - const CesiumGltf::ExtensionExtStructuralMetadataPropertyTexture& obj, + const CesiumGltf::PropertyTexture& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context); void writeJson( - const CesiumGltf::ExtensionExtStructuralMetadataPropertyTextureProperty& - obj, + const CesiumGltf::PropertyTextureProperty& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context); @@ -243,37 +241,37 @@ void writeJson( const CesiumJsonWriter::ExtensionWriterContext& context); void writeJson( - const CesiumGltf::ExtensionExtStructuralMetadataPropertyTable& obj, + const CesiumGltf::PropertyTable& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context); void writeJson( - const CesiumGltf::ExtensionExtStructuralMetadataPropertyTableProperty& obj, + const CesiumGltf::PropertyTableProperty& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context); void writeJson( - const CesiumGltf::ExtensionExtStructuralMetadataSchema& obj, + const CesiumGltf::Schema& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context); void writeJson( - const CesiumGltf::ExtensionExtStructuralMetadataEnum& obj, + const CesiumGltf::Enum& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context); void writeJson( - const CesiumGltf::ExtensionExtStructuralMetadataEnumValue& obj, + const CesiumGltf::EnumValue& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context); void writeJson( - const CesiumGltf::ExtensionExtStructuralMetadataClass& obj, + const CesiumGltf::Class& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context); void writeJson( - const CesiumGltf::ExtensionExtStructuralMetadataClassProperty& obj, + const CesiumGltf::ClassProperty& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context); @@ -1168,7 +1166,7 @@ void writeJson( } void writeJson( - const CesiumGltf::ExtensionExtStructuralMetadataPropertyAttribute& obj, + const CesiumGltf::PropertyAttribute& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context) { jsonWriter.StartObject(); @@ -1192,8 +1190,7 @@ void writeJson( } void writeJson( - const CesiumGltf::ExtensionExtStructuralMetadataPropertyAttributeProperty& - obj, + const CesiumGltf::PropertyAttributeProperty& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context) { jsonWriter.StartObject(); @@ -1227,7 +1224,7 @@ void writeJson( } void writeJson( - const CesiumGltf::ExtensionExtStructuralMetadataPropertyTexture& obj, + const CesiumGltf::PropertyTexture& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context) { jsonWriter.StartObject(); @@ -1251,8 +1248,7 @@ void writeJson( } void writeJson( - const CesiumGltf::ExtensionExtStructuralMetadataPropertyTextureProperty& - obj, + const CesiumGltf::PropertyTextureProperty& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context) { jsonWriter.StartObject(); @@ -1300,7 +1296,7 @@ void writeJson( } void writeJson( - const CesiumGltf::ExtensionExtStructuralMetadataPropertyTable& obj, + const CesiumGltf::PropertyTable& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context) { jsonWriter.StartObject(); @@ -1327,7 +1323,7 @@ void writeJson( } void writeJson( - const CesiumGltf::ExtensionExtStructuralMetadataPropertyTableProperty& obj, + const CesiumGltf::PropertyTableProperty& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context) { jsonWriter.StartObject(); @@ -1348,15 +1344,13 @@ void writeJson( } if (obj.arrayOffsetType != - CesiumGltf::ExtensionExtStructuralMetadataPropertyTableProperty:: - ArrayOffsetType::UINT32) { + CesiumGltf::PropertyTableProperty::ArrayOffsetType::UINT32) { jsonWriter.Key("arrayOffsetType"); writeJson(obj.arrayOffsetType, jsonWriter, context); } if (obj.stringOffsetType != - CesiumGltf::ExtensionExtStructuralMetadataPropertyTableProperty:: - StringOffsetType::UINT32) { + CesiumGltf::PropertyTableProperty::StringOffsetType::UINT32) { jsonWriter.Key("stringOffsetType"); writeJson(obj.stringOffsetType, jsonWriter, context); } @@ -1387,7 +1381,7 @@ void writeJson( } void writeJson( - const CesiumGltf::ExtensionExtStructuralMetadataSchema& obj, + const CesiumGltf::Schema& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context) { jsonWriter.StartObject(); @@ -1426,7 +1420,7 @@ void writeJson( } void writeJson( - const CesiumGltf::ExtensionExtStructuralMetadataEnum& obj, + const CesiumGltf::Enum& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context) { jsonWriter.StartObject(); @@ -1441,8 +1435,7 @@ void writeJson( writeJson(obj.description, jsonWriter, context); } - if (obj.valueType != - CesiumGltf::ExtensionExtStructuralMetadataEnum::ValueType::UINT16) { + if (obj.valueType != CesiumGltf::Enum::ValueType::UINT16) { jsonWriter.Key("valueType"); writeJson(obj.valueType, jsonWriter, context); } @@ -1458,7 +1451,7 @@ void writeJson( } void writeJson( - const CesiumGltf::ExtensionExtStructuralMetadataEnumValue& obj, + const CesiumGltf::EnumValue& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context) { jsonWriter.StartObject(); @@ -1480,7 +1473,7 @@ void writeJson( } void writeJson( - const CesiumGltf::ExtensionExtStructuralMetadataClass& obj, + const CesiumGltf::Class& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context) { jsonWriter.StartObject(); @@ -1506,7 +1499,7 @@ void writeJson( } void writeJson( - const CesiumGltf::ExtensionExtStructuralMetadataClassProperty& obj, + const CesiumGltf::ClassProperty& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context) { jsonWriter.StartObject(); @@ -3091,31 +3084,29 @@ void ExtensionModelKhrMaterialsVariantsValueJsonWriter::write( writeJson(obj, jsonWriter, context); } -void ExtensionExtStructuralMetadataPropertyAttributeJsonWriter::write( - const CesiumGltf::ExtensionExtStructuralMetadataPropertyAttribute& obj, +void PropertyAttributeJsonWriter::write( + const CesiumGltf::PropertyAttribute& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context) { writeJson(obj, jsonWriter, context); } -void ExtensionExtStructuralMetadataPropertyAttributePropertyJsonWriter::write( - const CesiumGltf::ExtensionExtStructuralMetadataPropertyAttributeProperty& - obj, +void PropertyAttributePropertyJsonWriter::write( + const CesiumGltf::PropertyAttributeProperty& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context) { writeJson(obj, jsonWriter, context); } -void ExtensionExtStructuralMetadataPropertyTextureJsonWriter::write( - const CesiumGltf::ExtensionExtStructuralMetadataPropertyTexture& obj, +void PropertyTextureJsonWriter::write( + const CesiumGltf::PropertyTexture& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context) { writeJson(obj, jsonWriter, context); } -void ExtensionExtStructuralMetadataPropertyTexturePropertyJsonWriter::write( - const CesiumGltf::ExtensionExtStructuralMetadataPropertyTextureProperty& - obj, +void PropertyTexturePropertyJsonWriter::write( + const CesiumGltf::PropertyTextureProperty& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context) { writeJson(obj, jsonWriter, context); @@ -3128,50 +3119,50 @@ void TextureInfoJsonWriter::write( writeJson(obj, jsonWriter, context); } -void ExtensionExtStructuralMetadataPropertyTableJsonWriter::write( - const CesiumGltf::ExtensionExtStructuralMetadataPropertyTable& obj, +void PropertyTableJsonWriter::write( + const CesiumGltf::PropertyTable& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context) { writeJson(obj, jsonWriter, context); } -void ExtensionExtStructuralMetadataPropertyTablePropertyJsonWriter::write( - const CesiumGltf::ExtensionExtStructuralMetadataPropertyTableProperty& obj, +void PropertyTablePropertyJsonWriter::write( + const CesiumGltf::PropertyTableProperty& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context) { writeJson(obj, jsonWriter, context); } -void ExtensionExtStructuralMetadataSchemaJsonWriter::write( - const CesiumGltf::ExtensionExtStructuralMetadataSchema& obj, +void SchemaJsonWriter::write( + const CesiumGltf::Schema& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context) { writeJson(obj, jsonWriter, context); } -void ExtensionExtStructuralMetadataEnumJsonWriter::write( - const CesiumGltf::ExtensionExtStructuralMetadataEnum& obj, +void EnumJsonWriter::write( + const CesiumGltf::Enum& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context) { writeJson(obj, jsonWriter, context); } -void ExtensionExtStructuralMetadataEnumValueJsonWriter::write( - const CesiumGltf::ExtensionExtStructuralMetadataEnumValue& obj, +void EnumValueJsonWriter::write( + const CesiumGltf::EnumValue& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context) { writeJson(obj, jsonWriter, context); } -void ExtensionExtStructuralMetadataClassJsonWriter::write( - const CesiumGltf::ExtensionExtStructuralMetadataClass& obj, +void ClassJsonWriter::write( + const CesiumGltf::Class& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context) { writeJson(obj, jsonWriter, context); } -void ExtensionExtStructuralMetadataClassPropertyJsonWriter::write( - const CesiumGltf::ExtensionExtStructuralMetadataClassProperty& obj, +void ClassPropertyJsonWriter::write( + const CesiumGltf::ClassProperty& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context) { writeJson(obj, jsonWriter, context); diff --git a/CesiumGltfWriter/generated/src/ModelJsonWriter.h b/CesiumGltfWriter/generated/src/ModelJsonWriter.h index 6ea76be94..e60717f4d 100644 --- a/CesiumGltfWriter/generated/src/ModelJsonWriter.h +++ b/CesiumGltfWriter/generated/src/ModelJsonWriter.h @@ -34,18 +34,18 @@ struct ExtensionNodeMaxarMeshVariantsMappingsValue; struct ExtensionModelMaxarMeshVariantsValue; struct ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValue; struct ExtensionModelKhrMaterialsVariantsValue; -struct ExtensionExtStructuralMetadataPropertyAttribute; -struct ExtensionExtStructuralMetadataPropertyAttributeProperty; -struct ExtensionExtStructuralMetadataPropertyTexture; -struct ExtensionExtStructuralMetadataPropertyTextureProperty; +struct PropertyAttribute; +struct PropertyAttributeProperty; +struct PropertyTexture; +struct PropertyTextureProperty; struct TextureInfo; -struct ExtensionExtStructuralMetadataPropertyTable; -struct ExtensionExtStructuralMetadataPropertyTableProperty; -struct ExtensionExtStructuralMetadataSchema; -struct ExtensionExtStructuralMetadataEnum; -struct ExtensionExtStructuralMetadataEnumValue; -struct ExtensionExtStructuralMetadataClass; -struct ExtensionExtStructuralMetadataClassProperty; +struct PropertyTable; +struct PropertyTableProperty; +struct Schema; +struct Enum; +struct EnumValue; +struct Class; +struct ClassProperty; struct FeatureId; struct FeatureIdTexture; struct ExtensionExtInstanceFeaturesFeatureId; @@ -354,42 +354,38 @@ struct ExtensionModelKhrMaterialsVariantsValueJsonWriter { const CesiumJsonWriter::ExtensionWriterContext& context); }; -struct ExtensionExtStructuralMetadataPropertyAttributeJsonWriter { - using ValueType = CesiumGltf::ExtensionExtStructuralMetadataPropertyAttribute; +struct PropertyAttributeJsonWriter { + using ValueType = CesiumGltf::PropertyAttribute; static void write( - const CesiumGltf::ExtensionExtStructuralMetadataPropertyAttribute& obj, + const CesiumGltf::PropertyAttribute& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context); }; -struct ExtensionExtStructuralMetadataPropertyAttributePropertyJsonWriter { - using ValueType = - CesiumGltf::ExtensionExtStructuralMetadataPropertyAttributeProperty; +struct PropertyAttributePropertyJsonWriter { + using ValueType = CesiumGltf::PropertyAttributeProperty; static void write( - const CesiumGltf::ExtensionExtStructuralMetadataPropertyAttributeProperty& - obj, + const CesiumGltf::PropertyAttributeProperty& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context); }; -struct ExtensionExtStructuralMetadataPropertyTextureJsonWriter { - using ValueType = CesiumGltf::ExtensionExtStructuralMetadataPropertyTexture; +struct PropertyTextureJsonWriter { + using ValueType = CesiumGltf::PropertyTexture; static void write( - const CesiumGltf::ExtensionExtStructuralMetadataPropertyTexture& obj, + const CesiumGltf::PropertyTexture& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context); }; -struct ExtensionExtStructuralMetadataPropertyTexturePropertyJsonWriter { - using ValueType = - CesiumGltf::ExtensionExtStructuralMetadataPropertyTextureProperty; +struct PropertyTexturePropertyJsonWriter { + using ValueType = CesiumGltf::PropertyTextureProperty; static void write( - const CesiumGltf::ExtensionExtStructuralMetadataPropertyTextureProperty& - obj, + const CesiumGltf::PropertyTextureProperty& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context); }; @@ -403,67 +399,65 @@ struct TextureInfoJsonWriter { const CesiumJsonWriter::ExtensionWriterContext& context); }; -struct ExtensionExtStructuralMetadataPropertyTableJsonWriter { - using ValueType = CesiumGltf::ExtensionExtStructuralMetadataPropertyTable; +struct PropertyTableJsonWriter { + using ValueType = CesiumGltf::PropertyTable; static void write( - const CesiumGltf::ExtensionExtStructuralMetadataPropertyTable& obj, + const CesiumGltf::PropertyTable& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context); }; -struct ExtensionExtStructuralMetadataPropertyTablePropertyJsonWriter { - using ValueType = - CesiumGltf::ExtensionExtStructuralMetadataPropertyTableProperty; +struct PropertyTablePropertyJsonWriter { + using ValueType = CesiumGltf::PropertyTableProperty; static void write( - const CesiumGltf::ExtensionExtStructuralMetadataPropertyTableProperty& - obj, + const CesiumGltf::PropertyTableProperty& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context); }; -struct ExtensionExtStructuralMetadataSchemaJsonWriter { - using ValueType = CesiumGltf::ExtensionExtStructuralMetadataSchema; +struct SchemaJsonWriter { + using ValueType = CesiumGltf::Schema; static void write( - const CesiumGltf::ExtensionExtStructuralMetadataSchema& obj, + const CesiumGltf::Schema& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context); }; -struct ExtensionExtStructuralMetadataEnumJsonWriter { - using ValueType = CesiumGltf::ExtensionExtStructuralMetadataEnum; +struct EnumJsonWriter { + using ValueType = CesiumGltf::Enum; static void write( - const CesiumGltf::ExtensionExtStructuralMetadataEnum& obj, + const CesiumGltf::Enum& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context); }; -struct ExtensionExtStructuralMetadataEnumValueJsonWriter { - using ValueType = CesiumGltf::ExtensionExtStructuralMetadataEnumValue; +struct EnumValueJsonWriter { + using ValueType = CesiumGltf::EnumValue; static void write( - const CesiumGltf::ExtensionExtStructuralMetadataEnumValue& obj, + const CesiumGltf::EnumValue& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context); }; -struct ExtensionExtStructuralMetadataClassJsonWriter { - using ValueType = CesiumGltf::ExtensionExtStructuralMetadataClass; +struct ClassJsonWriter { + using ValueType = CesiumGltf::Class; static void write( - const CesiumGltf::ExtensionExtStructuralMetadataClass& obj, + const CesiumGltf::Class& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context); }; -struct ExtensionExtStructuralMetadataClassPropertyJsonWriter { - using ValueType = CesiumGltf::ExtensionExtStructuralMetadataClassProperty; +struct ClassPropertyJsonWriter { + using ValueType = CesiumGltf::ClassProperty; static void write( - const CesiumGltf::ExtensionExtStructuralMetadataClassProperty& obj, + const CesiumGltf::ClassProperty& obj, CesiumJsonWriter::JsonWriter& jsonWriter, const CesiumJsonWriter::ExtensionWriterContext& context); }; diff --git a/tools/generate-classes/glTF.json b/tools/generate-classes/glTF.json index e6b7e699a..c08c9f865 100644 --- a/tools/generate-classes/glTF.json +++ b/tools/generate-classes/glTF.json @@ -118,37 +118,37 @@ "overrideName": "ExtensionMeshPrimitiveExtStructuralMetadata" }, "Class Property in EXT_structural_metadata": { - "overrideName": "ExtensionExtStructuralMetadataClassProperty" + "overrideName": "ClassProperty" }, "Class in EXT_structural_metadata": { - "overrideName": "ExtensionExtStructuralMetadataClass" + "overrideName": "Class" }, "Enum in EXT_structural_metadata": { - "overrideName": "ExtensionExtStructuralMetadataEnum" + "overrideName": "Enum" }, "Enum Value in EXT_structural_metadata": { - "overrideName": "ExtensionExtStructuralMetadataEnumValue" + "overrideName": "EnumValue" }, "Property Attribute Property in EXT_structural_metadata": { - "overrideName": "ExtensionExtStructuralMetadataPropertyAttributeProperty" + "overrideName": "PropertyAttributeProperty" }, "Property Attribute in EXT_structural_metadata": { - "overrideName": "ExtensionExtStructuralMetadataPropertyAttribute" + "overrideName": "PropertyAttribute" }, "Property Table Property in EXT_structural_metadata": { - "overrideName": "ExtensionExtStructuralMetadataPropertyTableProperty" + "overrideName": "PropertyTableProperty" }, "Property Table in EXT_structural_metadata": { - "overrideName": "ExtensionExtStructuralMetadataPropertyTable" + "overrideName": "PropertyTable" }, "Property Texture Property in EXT_structural_metadata": { - "overrideName": "ExtensionExtStructuralMetadataPropertyTextureProperty" + "overrideName": "PropertyTextureProperty" }, "Property Texture in EXT_structural_metadata": { - "overrideName": "ExtensionExtStructuralMetadataPropertyTexture" + "overrideName": "PropertyTexture" }, "Schema in EXT_structural_metadata": { - "overrideName": "ExtensionExtStructuralMetadataSchema" + "overrideName": "Schema" }, "Any Value": { "overrideName": "CesiumUtility::JsonValue" From c329a8d407aa683b67fdd88c6e653dc304cccdf0 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Fri, 16 Jun 2023 16:25:39 -0400 Subject: [PATCH 078/421] Replace class names in metadata implementation files --- .../BatchTableToGltfStructuralMetadata.cpp | 191 ++-- .../src/upsampleGltfForRasterOverlays.cpp | 3 +- ...gradeBatchTableToExtStructuralMetadata.cpp | 450 ++++------ .../include/CesiumGltf/PropertyArrayView.h | 3 +- .../CesiumGltf/PropertyTablePropertyView.h | 53 +- .../include/CesiumGltf/PropertyTableView.h | 92 +- .../CesiumGltf/PropertyTexturePropertyView.h | 22 +- .../include/CesiumGltf/PropertyTextureView.h | 24 +- CesiumGltf/src/PropertyTableView.cpp | 23 +- .../src/PropertyTexturePropertyView.cpp | 5 +- CesiumGltf/src/PropertyTextureView.cpp | 5 +- CesiumGltf/src/PropertyType.cpp | 117 ++- CesiumGltf/test/TestPropertyTableView.cpp | 815 +++++++----------- .../test/TestPropertyTexturePropertyView.cpp | 135 ++- CesiumGltf/test/TestPropertyTextureView.cpp | 89 +- CesiumGltf/test/TestPropertyType.cpp | 145 ++-- 16 files changed, 873 insertions(+), 1299 deletions(-) diff --git a/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp b/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp index 7010cc286..b78b73764 100644 --- a/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp +++ b/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp @@ -241,22 +241,10 @@ struct GltfPropertyTableType { }; const std::map batchTableTypeToGltfType = { - {"SCALAR", - GltfPropertyTableType{ - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, - 1}}, - {"VEC2", - GltfPropertyTableType{ - ExtensionExtStructuralMetadataClassProperty::Type::VEC2, - 2}}, - {"VEC3", - GltfPropertyTableType{ - ExtensionExtStructuralMetadataClassProperty::Type::VEC3, - 3}}, - {"VEC4", - GltfPropertyTableType{ - ExtensionExtStructuralMetadataClassProperty::Type::VEC4, - 4}}, + {"SCALAR", GltfPropertyTableType{ClassProperty::Type::SCALAR, 1}}, + {"VEC2", GltfPropertyTableType{ClassProperty::Type::VEC2, 2}}, + {"VEC3", GltfPropertyTableType{ClassProperty::Type::VEC3, 3}}, + {"VEC4", GltfPropertyTableType{ClassProperty::Type::VEC4, 4}}, }; struct GltfPropertyTableComponentType { @@ -268,37 +256,35 @@ const std::map batchTableComponentTypeToGltfComponentType = { {"BYTE", GltfPropertyTableComponentType{ - ExtensionExtStructuralMetadataClassProperty::ComponentType::INT8, + ClassProperty::ComponentType::INT8, sizeof(int8_t)}}, {"UNSIGNED_BYTE", GltfPropertyTableComponentType{ - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT8, + ClassProperty::ComponentType::UINT8, sizeof(uint8_t)}}, {"SHORT", GltfPropertyTableComponentType{ - ExtensionExtStructuralMetadataClassProperty::ComponentType::INT16, + ClassProperty::ComponentType::INT16, sizeof(int16_t)}}, {"UNSIGNED_SHORT", GltfPropertyTableComponentType{ - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT16, + ClassProperty::ComponentType::UINT16, sizeof(uint16_t)}}, {"INT", GltfPropertyTableComponentType{ - ExtensionExtStructuralMetadataClassProperty::ComponentType::INT32, + ClassProperty::ComponentType::INT32, sizeof(int32_t)}}, {"UNSIGNED_INT", GltfPropertyTableComponentType{ - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT32, + ClassProperty::ComponentType::UINT32, sizeof(uint32_t)}}, {"FLOAT", GltfPropertyTableComponentType{ - ExtensionExtStructuralMetadataClassProperty::ComponentType:: - FLOAT32, + ClassProperty::ComponentType::FLOAT32, sizeof(float)}}, {"DOUBLE", GltfPropertyTableComponentType{ - ExtensionExtStructuralMetadataClassProperty::ComponentType:: - FLOAT64, + ClassProperty::ComponentType::FLOAT64, sizeof(double)}}, }; @@ -466,9 +452,9 @@ int32_t addBufferToGltf(Model& gltf, std::vector&& buffer) { template void updateExtensionWithJsonStringProperty( Model& gltf, - ExtensionExtStructuralMetadataClassProperty& classProperty, - const ExtensionExtStructuralMetadataPropertyTable& propertyTable, - ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty, + ClassProperty& classProperty, + const PropertyTable& propertyTable, + PropertyTableProperty& propertyTableProperty, const TValueGetter& propertyValue) { rapidjson::StringBuffer rapidjsonStrBuffer; @@ -511,8 +497,7 @@ void updateExtensionWithJsonStringProperty( buffer, offsetBuffer); propertyTableProperty.stringOffsetType = - ExtensionExtStructuralMetadataPropertyTableProperty::StringOffsetType:: - UINT8; + PropertyTableProperty::StringOffsetType::UINT8; } else if (isInRangeForUnsignedInteger(totalSize)) { copyStringBuffer( rapidjsonStrBuffer, @@ -520,8 +505,7 @@ void updateExtensionWithJsonStringProperty( buffer, offsetBuffer); propertyTableProperty.stringOffsetType = - ExtensionExtStructuralMetadataPropertyTableProperty::StringOffsetType:: - UINT16; + PropertyTableProperty::StringOffsetType::UINT16; } else if (isInRangeForUnsignedInteger(totalSize)) { copyStringBuffer( rapidjsonStrBuffer, @@ -529,8 +513,7 @@ void updateExtensionWithJsonStringProperty( buffer, offsetBuffer); propertyTableProperty.stringOffsetType = - ExtensionExtStructuralMetadataPropertyTableProperty::StringOffsetType:: - UINT32; + PropertyTableProperty::StringOffsetType::UINT32; } else { copyStringBuffer( rapidjsonStrBuffer, @@ -538,12 +521,10 @@ void updateExtensionWithJsonStringProperty( buffer, offsetBuffer); propertyTableProperty.stringOffsetType = - ExtensionExtStructuralMetadataPropertyTableProperty::StringOffsetType:: - UINT64; + PropertyTableProperty::StringOffsetType::UINT64; } - classProperty.type = - ExtensionExtStructuralMetadataClassProperty::Type::STRING; + classProperty.type = ClassProperty::Type::STRING; propertyTableProperty.values = addBufferToGltf(gltf, std::move(buffer)); propertyTableProperty.stringOffsets = @@ -553,15 +534,14 @@ void updateExtensionWithJsonStringProperty( template void updateExtensionWithJsonScalarProperty( Model& gltf, - ExtensionExtStructuralMetadataClassProperty& classProperty, - const ExtensionExtStructuralMetadataPropertyTable& propertyTable, - ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty, + ClassProperty& classProperty, + const PropertyTable& propertyTable, + PropertyTableProperty& propertyTableProperty, const TValueGetter& propertyValue, const std::string& componentTypeName) { assert(propertyValue.size() >= propertyTable.count); - classProperty.type = - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR; + classProperty.type = ClassProperty::Type::SCALAR; classProperty.componentType = componentTypeName; // Create a new buffer for this property. @@ -581,9 +561,9 @@ void updateExtensionWithJsonScalarProperty( template void updateExtensionWithJsonBooleanProperty( Model& gltf, - ExtensionExtStructuralMetadataClassProperty& classProperty, - const ExtensionExtStructuralMetadataPropertyTable& propertyTable, - ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty, + ClassProperty& classProperty, + const PropertyTable& propertyTable, + PropertyTableProperty& propertyTableProperty, const TValueGetter& propertyValue) { assert(propertyValue.size() >= propertyTable.count); @@ -601,8 +581,7 @@ void updateExtensionWithJsonBooleanProperty( ++it; } - classProperty.type = - ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN; + classProperty.type = ClassProperty::Type::BOOLEAN; propertyTableProperty.values = addBufferToGltf(gltf, std::move(buffer)); } @@ -615,7 +594,7 @@ void copyVariableLengthScalarArraysToBuffers( std::vector& valueBuffer, std::vector& offsetBuffer, size_t numOfElements, - const ExtensionExtStructuralMetadataPropertyTable& propertyTable, + const PropertyTable& propertyTable, const TValueGetter& propertyValue) { valueBuffer.resize(sizeof(ValueType) * numOfElements); offsetBuffer.resize( @@ -645,15 +624,14 @@ void copyVariableLengthScalarArraysToBuffers( template void updateScalarArrayProperty( Model& gltf, - ExtensionExtStructuralMetadataClassProperty& classProperty, - ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty, - const ExtensionExtStructuralMetadataPropertyTable& propertyTable, + ClassProperty& classProperty, + PropertyTableProperty& propertyTableProperty, + const PropertyTable& propertyTable, const MaskedArrayType& arrayType, const TValueGetter& propertyValue) { assert(propertyValue.size() >= propertyTable.count); - classProperty.type = - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR; + classProperty.type = ClassProperty::Type::SCALAR; classProperty.componentType = convertPropertyComponentTypeToString(static_cast( TypeToPropertyType::component)); @@ -744,7 +722,7 @@ void copyStringsToBuffers( std::vector& offsetBuffer, size_t totalByteLength, size_t numOfString, - const ExtensionExtStructuralMetadataPropertyTable& propertyTable, + const PropertyTable& propertyTable, const TValueGetter& propertyValue) { valueBuffer.resize(totalByteLength); offsetBuffer.resize((numOfString + 1) * sizeof(OffsetType)); @@ -776,7 +754,7 @@ void copyStringsToBuffers( template void copyArrayOffsetsForStringArraysToBuffer( std::vector& offsetBuffer, - const ExtensionExtStructuralMetadataPropertyTable& propertyTable, + const PropertyTable& propertyTable, const TValueGetter& propertyValue) { OffsetType prevOffset = 0; offsetBuffer.resize( @@ -798,9 +776,9 @@ void copyArrayOffsetsForStringArraysToBuffer( template void updateStringArrayProperty( Model& gltf, - ExtensionExtStructuralMetadataClassProperty& classProperty, - ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty, - const ExtensionExtStructuralMetadataPropertyTable& propertyTable, + ClassProperty& classProperty, + PropertyTableProperty& propertyTableProperty, + const PropertyTable& propertyTable, const MaskedArrayType& arrayType, const TValueGetter& propertyValue) { assert(propertyValue.size() >= propertyTable.count); @@ -860,8 +838,7 @@ void updateStringArrayProperty( stringOffsetType = PropertyComponentType::Uint64; } - classProperty.type = - ExtensionExtStructuralMetadataClassProperty::Type::STRING; + classProperty.type = ClassProperty::Type::STRING; classProperty.array = true; propertyTableProperty.stringOffsetType = convertPropertyComponentTypeToString(stringOffsetType); @@ -918,7 +895,7 @@ void copyVariableLengthBooleanArraysToBuffers( std::vector& valueBuffer, std::vector& offsetBuffer, size_t numOfElements, - const ExtensionExtStructuralMetadataPropertyTable& propertyTable, + const PropertyTable& propertyTable, const TValueGetter& propertyValue) { size_t currentIndex = 0; const size_t totalByteLength = @@ -953,15 +930,14 @@ void copyVariableLengthBooleanArraysToBuffers( template void updateBooleanArrayProperty( Model& gltf, - ExtensionExtStructuralMetadataClassProperty& classProperty, - ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty, - const ExtensionExtStructuralMetadataPropertyTable& propertyTable, + ClassProperty& classProperty, + PropertyTableProperty& propertyTableProperty, + const PropertyTable& propertyTable, const MaskedArrayType& arrayType, const TValueGetter& propertyValue) { assert(propertyValue.size() >= propertyTable.count); - classProperty.type = - ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN; + classProperty.type = ClassProperty::Type::BOOLEAN; classProperty.array = true; // Fixed-length array of booleans @@ -1050,9 +1026,9 @@ void updateBooleanArrayProperty( template void updateExtensionWithArrayProperty( Model& gltf, - ExtensionExtStructuralMetadataClassProperty& classProperty, - const ExtensionExtStructuralMetadataPropertyTable& propertyTable, - ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty, + ClassProperty& classProperty, + const PropertyTable& propertyTable, + PropertyTableProperty& propertyTableProperty, const MaskedArrayType& arrayType, const TValueGetter& propertyValue) { assert(propertyValue.size() >= propertyTable.count); @@ -1162,9 +1138,9 @@ void updateExtensionWithArrayProperty( template void updateExtensionWithJsonProperty( Model& gltf, - ExtensionExtStructuralMetadataClassProperty& classProperty, - const ExtensionExtStructuralMetadataPropertyTable& propertyTable, - ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty, + ClassProperty& classProperty, + const PropertyTable& propertyTable, + PropertyTableProperty& propertyTableProperty, const TValueGetter& propertyValue) { if (propertyValue.size() == 0 || propertyValue.size() < propertyTable.count) { @@ -1208,7 +1184,7 @@ void updateExtensionWithJsonProperty( propertyTable, propertyTableProperty, propertyValue, - ExtensionExtStructuralMetadataClassProperty::ComponentType::INT8); + ClassProperty::ComponentType::INT8); } else if (type.isUint8) { updateExtensionWithJsonScalarProperty( gltf, @@ -1216,7 +1192,7 @@ void updateExtensionWithJsonProperty( propertyTable, propertyTableProperty, propertyValue, - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT8); + ClassProperty::ComponentType::UINT8); } else if (type.isInt16) { updateExtensionWithJsonScalarProperty( gltf, @@ -1224,7 +1200,7 @@ void updateExtensionWithJsonProperty( propertyTable, propertyTableProperty, propertyValue, - ExtensionExtStructuralMetadataClassProperty::ComponentType::INT16); + ClassProperty::ComponentType::INT16); } else if (type.isUint16) { updateExtensionWithJsonScalarProperty( gltf, @@ -1232,7 +1208,7 @@ void updateExtensionWithJsonProperty( propertyTable, propertyTableProperty, propertyValue, - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT16); + ClassProperty::ComponentType::UINT16); } else if (type.isInt32) { updateExtensionWithJsonScalarProperty( gltf, @@ -1240,7 +1216,7 @@ void updateExtensionWithJsonProperty( propertyTable, propertyTableProperty, propertyValue, - ExtensionExtStructuralMetadataClassProperty::ComponentType::INT32); + ClassProperty::ComponentType::INT32); } else if (type.isUint32) { updateExtensionWithJsonScalarProperty( gltf, @@ -1248,7 +1224,7 @@ void updateExtensionWithJsonProperty( propertyTable, propertyTableProperty, propertyValue, - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT32); + ClassProperty::ComponentType::UINT32); } else if (type.isInt64) { updateExtensionWithJsonScalarProperty( gltf, @@ -1256,7 +1232,7 @@ void updateExtensionWithJsonProperty( propertyTable, propertyTableProperty, propertyValue, - ExtensionExtStructuralMetadataClassProperty::ComponentType::INT64); + ClassProperty::ComponentType::INT64); } else if (type.isUint64) { updateExtensionWithJsonScalarProperty( gltf, @@ -1264,7 +1240,7 @@ void updateExtensionWithJsonProperty( propertyTable, propertyTableProperty, propertyValue, - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT64); + ClassProperty::ComponentType::UINT64); } else if (type.isFloat32) { updateExtensionWithJsonScalarProperty( gltf, @@ -1272,7 +1248,7 @@ void updateExtensionWithJsonProperty( propertyTable, propertyTableProperty, propertyValue, - ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT32); + ClassProperty::ComponentType::FLOAT32); } else if (type.isFloat64) { updateExtensionWithJsonScalarProperty( gltf, @@ -1280,7 +1256,7 @@ void updateExtensionWithJsonProperty( propertyTable, propertyTableProperty, propertyValue, - ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT64); + ClassProperty::ComponentType::FLOAT64); } else { updateExtensionWithJsonStringProperty( gltf, @@ -1296,9 +1272,9 @@ void updateExtensionWithBinaryProperty( int32_t gltfBufferIndex, int64_t gltfBufferOffset, BinaryProperty& binaryProperty, - ExtensionExtStructuralMetadataClassProperty& classProperty, - ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty, - const ExtensionExtStructuralMetadataPropertyTable& propertyTable, + ClassProperty& classProperty, + PropertyTableProperty& propertyTableProperty, + const PropertyTable& propertyTable, const std::string& propertyName, const rapidjson::Value& propertyValue, ErrorList& result) { @@ -1385,8 +1361,8 @@ void updateExtensionWithBinaryProperty( void updateExtensionWithBatchTableHierarchy( Model& gltf, - ExtensionExtStructuralMetadataClass& classDefinition, - ExtensionExtStructuralMetadataPropertyTable& propertyTable, + Class& classDefinition, + PropertyTable& propertyTable, ErrorList& result, const rapidjson::Value& batchTableHierarchy) { // EXT_structural_metadata can't support hierarchy, so we need to flatten it. @@ -1442,17 +1418,12 @@ void updateExtensionWithBatchTableHierarchy( propertyTable.count); for (const std::string& name : properties) { - ExtensionExtStructuralMetadataClassProperty& classProperty = - classDefinition.properties - .emplace(name, ExtensionExtStructuralMetadataClassProperty()) - .first->second; + ClassProperty& classProperty = + classDefinition.properties.emplace(name, ClassProperty()).first->second; classProperty.name = name; - ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = - propertyTable.properties - .emplace( - name, - ExtensionExtStructuralMetadataPropertyTableProperty()) + PropertyTableProperty& propertyTableProperty = + propertyTable.properties.emplace(name, PropertyTableProperty()) .first->second; batchTableHierarchyValues.setProperty(name); @@ -1485,16 +1456,13 @@ void convertBatchTableToGltfStructuralMetadataExtension( ExtensionModelExtStructuralMetadata& modelExtension = gltf.addExtension(); - ExtensionExtStructuralMetadataSchema& schema = - modelExtension.schema.emplace(); + Schema& schema = modelExtension.schema.emplace(); schema.id = "default"; // Required by the spec. - ExtensionExtStructuralMetadataClass& classDefinition = - schema.classes.emplace("default", ExtensionExtStructuralMetadataClass()) - .first->second; + Class& classDefinition = + schema.classes.emplace("default", Class()).first->second; - ExtensionExtStructuralMetadataPropertyTable& propertyTable = - modelExtension.propertyTables.emplace_back(); + PropertyTable& propertyTable = modelExtension.propertyTables.emplace_back(); propertyTable.name = "default"; propertyTable.count = featureCount; propertyTable.classProperty = "default"; @@ -1510,17 +1478,12 @@ void convertBatchTableToGltfStructuralMetadataExtension( continue; } - ExtensionExtStructuralMetadataClassProperty& classProperty = - classDefinition.properties - .emplace(name, ExtensionExtStructuralMetadataClassProperty()) - .first->second; + ClassProperty& classProperty = + classDefinition.properties.emplace(name, ClassProperty()).first->second; classProperty.name = name; - ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = - propertyTable.properties - .emplace( - name, - ExtensionExtStructuralMetadataPropertyTableProperty()) + PropertyTableProperty& propertyTableProperty = + propertyTable.properties.emplace(name, PropertyTableProperty()) .first->second; const rapidjson::Value& propertyValue = propertyIt->value; if (propertyValue.IsArray()) { diff --git a/Cesium3DTilesSelection/src/upsampleGltfForRasterOverlays.cpp b/Cesium3DTilesSelection/src/upsampleGltfForRasterOverlays.cpp index 8469f1134..e198912e9 100644 --- a/Cesium3DTilesSelection/src/upsampleGltfForRasterOverlays.cpp +++ b/Cesium3DTilesSelection/src/upsampleGltfForRasterOverlays.cpp @@ -1252,8 +1252,7 @@ static void copyMetadataTables(const Model& parentModel, Model& result) { if (pMetadata) { for (auto& propertyTable : pMetadata->propertyTables) { for (auto& propertyPair : propertyTable.properties) { - ExtensionExtStructuralMetadataPropertyTableProperty& property = - propertyPair.second; + PropertyTableProperty& property = propertyPair.second; property.values = copyBufferView(parentModel, property.values, result); property.arrayOffsets = copyBufferView(parentModel, property.arrayOffsets, result); diff --git a/Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp b/Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp index 445416236..46dc79b45 100644 --- a/Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp +++ b/Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp @@ -24,15 +24,14 @@ using namespace CesiumUtility; template static void checkNonArrayProperty( const Model& model, - const ExtensionExtStructuralMetadataPropertyTable& propertyTable, - const ExtensionExtStructuralMetadataClass& metaClass, + const PropertyTable& propertyTable, + const Class& metaClass, const std::string& propertyName, const std::string& expectedType, const std::optional& expectedComponentType, const std::vector& expected, size_t expectedTotalInstances) { - const ExtensionExtStructuralMetadataClassProperty& property = - metaClass.properties.at(propertyName); + const ClassProperty& property = metaClass.properties.at(propertyName); REQUIRE(property.type == expectedType); REQUIRE(property.componentType == expectedComponentType); REQUIRE(!property.array); @@ -68,16 +67,15 @@ static void checkNonArrayProperty( template static void checkArrayProperty( const Model& model, - const ExtensionExtStructuralMetadataPropertyTable& propertyTable, - const ExtensionExtStructuralMetadataClass& metaClass, + const PropertyTable& propertyTable, + const Class& metaClass, const std::string& propertyName, int64_t expectedCount, const std::string& expectedType, const std::optional& expectedComponentType, const std::vector>& expected, size_t expectedTotalInstances) { - const ExtensionExtStructuralMetadataClassProperty& property = - metaClass.properties.at(propertyName); + const ClassProperty& property = metaClass.properties.at(propertyName); REQUIRE(property.type == expectedType); REQUIRE(property.componentType == expectedComponentType); REQUIRE(property.array); @@ -161,26 +159,20 @@ static void createTestForNonArrayJson( model.getExtension(); REQUIRE(pMetadata); - const std::optional schema = - pMetadata->schema; + const std::optional schema = pMetadata->schema; REQUIRE(schema); - const std::unordered_map& - classes = schema->classes; + const std::unordered_map& classes = schema->classes; REQUIRE(classes.size() == 1); - const ExtensionExtStructuralMetadataClass& defaultClass = - classes.at("default"); - const std::unordered_map< - std::string, - ExtensionExtStructuralMetadataClassProperty>& properties = + const Class& defaultClass = classes.at("default"); + const std::unordered_map& properties = defaultClass.properties; REQUIRE(properties.size() == 1); REQUIRE(pMetadata->propertyTables.size() == 1); - const ExtensionExtStructuralMetadataPropertyTable& propertyTable = - pMetadata->propertyTables[0]; + const PropertyTable& propertyTable = pMetadata->propertyTables[0]; checkNonArrayProperty( model, propertyTable, @@ -247,18 +239,15 @@ static void createTestForArrayJson( model.getExtension(); REQUIRE(pMetadata); - const std::optional& schema = - pMetadata->schema; + const std::optional& schema = pMetadata->schema; REQUIRE(schema); REQUIRE(schema->classes.find("default") != schema->classes.end()); - const ExtensionExtStructuralMetadataClass& defaultClass = - schema->classes.at("default"); + const Class& defaultClass = schema->classes.at("default"); REQUIRE(defaultClass.properties.size() == 1); REQUIRE(pMetadata->propertyTables.size() == 1); - const ExtensionExtStructuralMetadataPropertyTable& propertyTable = - pMetadata->propertyTables[0]; + const PropertyTable& propertyTable = pMetadata->propertyTables[0]; checkArrayProperty( model, @@ -274,7 +263,7 @@ static void createTestForArrayJson( std::set getUniqueBufferViewIds( const std::vector& accessors, - const ExtensionExtStructuralMetadataPropertyTable& propertyTable) { + const PropertyTable& propertyTable) { std::set result; for (auto it = accessors.begin(); it != accessors.end(); it++) { result.insert(it->bufferView); @@ -316,7 +305,7 @@ TEST_CASE("Converts JSON B3DM batch table to EXT_feature_metadata") { auto firstClassIt = pExtension->schema->classes.begin(); CHECK(firstClassIt->first == "default"); - ExtensionExtStructuralMetadataClass& defaultClass = firstClassIt->second; + Class& defaultClass = firstClassIt->second; REQUIRE(defaultClass.properties.size() == 4); auto idIt = defaultClass.properties.find("id"); @@ -328,36 +317,24 @@ TEST_CASE("Converts JSON B3DM batch table to EXT_feature_metadata") { auto heightIt = defaultClass.properties.find("Height"); REQUIRE(heightIt != defaultClass.properties.end()); - CHECK( - idIt->second.type == - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR); - CHECK( - longitudeIt->second.type == - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR); - CHECK( - latitudeIt->second.type == - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR); - CHECK( - heightIt->second.type == - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR); + CHECK(idIt->second.type == ClassProperty::Type::SCALAR); + CHECK(longitudeIt->second.type == ClassProperty::Type::SCALAR); + CHECK(latitudeIt->second.type == ClassProperty::Type::SCALAR); + CHECK(heightIt->second.type == ClassProperty::Type::SCALAR); - CHECK( - idIt->second.componentType == - ExtensionExtStructuralMetadataClassProperty::ComponentType::INT8); + CHECK(idIt->second.componentType == ClassProperty::ComponentType::INT8); CHECK( longitudeIt->second.componentType == - ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT64); + ClassProperty::ComponentType::FLOAT64); CHECK( latitudeIt->second.componentType == - ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT64); + ClassProperty::ComponentType::FLOAT64); CHECK( - heightIt->second.componentType == - ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT64); + heightIt->second.componentType == ClassProperty::ComponentType::FLOAT64); // Check the property table REQUIRE(pExtension->propertyTables.size() == 1); - ExtensionExtStructuralMetadataPropertyTable& propertyTable = - pExtension->propertyTables[0]; + PropertyTable& propertyTable = pExtension->propertyTables[0]; CHECK(propertyTable.classProperty == "default"); REQUIRE(propertyTable.properties.size() == 4); @@ -426,8 +403,8 @@ TEST_CASE("Converts JSON B3DM batch table to EXT_feature_metadata") { propertyTable, defaultClass, "id", - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, - ExtensionExtStructuralMetadataClassProperty::ComponentType::INT8, + ClassProperty::Type::SCALAR, + ClassProperty::ComponentType::INT8, expected, expected.size()); } @@ -449,8 +426,8 @@ TEST_CASE("Converts JSON B3DM batch table to EXT_feature_metadata") { propertyTable, defaultClass, "Height", - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, - ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT64, + ClassProperty::Type::SCALAR, + ClassProperty::ComponentType::FLOAT64, expected, expected.size()); } @@ -472,8 +449,8 @@ TEST_CASE("Converts JSON B3DM batch table to EXT_feature_metadata") { propertyTable, defaultClass, "Longitude", - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, - ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT64, + ClassProperty::Type::SCALAR, + ClassProperty::ComponentType::FLOAT64, expected, expected.size()); } @@ -495,8 +472,8 @@ TEST_CASE("Converts JSON B3DM batch table to EXT_feature_metadata") { propertyTable, defaultClass, "Latitude", - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, - ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT64, + ClassProperty::Type::SCALAR, + ClassProperty::ComponentType::FLOAT64, expected, expected.size()); } @@ -518,24 +495,19 @@ TEST_CASE("Convert binary B3DM batch table to EXT_structural_metadata") { model.getExtension(); REQUIRE(metadata); - std::optional schema = metadata->schema; + std::optional schema = metadata->schema; REQUIRE(schema); - const std::unordered_map& - classes = schema->classes; + const std::unordered_map& classes = schema->classes; REQUIRE(classes.size() == 1); - const ExtensionExtStructuralMetadataClass& defaultClass = - classes.at("default"); - const std::unordered_map< - std::string, - ExtensionExtStructuralMetadataClassProperty>& properties = + const Class& defaultClass = classes.at("default"); + const std::unordered_map& properties = defaultClass.properties; REQUIRE(properties.size() == 6); REQUIRE(metadata->propertyTables.size() == 1); - const ExtensionExtStructuralMetadataPropertyTable& propertyTable = - metadata->propertyTables[0]; + const PropertyTable& propertyTable = metadata->propertyTables[0]; // Check that batch IDs were converted to EXT_mesh_features CHECK(!model.meshes.empty()); @@ -570,8 +542,8 @@ TEST_CASE("Convert binary B3DM batch table to EXT_structural_metadata") { propertyTable, defaultClass, "id", - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, - ExtensionExtStructuralMetadataClassProperty::ComponentType::INT8, + ClassProperty::Type::SCALAR, + ClassProperty::ComponentType::INT8, expected, expected.size()); } @@ -593,8 +565,8 @@ TEST_CASE("Convert binary B3DM batch table to EXT_structural_metadata") { propertyTable, defaultClass, "Height", - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, - ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT64, + ClassProperty::Type::SCALAR, + ClassProperty::ComponentType::FLOAT64, expected, expected.size()); } @@ -616,8 +588,8 @@ TEST_CASE("Convert binary B3DM batch table to EXT_structural_metadata") { propertyTable, defaultClass, "Longitude", - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, - ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT64, + ClassProperty::Type::SCALAR, + ClassProperty::ComponentType::FLOAT64, expected, expected.size()); } @@ -639,8 +611,8 @@ TEST_CASE("Convert binary B3DM batch table to EXT_structural_metadata") { propertyTable, defaultClass, "Latitude", - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, - ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT64, + ClassProperty::Type::SCALAR, + ClassProperty::ComponentType::FLOAT64, expected, expected.size()); } @@ -652,8 +624,8 @@ TEST_CASE("Convert binary B3DM batch table to EXT_structural_metadata") { propertyTable, defaultClass, "code", - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT8, + ClassProperty::Type::SCALAR, + ClassProperty::ComponentType::UINT8, expected, expected.size()); } @@ -678,8 +650,8 @@ TEST_CASE("Convert binary B3DM batch table to EXT_structural_metadata") { propertyTable, defaultClass, "cartographic", - ExtensionExtStructuralMetadataClassProperty::Type::VEC3, - ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT64, + ClassProperty::Type::VEC3, + ClassProperty::ComponentType::FLOAT64, expected, expected.size()); } @@ -705,8 +677,7 @@ TEST_CASE("Converts batched PNTS batch table to EXT_structural_metadata") { auto firstClassIt = pExtension->schema->classes.begin(); CHECK(firstClassIt->first == "default"); - const ExtensionExtStructuralMetadataClass& defaultClass = - firstClassIt->second; + const Class& defaultClass = firstClassIt->second; REQUIRE(defaultClass.properties.size() == 3); { @@ -717,27 +688,18 @@ TEST_CASE("Converts batched PNTS batch table to EXT_structural_metadata") { auto idIt = defaultClass.properties.find("id"); REQUIRE(idIt != defaultClass.properties.end()); - CHECK( - nameIt->second.type == - ExtensionExtStructuralMetadataClassProperty::Type::STRING); - CHECK( - dimensionsIt->second.type == - ExtensionExtStructuralMetadataClassProperty::Type::VEC3); + CHECK(nameIt->second.type == ClassProperty::Type::STRING); + CHECK(dimensionsIt->second.type == ClassProperty::Type::VEC3); CHECK( dimensionsIt->second.componentType == - ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT32); - CHECK( - idIt->second.type == - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR); - CHECK( - idIt->second.componentType == - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT32); + ClassProperty::ComponentType::FLOAT32); + CHECK(idIt->second.type == ClassProperty::Type::SCALAR); + CHECK(idIt->second.componentType == ClassProperty::ComponentType::UINT32); } // Check the property table REQUIRE(pExtension->propertyTables.size() == 1); - const ExtensionExtStructuralMetadataPropertyTable& propertyTable = - pExtension->propertyTables[0]; + const PropertyTable& propertyTable = pExtension->propertyTables[0]; CHECK(propertyTable.classProperty == "default"); REQUIRE(propertyTable.properties.size() == 3); @@ -799,7 +761,7 @@ TEST_CASE("Converts batched PNTS batch table to EXT_structural_metadata") { propertyTable, defaultClass, "name", - ExtensionExtStructuralMetadataClassProperty::Type::STRING, + ClassProperty::Type::STRING, std::nullopt, expected, expected.size()); @@ -820,8 +782,8 @@ TEST_CASE("Converts batched PNTS batch table to EXT_structural_metadata") { propertyTable, defaultClass, "dimensions", - ExtensionExtStructuralMetadataClassProperty::Type::VEC3, - ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT32, + ClassProperty::Type::VEC3, + ClassProperty::ComponentType::FLOAT32, expected, expected.size()); } @@ -833,8 +795,8 @@ TEST_CASE("Converts batched PNTS batch table to EXT_structural_metadata") { propertyTable, defaultClass, "id", - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT32, + ClassProperty::Type::SCALAR, + ClassProperty::ComponentType::UINT32, expected, expected.size()); } @@ -861,8 +823,7 @@ TEST_CASE("Converts per-point PNTS batch table to EXT_structural_metadata") { auto firstClassIt = pExtension->schema->classes.begin(); CHECK(firstClassIt->first == "default"); - const ExtensionExtStructuralMetadataClass& defaultClass = - firstClassIt->second; + const Class& defaultClass = firstClassIt->second; REQUIRE(defaultClass.properties.size() == 3); { @@ -873,31 +834,22 @@ TEST_CASE("Converts per-point PNTS batch table to EXT_structural_metadata") { auto idIt = defaultClass.properties.find("id"); REQUIRE(idIt != defaultClass.properties.end()); - CHECK( - temperatureIt->second.type == - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR); + CHECK(temperatureIt->second.type == ClassProperty::Type::SCALAR); CHECK( temperatureIt->second.componentType == - ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT32); - CHECK( - secondaryColorIt->second.type == - ExtensionExtStructuralMetadataClassProperty::Type::VEC3); + ClassProperty::ComponentType::FLOAT32); + CHECK(secondaryColorIt->second.type == ClassProperty::Type::VEC3); REQUIRE(secondaryColorIt->second.componentType); CHECK( secondaryColorIt->second.componentType == - ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT32); - CHECK( - idIt->second.type == - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR); - CHECK( - idIt->second.componentType == - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT16); + ClassProperty::ComponentType::FLOAT32); + CHECK(idIt->second.type == ClassProperty::Type::SCALAR); + CHECK(idIt->second.componentType == ClassProperty::ComponentType::UINT16); } // Check the property table REQUIRE(pExtension->propertyTables.size() == 1); - const ExtensionExtStructuralMetadataPropertyTable& propertyTable = - pExtension->propertyTables[0]; + const PropertyTable& propertyTable = pExtension->propertyTables[0]; CHECK(propertyTable.classProperty == "default"); REQUIRE(propertyTable.properties.size() == 3); @@ -960,8 +912,8 @@ TEST_CASE("Converts per-point PNTS batch table to EXT_structural_metadata") { propertyTable, defaultClass, "temperature", - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, - ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT32, + ClassProperty::Type::SCALAR, + ClassProperty::ComponentType::FLOAT32, expected, expected.size()); } @@ -981,8 +933,8 @@ TEST_CASE("Converts per-point PNTS batch table to EXT_structural_metadata") { propertyTable, defaultClass, "secondaryColor", - ExtensionExtStructuralMetadataClassProperty::Type::VEC3, - ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT32, + ClassProperty::Type::VEC3, + ClassProperty::ComponentType::FLOAT32, expected, expected.size()); } @@ -994,8 +946,8 @@ TEST_CASE("Converts per-point PNTS batch table to EXT_structural_metadata") { propertyTable, defaultClass, "id", - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT16, + ClassProperty::Type::SCALAR, + ClassProperty::ComponentType::UINT16, expected, expected.size()); } @@ -1022,8 +974,7 @@ TEST_CASE("Converts Draco per-point PNTS batch table to " auto firstClassIt = pExtension->schema->classes.begin(); CHECK(firstClassIt->first == "default"); - const ExtensionExtStructuralMetadataClass& defaultClass = - firstClassIt->second; + const Class& defaultClass = firstClassIt->second; REQUIRE(defaultClass.properties.size() == 3); { @@ -1034,31 +985,22 @@ TEST_CASE("Converts Draco per-point PNTS batch table to " auto idIt = defaultClass.properties.find("id"); REQUIRE(idIt != defaultClass.properties.end()); - CHECK( - temperatureIt->second.type == - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR); + CHECK(temperatureIt->second.type == ClassProperty::Type::SCALAR); CHECK( temperatureIt->second.componentType == - ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT32); - CHECK( - secondaryColorIt->second.type == - ExtensionExtStructuralMetadataClassProperty::Type::VEC3); + ClassProperty::ComponentType::FLOAT32); + CHECK(secondaryColorIt->second.type == ClassProperty::Type::VEC3); REQUIRE(secondaryColorIt->second.componentType); CHECK( secondaryColorIt->second.componentType == - ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT32); - CHECK( - idIt->second.type == - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR); - CHECK( - idIt->second.componentType == - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT16); + ClassProperty::ComponentType::FLOAT32); + CHECK(idIt->second.type == ClassProperty::Type::SCALAR); + CHECK(idIt->second.componentType == ClassProperty::ComponentType::UINT16); } // Check the property table REQUIRE(pExtension->propertyTables.size() == 1); - const ExtensionExtStructuralMetadataPropertyTable& propertyTable = - pExtension->propertyTables[0]; + const PropertyTable& propertyTable = pExtension->propertyTables[0]; CHECK(propertyTable.classProperty == "default"); REQUIRE(propertyTable.properties.size() == 3); @@ -1121,8 +1063,8 @@ TEST_CASE("Converts Draco per-point PNTS batch table to " propertyTable, defaultClass, "temperature", - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, - ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT32, + ClassProperty::Type::SCALAR, + ClassProperty::ComponentType::FLOAT32, expected, expected.size()); } @@ -1142,8 +1084,8 @@ TEST_CASE("Converts Draco per-point PNTS batch table to " propertyTable, defaultClass, "secondaryColor", - ExtensionExtStructuralMetadataClassProperty::Type::VEC3, - ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT32, + ClassProperty::Type::VEC3, + ClassProperty::ComponentType::FLOAT32, expected, expected.size()); } @@ -1155,8 +1097,8 @@ TEST_CASE("Converts Draco per-point PNTS batch table to " propertyTable, defaultClass, "id", - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT16, + ClassProperty::Type::SCALAR, + ClassProperty::ComponentType::UINT16, expected, expected.size()); } @@ -1177,25 +1119,19 @@ TEST_CASE("Upgrade nested JSON metadata to string") { result.model->getExtension(); REQUIRE(pMetadata); - const std::optional& schema = - pMetadata->schema; + const std::optional& schema = pMetadata->schema; REQUIRE(schema); - const std::unordered_map& - classes = schema->classes; + const std::unordered_map& classes = schema->classes; REQUIRE(classes.size() == 1); - const ExtensionExtStructuralMetadataClass& defaultClass = - classes.at("default"); - const std::unordered_map< - std::string, - ExtensionExtStructuralMetadataClassProperty>& properties = + const Class& defaultClass = classes.at("default"); + const std::unordered_map& properties = defaultClass.properties; REQUIRE(properties.size() == 6); REQUIRE(pMetadata->propertyTables.size() == 1); - const ExtensionExtStructuralMetadataPropertyTable& propertyTable = - pMetadata->propertyTables[0]; + const PropertyTable& propertyTable = pMetadata->propertyTables[0]; REQUIRE(propertyTable.count == 10); { @@ -1210,7 +1146,7 @@ TEST_CASE("Upgrade nested JSON metadata to string") { propertyTable, defaultClass, "info", - ExtensionExtStructuralMetadataClassProperty::Type::STRING, + ClassProperty::Type::STRING, std::nullopt, expected, expected.size()); @@ -1231,7 +1167,7 @@ TEST_CASE("Upgrade nested JSON metadata to string") { defaultClass, "rooms", 3, - ExtensionExtStructuralMetadataClassProperty::Type::STRING, + ClassProperty::Type::STRING, std::nullopt, expected, expected.size()); @@ -1275,35 +1211,28 @@ TEST_CASE("Upgrade JSON booleans to binary") { model.getExtension(); REQUIRE(pMetadata); - const std::optional& schema = - pMetadata->schema; + const std::optional& schema = pMetadata->schema; REQUIRE(schema); - const std::unordered_map& - classes = schema->classes; + const std::unordered_map& classes = schema->classes; REQUIRE(classes.size() == 1); - const ExtensionExtStructuralMetadataClass& defaultClass = - classes.at("default"); - const std::unordered_map< - std::string, - ExtensionExtStructuralMetadataClassProperty>& properties = + const Class& defaultClass = classes.at("default"); + const std::unordered_map& properties = defaultClass.properties; REQUIRE(properties.size() == 1); - const ExtensionExtStructuralMetadataClassProperty& propertyClass = - properties.at("boolProp"); + const ClassProperty& propertyClass = properties.at("boolProp"); REQUIRE(propertyClass.type == "BOOLEAN"); REQUIRE(pMetadata->propertyTables.size() == 1); - const ExtensionExtStructuralMetadataPropertyTable& propertyTable = - pMetadata->propertyTables[0]; + const PropertyTable& propertyTable = pMetadata->propertyTables[0]; checkNonArrayProperty( model, propertyTable, defaultClass, "boolProp", - ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN, + ClassProperty::Type::BOOLEAN, std::nullopt, expected, expected.size()); @@ -1322,8 +1251,8 @@ TEST_CASE("Upgrade fixed-length JSON arrays") { createTestForArrayJson( expected, - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, - ExtensionExtStructuralMetadataClassProperty::ComponentType::INT8, + ClassProperty::Type::SCALAR, + ClassProperty::ComponentType::INT8, 4, expected.size()); } @@ -1340,8 +1269,8 @@ TEST_CASE("Upgrade fixed-length JSON arrays") { createTestForArrayJson( expected, - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT8, + ClassProperty::Type::SCALAR, + ClassProperty::ComponentType::UINT8, 5, expected.size()); } @@ -1358,8 +1287,8 @@ TEST_CASE("Upgrade fixed-length JSON arrays") { createTestForArrayJson( expected, - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, - ExtensionExtStructuralMetadataClassProperty::ComponentType::INT16, + ClassProperty::Type::SCALAR, + ClassProperty::ComponentType::INT16, 4, expected.size()); } @@ -1376,8 +1305,8 @@ TEST_CASE("Upgrade fixed-length JSON arrays") { createTestForArrayJson( expected, - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT16, + ClassProperty::Type::SCALAR, + ClassProperty::ComponentType::UINT16, 4, expected.size()); } @@ -1394,8 +1323,8 @@ TEST_CASE("Upgrade fixed-length JSON arrays") { createTestForArrayJson( expected, - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, - ExtensionExtStructuralMetadataClassProperty::ComponentType::INT32, + ClassProperty::Type::SCALAR, + ClassProperty::ComponentType::INT32, 4, expected.size()); } @@ -1412,8 +1341,8 @@ TEST_CASE("Upgrade fixed-length JSON arrays") { createTestForArrayJson( expected, - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT32, + ClassProperty::Type::SCALAR, + ClassProperty::ComponentType::UINT32, 4, expected.size()); } @@ -1432,8 +1361,8 @@ TEST_CASE("Upgrade fixed-length JSON arrays") { createTestForArrayJson( expected, - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, - ExtensionExtStructuralMetadataClassProperty::ComponentType::INT64, + ClassProperty::Type::SCALAR, + ClassProperty::ComponentType::INT64, 4, expected.size()); } @@ -1450,8 +1379,8 @@ TEST_CASE("Upgrade fixed-length JSON arrays") { createTestForArrayJson( expected, - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT64, + ClassProperty::Type::SCALAR, + ClassProperty::ComponentType::UINT64, 4, expected.size()); } @@ -1468,8 +1397,8 @@ TEST_CASE("Upgrade fixed-length JSON arrays") { createTestForArrayJson( expected, - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, - ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT32, + ClassProperty::Type::SCALAR, + ClassProperty::ComponentType::FLOAT32, 4, expected.size()); } @@ -1486,8 +1415,8 @@ TEST_CASE("Upgrade fixed-length JSON arrays") { createTestForArrayJson( expected, - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, - ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT64, + ClassProperty::Type::SCALAR, + ClassProperty::ComponentType::FLOAT64, 4, expected.size()); } @@ -1504,7 +1433,7 @@ TEST_CASE("Upgrade fixed-length JSON arrays") { createTestForArrayJson( expected, - ExtensionExtStructuralMetadataClassProperty::Type::STRING, + ClassProperty::Type::STRING, std::nullopt, 4, expected.size()); @@ -1522,7 +1451,7 @@ TEST_CASE("Upgrade fixed-length JSON arrays") { createTestForArrayJson( expected, - ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN, + ClassProperty::Type::BOOLEAN, std::nullopt, 6, expected.size()); @@ -1542,8 +1471,8 @@ TEST_CASE("Upgrade variable-length JSON arrays") { createTestForArrayJson( expected, - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, - ExtensionExtStructuralMetadataClassProperty::ComponentType::INT8, + ClassProperty::Type::SCALAR, + ClassProperty::ComponentType::INT8, 0, expected.size()); } @@ -1560,8 +1489,8 @@ TEST_CASE("Upgrade variable-length JSON arrays") { createTestForArrayJson( expected, - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT8, + ClassProperty::Type::SCALAR, + ClassProperty::ComponentType::UINT8, 0, expected.size()); } @@ -1578,8 +1507,8 @@ TEST_CASE("Upgrade variable-length JSON arrays") { createTestForArrayJson( expected, - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, - ExtensionExtStructuralMetadataClassProperty::ComponentType::INT16, + ClassProperty::Type::SCALAR, + ClassProperty::ComponentType::INT16, 0, expected.size()); } @@ -1596,8 +1525,8 @@ TEST_CASE("Upgrade variable-length JSON arrays") { createTestForArrayJson( expected, - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT16, + ClassProperty::Type::SCALAR, + ClassProperty::ComponentType::UINT16, 0, expected.size()); } @@ -1614,8 +1543,8 @@ TEST_CASE("Upgrade variable-length JSON arrays") { createTestForArrayJson( expected, - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, - ExtensionExtStructuralMetadataClassProperty::ComponentType::INT32, + ClassProperty::Type::SCALAR, + ClassProperty::ComponentType::INT32, 0, expected.size()); } @@ -1632,8 +1561,8 @@ TEST_CASE("Upgrade variable-length JSON arrays") { createTestForArrayJson( expected, - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT32, + ClassProperty::Type::SCALAR, + ClassProperty::ComponentType::UINT32, 0, expected.size()); } @@ -1650,8 +1579,8 @@ TEST_CASE("Upgrade variable-length JSON arrays") { createTestForArrayJson( expected, - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, - ExtensionExtStructuralMetadataClassProperty::ComponentType::INT64, + ClassProperty::Type::SCALAR, + ClassProperty::ComponentType::INT64, 0, expected.size()); } @@ -1668,8 +1597,8 @@ TEST_CASE("Upgrade variable-length JSON arrays") { createTestForArrayJson( expected, - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT64, + ClassProperty::Type::SCALAR, + ClassProperty::ComponentType::UINT64, 0, expected.size()); } @@ -1686,8 +1615,8 @@ TEST_CASE("Upgrade variable-length JSON arrays") { createTestForArrayJson( expected, - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, - ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT32, + ClassProperty::Type::SCALAR, + ClassProperty::ComponentType::FLOAT32, 0, expected.size()); } @@ -1704,8 +1633,8 @@ TEST_CASE("Upgrade variable-length JSON arrays") { createTestForArrayJson( expected, - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, - ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT64, + ClassProperty::Type::SCALAR, + ClassProperty::ComponentType::FLOAT64, 0, expected.size()); } @@ -1722,7 +1651,7 @@ TEST_CASE("Upgrade variable-length JSON arrays") { createTestForArrayJson( expected, - ExtensionExtStructuralMetadataClassProperty::Type::STRING, + ClassProperty::Type::STRING, std::nullopt, 0, expected.size()); @@ -1741,7 +1670,7 @@ TEST_CASE("Upgrade variable-length JSON arrays") { createTestForArrayJson( expected, - ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN, + ClassProperty::Type::BOOLEAN, std::nullopt, 0, expected.size()); @@ -1755,8 +1684,8 @@ TEST_CASE("Upgrade JSON values") { std::vector expected{32, 45, 21, 65, 78}; createTestForNonArrayJson( expected, - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, - ExtensionExtStructuralMetadataClassProperty::ComponentType::INT8, + ClassProperty::Type::SCALAR, + ClassProperty::ComponentType::INT8, expected.size()); } @@ -1764,7 +1693,7 @@ TEST_CASE("Upgrade JSON values") { std::vector expected{true, false, true, false, true, true, false}; createTestForNonArrayJson( expected, - ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN, + ClassProperty::Type::BOOLEAN, std::nullopt, expected.size()); } @@ -1773,7 +1702,7 @@ TEST_CASE("Upgrade JSON values") { std::vector expected{"Test 0", "Test 1", "Test 2", "Test 3"}; createTestForNonArrayJson( expected, - ExtensionExtStructuralMetadataClassProperty::Type::STRING, + ClassProperty::Type::STRING, std::nullopt, expected.size()); } @@ -1784,8 +1713,8 @@ TEST_CASE("Cannot write past batch table length") { std::vector expected{32, 45, 21, 65, 78, 20, 33, 12}; createTestForNonArrayJson( expected, - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, - ExtensionExtStructuralMetadataClassProperty::ComponentType::INT8, + ClassProperty::Type::SCALAR, + ClassProperty::ComponentType::INT8, 4); } @@ -1793,7 +1722,7 @@ TEST_CASE("Cannot write past batch table length") { std::vector expected{true, false, true, false, true, true, false}; createTestForNonArrayJson( expected, - ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN, + ClassProperty::Type::BOOLEAN, std::nullopt, 4); } @@ -1803,7 +1732,7 @@ TEST_CASE("Cannot write past batch table length") { expected{"Test 0", "Test 1", "Test 2", "Test 3", "Test 4"}; createTestForNonArrayJson( expected, - ExtensionExtStructuralMetadataClassProperty::Type::STRING, + ClassProperty::Type::STRING, std::nullopt, 3); } @@ -1820,8 +1749,8 @@ TEST_CASE("Cannot write past batch table length") { createTestForArrayJson( expected, - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT64, + ClassProperty::Type::SCALAR, + ClassProperty::ComponentType::UINT64, 4, 2); } @@ -1838,7 +1767,7 @@ TEST_CASE("Cannot write past batch table length") { createTestForArrayJson( expected, - ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN, + ClassProperty::Type::BOOLEAN, std::nullopt, 3, 2); @@ -1856,7 +1785,7 @@ TEST_CASE("Cannot write past batch table length") { createTestForArrayJson( expected, - ExtensionExtStructuralMetadataClassProperty::Type::STRING, + ClassProperty::Type::STRING, std::nullopt, 4, 2); @@ -1874,8 +1803,8 @@ TEST_CASE("Cannot write past batch table length") { createTestForArrayJson( expected, - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR, - ExtensionExtStructuralMetadataClassProperty::ComponentType::INT32, + ClassProperty::Type::SCALAR, + ClassProperty::ComponentType::INT32, 0, 3); } @@ -1893,7 +1822,7 @@ TEST_CASE("Cannot write past batch table length") { createTestForArrayJson( expected, - ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN, + ClassProperty::Type::BOOLEAN, std::nullopt, 0, 2); @@ -1911,7 +1840,7 @@ TEST_CASE("Cannot write past batch table length") { createTestForArrayJson( expected, - ExtensionExtStructuralMetadataClassProperty::Type::STRING, + ClassProperty::Type::STRING, std::nullopt, 0, 2); @@ -1990,13 +1919,12 @@ TEST_CASE("Converts \"Feature Classes\" 3DTILES_batch_table_hierarchy example " auto firstClassIt = pExtension->schema->classes.begin(); CHECK(firstClassIt->first == "default"); - ExtensionExtStructuralMetadataClass& defaultClass = firstClassIt->second; + Class& defaultClass = firstClassIt->second; REQUIRE(defaultClass.properties.size() == 6); // Check the property table REQUIRE(pExtension->propertyTables.size() == 1); - ExtensionExtStructuralMetadataPropertyTable& propertyTable = - pExtension->propertyTables[0]; + PropertyTable& propertyTable = pExtension->propertyTables[0]; CHECK(propertyTable.classProperty == "default"); REQUIRE(propertyTable.properties.size() == 6); @@ -2012,27 +1940,27 @@ TEST_CASE("Converts \"Feature Classes\" 3DTILES_batch_table_hierarchy example " std::vector expectedProperties{ {"lampStrength", - ExtensionExtStructuralMetadataClassProperty::Type::STRING, + ClassProperty::Type::STRING, std::nullopt, {"10", "5", "7", "null", "null", "null", "null", "null"}}, {"lampColor", - ExtensionExtStructuralMetadataClassProperty::Type::STRING, + ClassProperty::Type::STRING, std::nullopt, {"yellow", "white", "white", "null", "null", "null", "null", "null"}}, {"carType", - ExtensionExtStructuralMetadataClassProperty::Type::STRING, + ClassProperty::Type::STRING, std::nullopt, {"null", "null", "null", "truck", "bus", "sedan", "null", "null"}}, {"carColor", - ExtensionExtStructuralMetadataClassProperty::Type::STRING, + ClassProperty::Type::STRING, std::nullopt, {"null", "null", "null", "green", "blue", "red", "null", "null"}}, {"treeHeight", - ExtensionExtStructuralMetadataClassProperty::Type::STRING, + ClassProperty::Type::STRING, std::nullopt, {"null", "null", "null", "null", "null", "null", "10", "15"}}, {"treeAge", - ExtensionExtStructuralMetadataClassProperty::Type::STRING, + ClassProperty::Type::STRING, std::nullopt, {"null", "null", "null", "null", "null", "null", "5", "8"}}}; @@ -2128,22 +2056,19 @@ TEST_CASE( auto firstClassIt = pExtension->schema->classes.begin(); CHECK(firstClassIt->first == "default"); - ExtensionExtStructuralMetadataClass& defaultClass = firstClassIt->second; + Class& defaultClass = firstClassIt->second; REQUIRE(defaultClass.properties.size() == 7); // Check the property table REQUIRE(pExtension->propertyTables.size() == 1); - ExtensionExtStructuralMetadataPropertyTable& propertyTable = - pExtension->propertyTables[0]; + PropertyTable& propertyTable = pExtension->propertyTables[0]; CHECK(propertyTable.classProperty == "default"); REQUIRE(propertyTable.properties.size() == 7); struct ExpectedString { std::string name; std::vector values; - std::string type() const { - return ExtensionExtStructuralMetadataClassProperty::Type::STRING; - } + std::string type() const { return ClassProperty::Type::STRING; } std::optional componentType() const { return std::nullopt; } }; @@ -2186,11 +2111,9 @@ TEST_CASE( struct ExpectedInt8Properties { std::string name; std::vector values; - std::string type() const { - return ExtensionExtStructuralMetadataClassProperty::Type::SCALAR; - } + std::string type() const { return ClassProperty::Type::SCALAR; } std::optional componentType() const { - return ExtensionExtStructuralMetadataClassProperty::ComponentType::INT8; + return ClassProperty::ComponentType::INT8; } }; @@ -2220,12 +2143,9 @@ TEST_CASE( std::string name; int64_t count; std::vector> values; - std::string type() const { - return ExtensionExtStructuralMetadataClassProperty::Type::SCALAR; - } + std::string type() const { return ClassProperty::Type::SCALAR; } std::optional componentType() const { - return ExtensionExtStructuralMetadataClassProperty::ComponentType:: - FLOAT64; + return ClassProperty::ComponentType::FLOAT64; } }; @@ -2340,14 +2260,12 @@ TEST_CASE("3DTILES_batch_table_hierarchy with parentCounts is okay if all " auto firstClassIt = pExtension->schema->classes.begin(); CHECK(firstClassIt->first == "default"); - const ExtensionExtStructuralMetadataClass& defaultClass = - firstClassIt->second; + const Class& defaultClass = firstClassIt->second; REQUIRE(defaultClass.properties.size() == 3); // Check the property table REQUIRE(pExtension->propertyTables.size() == 1); - const ExtensionExtStructuralMetadataPropertyTable& propertyTable = - pExtension->propertyTables[0]; + const PropertyTable& propertyTable = pExtension->propertyTables[0]; CHECK(propertyTable.classProperty == "default"); REQUIRE(propertyTable.properties.size() == 3); } @@ -2431,14 +2349,12 @@ TEST_CASE("3DTILES_batch_table_hierarchy with parentCounts values != 1 is " auto firstClassIt = pExtension->schema->classes.begin(); CHECK(firstClassIt->first == "default"); - const ExtensionExtStructuralMetadataClass& defaultClass = - firstClassIt->second; + const Class& defaultClass = firstClassIt->second; REQUIRE(defaultClass.properties.size() == 0); // Check the property table REQUIRE(pExtension->propertyTables.size() == 1); - const ExtensionExtStructuralMetadataPropertyTable& propertyTable = - pExtension->propertyTables[0]; + const PropertyTable& propertyTable = pExtension->propertyTables[0]; CHECK(propertyTable.classProperty == "default"); REQUIRE(propertyTable.properties.size() == 0); diff --git a/CesiumGltf/include/CesiumGltf/PropertyArrayView.h b/CesiumGltf/include/CesiumGltf/PropertyArrayView.h index 9f3ea6d2f..ef89f5766 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyArrayView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyArrayView.h @@ -12,8 +12,7 @@ namespace CesiumGltf { /** - * @brief A view on an array element of a - * {@link ExtensionExtStructuralMetadataPropertyTableProperty}. + * @brief A view on an array element of a {@link PropertyTableProperty}. * * Provides utility to retrieve the data stored in the array of * elements via the array index operator. diff --git a/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h index 451c5f14d..05b1adcd5 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h @@ -16,10 +16,9 @@ namespace CesiumGltf { * @brief Indicates the status of a property table property view. * * The {@link PropertyTablePropertyView} constructor always completes successfully. - * However, it may not always reflect the actual content of the - * {@link ExtensionExtStructuralMetadataPropertyTableProperty}, but instead - * indicate that its {@link PropertyTablePropertyView::size} is 0. This enumeration - * provides the reason. + * However, it may not always reflect the actual content of the {@link PropertyTableProperty}, + * but instead indicate that its {@link PropertyTablePropertyView::size} is 0. + * This enumeration provides the reason. */ enum class PropertyTablePropertyViewStatus { /** @@ -29,32 +28,31 @@ enum class PropertyTablePropertyViewStatus { /** * @brief This property view was attempting to view an invalid - * {@link ExtensionExtStructuralMetadataPropertyTable}. + * {@link PropertyTable}. */ ErrorInvalidPropertyTable, /** * @brief This property view is trying to view a property that does not exist - * in the - * {@link ExtensionExtStructuralMetadataPropertyTable}. + * in the {@link PropertyTable}. */ ErrorPropertyDoesNotExist, /** * @brief This property view's type does not match what is - * specified in {@link ExtensionExtStructuralMetadataClassProperty::type}. + * specified in {@link ClassProperty::type}. */ ErrorTypeMismatch, /** * @brief This property view's component type does not match what - * is specified in {@link ExtensionExtStructuralMetadataClassProperty::componentType}. + * is specified in {@link ClassProperty::componentType}. */ ErrorComponentTypeMismatch, /** * @brief This property view differs from what is specified in - * {@link ExtensionExtStructuralMetadataClassProperty::array}. + * {@link ClassProperty::array}. */ ErrorArrayTypeMismatch, @@ -157,13 +155,12 @@ enum class PropertyTablePropertyViewStatus { }; /** - * @brief A view on the data of the - * {@link ExtensionExtStructuralMetadataPropertyTableProperty that is created by - * a {@link PropertyTableView}. + * @brief A view on the data of the {@link PropertyTableProperty that is created + * by a {@link PropertyTableView}. * * It provides utility to retrieve the actual data stored in the - * {@link ExtensionExtStructuralMetadataPropertyTableProperty::values} like an array of elements. - * Data of each instance can be accessed through the {@link get(int64_t instance)} method. + * {@link PropertyTableProperty::values} like an array of elements. Data of each + * instance can be accessed through the {@link get(int64_t instance)} method. * * @param ElementType must be one of the following: a scalar (uint8_t, int8_t, * uint16_t, int16_t, uint32_t, int32_t, uint64_t, int64_t, float, double), a @@ -185,9 +182,9 @@ template class PropertyTablePropertyView { /** * @brief Construct a new instance pointing to non-array data specified by - * {@link ExtensionExtStructuralMetadataPropertyTableProperty}. - * @param values The raw buffer specified by {@link ExtensionExtStructuralMetadataPropertyTableProperty::values} - * @param size The number of elements in the property table specified by {@link ExtensionExtStructuralMetadataPropertyTable::count} + * {@link PropertyTableProperty}. + * @param values The raw buffer specified by {@link PropertyTableProperty::values} + * @param size The number of elements in the property table specified by {@link PropertyTable::count} * @param normalized Whether this property has a normalized integer type. */ PropertyTablePropertyView( @@ -209,14 +206,14 @@ template class PropertyTablePropertyView { /** * @brief Construct a new instance pointing to the data specified by - * {@link ExtensionExtStructuralMetadataPropertyTableProperty}. - * @param values The raw buffer specified by {@link ExtensionExtStructuralMetadataPropertyTableProperty::values} - * @param arrayOffsets The raw buffer specified by {@link ExtensionExtStructuralMetadataPropertyTableProperty::arrayOffsets} - * @param stringOffsets The raw buffer specified by {@link ExtensionExtStructuralMetadataPropertyTableProperty::stringOffsets} - * @param offsetType The offset type of arrayOffsets specified by {@link ExtensionExtStructuralMetadataPropertyTableProperty::arrayOffsetType} - * @param offsetType The offset type of stringOffsets specified by {@link ExtensionExtStructuralMetadataPropertyTableProperty::stringOffsetType} - * @param arrayCount The number of elements in each array value specified by {@link ExtensionExtStructuralMetadataClassProperty::count} - * @param size The number of elements in the property table specified by {@link ExtensionExtStructuralMetadataPropertyTable::count} + * {@link PropertyTableProperty}. + * @param values The raw buffer specified by {@link PropertyTableProperty::values} + * @param arrayOffsets The raw buffer specified by {@link PropertyTableProperty::arrayOffsets} + * @param stringOffsets The raw buffer specified by {@link PropertyTableProperty::stringOffsets} + * @param offsetType The offset type of arrayOffsets specified by {@link PropertyTableProperty::arrayOffsetType} + * @param offsetType The offset type of stringOffsets specified by {@link PropertyTableProperty::stringOffsetType} + * @param arrayCount The number of elements in each array value specified by {@link ClassProperty::count} + * @param size The number of elements in the property table specified by {@link PropertyTable::count} * @param normalized Whether this property has a normalized integer type. */ PropertyTablePropertyView( @@ -252,7 +249,7 @@ template class PropertyTablePropertyView { PropertyTablePropertyViewStatus status() const noexcept { return _status; } /** - * @brief Get the value of an element of the {@link ExtensionExtStructuralMetadataPropertyTable}. + * @brief Get the value of an element of the {@link PropertyTable}. * @param index The element index * @return The value of the element */ @@ -295,7 +292,7 @@ template class PropertyTablePropertyView { /** * @brief Get the number of elements in this * PropertyTablePropertyView. If the view is valid, this returns - * {@link ExtensionExtStructuralMetadataPropertyTable::count}. Otherwise, this returns 0. + * {@link PropertyTable::count}. Otherwise, this returns 0. * * @return The number of elements in this PropertyTablePropertyView. */ diff --git a/CesiumGltf/include/CesiumGltf/PropertyTableView.h b/CesiumGltf/include/CesiumGltf/PropertyTableView.h index 84757d07f..210ecd0c5 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTableView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTableView.h @@ -14,9 +14,9 @@ namespace CesiumGltf { * @brief Indicates the status of a property table view. * * The {@link PropertyTableView} constructor always completes successfully. - * However, it may not always reflect the actual content of the - * {@link ExtensionExtStructuralMetadataPropertyTable}, but instead indicate that its - * {@link PropertyTableView::size} is 0. This enumeration provides the reason. + * However, it may not always reflect the actual content of the {@link PropertyTable}, + * but instead indicate that its {@link PropertyTableView::size} is 0. + * This enumeration provides the reason. */ enum class PropertyTableViewStatus { /** @@ -44,8 +44,7 @@ enum class PropertyTableViewStatus { }; /** - * @brief Utility to retrieve the data of - * {@link ExtensionExtStructuralMetadataPropertyTable}. + * @brief Utility to retrieve the data of {@link PropertyTable}. * * This should be used to get a {@link PropertyTablePropertyView} of a property in the property table. * It will validate the EXT_structural_metadata format and ensure {@link PropertyTablePropertyView} @@ -56,12 +55,10 @@ class PropertyTableView { /** * @brief Creates an instance of PropertyTableView. * @param model The Gltf Model that contains property table data. - * @param propertyTable The {@link ExtensionExtStructuralMetadataPropertyTable} + * @param propertyTable The {@link PropertyTable} * from which the view will retrieve data. */ - PropertyTableView( - const Model& model, - const ExtensionExtStructuralMetadataPropertyTable& propertyTable); + PropertyTableView(const Model& model, const PropertyTable& propertyTable); /** * @brief Gets the status of this property table view. @@ -75,8 +72,7 @@ class PropertyTableView { /** * @brief Get the number of elements in this PropertyTableView. If the - * view is valid, this returns - * {@link ExtensionExtStructuralMetadataPropertyTable::count}. Otherwise, this returns 0. + * view is valid, this returns {@link PropertyTable::count}. Otherwise, this returns 0. * * @return The number of elements in this PropertyTableView. */ @@ -86,19 +82,18 @@ class PropertyTableView { } /** - * @brief Finds the {@link ExtensionExtStructuralMetadataClassProperty} that + * @brief Finds the {@link ClassProperty} that * describes the type information of the property with the specified name. * @param propertyName The name of the property to retrieve the class for. - * @return A pointer to the {@link ExtensionExtStructuralMetadataClassProperty}. + * @return A pointer to the {@link ClassProperty}. * Return nullptr if the PropertyTableView is invalid or if no class * property was found. */ - const ExtensionExtStructuralMetadataClassProperty* - getClassProperty(const std::string& propertyName) const; + const ClassProperty* getClassProperty(const std::string& propertyName) const; /** * @brief Gets a {@link PropertyTablePropertyView} that views the data of a property stored - * in the {@link ExtensionExtStructuralMetadataPropertyTable}. + * in the {@link PropertyTable}. * * This method will validate the EXT_structural_metadata format to ensure * {@link PropertyTablePropertyView} retrieves the correct data. T must be one of the @@ -120,8 +115,7 @@ class PropertyTableView { PropertyTablePropertyViewStatus::ErrorInvalidPropertyTable); } - const ExtensionExtStructuralMetadataClassProperty* pClassProperty = - getClassProperty(propertyName); + const ClassProperty* pClassProperty = getClassProperty(propertyName); if (!pClassProperty) { return createInvalidPropertyView( PropertyTablePropertyViewStatus::ErrorPropertyDoesNotExist); @@ -160,8 +154,7 @@ class PropertyTableView { return; } - const ExtensionExtStructuralMetadataClassProperty* pClassProperty = - getClassProperty(propertyName); + const ClassProperty* pClassProperty = getClassProperty(propertyName); if (!pClassProperty) { callback( propertyName, @@ -221,10 +214,9 @@ class PropertyTableView { } /** - * @brief Iterates over each property in the - * {@link ExtensionExtStructuralMetadataPropertyTable} with a callback that accepts a - * property name and a {@link PropertyTablePropertyView} to view the data - * stored in the {@link ExtensionExtStructuralMetadataPropertyTableProperty}. + * @brief Iterates over each property in the {@link PropertyTable} with a callback + * that accepts a property name and a {@link PropertyTablePropertyView} to view + * the data stored in the {@link PropertyTableProperty}. * * This method will validate the EXT_structural_metadata format to ensure * {@link PropertyTablePropertyView} retrieves the correct data. T must be one of the @@ -267,7 +259,7 @@ class PropertyTableView { template void getScalarArrayPropertyViewImpl( const std::string& propertyName, - const ExtensionExtStructuralMetadataClassProperty& classProperty, + const ClassProperty& classProperty, PropertyComponentType componentType, Callback&& callback) const { switch (componentType) { @@ -353,7 +345,7 @@ class PropertyTableView { template void getVecNArrayPropertyViewImpl( const std::string& propertyName, - const ExtensionExtStructuralMetadataClassProperty& classProperty, + const ClassProperty& classProperty, PropertyComponentType componentType, Callback&& callback) const { switch (componentType) { @@ -439,7 +431,7 @@ class PropertyTableView { template void getVecNArrayPropertyViewImpl( const std::string& propertyName, - const ExtensionExtStructuralMetadataClassProperty& classProperty, + const ClassProperty& classProperty, PropertyType type, PropertyComponentType componentType, Callback&& callback) const { @@ -478,7 +470,7 @@ class PropertyTableView { template void getMatNArrayPropertyViewImpl( const std::string& propertyName, - const ExtensionExtStructuralMetadataClassProperty& classProperty, + const ClassProperty& classProperty, PropertyComponentType componentType, Callback&& callback) const { switch (componentType) { @@ -564,7 +556,7 @@ class PropertyTableView { template void getMatNArrayPropertyViewImpl( const std::string& propertyName, - const ExtensionExtStructuralMetadataClassProperty& classProperty, + const ClassProperty& classProperty, PropertyType type, PropertyComponentType componentType, Callback&& callback) const { @@ -603,7 +595,7 @@ class PropertyTableView { template void getArrayPropertyViewImpl( const std::string& propertyName, - const ExtensionExtStructuralMetadataClassProperty& classProperty, + const ClassProperty& classProperty, PropertyType type, PropertyComponentType componentType, Callback&& callback) const { @@ -651,7 +643,7 @@ class PropertyTableView { template void getVecNPropertyViewImpl( const std::string& propertyName, - const ExtensionExtStructuralMetadataClassProperty& classProperty, + const ClassProperty& classProperty, PropertyComponentType componentType, Callback&& callback) const { @@ -736,7 +728,7 @@ class PropertyTableView { template void getVecNPropertyViewImpl( const std::string& propertyName, - const ExtensionExtStructuralMetadataClassProperty& classProperty, + const ClassProperty& classProperty, PropertyType type, PropertyComponentType componentType, Callback&& callback) const { @@ -775,7 +767,7 @@ class PropertyTableView { template void getMatNPropertyViewImpl( const std::string& propertyName, - const ExtensionExtStructuralMetadataClassProperty& classProperty, + const ClassProperty& classProperty, PropertyComponentType componentType, Callback&& callback) const { switch (componentType) { @@ -861,7 +853,7 @@ class PropertyTableView { template void getMatNPropertyViewImpl( const std::string& propertyName, - const ExtensionExtStructuralMetadataClassProperty& classProperty, + const ClassProperty& classProperty, PropertyType type, PropertyComponentType componentType, Callback&& callback) const { @@ -900,7 +892,7 @@ class PropertyTableView { template void getScalarPropertyViewImpl( const std::string& propertyName, - const ExtensionExtStructuralMetadataClassProperty& classProperty, + const ClassProperty& classProperty, PropertyComponentType componentType, Callback&& callback) const { switch (componentType) { @@ -966,7 +958,7 @@ class PropertyTableView { template PropertyTablePropertyView getPropertyViewImpl( const std::string& propertyName, - const ExtensionExtStructuralMetadataClassProperty& classProperty) const { + const ClassProperty& classProperty) const { auto propertyTablePropertyIter = _pPropertyTable->properties.find(propertyName); if (propertyTablePropertyIter == _pPropertyTable->properties.end()) { @@ -974,8 +966,8 @@ class PropertyTableView { PropertyTablePropertyViewStatus::ErrorPropertyDoesNotExist); } - const ExtensionExtStructuralMetadataPropertyTableProperty& - propertyTableProperty = propertyTablePropertyIter->second; + const PropertyTableProperty& propertyTableProperty = + propertyTablePropertyIter->second; if constexpr (IsMetadataNumeric::value || IsMetadataBoolean::value) { return getNumericOrBooleanPropertyValues( @@ -1002,9 +994,8 @@ class PropertyTableView { template PropertyTablePropertyView getNumericOrBooleanPropertyValues( - const ExtensionExtStructuralMetadataClassProperty& classProperty, - const ExtensionExtStructuralMetadataPropertyTableProperty& - propertyTableProperty) const { + const ClassProperty& classProperty, + const PropertyTableProperty& propertyTableProperty) const { if (classProperty.array) { return createInvalidPropertyView( PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); @@ -1057,16 +1048,14 @@ class PropertyTableView { } PropertyTablePropertyView getStringPropertyValues( - const ExtensionExtStructuralMetadataClassProperty& classProperty, - const ExtensionExtStructuralMetadataPropertyTableProperty& - propertyTableProperty) const; + const ClassProperty& classProperty, + const PropertyTableProperty& propertyTableProperty) const; template PropertyTablePropertyView> getPrimitiveArrayPropertyValues( - const ExtensionExtStructuralMetadataClassProperty& classProperty, - const ExtensionExtStructuralMetadataPropertyTableProperty& - propertyTableProperty) const { + const ClassProperty& classProperty, + const PropertyTableProperty& propertyTableProperty) const { if (!classProperty.array) { return createInvalidPropertyView>( PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); @@ -1178,9 +1167,8 @@ class PropertyTableView { PropertyTablePropertyView> getStringArrayPropertyValues( - const ExtensionExtStructuralMetadataClassProperty& classProperty, - const ExtensionExtStructuralMetadataPropertyTableProperty& - propertyTableProperty) const; + const ClassProperty& classProperty, + const PropertyTableProperty& propertyTableProperty) const; PropertyTablePropertyViewStatus getBufferSafe( int32_t bufferView, @@ -1212,8 +1200,8 @@ class PropertyTableView { } const Model* _pModel; - const ExtensionExtStructuralMetadataPropertyTable* _pPropertyTable; - const ExtensionExtStructuralMetadataClass* _pClass; + const PropertyTable* _pPropertyTable; + const Class* _pClass; PropertyTableViewStatus _status; }; } // namespace CesiumGltf diff --git a/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h index a6b97ccd1..425ad1f58 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h @@ -1,11 +1,11 @@ #pragma once -#include "CesiumGltf/ExtensionExtStructuralMetadataClassProperty.h" -#include "CesiumGltf/ExtensionExtStructuralMetadataPropertyTexture.h" +#include "CesiumGltf/ClassProperty.h" #include "CesiumGltf/ExtensionModelExtStructuralMetadata.h" #include "CesiumGltf/Image.h" #include "CesiumGltf/ImageCesium.h" #include "CesiumGltf/Model.h" +#include "CesiumGltf/PropertyTexture.h" #include "CesiumGltf/Sampler.h" #include "CesiumGltf/Texture.h" @@ -89,11 +89,12 @@ enum class PropertyTexturePropertyComponentType { * @tparam T The component type, must correspond to a valid * {@link PropertyTexturePropertyComponentType}. */ -template struct PropertyTexturePropertyValue { T components[4]; }; +template struct PropertyTexturePropertyValue { + T components[4]; +}; /** - * @brief A view of the data specified by a - * {@link ExtensionExtStructuralMetadataPropertyTextureProperty}. + * @brief A view of the data specified by a {@link PropertyTextureProperty}. * * Provides utilities to sample the property texture property using texture * coordinates. @@ -107,8 +108,8 @@ class PropertyTexturePropertyView { /** * @brief Construct a view of the data specified by the given property texture - * property. Assumes the model has already been validated by - * PropertyTextureView. + * property. Assumes the model has already been validated by the + * {@link PropertyTextureView} that invoked this constructor. * * @param model The glTF in which to look for the data specified by the * property texture property. @@ -117,9 +118,8 @@ class PropertyTexturePropertyView { */ PropertyTexturePropertyView( const Model& model, - const ExtensionExtStructuralMetadataClassProperty& classProperty, - const ExtensionExtStructuralMetadataPropertyTextureProperty& - propertyTextureProperty) noexcept; + const ClassProperty& classProperty, + const PropertyTextureProperty& propertyTextureProperty) noexcept; /** * @brief Gets the unswizzled property for the given texture coordinates. @@ -229,7 +229,7 @@ class PropertyTexturePropertyView { private: PropertyTexturePropertyViewStatus _status; - const ExtensionExtStructuralMetadataClassProperty* _pClassProperty; + const ClassProperty* _pClassProperty; const Sampler* _pSampler; const ImageCesium* _pImage; diff --git a/CesiumGltf/include/CesiumGltf/PropertyTextureView.h b/CesiumGltf/include/CesiumGltf/PropertyTextureView.h index 8fc438670..53189aa31 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTextureView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTextureView.h @@ -1,9 +1,9 @@ #pragma once -#include "CesiumGltf/ExtensionExtStructuralMetadataClass.h" -#include "CesiumGltf/ExtensionExtStructuralMetadataClassProperty.h" -#include "CesiumGltf/ExtensionExtStructuralMetadataPropertyTexture.h" +#include "CesiumGltf/Class.h" +#include "CesiumGltf/ClassProperty.h" #include "CesiumGltf/ExtensionModelExtStructuralMetadata.h" +#include "CesiumGltf/PropertyTexture.h" #include "CesiumGltf/PropertyTexturePropertyView.h" #include "CesiumGltf/Texture.h" #include "Image.h" @@ -16,7 +16,7 @@ namespace CesiumGltf { * * The {@link PropertyTextureView} constructor always completes successfully. * However it may not always reflect the actual content of the - * {@link ExtensionExtStructuralMetadataPropertyTexture}. This enumeration provides the reason. + * {@link PropertyTexture}. This enumeration provides the reason. */ enum class PropertyTextureViewStatus { /** @@ -60,7 +60,7 @@ enum class PropertyTextureViewStatus { }; /** - * @brief A view on the {@link ExtensionExtStructuralMetadataPropertyTexture}. + * @brief A view on the {@link PropertyTexture}. * * Provides access to views on the property texture properties. */ @@ -79,8 +79,7 @@ class PropertyTextureView { */ PropertyTextureView( const Model& model, - const ExtensionExtStructuralMetadataPropertyTexture& - propertyTexture) noexcept; + const PropertyTexture& propertyTexture) noexcept; /** * @brief Gets the status of this property texture view. @@ -91,15 +90,14 @@ class PropertyTextureView { PropertyTextureViewStatus status() const noexcept { return this->_status; } /** - * @brief Finds the {@link ExtensionExtStructuralMetadataClassProperty} that + * @brief Finds the {@link ClassProperty} that * describes the type information of the property with the specified name. * @param propertyName The name of the property to retrieve the class for. - * @return A pointer to the {@link ExtensionExtStructuralMetadataClassProperty}. + * @return A pointer to the {@link ClassProperty}. * Return nullptr if the PropertyTextureView is invalid or if no class * property was found. */ - const ExtensionExtStructuralMetadataClassProperty* - getClassProperty(const std::string& propertyName) const; + const ClassProperty* getClassProperty(const std::string& propertyName) const; /** * @brief Get the views for this property texture's properties. @@ -111,8 +109,8 @@ class PropertyTextureView { private: const Model* _pModel; - const ExtensionExtStructuralMetadataPropertyTexture* _pPropertyTexture; - const ExtensionExtStructuralMetadataClass* _pClass; + const PropertyTexture* _pPropertyTexture; + const Class* _pClass; std::unordered_map _propertyViews; PropertyTextureViewStatus _status; }; diff --git a/CesiumGltf/src/PropertyTableView.cpp b/CesiumGltf/src/PropertyTableView.cpp index 0e06b7d0f..a280527f6 100644 --- a/CesiumGltf/src/PropertyTableView.cpp +++ b/CesiumGltf/src/PropertyTableView.cpp @@ -106,7 +106,7 @@ static PropertyTablePropertyViewStatus checkStringAndArrayOffsetsBuffers( PropertyTableView::PropertyTableView( const Model& model, - const ExtensionExtStructuralMetadataPropertyTable& propertyTable) + const PropertyTable& propertyTable) : _pModel{&model}, _pPropertyTable{&propertyTable}, _pClass{nullptr}, @@ -118,8 +118,7 @@ PropertyTableView::PropertyTableView( return; } - const std::optional& schema = - pMetadata->schema; + const std::optional& schema = pMetadata->schema; if (!schema) { _status = PropertyTableViewStatus::ErrorMissingSchema; return; @@ -135,7 +134,7 @@ PropertyTableView::PropertyTableView( } } -const ExtensionExtStructuralMetadataClassProperty* +const ClassProperty* PropertyTableView::getClassProperty(const std::string& propertyName) const { if (_status != PropertyTableViewStatus::Valid) { return nullptr; @@ -280,16 +279,14 @@ PropertyTablePropertyViewStatus PropertyTableView::getStringOffsetsBufferSafe( PropertyTablePropertyView PropertyTableView::getStringPropertyValues( - const ExtensionExtStructuralMetadataClassProperty& classProperty, - const ExtensionExtStructuralMetadataPropertyTableProperty& - propertyTableProperty) const { + const ClassProperty& classProperty, + const PropertyTableProperty& propertyTableProperty) const { if (classProperty.array) { return createInvalidPropertyView( PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); } - if (classProperty.type != - ExtensionExtStructuralMetadataClassProperty::Type::STRING) { + if (classProperty.type != ClassProperty::Type::STRING) { return createInvalidPropertyView( PropertyTablePropertyViewStatus::ErrorTypeMismatch); } @@ -333,16 +330,14 @@ PropertyTableView::getStringPropertyValues( PropertyTablePropertyView> PropertyTableView::getStringArrayPropertyValues( - const ExtensionExtStructuralMetadataClassProperty& classProperty, - const ExtensionExtStructuralMetadataPropertyTableProperty& - propertyTableProperty) const { + const ClassProperty& classProperty, + const PropertyTableProperty& propertyTableProperty) const { if (!classProperty.array) { return createInvalidPropertyView>( PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); } - if (classProperty.type != - ExtensionExtStructuralMetadataClassProperty::Type::STRING) { + if (classProperty.type != ClassProperty::Type::STRING) { return createInvalidPropertyView>( PropertyTablePropertyViewStatus::ErrorTypeMismatch); } diff --git a/CesiumGltf/src/PropertyTexturePropertyView.cpp b/CesiumGltf/src/PropertyTexturePropertyView.cpp index d854468d2..253fdecb9 100644 --- a/CesiumGltf/src/PropertyTexturePropertyView.cpp +++ b/CesiumGltf/src/PropertyTexturePropertyView.cpp @@ -16,9 +16,8 @@ PropertyTexturePropertyView::PropertyTexturePropertyView() noexcept PropertyTexturePropertyView::PropertyTexturePropertyView( const Model& model, - const ExtensionExtStructuralMetadataClassProperty& classProperty, - const ExtensionExtStructuralMetadataPropertyTextureProperty& - propertyTextureProperty) noexcept + const ClassProperty& classProperty, + const PropertyTextureProperty& propertyTextureProperty) noexcept : _status(PropertyTexturePropertyViewStatus::ErrorUninitialized), _pClassProperty(&classProperty), _pSampler(nullptr), diff --git a/CesiumGltf/src/PropertyTextureView.cpp b/CesiumGltf/src/PropertyTextureView.cpp index 7f917c8a9..90dc0d6f7 100644 --- a/CesiumGltf/src/PropertyTextureView.cpp +++ b/CesiumGltf/src/PropertyTextureView.cpp @@ -10,8 +10,7 @@ PropertyTextureView::PropertyTextureView() noexcept PropertyTextureView::PropertyTextureView( const Model& model, - const ExtensionExtStructuralMetadataPropertyTexture& - propertyTexture) noexcept + const PropertyTexture& propertyTexture) noexcept : _pModel(&model), _pPropertyTexture(&propertyTexture), _pClass(nullptr), @@ -66,7 +65,7 @@ PropertyTextureView::PropertyTextureView( this->_status = PropertyTextureViewStatus::Valid; } -const ExtensionExtStructuralMetadataClassProperty* +const ClassProperty* PropertyTextureView::getClassProperty(const std::string& propertyName) const { if (_status != PropertyTextureViewStatus::Valid) { return nullptr; diff --git a/CesiumGltf/src/PropertyType.cpp b/CesiumGltf/src/PropertyType.cpp index 826997cf8..1e5e4ed62 100644 --- a/CesiumGltf/src/PropertyType.cpp +++ b/CesiumGltf/src/PropertyType.cpp @@ -1,74 +1,74 @@ #include "CesiumGltf/PropertyType.h" -#include "CesiumGltf/ExtensionExtStructuralMetadataClassProperty.h" -#include "CesiumGltf/ExtensionExtStructuralMetadataPropertyTable.h" +#include "CesiumGltf/ClassProperty.h" +#include "CesiumGltf/PropertyTable.h" namespace CesiumGltf { std::string convertPropertyTypeToString(PropertyType type) { switch (type) { case PropertyType::Scalar: - return ExtensionExtStructuralMetadataClassProperty::Type::SCALAR; + return ClassProperty::Type::SCALAR; case PropertyType::Vec2: - return ExtensionExtStructuralMetadataClassProperty::Type::VEC2; + return ClassProperty::Type::VEC2; case PropertyType::Vec3: - return ExtensionExtStructuralMetadataClassProperty::Type::VEC3; + return ClassProperty::Type::VEC3; case PropertyType::Vec4: - return ExtensionExtStructuralMetadataClassProperty::Type::VEC4; + return ClassProperty::Type::VEC4; case PropertyType::Mat2: - return ExtensionExtStructuralMetadataClassProperty::Type::MAT2; + return ClassProperty::Type::MAT2; case PropertyType::Mat3: - return ExtensionExtStructuralMetadataClassProperty::Type::MAT3; + return ClassProperty::Type::MAT3; case PropertyType::Mat4: - return ExtensionExtStructuralMetadataClassProperty::Type::MAT4; + return ClassProperty::Type::MAT4; case PropertyType::Boolean: - return ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN; + return ClassProperty::Type::BOOLEAN; case PropertyType::Enum: - return ExtensionExtStructuralMetadataClassProperty::Type::ENUM; + return ClassProperty::Type::ENUM; case PropertyType::String: - return ExtensionExtStructuralMetadataClassProperty::Type::STRING; + return ClassProperty::Type::STRING; default: return "INVALID"; } } PropertyType convertStringToPropertyType(const std::string& str) { - if (str == ExtensionExtStructuralMetadataClassProperty::Type::SCALAR) { + if (str == ClassProperty::Type::SCALAR) { return PropertyType::Scalar; } - if (str == ExtensionExtStructuralMetadataClassProperty::Type::VEC2) { + if (str == ClassProperty::Type::VEC2) { return PropertyType::Vec2; } - if (str == ExtensionExtStructuralMetadataClassProperty::Type::VEC3) { + if (str == ClassProperty::Type::VEC3) { return PropertyType::Vec3; } - if (str == ExtensionExtStructuralMetadataClassProperty::Type::VEC4) { + if (str == ClassProperty::Type::VEC4) { return PropertyType::Vec4; } - if (str == ExtensionExtStructuralMetadataClassProperty::Type::MAT2) { + if (str == ClassProperty::Type::MAT2) { return PropertyType::Mat2; } - if (str == ExtensionExtStructuralMetadataClassProperty::Type::MAT3) { + if (str == ClassProperty::Type::MAT3) { return PropertyType::Mat3; } - if (str == ExtensionExtStructuralMetadataClassProperty::Type::MAT4) { + if (str == ClassProperty::Type::MAT4) { return PropertyType::Mat4; } - if (str == ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN) { + if (str == ClassProperty::Type::BOOLEAN) { return PropertyType::Boolean; } - if (str == ExtensionExtStructuralMetadataClassProperty::Type::STRING) { + if (str == ClassProperty::Type::STRING) { return PropertyType::String; } - if (str == ExtensionExtStructuralMetadataClassProperty::Type::ENUM) { + if (str == ClassProperty::Type::ENUM) { return PropertyType::Enum; } @@ -80,25 +80,25 @@ std::string convertPropertyComponentTypeToString(PropertyComponentType type) { case PropertyComponentType::None: return "NONE"; case PropertyComponentType::Uint8: - return ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT8; + return ClassProperty::ComponentType::UINT8; case PropertyComponentType::Int8: - return ExtensionExtStructuralMetadataClassProperty::ComponentType::INT8; + return ClassProperty::ComponentType::INT8; case PropertyComponentType::Uint16: - return ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT16; + return ClassProperty::ComponentType::UINT16; case PropertyComponentType::Int16: - return ExtensionExtStructuralMetadataClassProperty::ComponentType::INT16; + return ClassProperty::ComponentType::INT16; case PropertyComponentType::Uint32: - return ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT32; + return ClassProperty::ComponentType::UINT32; case PropertyComponentType::Int32: - return ExtensionExtStructuralMetadataClassProperty::ComponentType::INT32; + return ClassProperty::ComponentType::INT32; case PropertyComponentType::Uint64: - return ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT64; + return ClassProperty::ComponentType::UINT64; case PropertyComponentType::Int64: - return ExtensionExtStructuralMetadataClassProperty::ComponentType::INT64; + return ClassProperty::ComponentType::INT64; case PropertyComponentType::Float32: - return ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT32; + return ClassProperty::ComponentType::FLOAT32; case PropertyComponentType::Float64: - return ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT64; + return ClassProperty::ComponentType::FLOAT64; default: return "NONE"; } @@ -106,52 +106,43 @@ std::string convertPropertyComponentTypeToString(PropertyComponentType type) { PropertyComponentType convertStringToPropertyComponentType(const std::string& str) { - if (str == - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT8) { + if (str == ClassProperty::ComponentType::UINT8) { return PropertyComponentType::Uint8; } - if (str == ExtensionExtStructuralMetadataClassProperty::ComponentType::INT8) { + if (str == ClassProperty::ComponentType::INT8) { return PropertyComponentType::Int8; } - if (str == - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT16) { + if (str == ClassProperty::ComponentType::UINT16) { return PropertyComponentType::Uint16; } - if (str == - ExtensionExtStructuralMetadataClassProperty::ComponentType::INT16) { + if (str == ClassProperty::ComponentType::INT16) { return PropertyComponentType::Int16; } - if (str == - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT32) { + if (str == ClassProperty::ComponentType::UINT32) { return PropertyComponentType::Uint32; } - if (str == - ExtensionExtStructuralMetadataClassProperty::ComponentType::INT32) { + if (str == ClassProperty::ComponentType::INT32) { return PropertyComponentType::Int32; } - if (str == - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT64) { + if (str == ClassProperty::ComponentType::UINT64) { return PropertyComponentType::Uint64; } - if (str == - ExtensionExtStructuralMetadataClassProperty::ComponentType::INT64) { + if (str == ClassProperty::ComponentType::INT64) { return PropertyComponentType::Int64; } - if (str == - ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT32) { + if (str == ClassProperty::ComponentType::FLOAT32) { return PropertyComponentType::Float32; } - if (str == - ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT64) { + if (str == ClassProperty::ComponentType::FLOAT64) { return PropertyComponentType::Float64; } @@ -160,23 +151,19 @@ convertStringToPropertyComponentType(const std::string& str) { PropertyComponentType convertArrayOffsetTypeStringToPropertyComponentType(const std::string& str) { - if (str == ExtensionExtStructuralMetadataPropertyTableProperty:: - ArrayOffsetType::UINT8) { + if (str == PropertyTableProperty::ArrayOffsetType::UINT8) { return PropertyComponentType::Uint8; } - if (str == ExtensionExtStructuralMetadataPropertyTableProperty:: - ArrayOffsetType::UINT16) { + if (str == PropertyTableProperty::ArrayOffsetType::UINT16) { return PropertyComponentType::Uint16; } - if (str == ExtensionExtStructuralMetadataPropertyTableProperty:: - ArrayOffsetType::UINT32) { + if (str == PropertyTableProperty::ArrayOffsetType::UINT32) { return PropertyComponentType::Uint32; } - if (str == ExtensionExtStructuralMetadataPropertyTableProperty:: - ArrayOffsetType::UINT64) { + if (str == PropertyTableProperty::ArrayOffsetType::UINT64) { return PropertyComponentType::Uint64; } @@ -185,23 +172,19 @@ convertArrayOffsetTypeStringToPropertyComponentType(const std::string& str) { PropertyComponentType convertStringOffsetTypeStringToPropertyComponentType(const std::string& str) { - if (str == ExtensionExtStructuralMetadataPropertyTableProperty:: - StringOffsetType::UINT8) { + if (str == PropertyTableProperty::StringOffsetType::UINT8) { return PropertyComponentType::Uint8; } - if (str == ExtensionExtStructuralMetadataPropertyTableProperty:: - StringOffsetType::UINT16) { + if (str == PropertyTableProperty::StringOffsetType::UINT16) { return PropertyComponentType::Uint16; } - if (str == ExtensionExtStructuralMetadataPropertyTableProperty:: - StringOffsetType::UINT32) { + if (str == PropertyTableProperty::StringOffsetType::UINT32) { return PropertyComponentType::Uint32; } - if (str == ExtensionExtStructuralMetadataPropertyTableProperty:: - StringOffsetType::UINT64) { + if (str == PropertyTableProperty::StringOffsetType::UINT64) { return PropertyComponentType::Uint64; } diff --git a/CesiumGltf/test/TestPropertyTableView.cpp b/CesiumGltf/test/TestPropertyTableView.cpp index cfadc098b..d1984cd67 100644 --- a/CesiumGltf/test/TestPropertyTableView.cpp +++ b/CesiumGltf/test/TestPropertyTableView.cpp @@ -11,11 +11,11 @@ TEST_CASE("Test PropertyTableView on model without EXT_structural_metadata " Model model; // Create an erroneously isolated property table. - ExtensionExtStructuralMetadataPropertyTable propertyTable; + PropertyTable propertyTable; propertyTable.classProperty = "TestClass"; propertyTable.count = static_cast(10); - ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + PropertyTableProperty& propertyTableProperty = propertyTable.properties["TestClassProperty"]; propertyTableProperty.values = static_cast(0); @@ -24,7 +24,7 @@ TEST_CASE("Test PropertyTableView on model without EXT_structural_metadata " view.status() == PropertyTableViewStatus::ErrorMissingMetadataExtension); REQUIRE(view.size() == 0); - const ExtensionExtStructuralMetadataClassProperty* classProperty = + const ClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE(!classProperty); } @@ -35,12 +35,11 @@ TEST_CASE("Test PropertyTableView on model without metadata schema") { ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); - ExtensionExtStructuralMetadataPropertyTable& propertyTable = - metadata.propertyTables.emplace_back(); + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); propertyTable.classProperty = "TestClass"; propertyTable.count = static_cast(10); - ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + PropertyTableProperty& propertyTableProperty = propertyTable.properties["TestClassProperty"]; propertyTableProperty.values = static_cast(0); @@ -48,7 +47,7 @@ TEST_CASE("Test PropertyTableView on model without metadata schema") { REQUIRE(view.status() == PropertyTableViewStatus::ErrorMissingSchema); REQUIRE(view.size() == 0); - const ExtensionExtStructuralMetadataClassProperty* classProperty = + const ClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE(!classProperty); } @@ -59,21 +58,17 @@ TEST_CASE("Test property table with nonexistent class") { ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); - ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); - ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; - ExtensionExtStructuralMetadataClassProperty& testClassProperty = - testClass.properties["TestClassProperty"]; - testClassProperty.type = - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR; - testClassProperty.componentType = - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT32; - - ExtensionExtStructuralMetadataPropertyTable& propertyTable = - metadata.propertyTables.emplace_back(); + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::UINT32; + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); propertyTable.classProperty = "I Don't Exist"; propertyTable.count = static_cast(10); - ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + PropertyTableProperty& propertyTableProperty = propertyTable.properties["TestClassProperty"]; propertyTableProperty.values = static_cast(0); @@ -81,7 +76,7 @@ TEST_CASE("Test property table with nonexistent class") { REQUIRE(view.status() == PropertyTableViewStatus::ErrorClassNotFound); REQUIRE(view.size() == 0); - const ExtensionExtStructuralMetadataClassProperty* classProperty = + const ClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE(!classProperty); } @@ -117,21 +112,17 @@ TEST_CASE("Test scalar property") { ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); - ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); - ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; - ExtensionExtStructuralMetadataClassProperty& testClassProperty = - testClass.properties["TestClassProperty"]; - testClassProperty.type = - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR; - testClassProperty.componentType = - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT32; - - ExtensionExtStructuralMetadataPropertyTable& propertyTable = - metadata.propertyTables.emplace_back(); + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::UINT32; + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); propertyTable.classProperty = "TestClass"; propertyTable.count = static_cast(values.size()); - ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + PropertyTableProperty& propertyTableProperty = propertyTable.properties["TestClassProperty"]; propertyTableProperty.values = static_cast(valueBufferViewIndex); @@ -139,15 +130,11 @@ TEST_CASE("Test scalar property") { REQUIRE(view.status() == PropertyTableViewStatus::Valid); REQUIRE(view.size() == propertyTable.count); - const ExtensionExtStructuralMetadataClassProperty* classProperty = + const ClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE(classProperty); - REQUIRE( - classProperty->type == - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR); - REQUIRE( - classProperty->componentType == - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT32); + REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT32); REQUIRE(classProperty->count == std::nullopt); REQUIRE(!classProperty->array); @@ -299,21 +286,17 @@ TEST_CASE("Test vecN property") { ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); - ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); - ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; - ExtensionExtStructuralMetadataClassProperty& testClassProperty = - testClass.properties["TestClassProperty"]; - testClassProperty.type = - ExtensionExtStructuralMetadataClassProperty::Type::VEC3; - testClassProperty.componentType = - ExtensionExtStructuralMetadataClassProperty::ComponentType::INT32; - - ExtensionExtStructuralMetadataPropertyTable& propertyTable = - metadata.propertyTables.emplace_back(); + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::VEC3; + testClassProperty.componentType = ClassProperty::ComponentType::INT32; + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); propertyTable.classProperty = "TestClass"; propertyTable.count = static_cast(values.size()); - ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + PropertyTableProperty& propertyTableProperty = propertyTable.properties["TestClassProperty"]; propertyTableProperty.values = static_cast(valueBufferViewIndex); @@ -321,15 +304,11 @@ TEST_CASE("Test vecN property") { REQUIRE(view.status() == PropertyTableViewStatus::Valid); REQUIRE(view.size() == propertyTable.count); - const ExtensionExtStructuralMetadataClassProperty* classProperty = + const ClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE(classProperty); - REQUIRE( - classProperty->type == - ExtensionExtStructuralMetadataClassProperty::Type::VEC3); - REQUIRE( - classProperty->componentType == - ExtensionExtStructuralMetadataClassProperty::ComponentType::INT32); + REQUIRE(classProperty->type == ClassProperty::Type::VEC3); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT32); REQUIRE(classProperty->count == std::nullopt); REQUIRE(!classProperty->array); @@ -498,21 +477,17 @@ TEST_CASE("Test matN property") { ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); - ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); - ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; - ExtensionExtStructuralMetadataClassProperty& testClassProperty = - testClass.properties["TestClassProperty"]; - testClassProperty.type = - ExtensionExtStructuralMetadataClassProperty::Type::MAT2; - testClassProperty.componentType = - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT32; - - ExtensionExtStructuralMetadataPropertyTable& propertyTable = - metadata.propertyTables.emplace_back(); + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::MAT2; + testClassProperty.componentType = ClassProperty::ComponentType::UINT32; + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); propertyTable.classProperty = "TestClass"; propertyTable.count = static_cast(values.size()); - ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + PropertyTableProperty& propertyTableProperty = propertyTable.properties["TestClassProperty"]; propertyTableProperty.values = static_cast(valueBufferViewIndex); @@ -520,15 +495,11 @@ TEST_CASE("Test matN property") { REQUIRE(view.status() == PropertyTableViewStatus::Valid); REQUIRE(view.size() == propertyTable.count); - const ExtensionExtStructuralMetadataClassProperty* classProperty = + const ClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE(classProperty); - REQUIRE( - classProperty->type == - ExtensionExtStructuralMetadataClassProperty::Type::MAT2); - REQUIRE( - classProperty->componentType == - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT32); + REQUIRE(classProperty->type == ClassProperty::Type::MAT2); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT32); REQUIRE(classProperty->count == std::nullopt); REQUIRE(!classProperty->array); @@ -698,19 +669,16 @@ TEST_CASE("Test boolean property") { ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); - ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); - ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; - ExtensionExtStructuralMetadataClassProperty& testClassProperty = - testClass.properties["TestClassProperty"]; - testClassProperty.type = - ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN; + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::BOOLEAN; - ExtensionExtStructuralMetadataPropertyTable& propertyTable = - metadata.propertyTables.emplace_back(); + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); propertyTable.classProperty = "TestClass"; propertyTable.count = static_cast(instanceCount); - ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + PropertyTableProperty& propertyTableProperty = propertyTable.properties["TestClassProperty"]; propertyTableProperty.values = static_cast(model.bufferViews.size() - 1); @@ -719,12 +687,10 @@ TEST_CASE("Test boolean property") { REQUIRE(view.status() == PropertyTableViewStatus::Valid); REQUIRE(view.size() == propertyTable.count); - const ExtensionExtStructuralMetadataClassProperty* classProperty = + const ClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE(classProperty); - REQUIRE( - classProperty->type == - ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN); + REQUIRE(classProperty->type == ClassProperty::Type::BOOLEAN); REQUIRE(classProperty->componentType == std::nullopt); REQUIRE(classProperty->count == std::nullopt); REQUIRE(!classProperty->array); @@ -809,23 +775,19 @@ TEST_CASE("Test string property") { ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); - ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); - ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; - ExtensionExtStructuralMetadataClassProperty& testClassProperty = - testClass.properties["TestClassProperty"]; - testClassProperty.type = - ExtensionExtStructuralMetadataClassProperty::Type::STRING; + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::STRING; - ExtensionExtStructuralMetadataPropertyTable& propertyTable = - metadata.propertyTables.emplace_back(); + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); propertyTable.classProperty = "TestClass"; propertyTable.count = static_cast(expected.size()); - ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + PropertyTableProperty& propertyTableProperty = propertyTable.properties["TestClassProperty"]; propertyTableProperty.stringOffsetType = - ExtensionExtStructuralMetadataPropertyTableProperty::StringOffsetType:: - UINT32; + PropertyTableProperty::StringOffsetType::UINT32; propertyTableProperty.values = static_cast(valueBufferViewIndex); propertyTableProperty.stringOffsets = static_cast(offsetBufferViewIndex); @@ -834,12 +796,10 @@ TEST_CASE("Test string property") { REQUIRE(view.status() == PropertyTableViewStatus::Valid); REQUIRE(view.size() == propertyTable.count); - const ExtensionExtStructuralMetadataClassProperty* classProperty = + const ClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE(classProperty); - REQUIRE( - classProperty->type == - ExtensionExtStructuralMetadataClassProperty::Type::STRING); + REQUIRE(classProperty->type == ClassProperty::Type::STRING); REQUIRE(classProperty->componentType == std::nullopt); REQUIRE(classProperty->count == std::nullopt); REQUIRE(!classProperty->array); @@ -865,8 +825,7 @@ TEST_CASE("Test string property") { SECTION("Wrong offset type") { propertyTableProperty.stringOffsetType = - ExtensionExtStructuralMetadataPropertyTableProperty::StringOffsetType:: - UINT8; + PropertyTableProperty::StringOffsetType::UINT8; PropertyTablePropertyView stringProperty = view.getPropertyView("TestClassProperty"); REQUIRE( @@ -875,8 +834,7 @@ TEST_CASE("Test string property") { ErrorBufferViewSizeDoesNotMatchPropertyTableCount); propertyTableProperty.stringOffsetType = - ExtensionExtStructuralMetadataPropertyTableProperty::StringOffsetType:: - UINT64; + PropertyTableProperty::StringOffsetType::UINT64; stringProperty = view.getPropertyView("TestClassProperty"); REQUIRE( @@ -893,8 +851,7 @@ TEST_CASE("Test string property") { propertyTableProperty.stringOffsetType = ""; propertyTableProperty.arrayOffsetType = - ExtensionExtStructuralMetadataPropertyTableProperty::StringOffsetType:: - UINT32; + PropertyTableProperty::StringOffsetType::UINT32; stringProperty = view.getPropertyView("TestClassProperty"); REQUIRE( @@ -954,24 +911,20 @@ TEST_CASE("Test fixed-length scalar array") { ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); - ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); - ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; - ExtensionExtStructuralMetadataClassProperty& testClassProperty = - testClass.properties["TestClassProperty"]; - testClassProperty.type = - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR; - testClassProperty.componentType = - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT32; + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::UINT32; testClassProperty.array = true; testClassProperty.count = 3; - ExtensionExtStructuralMetadataPropertyTable& propertyTable = - metadata.propertyTables.emplace_back(); + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); propertyTable.classProperty = "TestClass"; propertyTable.count = static_cast( values.size() / static_cast(testClassProperty.count.value())); - ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + PropertyTableProperty& propertyTableProperty = propertyTable.properties["TestClassProperty"]; propertyTableProperty.values = static_cast(model.bufferViews.size() - 1); @@ -980,15 +933,11 @@ TEST_CASE("Test fixed-length scalar array") { REQUIRE(view.status() == PropertyTableViewStatus::Valid); REQUIRE(view.size() == propertyTable.count); - const ExtensionExtStructuralMetadataClassProperty* classProperty = + const ClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE(classProperty); - REQUIRE( - classProperty->type == - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR); - REQUIRE( - classProperty->componentType == - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT32); + REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT32); REQUIRE(classProperty->array); REQUIRE(classProperty->count == 3); @@ -1127,43 +1076,34 @@ TEST_CASE("Test variable-length scalar array") { ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); - ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); - ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; - ExtensionExtStructuralMetadataClassProperty& testClassProperty = - testClass.properties["TestClassProperty"]; - testClassProperty.type = - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR; - testClassProperty.componentType = - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT16; + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::UINT16; testClassProperty.array = true; - ExtensionExtStructuralMetadataPropertyTable& propertyTable = - metadata.propertyTables.emplace_back(); + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); propertyTable.classProperty = "TestClass"; propertyTable.count = static_cast(expected.size()); - ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + PropertyTableProperty& propertyTableProperty = propertyTable.properties["TestClassProperty"]; propertyTableProperty.values = static_cast(valueBufferViewIndex); propertyTableProperty.arrayOffsets = static_cast(offsetBufferViewIndex); propertyTableProperty.arrayOffsetType = - ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: - UINT64; + PropertyTableProperty::ArrayOffsetType::UINT64; PropertyTableView view(model, propertyTable); REQUIRE(view.status() == PropertyTableViewStatus::Valid); REQUIRE(view.size() == propertyTable.count); - const ExtensionExtStructuralMetadataClassProperty* classProperty = + const ClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE(classProperty); - REQUIRE( - classProperty->type == - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR); - REQUIRE( - classProperty->componentType == - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT16); + REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT16); REQUIRE(classProperty->array); REQUIRE(!classProperty->count); @@ -1182,8 +1122,7 @@ TEST_CASE("Test variable-length scalar array") { } SECTION("Wrong offset type") { propertyTableProperty.arrayOffsetType = - ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: - UINT8; + PropertyTableProperty::ArrayOffsetType::UINT8; PropertyTablePropertyView> arrayProperty = view.getPropertyView>("TestClassProperty"); REQUIRE( @@ -1192,8 +1131,7 @@ TEST_CASE("Test variable-length scalar array") { ErrorBufferViewSizeDoesNotMatchPropertyTableCount); propertyTableProperty.arrayOffsetType = - ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: - UINT16; + PropertyTableProperty::ArrayOffsetType::UINT16; arrayProperty = view.getPropertyView>("TestClassProperty"); REQUIRE( @@ -1210,8 +1148,7 @@ TEST_CASE("Test variable-length scalar array") { propertyTableProperty.arrayOffsetType = ""; propertyTableProperty.stringOffsetType = - ExtensionExtStructuralMetadataPropertyTableProperty::StringOffsetType:: - UINT64; + PropertyTableProperty::StringOffsetType::UINT64; arrayProperty = view.getPropertyView>("TestClassProperty"); REQUIRE( @@ -1221,8 +1158,7 @@ TEST_CASE("Test variable-length scalar array") { SECTION("Offset values are not sorted ascending") { propertyTableProperty.arrayOffsetType = - ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: - UINT64; + PropertyTableProperty::ArrayOffsetType::UINT64; uint64_t* offset = reinterpret_cast( model.buffers[offsetBufferIndex].cesium.data.data()); offset[propertyTable.count] = 0; @@ -1288,24 +1224,20 @@ TEST_CASE("Test fixed-length vecN array") { ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); - ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); - ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; - ExtensionExtStructuralMetadataClassProperty& testClassProperty = - testClass.properties["TestClassProperty"]; - testClassProperty.type = - ExtensionExtStructuralMetadataClassProperty::Type::VEC3; - testClassProperty.componentType = - ExtensionExtStructuralMetadataClassProperty::ComponentType::INT32; + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::VEC3; + testClassProperty.componentType = ClassProperty::ComponentType::INT32; testClassProperty.array = true; testClassProperty.count = 2; - ExtensionExtStructuralMetadataPropertyTable& propertyTable = - metadata.propertyTables.emplace_back(); + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); propertyTable.classProperty = "TestClass"; propertyTable.count = static_cast( values.size() / static_cast(testClassProperty.count.value())); - ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + PropertyTableProperty& propertyTableProperty = propertyTable.properties["TestClassProperty"]; propertyTableProperty.values = static_cast(model.bufferViews.size() - 1); @@ -1314,15 +1246,11 @@ TEST_CASE("Test fixed-length vecN array") { REQUIRE(view.status() == PropertyTableViewStatus::Valid); REQUIRE(view.size() == propertyTable.count); - const ExtensionExtStructuralMetadataClassProperty* classProperty = + const ClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE(classProperty); - REQUIRE( - classProperty->type == - ExtensionExtStructuralMetadataClassProperty::Type::VEC3); - REQUIRE( - classProperty->componentType == - ExtensionExtStructuralMetadataClassProperty::ComponentType::INT32); + REQUIRE(classProperty->type == ClassProperty::Type::VEC3); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT32); REQUIRE(classProperty->array); REQUIRE(classProperty->count == 2); @@ -1468,43 +1396,34 @@ TEST_CASE("Test variable-length vecN array") { ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); - ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); - ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; - ExtensionExtStructuralMetadataClassProperty& testClassProperty = - testClass.properties["TestClassProperty"]; - testClassProperty.type = - ExtensionExtStructuralMetadataClassProperty::Type::VEC3; - testClassProperty.componentType = - ExtensionExtStructuralMetadataClassProperty::ComponentType::INT32; + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::VEC3; + testClassProperty.componentType = ClassProperty::ComponentType::INT32; testClassProperty.array = true; - ExtensionExtStructuralMetadataPropertyTable& propertyTable = - metadata.propertyTables.emplace_back(); + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); propertyTable.classProperty = "TestClass"; propertyTable.count = static_cast(expected.size()); - ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + PropertyTableProperty& propertyTableProperty = propertyTable.properties["TestClassProperty"]; propertyTableProperty.values = static_cast(valueBufferViewIndex); propertyTableProperty.arrayOffsets = static_cast(offsetBufferViewIndex); propertyTableProperty.arrayOffsetType = - ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: - UINT64; + PropertyTableProperty::ArrayOffsetType::UINT64; PropertyTableView view(model, propertyTable); REQUIRE(view.status() == PropertyTableViewStatus::Valid); REQUIRE(view.size() == propertyTable.count); - const ExtensionExtStructuralMetadataClassProperty* classProperty = + const ClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE(classProperty); - REQUIRE( - classProperty->type == - ExtensionExtStructuralMetadataClassProperty::Type::VEC3); - REQUIRE( - classProperty->componentType == - ExtensionExtStructuralMetadataClassProperty::ComponentType::INT32); + REQUIRE(classProperty->type == ClassProperty::Type::VEC3); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT32); REQUIRE(classProperty->array); REQUIRE(!classProperty->count); @@ -1525,8 +1444,7 @@ TEST_CASE("Test variable-length vecN array") { SECTION("Wrong offset type") { propertyTableProperty.arrayOffsetType = - ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: - UINT8; + PropertyTableProperty::ArrayOffsetType::UINT8; PropertyTablePropertyView> arrayProperty = view.getPropertyView>( "TestClassProperty"); @@ -1536,8 +1454,7 @@ TEST_CASE("Test variable-length vecN array") { ErrorBufferViewSizeDoesNotMatchPropertyTableCount); propertyTableProperty.arrayOffsetType = - ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: - UINT16; + PropertyTableProperty::ArrayOffsetType::UINT16; arrayProperty = view.getPropertyView>( "TestClassProperty"); REQUIRE( @@ -1554,8 +1471,7 @@ TEST_CASE("Test variable-length vecN array") { propertyTableProperty.arrayOffsetType = ""; propertyTableProperty.stringOffsetType = - ExtensionExtStructuralMetadataPropertyTableProperty::StringOffsetType:: - UINT64; + PropertyTableProperty::StringOffsetType::UINT64; arrayProperty = view.getPropertyView>( "TestClassProperty"); REQUIRE( @@ -1565,8 +1481,7 @@ TEST_CASE("Test variable-length vecN array") { SECTION("Offset values are not sorted ascending") { propertyTableProperty.arrayOffsetType = - ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: - UINT64; + PropertyTableProperty::ArrayOffsetType::UINT64; uint64_t* offset = reinterpret_cast( model.buffers[offsetBufferIndex].cesium.data.data()); offset[propertyTable.count] = 0; @@ -1649,24 +1564,20 @@ TEST_CASE("Test fixed-length matN array") { ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); - ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); - ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; - ExtensionExtStructuralMetadataClassProperty& testClassProperty = - testClass.properties["TestClassProperty"]; - testClassProperty.type = - ExtensionExtStructuralMetadataClassProperty::Type::MAT2; - testClassProperty.componentType = - ExtensionExtStructuralMetadataClassProperty::ComponentType::INT32; + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::MAT2; + testClassProperty.componentType = ClassProperty::ComponentType::INT32; testClassProperty.array = true; testClassProperty.count = 2; - ExtensionExtStructuralMetadataPropertyTable& propertyTable = - metadata.propertyTables.emplace_back(); + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); propertyTable.classProperty = "TestClass"; propertyTable.count = static_cast( values.size() / static_cast(testClassProperty.count.value())); - ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + PropertyTableProperty& propertyTableProperty = propertyTable.properties["TestClassProperty"]; propertyTableProperty.values = static_cast(model.bufferViews.size() - 1); @@ -1675,15 +1586,11 @@ TEST_CASE("Test fixed-length matN array") { REQUIRE(view.status() == PropertyTableViewStatus::Valid); REQUIRE(view.size() == propertyTable.count); - const ExtensionExtStructuralMetadataClassProperty* classProperty = + const ClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE(classProperty); - REQUIRE( - classProperty->type == - ExtensionExtStructuralMetadataClassProperty::Type::MAT2); - REQUIRE( - classProperty->componentType == - ExtensionExtStructuralMetadataClassProperty::ComponentType::INT32); + REQUIRE(classProperty->type == ClassProperty::Type::MAT2); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT32); REQUIRE(classProperty->array); REQUIRE(classProperty->count == 2); @@ -1850,43 +1757,34 @@ TEST_CASE("Test variable-length matN array") { ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); - ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); - ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; - ExtensionExtStructuralMetadataClassProperty& testClassProperty = - testClass.properties["TestClassProperty"]; - testClassProperty.type = - ExtensionExtStructuralMetadataClassProperty::Type::MAT2; - testClassProperty.componentType = - ExtensionExtStructuralMetadataClassProperty::ComponentType::INT32; + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::MAT2; + testClassProperty.componentType = ClassProperty::ComponentType::INT32; testClassProperty.array = true; - ExtensionExtStructuralMetadataPropertyTable& propertyTable = - metadata.propertyTables.emplace_back(); + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); propertyTable.classProperty = "TestClass"; propertyTable.count = static_cast(expected.size()); - ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + PropertyTableProperty& propertyTableProperty = propertyTable.properties["TestClassProperty"]; propertyTableProperty.values = static_cast(valueBufferViewIndex); propertyTableProperty.arrayOffsets = static_cast(offsetBufferViewIndex); propertyTableProperty.arrayOffsetType = - ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: - UINT64; + PropertyTableProperty::ArrayOffsetType::UINT64; PropertyTableView view(model, propertyTable); REQUIRE(view.status() == PropertyTableViewStatus::Valid); REQUIRE(view.size() == propertyTable.count); - const ExtensionExtStructuralMetadataClassProperty* classProperty = + const ClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE(classProperty); - REQUIRE( - classProperty->type == - ExtensionExtStructuralMetadataClassProperty::Type::MAT2); - REQUIRE( - classProperty->componentType == - ExtensionExtStructuralMetadataClassProperty::ComponentType::INT32); + REQUIRE(classProperty->type == ClassProperty::Type::MAT2); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT32); REQUIRE(classProperty->array); REQUIRE(!classProperty->count); @@ -1907,8 +1805,7 @@ TEST_CASE("Test variable-length matN array") { SECTION("Wrong offset type") { propertyTableProperty.arrayOffsetType = - ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: - UINT8; + PropertyTableProperty::ArrayOffsetType::UINT8; PropertyTablePropertyView> arrayProperty = view.getPropertyView>( "TestClassProperty"); @@ -1918,8 +1815,7 @@ TEST_CASE("Test variable-length matN array") { ErrorBufferViewSizeDoesNotMatchPropertyTableCount); propertyTableProperty.arrayOffsetType = - ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: - UINT16; + PropertyTableProperty::ArrayOffsetType::UINT16; arrayProperty = view.getPropertyView>( "TestClassProperty"); REQUIRE( @@ -1936,8 +1832,7 @@ TEST_CASE("Test variable-length matN array") { propertyTableProperty.arrayOffsetType = ""; propertyTableProperty.stringOffsetType = - ExtensionExtStructuralMetadataPropertyTableProperty::StringOffsetType:: - UINT64; + PropertyTableProperty::StringOffsetType::UINT64; arrayProperty = view.getPropertyView>( "TestClassProperty"); REQUIRE( @@ -1947,8 +1842,7 @@ TEST_CASE("Test variable-length matN array") { SECTION("Offset values are not sorted ascending") { propertyTableProperty.arrayOffsetType = - ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: - UINT64; + PropertyTableProperty::ArrayOffsetType::UINT64; uint64_t* offset = reinterpret_cast( model.buffers[offsetBufferIndex].cesium.data.data()); offset[propertyTable.count] = 0; @@ -2031,22 +1925,19 @@ TEST_CASE("Test fixed-length boolean array") { ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); - ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); - ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; - ExtensionExtStructuralMetadataClassProperty& testClassProperty = - testClass.properties["TestClassProperty"]; - testClassProperty.type = - ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN; + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::BOOLEAN; testClassProperty.array = true; testClassProperty.count = 3; - ExtensionExtStructuralMetadataPropertyTable& propertyTable = - metadata.propertyTables.emplace_back(); + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); propertyTable.classProperty = "TestClass"; propertyTable.count = static_cast( expected.size() / static_cast(testClassProperty.count.value())); - ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + PropertyTableProperty& propertyTableProperty = propertyTable.properties["TestClassProperty"]; propertyTableProperty.values = static_cast(model.bufferViews.size() - 1); @@ -2055,12 +1946,10 @@ TEST_CASE("Test fixed-length boolean array") { REQUIRE(view.status() == PropertyTableViewStatus::Valid); REQUIRE(view.size() == propertyTable.count); - const ExtensionExtStructuralMetadataClassProperty* classProperty = + const ClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE(classProperty); - REQUIRE( - classProperty->type == - ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN); + REQUIRE(classProperty->type == ClassProperty::Type::BOOLEAN); REQUIRE(classProperty->array); REQUIRE(classProperty->count == 3); @@ -2184,38 +2073,32 @@ TEST_CASE("Test variable-length boolean array") { ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); - ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); - ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; - ExtensionExtStructuralMetadataClassProperty& testClassProperty = - testClass.properties["TestClassProperty"]; - testClassProperty.type = - ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN; + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::BOOLEAN; testClassProperty.array = true; - ExtensionExtStructuralMetadataPropertyTable& propertyTable = - metadata.propertyTables.emplace_back(); + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); propertyTable.classProperty = "TestClass"; propertyTable.count = static_cast(expected.size()); - ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + PropertyTableProperty& propertyTableProperty = propertyTable.properties["TestClassProperty"]; propertyTableProperty.values = static_cast(valueBufferViewIndex); propertyTableProperty.arrayOffsets = static_cast(offsetBufferViewIndex); propertyTableProperty.arrayOffsetType = - ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: - UINT64; + PropertyTableProperty::ArrayOffsetType::UINT64; PropertyTableView view(model, propertyTable); REQUIRE(view.status() == PropertyTableViewStatus::Valid); REQUIRE(view.size() == propertyTable.count); - const ExtensionExtStructuralMetadataClassProperty* classProperty = + const ClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE(classProperty); - REQUIRE( - classProperty->type == - ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN); + REQUIRE(classProperty->type == ClassProperty::Type::BOOLEAN); REQUIRE(classProperty->array); REQUIRE(!classProperty->count); @@ -2236,8 +2119,7 @@ TEST_CASE("Test variable-length boolean array") { SECTION("Wrong offset type") { propertyTableProperty.arrayOffsetType = - ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: - UINT8; + PropertyTableProperty::ArrayOffsetType::UINT8; PropertyTablePropertyView> arrayProperty = view.getPropertyView>("TestClassProperty"); REQUIRE( @@ -2246,8 +2128,7 @@ TEST_CASE("Test variable-length boolean array") { ErrorBufferViewSizeDoesNotMatchPropertyTableCount); propertyTableProperty.arrayOffsetType = - ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: - UINT16; + PropertyTableProperty::ArrayOffsetType::UINT16; arrayProperty = view.getPropertyView>("TestClassProperty"); REQUIRE( @@ -2264,8 +2145,7 @@ TEST_CASE("Test variable-length boolean array") { propertyTableProperty.arrayOffsetType = ""; propertyTableProperty.stringOffsetType = - ExtensionExtStructuralMetadataPropertyTableProperty::StringOffsetType:: - UINT64; + PropertyTableProperty::StringOffsetType::UINT64; arrayProperty = view.getPropertyView>("TestClassProperty"); REQUIRE( @@ -2275,8 +2155,7 @@ TEST_CASE("Test variable-length boolean array") { SECTION("Offset values are not sorted ascending") { propertyTableProperty.arrayOffsetType = - ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: - UINT64; + PropertyTableProperty::ArrayOffsetType::UINT64; uint64_t* offset = reinterpret_cast( model.buffers[offsetBufferIndex].cesium.data.data()); offset[propertyTable.count] = 0; @@ -2367,26 +2246,22 @@ TEST_CASE("Test fixed-length arrays of strings") { ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); - ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); - ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; - ExtensionExtStructuralMetadataClassProperty& testClassProperty = - testClass.properties["TestClassProperty"]; - testClassProperty.type = - ExtensionExtStructuralMetadataClassProperty::Type::STRING; + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::STRING; testClassProperty.array = true; testClassProperty.count = 2; - ExtensionExtStructuralMetadataPropertyTable& propertyTable = - metadata.propertyTables.emplace_back(); + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); propertyTable.classProperty = "TestClass"; propertyTable.count = static_cast( expected.size() / static_cast(testClassProperty.count.value())); - ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + PropertyTableProperty& propertyTableProperty = propertyTable.properties["TestClassProperty"]; propertyTableProperty.stringOffsetType = - ExtensionExtStructuralMetadataPropertyTableProperty::StringOffsetType:: - UINT32; + PropertyTableProperty::StringOffsetType::UINT32; propertyTableProperty.values = static_cast(valueBufferViewIndex); propertyTableProperty.stringOffsets = static_cast(offsetBufferViewIndex); @@ -2395,12 +2270,10 @@ TEST_CASE("Test fixed-length arrays of strings") { REQUIRE(view.status() == PropertyTableViewStatus::Valid); REQUIRE(view.size() == propertyTable.count); - const ExtensionExtStructuralMetadataClassProperty* classProperty = + const ClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE(classProperty); - REQUIRE( - classProperty->type == - ExtensionExtStructuralMetadataClassProperty::Type::STRING); + REQUIRE(classProperty->type == ClassProperty::Type::STRING); REQUIRE(classProperty->array); REQUIRE(classProperty->count == 2); @@ -2459,8 +2332,7 @@ TEST_CASE("Test fixed-length arrays of strings") { propertyTableProperty.stringOffsetType = ""; propertyTableProperty.arrayOffsetType = - ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: - UINT32; + PropertyTableProperty::ArrayOffsetType::UINT32; stringProperty = view.getPropertyView>( "TestClassProperty"); REQUIRE( @@ -2573,27 +2445,22 @@ TEST_CASE("Test variable-length arrays of strings") { ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); - ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); - ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; - ExtensionExtStructuralMetadataClassProperty& testClassProperty = - testClass.properties["TestClassProperty"]; - testClassProperty.type = - ExtensionExtStructuralMetadataClassProperty::Type::STRING; + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::STRING; testClassProperty.array = true; - ExtensionExtStructuralMetadataPropertyTable& propertyTable = - metadata.propertyTables.emplace_back(); + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); propertyTable.classProperty = "TestClass"; propertyTable.count = static_cast(expected.size()); - ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + PropertyTableProperty& propertyTableProperty = propertyTable.properties["TestClassProperty"]; propertyTableProperty.arrayOffsetType = - ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: - UINT32; + PropertyTableProperty::ArrayOffsetType::UINT32; propertyTableProperty.stringOffsetType = - ExtensionExtStructuralMetadataPropertyTableProperty::StringOffsetType:: - UINT32; + PropertyTableProperty::StringOffsetType::UINT32; propertyTableProperty.values = static_cast(valueBufferViewIndex); propertyTableProperty.arrayOffsets = static_cast(arrayOffsetBufferView); @@ -2604,12 +2471,10 @@ TEST_CASE("Test variable-length arrays of strings") { REQUIRE(view.status() == PropertyTableViewStatus::Valid); REQUIRE(view.size() == propertyTable.count); - const ExtensionExtStructuralMetadataClassProperty* classProperty = + const ClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE(classProperty); - REQUIRE( - classProperty->type == - ExtensionExtStructuralMetadataClassProperty::Type::STRING); + REQUIRE(classProperty->type == ClassProperty::Type::STRING); REQUIRE(classProperty->array); REQUIRE(!classProperty->componentType); REQUIRE(!classProperty->count); @@ -2631,8 +2496,7 @@ TEST_CASE("Test variable-length arrays of strings") { SECTION("Wrong array offset type") { propertyTableProperty.arrayOffsetType = - ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: - UINT8; + PropertyTableProperty::ArrayOffsetType::UINT8; PropertyTablePropertyView> arrayProperty = view.getPropertyView>( @@ -2643,8 +2507,7 @@ TEST_CASE("Test variable-length arrays of strings") { ErrorBufferViewSizeDoesNotMatchPropertyTableCount); propertyTableProperty.arrayOffsetType = - ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: - UINT16; + PropertyTableProperty::ArrayOffsetType::UINT16; arrayProperty = view.getPropertyView>( "TestClassProperty"); REQUIRE( @@ -2659,14 +2522,12 @@ TEST_CASE("Test variable-length arrays of strings") { arrayProperty.status() == PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); propertyTableProperty.arrayOffsetType = - ExtensionExtStructuralMetadataPropertyTableProperty::ArrayOffsetType:: - UINT32; + PropertyTableProperty::ArrayOffsetType::UINT32; } SECTION("Wrong string offset type") { propertyTableProperty.stringOffsetType = - ExtensionExtStructuralMetadataPropertyTableProperty::StringOffsetType:: - UINT8; + PropertyTableProperty::StringOffsetType::UINT8; PropertyTablePropertyView> arrayProperty = view.getPropertyView>( @@ -2677,8 +2538,7 @@ TEST_CASE("Test variable-length arrays of strings") { ErrorBufferViewSizeDoesNotMatchPropertyTableCount); propertyTableProperty.stringOffsetType = - ExtensionExtStructuralMetadataPropertyTableProperty::StringOffsetType:: - UINT16; + PropertyTableProperty::StringOffsetType::UINT16; arrayProperty = view.getPropertyView>( "TestClassProperty"); REQUIRE( @@ -2693,8 +2553,7 @@ TEST_CASE("Test variable-length arrays of strings") { arrayProperty.status() == PropertyTablePropertyViewStatus::ErrorInvalidStringOffsetType); propertyTableProperty.stringOffsetType = - ExtensionExtStructuralMetadataPropertyTableProperty::StringOffsetType:: - UINT32; + PropertyTableProperty::StringOffsetType::UINT32; } SECTION("Array offset values are not sorted ascending") { @@ -2774,12 +2633,11 @@ TEST_CASE("Test callback on invalid property table view") { metadata.schema.emplace(); // Property table has a nonexistent class. - ExtensionExtStructuralMetadataPropertyTable& propertyTable = - metadata.propertyTables.emplace_back(); + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); propertyTable.classProperty = "TestClass"; propertyTable.count = static_cast(5); - ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + PropertyTableProperty& propertyTableProperty = propertyTable.properties["TestClassProperty"]; propertyTableProperty.values = static_cast(-1); @@ -2787,7 +2645,7 @@ TEST_CASE("Test callback on invalid property table view") { REQUIRE(view.status() == PropertyTableViewStatus::ErrorClassNotFound); REQUIRE(view.size() == 0); - const ExtensionExtStructuralMetadataClassProperty* classProperty = + const ClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE(!classProperty); @@ -2812,21 +2670,17 @@ TEST_CASE("Test callback for invalid property") { ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); - ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); - ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; - ExtensionExtStructuralMetadataClassProperty& testClassProperty = - testClass.properties["InvalidProperty"]; - testClassProperty.type = - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR; - testClassProperty.componentType = - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT32; - - ExtensionExtStructuralMetadataPropertyTable& propertyTable = - metadata.propertyTables.emplace_back(); + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["InvalidProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::UINT32; + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); propertyTable.classProperty = "TestClass"; propertyTable.count = static_cast(5); - ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + PropertyTableProperty& propertyTableProperty = propertyTable.properties["InvalidProperty"]; propertyTableProperty.values = static_cast(-1); @@ -2834,8 +2688,7 @@ TEST_CASE("Test callback for invalid property") { REQUIRE(view.status() == PropertyTableViewStatus::Valid); REQUIRE(view.size() == propertyTable.count); - const ExtensionExtStructuralMetadataClassProperty* classProperty = - view.getClassProperty("InvalidProperty"); + const ClassProperty* classProperty = view.getClassProperty("InvalidProperty"); REQUIRE(classProperty); classProperty = view.getClassProperty("NonexistentProperty"); @@ -2884,21 +2737,17 @@ TEST_CASE("Test callback for scalar property") { ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); - ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); - ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; - ExtensionExtStructuralMetadataClassProperty& testClassProperty = - testClass.properties["TestClassProperty"]; - testClassProperty.type = - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR; - testClassProperty.componentType = - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT32; - - ExtensionExtStructuralMetadataPropertyTable& propertyTable = - metadata.propertyTables.emplace_back(); + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::UINT32; + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); propertyTable.classProperty = "TestClass"; propertyTable.count = static_cast(values.size()); - ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + PropertyTableProperty& propertyTableProperty = propertyTable.properties["TestClassProperty"]; propertyTableProperty.values = static_cast(valueBufferViewIndex); @@ -2906,15 +2755,11 @@ TEST_CASE("Test callback for scalar property") { REQUIRE(view.status() == PropertyTableViewStatus::Valid); REQUIRE(view.size() == propertyTable.count); - const ExtensionExtStructuralMetadataClassProperty* classProperty = + const ClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE(classProperty); - REQUIRE( - classProperty->type == - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR); - REQUIRE( - classProperty->componentType == - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT32); + REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT32); REQUIRE(!classProperty->array); REQUIRE(classProperty->count == std::nullopt); @@ -2980,21 +2825,17 @@ TEST_CASE("Test callback for vecN property") { ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); - ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); - ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; - ExtensionExtStructuralMetadataClassProperty& testClassProperty = - testClass.properties["TestClassProperty"]; - testClassProperty.type = - ExtensionExtStructuralMetadataClassProperty::Type::VEC3; - testClassProperty.componentType = - ExtensionExtStructuralMetadataClassProperty::ComponentType::INT32; - - ExtensionExtStructuralMetadataPropertyTable& propertyTable = - metadata.propertyTables.emplace_back(); + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::VEC3; + testClassProperty.componentType = ClassProperty::ComponentType::INT32; + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); propertyTable.classProperty = "TestClass"; propertyTable.count = static_cast(values.size()); - ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + PropertyTableProperty& propertyTableProperty = propertyTable.properties["TestClassProperty"]; propertyTableProperty.values = static_cast(valueBufferViewIndex); @@ -3002,15 +2843,11 @@ TEST_CASE("Test callback for vecN property") { REQUIRE(view.status() == PropertyTableViewStatus::Valid); REQUIRE(view.size() == propertyTable.count); - const ExtensionExtStructuralMetadataClassProperty* classProperty = + const ClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE(classProperty); - REQUIRE( - classProperty->type == - ExtensionExtStructuralMetadataClassProperty::Type::VEC3); - REQUIRE( - classProperty->componentType == - ExtensionExtStructuralMetadataClassProperty::ComponentType::INT32); + REQUIRE(classProperty->type == ClassProperty::Type::VEC3); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT32); REQUIRE(classProperty->count == std::nullopt); REQUIRE(!classProperty->array); @@ -3086,21 +2923,17 @@ TEST_CASE("Test callback for matN property") { ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); - ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); - ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; - ExtensionExtStructuralMetadataClassProperty& testClassProperty = - testClass.properties["TestClassProperty"]; - testClassProperty.type = - ExtensionExtStructuralMetadataClassProperty::Type::MAT2; - testClassProperty.componentType = - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT32; - - ExtensionExtStructuralMetadataPropertyTable& propertyTable = - metadata.propertyTables.emplace_back(); + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::MAT2; + testClassProperty.componentType = ClassProperty::ComponentType::UINT32; + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); propertyTable.classProperty = "TestClass"; propertyTable.count = static_cast(values.size()); - ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + PropertyTableProperty& propertyTableProperty = propertyTable.properties["TestClassProperty"]; propertyTableProperty.values = static_cast(valueBufferViewIndex); @@ -3108,15 +2941,11 @@ TEST_CASE("Test callback for matN property") { REQUIRE(view.status() == PropertyTableViewStatus::Valid); REQUIRE(view.size() == propertyTable.count); - const ExtensionExtStructuralMetadataClassProperty* classProperty = + const ClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE(classProperty); - REQUIRE( - classProperty->type == - ExtensionExtStructuralMetadataClassProperty::Type::MAT2); - REQUIRE( - classProperty->componentType == - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT32); + REQUIRE(classProperty->type == ClassProperty::Type::MAT2); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT32); REQUIRE(classProperty->count == std::nullopt); REQUIRE(!classProperty->array); @@ -3191,19 +3020,16 @@ TEST_CASE("Test callback for boolean property") { ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); - ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); - ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; - ExtensionExtStructuralMetadataClassProperty& testClassProperty = - testClass.properties["TestClassProperty"]; - testClassProperty.type = - ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN; + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::BOOLEAN; - ExtensionExtStructuralMetadataPropertyTable& propertyTable = - metadata.propertyTables.emplace_back(); + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); propertyTable.classProperty = "TestClass"; propertyTable.count = static_cast(instanceCount); - ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + PropertyTableProperty& propertyTableProperty = propertyTable.properties["TestClassProperty"]; propertyTableProperty.values = static_cast(model.bufferViews.size() - 1); @@ -3212,12 +3038,10 @@ TEST_CASE("Test callback for boolean property") { REQUIRE(view.status() == PropertyTableViewStatus::Valid); REQUIRE(view.size() == propertyTable.count); - const ExtensionExtStructuralMetadataClassProperty* classProperty = + const ClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE(classProperty); - REQUIRE( - classProperty->type == - ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN); + REQUIRE(classProperty->type == ClassProperty::Type::BOOLEAN); REQUIRE(classProperty->componentType == std::nullopt); REQUIRE(classProperty->count == std::nullopt); REQUIRE(!classProperty->array); @@ -3310,23 +3134,19 @@ TEST_CASE("Test callback for string property") { ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); - ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); - ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; - ExtensionExtStructuralMetadataClassProperty& testClassProperty = - testClass.properties["TestClassProperty"]; - testClassProperty.type = - ExtensionExtStructuralMetadataClassProperty::Type::STRING; + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::STRING; - ExtensionExtStructuralMetadataPropertyTable& propertyTable = - metadata.propertyTables.emplace_back(); + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); propertyTable.classProperty = "TestClass"; propertyTable.count = static_cast(expected.size()); - ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + PropertyTableProperty& propertyTableProperty = propertyTable.properties["TestClassProperty"]; propertyTableProperty.stringOffsetType = - ExtensionExtStructuralMetadataPropertyTableProperty::StringOffsetType:: - UINT32; + PropertyTableProperty::StringOffsetType::UINT32; propertyTableProperty.values = static_cast(valueBufferViewIndex); propertyTableProperty.stringOffsets = static_cast(offsetBufferViewIndex); @@ -3335,12 +3155,10 @@ TEST_CASE("Test callback for string property") { REQUIRE(view.status() == PropertyTableViewStatus::Valid); REQUIRE(view.size() == propertyTable.count); - const ExtensionExtStructuralMetadataClassProperty* classProperty = + const ClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE(classProperty); - REQUIRE( - classProperty->type == - ExtensionExtStructuralMetadataClassProperty::Type::STRING); + REQUIRE(classProperty->type == ClassProperty::Type::STRING); REQUIRE(classProperty->componentType == std::nullopt); REQUIRE(classProperty->count == std::nullopt); REQUIRE(!classProperty->array); @@ -3399,24 +3217,20 @@ TEST_CASE("Test callback for scalar array") { ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); - ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); - ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; - ExtensionExtStructuralMetadataClassProperty& testClassProperty = - testClass.properties["TestClassProperty"]; - testClassProperty.type = - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR; - testClassProperty.componentType = - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT32; + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::UINT32; testClassProperty.array = true; testClassProperty.count = 3; - ExtensionExtStructuralMetadataPropertyTable& propertyTable = - metadata.propertyTables.emplace_back(); + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); propertyTable.classProperty = "TestClass"; propertyTable.count = static_cast( values.size() / static_cast(testClassProperty.count.value())); - ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + PropertyTableProperty& propertyTableProperty = propertyTable.properties["TestClassProperty"]; propertyTableProperty.values = static_cast(model.bufferViews.size() - 1); @@ -3425,15 +3239,11 @@ TEST_CASE("Test callback for scalar array") { REQUIRE(view.status() == PropertyTableViewStatus::Valid); REQUIRE(view.size() == propertyTable.count); - const ExtensionExtStructuralMetadataClassProperty* classProperty = + const ClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE(classProperty); - REQUIRE( - classProperty->type == - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR); - REQUIRE( - classProperty->componentType == - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT32); + REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT32); REQUIRE(classProperty->array); REQUIRE(classProperty->count == 3); @@ -3499,24 +3309,20 @@ TEST_CASE("Test callback for vecN array") { ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); - ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); - ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; - ExtensionExtStructuralMetadataClassProperty& testClassProperty = - testClass.properties["TestClassProperty"]; - testClassProperty.type = - ExtensionExtStructuralMetadataClassProperty::Type::VEC3; - testClassProperty.componentType = - ExtensionExtStructuralMetadataClassProperty::ComponentType::INT32; + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::VEC3; + testClassProperty.componentType = ClassProperty::ComponentType::INT32; testClassProperty.array = true; testClassProperty.count = 2; - ExtensionExtStructuralMetadataPropertyTable& propertyTable = - metadata.propertyTables.emplace_back(); + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); propertyTable.classProperty = "TestClass"; propertyTable.count = static_cast( values.size() / static_cast(testClassProperty.count.value())); - ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + PropertyTableProperty& propertyTableProperty = propertyTable.properties["TestClassProperty"]; propertyTableProperty.values = static_cast(model.bufferViews.size() - 1); @@ -3525,15 +3331,11 @@ TEST_CASE("Test callback for vecN array") { REQUIRE(view.status() == PropertyTableViewStatus::Valid); REQUIRE(view.size() == propertyTable.count); - const ExtensionExtStructuralMetadataClassProperty* classProperty = + const ClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE(classProperty); - REQUIRE( - classProperty->type == - ExtensionExtStructuralMetadataClassProperty::Type::VEC3); - REQUIRE( - classProperty->componentType == - ExtensionExtStructuralMetadataClassProperty::ComponentType::INT32); + REQUIRE(classProperty->type == ClassProperty::Type::VEC3); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT32); REQUIRE(classProperty->array); REQUIRE(classProperty->count == 2); @@ -3613,24 +3415,20 @@ TEST_CASE("Test callback for matN array") { ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); - ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); - ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; - ExtensionExtStructuralMetadataClassProperty& testClassProperty = - testClass.properties["TestClassProperty"]; - testClassProperty.type = - ExtensionExtStructuralMetadataClassProperty::Type::MAT2; - testClassProperty.componentType = - ExtensionExtStructuralMetadataClassProperty::ComponentType::INT32; + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::MAT2; + testClassProperty.componentType = ClassProperty::ComponentType::INT32; testClassProperty.array = true; testClassProperty.count = 2; - ExtensionExtStructuralMetadataPropertyTable& propertyTable = - metadata.propertyTables.emplace_back(); + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); propertyTable.classProperty = "TestClass"; propertyTable.count = static_cast( values.size() / static_cast(testClassProperty.count.value())); - ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + PropertyTableProperty& propertyTableProperty = propertyTable.properties["TestClassProperty"]; propertyTableProperty.values = static_cast(model.bufferViews.size() - 1); @@ -3639,15 +3437,11 @@ TEST_CASE("Test callback for matN array") { REQUIRE(view.status() == PropertyTableViewStatus::Valid); REQUIRE(view.size() == propertyTable.count); - const ExtensionExtStructuralMetadataClassProperty* classProperty = + const ClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE(classProperty); - REQUIRE( - classProperty->type == - ExtensionExtStructuralMetadataClassProperty::Type::MAT2); - REQUIRE( - classProperty->componentType == - ExtensionExtStructuralMetadataClassProperty::ComponentType::INT32); + REQUIRE(classProperty->type == ClassProperty::Type::MAT2); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT32); REQUIRE(classProperty->array); REQUIRE(classProperty->count == 2); @@ -3729,22 +3523,19 @@ TEST_CASE("Test callback for boolean array") { ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); - ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); - ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; - ExtensionExtStructuralMetadataClassProperty& testClassProperty = - testClass.properties["TestClassProperty"]; - testClassProperty.type = - ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN; + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::BOOLEAN; testClassProperty.array = true; testClassProperty.count = 3; - ExtensionExtStructuralMetadataPropertyTable& propertyTable = - metadata.propertyTables.emplace_back(); + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); propertyTable.classProperty = "TestClass"; propertyTable.count = static_cast( expected.size() / static_cast(testClassProperty.count.value())); - ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + PropertyTableProperty& propertyTableProperty = propertyTable.properties["TestClassProperty"]; propertyTableProperty.values = static_cast(model.bufferViews.size() - 1); @@ -3753,12 +3544,10 @@ TEST_CASE("Test callback for boolean array") { REQUIRE(view.status() == PropertyTableViewStatus::Valid); REQUIRE(view.size() == propertyTable.count); - const ExtensionExtStructuralMetadataClassProperty* classProperty = + const ClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE(classProperty); - REQUIRE( - classProperty->type == - ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN); + REQUIRE(classProperty->type == ClassProperty::Type::BOOLEAN); REQUIRE(classProperty->array); REQUIRE(classProperty->count == 3); @@ -3850,26 +3639,22 @@ TEST_CASE("Test callback for array of strings") { ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); - ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); - ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; - ExtensionExtStructuralMetadataClassProperty& testClassProperty = - testClass.properties["TestClassProperty"]; - testClassProperty.type = - ExtensionExtStructuralMetadataClassProperty::Type::STRING; + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::STRING; testClassProperty.array = true; testClassProperty.count = 2; - ExtensionExtStructuralMetadataPropertyTable& propertyTable = - metadata.propertyTables.emplace_back(); + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); propertyTable.classProperty = "TestClass"; propertyTable.count = static_cast( expected.size() / static_cast(testClassProperty.count.value())); - ExtensionExtStructuralMetadataPropertyTableProperty& propertyTableProperty = + PropertyTableProperty& propertyTableProperty = propertyTable.properties["TestClassProperty"]; propertyTableProperty.stringOffsetType = - ExtensionExtStructuralMetadataPropertyTableProperty::StringOffsetType:: - UINT32; + PropertyTableProperty::StringOffsetType::UINT32; propertyTableProperty.values = static_cast(valueBufferViewIndex); propertyTableProperty.stringOffsets = static_cast(offsetBufferViewIndex); @@ -3878,12 +3663,10 @@ TEST_CASE("Test callback for array of strings") { REQUIRE(view.status() == PropertyTableViewStatus::Valid); REQUIRE(view.size() == propertyTable.count); - const ExtensionExtStructuralMetadataClassProperty* classProperty = + const ClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE(classProperty); - REQUIRE( - classProperty->type == - ExtensionExtStructuralMetadataClassProperty::Type::STRING); + REQUIRE(classProperty->type == ClassProperty::Type::STRING); REQUIRE(classProperty->array); REQUIRE(classProperty->count == 2); diff --git a/CesiumGltf/test/TestPropertyTexturePropertyView.cpp b/CesiumGltf/test/TestPropertyTexturePropertyView.cpp index e6d96520e..12ddfd3bf 100644 --- a/CesiumGltf/test/TestPropertyTexturePropertyView.cpp +++ b/CesiumGltf/test/TestPropertyTexturePropertyView.cpp @@ -17,20 +17,17 @@ TEST_CASE( ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); - ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); - ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; - ExtensionExtStructuralMetadataClassProperty& testClassProperty = - testClass.properties["TestClassProperty"]; - testClassProperty.type = - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR; - testClassProperty.componentType = - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT8; - - ExtensionExtStructuralMetadataPropertyTexture propertyTexture; + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::UINT8; + + PropertyTexture propertyTexture; propertyTexture.classProperty = "TestClass"; - ExtensionExtStructuralMetadataPropertyTextureProperty& - propertyTextureProperty = propertyTexture.properties["TestClassProperty"]; + PropertyTextureProperty& propertyTextureProperty = + propertyTexture.properties["TestClassProperty"]; propertyTextureProperty.index = -1; propertyTextureProperty.texCoord = 0; propertyTextureProperty.channels = {0}; @@ -49,14 +46,11 @@ TEST_CASE( ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); - ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); - ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; - ExtensionExtStructuralMetadataClassProperty& testClassProperty = - testClass.properties["TestClassProperty"]; - testClassProperty.type = - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR; - testClassProperty.componentType = - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT8; + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::UINT8; Image& image = model.images.emplace_back(); image.cesium.width = 1; @@ -65,11 +59,11 @@ TEST_CASE( texture.sampler = -1; texture.source = 0; - ExtensionExtStructuralMetadataPropertyTexture propertyTexture; + PropertyTexture propertyTexture; propertyTexture.classProperty = "TestClass"; - ExtensionExtStructuralMetadataPropertyTextureProperty& - propertyTextureProperty = propertyTexture.properties["TestClassProperty"]; + PropertyTextureProperty& propertyTextureProperty = + propertyTexture.properties["TestClassProperty"]; propertyTextureProperty.index = 0; propertyTextureProperty.texCoord = 0; propertyTextureProperty.channels = {0}; @@ -89,14 +83,11 @@ TEST_CASE( ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); - ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); - ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; - ExtensionExtStructuralMetadataClassProperty& testClassProperty = - testClass.properties["TestClassProperty"]; - testClassProperty.type = - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR; - testClassProperty.componentType = - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT8; + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::UINT8; model.samplers.emplace_back(); @@ -104,11 +95,11 @@ TEST_CASE( texture.sampler = 0; texture.source = -1; - ExtensionExtStructuralMetadataPropertyTexture propertyTexture; + PropertyTexture propertyTexture; propertyTexture.classProperty = "TestClass"; - ExtensionExtStructuralMetadataPropertyTextureProperty& - propertyTextureProperty = propertyTexture.properties["TestClassProperty"]; + PropertyTextureProperty& propertyTextureProperty = + propertyTexture.properties["TestClassProperty"]; propertyTextureProperty.index = 0; propertyTextureProperty.texCoord = 0; propertyTextureProperty.channels = {0}; @@ -126,14 +117,11 @@ TEST_CASE("Test PropertyTexturePropertyView on property with empty image") { ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); - ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); - ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; - ExtensionExtStructuralMetadataClassProperty& testClassProperty = - testClass.properties["TestClassProperty"]; - testClassProperty.type = - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR; - testClassProperty.componentType = - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT8; + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::UINT8; Image& image = model.images.emplace_back(); image.cesium.width = 0; @@ -145,11 +133,11 @@ TEST_CASE("Test PropertyTexturePropertyView on property with empty image") { texture.sampler = 0; texture.source = 0; - ExtensionExtStructuralMetadataPropertyTexture propertyTexture; + PropertyTexture propertyTexture; propertyTexture.classProperty = "TestClass"; - ExtensionExtStructuralMetadataPropertyTextureProperty& - propertyTextureProperty = propertyTexture.properties["TestClassProperty"]; + PropertyTextureProperty& propertyTextureProperty = + propertyTexture.properties["TestClassProperty"]; propertyTextureProperty.index = 0; propertyTextureProperty.texCoord = 0; propertyTextureProperty.channels = {0}; @@ -167,14 +155,11 @@ TEST_CASE("Test PropertyTextureView on property table property with negative " ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); - ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); - ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; - ExtensionExtStructuralMetadataClassProperty& testClassProperty = - testClass.properties["TestClassProperty"]; - testClassProperty.type = - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR; - testClassProperty.componentType = - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT8; + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::UINT8; Image& image = model.images.emplace_back(); image.cesium.width = 1; @@ -186,11 +171,11 @@ TEST_CASE("Test PropertyTextureView on property table property with negative " texture.sampler = 0; texture.source = 0; - ExtensionExtStructuralMetadataPropertyTexture propertyTexture; + PropertyTexture propertyTexture; propertyTexture.classProperty = "TestClass"; - ExtensionExtStructuralMetadataPropertyTextureProperty& - propertyTextureProperty = propertyTexture.properties["TestClassProperty"]; + PropertyTextureProperty& propertyTextureProperty = + propertyTexture.properties["TestClassProperty"]; propertyTextureProperty.index = 0; propertyTextureProperty.texCoord = -1; propertyTextureProperty.channels = {0}; @@ -210,14 +195,11 @@ TEST_CASE("Test PropertyTextureView on property texture property with zero " ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); - ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); - ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; - ExtensionExtStructuralMetadataClassProperty& testClassProperty = - testClass.properties["TestClassProperty"]; - testClassProperty.type = - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR; - testClassProperty.componentType = - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT8; + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::UINT8; Image& image = model.images.emplace_back(); image.cesium.width = 1; @@ -230,11 +212,11 @@ TEST_CASE("Test PropertyTextureView on property texture property with zero " texture.sampler = 0; texture.source = 0; - ExtensionExtStructuralMetadataPropertyTexture propertyTexture; + PropertyTexture propertyTexture; propertyTexture.classProperty = "TestClass"; - ExtensionExtStructuralMetadataPropertyTextureProperty& - propertyTextureProperty = propertyTexture.properties["TestClassProperty"]; + PropertyTextureProperty& propertyTextureProperty = + propertyTexture.properties["TestClassProperty"]; propertyTextureProperty.index = 0; propertyTextureProperty.texCoord = 0; propertyTextureProperty.channels = {}; @@ -253,14 +235,11 @@ TEST_CASE("Test PropertyTextureView on property texture property with too many " ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); - ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); - ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; - ExtensionExtStructuralMetadataClassProperty& testClassProperty = - testClass.properties["TestClassProperty"]; - testClassProperty.type = - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR; - testClassProperty.componentType = - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT8; + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::UINT8; Image& image = model.images.emplace_back(); image.cesium.width = 1; @@ -273,11 +252,11 @@ TEST_CASE("Test PropertyTextureView on property texture property with too many " texture.sampler = 0; texture.source = 0; - ExtensionExtStructuralMetadataPropertyTexture propertyTexture; + PropertyTexture propertyTexture; propertyTexture.classProperty = "TestClass"; - ExtensionExtStructuralMetadataPropertyTextureProperty& - propertyTextureProperty = propertyTexture.properties["TestClassProperty"]; + PropertyTextureProperty& propertyTextureProperty = + propertyTexture.properties["TestClassProperty"]; propertyTextureProperty.index = 0; propertyTextureProperty.texCoord = 0; propertyTextureProperty.channels = {0, 1}; diff --git a/CesiumGltf/test/TestPropertyTextureView.cpp b/CesiumGltf/test/TestPropertyTextureView.cpp index 6ca05cad2..514a979e6 100644 --- a/CesiumGltf/test/TestPropertyTextureView.cpp +++ b/CesiumGltf/test/TestPropertyTextureView.cpp @@ -16,11 +16,11 @@ TEST_CASE("Test PropertyTextureView on model without EXT_structural_metadata " Model model; // Create an erroneously isolated property texture. - ExtensionExtStructuralMetadataPropertyTexture propertyTexture; + PropertyTexture propertyTexture; propertyTexture.classProperty = "TestClass"; - ExtensionExtStructuralMetadataPropertyTextureProperty& - propertyTextureProperty = propertyTexture.properties["TestClassProperty"]; + PropertyTextureProperty& propertyTextureProperty = + propertyTexture.properties["TestClassProperty"]; propertyTextureProperty.index = 0; propertyTextureProperty.texCoord = 0; propertyTextureProperty.channels = {0}; @@ -31,7 +31,7 @@ TEST_CASE("Test PropertyTextureView on model without EXT_structural_metadata " PropertyTextureViewStatus::ErrorMissingMetadataExtension); REQUIRE(view.getProperties().empty()); - const ExtensionExtStructuralMetadataClassProperty* classProperty = + const ClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE(!classProperty); } @@ -42,12 +42,11 @@ TEST_CASE("Test PropertyTextureView on model without metadata schema") { ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); - ExtensionExtStructuralMetadataPropertyTexture& propertyTexture = - metadata.propertyTextures.emplace_back(); + PropertyTexture& propertyTexture = metadata.propertyTextures.emplace_back(); propertyTexture.classProperty = "TestClass"; - ExtensionExtStructuralMetadataPropertyTextureProperty& - propertyTextureProperty = propertyTexture.properties["TestClassProperty"]; + PropertyTextureProperty& propertyTextureProperty = + propertyTexture.properties["TestClassProperty"]; propertyTextureProperty.index = 0; propertyTextureProperty.texCoord = 0; propertyTextureProperty.channels = {0}; @@ -56,7 +55,7 @@ TEST_CASE("Test PropertyTextureView on model without metadata schema") { REQUIRE(view.status() == PropertyTextureViewStatus::ErrorMissingSchema); REQUIRE(view.getProperties().empty()); - const ExtensionExtStructuralMetadataClassProperty* classProperty = + const ClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE(!classProperty); } @@ -67,21 +66,17 @@ TEST_CASE("Test property texture with nonexistent class") { ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); - ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); - ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; - ExtensionExtStructuralMetadataClassProperty& testClassProperty = - testClass.properties["TestClassProperty"]; - testClassProperty.type = - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR; - testClassProperty.componentType = - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT8; - - ExtensionExtStructuralMetadataPropertyTexture& propertyTexture = - metadata.propertyTextures.emplace_back(); + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::UINT8; + + PropertyTexture& propertyTexture = metadata.propertyTextures.emplace_back(); propertyTexture.classProperty = "I Don't Exist"; - ExtensionExtStructuralMetadataPropertyTextureProperty& - propertyTextureProperty = propertyTexture.properties["TestClassProperty"]; + PropertyTextureProperty& propertyTextureProperty = + propertyTexture.properties["TestClassProperty"]; propertyTextureProperty.index = 0; propertyTextureProperty.texCoord = 0; propertyTextureProperty.channels = {0}; @@ -90,7 +85,7 @@ TEST_CASE("Test property texture with nonexistent class") { REQUIRE(view.status() == PropertyTextureViewStatus::ErrorClassNotFound); REQUIRE(view.getProperties().empty()); - const ExtensionExtStructuralMetadataClassProperty* classProperty = + const ClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE(!classProperty); } @@ -101,21 +96,17 @@ TEST_CASE("Test property texture with nonexistent class property") { ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); - ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); - ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; - ExtensionExtStructuralMetadataClassProperty& testClassProperty = - testClass.properties["TestClassProperty"]; - testClassProperty.type = - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR; - testClassProperty.componentType = - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT8; - - ExtensionExtStructuralMetadataPropertyTexture& propertyTexture = - metadata.propertyTextures.emplace_back(); + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::UINT8; + + PropertyTexture& propertyTexture = metadata.propertyTextures.emplace_back(); propertyTexture.classProperty = "TestClass"; - ExtensionExtStructuralMetadataPropertyTextureProperty& - propertyTextureProperty = propertyTexture.properties["I Don't Exist"]; + PropertyTextureProperty& propertyTextureProperty = + propertyTexture.properties["I Don't Exist"]; propertyTextureProperty.index = 0; propertyTextureProperty.texCoord = 0; propertyTextureProperty.channels = {0}; @@ -125,7 +116,7 @@ TEST_CASE("Test property texture with nonexistent class property") { view.status() == PropertyTextureViewStatus::ErrorClassPropertyNotFound); REQUIRE(view.getProperties().empty()); - const ExtensionExtStructuralMetadataClassProperty* classProperty = + const ClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE(!classProperty); } @@ -136,21 +127,17 @@ TEST_CASE("Test property texture with invalid property") { ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); - ExtensionExtStructuralMetadataSchema& schema = metadata.schema.emplace(); - ExtensionExtStructuralMetadataClass& testClass = schema.classes["TestClass"]; - ExtensionExtStructuralMetadataClassProperty& testClassProperty = - testClass.properties["TestClassProperty"]; - testClassProperty.type = - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR; - testClassProperty.componentType = - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT8; - - ExtensionExtStructuralMetadataPropertyTexture& propertyTexture = - metadata.propertyTextures.emplace_back(); + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::UINT8; + + PropertyTexture& propertyTexture = metadata.propertyTextures.emplace_back(); propertyTexture.classProperty = "TestClass"; - ExtensionExtStructuralMetadataPropertyTextureProperty& - propertyTextureProperty = propertyTexture.properties["TestClassProperty"]; + PropertyTextureProperty& propertyTextureProperty = + propertyTexture.properties["TestClassProperty"]; propertyTextureProperty.index = -1; PropertyTextureView view(model, propertyTexture); @@ -162,7 +149,7 @@ TEST_CASE("Test property texture with invalid property") { PropertyTexturePropertyView& propertyView = properties["TestClassProperty"]; REQUIRE(propertyView.status() != PropertyTexturePropertyViewStatus::Valid); - const ExtensionExtStructuralMetadataClassProperty* classProperty = + const ClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE(!classProperty); } diff --git a/CesiumGltf/test/TestPropertyType.cpp b/CesiumGltf/test/TestPropertyType.cpp index 9f345f6aa..539d01a3b 100644 --- a/CesiumGltf/test/TestPropertyType.cpp +++ b/CesiumGltf/test/TestPropertyType.cpp @@ -1,5 +1,5 @@ -#include "CesiumGltf/ExtensionExtStructuralMetadataClassProperty.h" -#include "CesiumGltf/ExtensionExtStructuralMetadataPropertyTableProperty.h" +#include "CesiumGltf/ClassProperty.h" +#include "CesiumGltf/PropertyTableProperty.h" #include "CesiumGltf/PropertyType.h" #include @@ -9,53 +9,43 @@ using namespace CesiumGltf; TEST_CASE("Test PropertyType utilities function") { SECTION("Convert string to PropertyType") { REQUIRE( - convertStringToPropertyType( - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR) == + convertStringToPropertyType(ClassProperty::Type::SCALAR) == PropertyType::Scalar); REQUIRE( - convertStringToPropertyType( - ExtensionExtStructuralMetadataClassProperty::Type::VEC2) == + convertStringToPropertyType(ClassProperty::Type::VEC2) == PropertyType::Vec2); REQUIRE( - convertStringToPropertyType( - ExtensionExtStructuralMetadataClassProperty::Type::VEC3) == + convertStringToPropertyType(ClassProperty::Type::VEC3) == PropertyType::Vec3); REQUIRE( - convertStringToPropertyType( - ExtensionExtStructuralMetadataClassProperty::Type::VEC4) == + convertStringToPropertyType(ClassProperty::Type::VEC4) == PropertyType::Vec4); REQUIRE( - convertStringToPropertyType( - ExtensionExtStructuralMetadataClassProperty::Type::MAT2) == + convertStringToPropertyType(ClassProperty::Type::MAT2) == PropertyType::Mat2); REQUIRE( - convertStringToPropertyType( - ExtensionExtStructuralMetadataClassProperty::Type::MAT3) == + convertStringToPropertyType(ClassProperty::Type::MAT3) == PropertyType::Mat3); REQUIRE( - convertStringToPropertyType( - ExtensionExtStructuralMetadataClassProperty::Type::MAT4) == + convertStringToPropertyType(ClassProperty::Type::MAT4) == PropertyType::Mat4); REQUIRE( - convertStringToPropertyType( - ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN) == + convertStringToPropertyType(ClassProperty::Type::BOOLEAN) == PropertyType::Boolean); REQUIRE( - convertStringToPropertyType( - ExtensionExtStructuralMetadataClassProperty::Type::STRING) == + convertStringToPropertyType(ClassProperty::Type::STRING) == PropertyType::String); REQUIRE( - convertStringToPropertyType( - ExtensionExtStructuralMetadataClassProperty::Type::ENUM) == + convertStringToPropertyType(ClassProperty::Type::ENUM) == PropertyType::Enum); REQUIRE(convertStringToPropertyType("invalid") == PropertyType::Invalid); @@ -64,50 +54,49 @@ TEST_CASE("Test PropertyType utilities function") { SECTION("Convert string to PropertyComponentType") { REQUIRE( convertStringToPropertyComponentType( - ExtensionExtStructuralMetadataClassProperty::ComponentType:: - UINT8) == PropertyComponentType::Uint8); + ClassProperty::ComponentType::UINT8) == + PropertyComponentType::Uint8); REQUIRE( convertStringToPropertyComponentType( - ExtensionExtStructuralMetadataClassProperty::ComponentType::INT8) == - PropertyComponentType::Int8); + ClassProperty::ComponentType::INT8) == PropertyComponentType::Int8); REQUIRE( convertStringToPropertyComponentType( - ExtensionExtStructuralMetadataClassProperty::ComponentType:: - UINT16) == PropertyComponentType::Uint16); + ClassProperty::ComponentType::UINT16) == + PropertyComponentType::Uint16); REQUIRE( convertStringToPropertyComponentType( - ExtensionExtStructuralMetadataClassProperty::ComponentType:: - INT16) == PropertyComponentType::Int16); + ClassProperty::ComponentType::INT16) == + PropertyComponentType::Int16); REQUIRE( convertStringToPropertyComponentType( - ExtensionExtStructuralMetadataClassProperty::ComponentType:: - UINT32) == PropertyComponentType::Uint32); + ClassProperty::ComponentType::UINT32) == + PropertyComponentType::Uint32); REQUIRE( convertStringToPropertyComponentType( - ExtensionExtStructuralMetadataClassProperty::ComponentType:: - INT32) == PropertyComponentType::Int32); + ClassProperty::ComponentType::INT32) == + PropertyComponentType::Int32); REQUIRE( convertStringToPropertyComponentType( - ExtensionExtStructuralMetadataClassProperty::ComponentType:: - UINT64) == PropertyComponentType::Uint64); + ClassProperty::ComponentType::UINT64) == + PropertyComponentType::Uint64); REQUIRE( convertStringToPropertyComponentType( - ExtensionExtStructuralMetadataClassProperty::ComponentType:: - INT64) == PropertyComponentType::Int64); + ClassProperty::ComponentType::INT64) == + PropertyComponentType::Int64); REQUIRE( convertStringToPropertyComponentType( - ExtensionExtStructuralMetadataClassProperty::ComponentType:: - FLOAT32) == PropertyComponentType::Float32); + ClassProperty::ComponentType::FLOAT32) == + PropertyComponentType::Float32); REQUIRE( convertStringToPropertyComponentType( - ExtensionExtStructuralMetadataClassProperty::ComponentType:: - FLOAT64) == PropertyComponentType::Float64); + ClassProperty::ComponentType::FLOAT64) == + PropertyComponentType::Float64); REQUIRE( convertStringToPropertyComponentType("invalid") == @@ -117,107 +106,107 @@ TEST_CASE("Test PropertyType utilities function") { SECTION("Convert PropertyType to string") { REQUIRE( convertPropertyTypeToString(PropertyType::Scalar) == - ExtensionExtStructuralMetadataClassProperty::Type::SCALAR); + ClassProperty::Type::SCALAR); REQUIRE( convertPropertyTypeToString(PropertyType::Vec2) == - ExtensionExtStructuralMetadataClassProperty::Type::VEC2); + ClassProperty::Type::VEC2); REQUIRE( convertPropertyTypeToString(PropertyType::Vec3) == - ExtensionExtStructuralMetadataClassProperty::Type::VEC3); + ClassProperty::Type::VEC3); REQUIRE( convertPropertyTypeToString(PropertyType::Vec4) == - ExtensionExtStructuralMetadataClassProperty::Type::VEC4); + ClassProperty::Type::VEC4); REQUIRE( convertPropertyTypeToString(PropertyType::Mat2) == - ExtensionExtStructuralMetadataClassProperty::Type::MAT2); + ClassProperty::Type::MAT2); REQUIRE( convertPropertyTypeToString(PropertyType::Mat3) == - ExtensionExtStructuralMetadataClassProperty::Type::MAT3); + ClassProperty::Type::MAT3); REQUIRE( convertPropertyTypeToString(PropertyType::Mat4) == - ExtensionExtStructuralMetadataClassProperty::Type::MAT4); + ClassProperty::Type::MAT4); REQUIRE( convertPropertyTypeToString(PropertyType::Boolean) == - ExtensionExtStructuralMetadataClassProperty::Type::BOOLEAN); + ClassProperty::Type::BOOLEAN); REQUIRE( convertPropertyTypeToString(PropertyType::String) == - ExtensionExtStructuralMetadataClassProperty::Type::STRING); + ClassProperty::Type::STRING); REQUIRE( convertPropertyTypeToString(PropertyType::Enum) == - ExtensionExtStructuralMetadataClassProperty::Type::ENUM); + ClassProperty::Type::ENUM); } SECTION("Convert PropertyComponentType to string") { REQUIRE( convertPropertyComponentTypeToString(PropertyComponentType::Uint8) == - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT8); + ClassProperty::ComponentType::UINT8); REQUIRE( convertPropertyComponentTypeToString(PropertyComponentType::Int8) == - ExtensionExtStructuralMetadataClassProperty::ComponentType::INT8); + ClassProperty::ComponentType::INT8); REQUIRE( convertPropertyComponentTypeToString(PropertyComponentType::Uint16) == - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT16); + ClassProperty::ComponentType::UINT16); REQUIRE( convertPropertyComponentTypeToString(PropertyComponentType::Int16) == - ExtensionExtStructuralMetadataClassProperty::ComponentType::INT16); + ClassProperty::ComponentType::INT16); REQUIRE( convertPropertyComponentTypeToString(PropertyComponentType::Uint32) == - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT32); + ClassProperty::ComponentType::UINT32); REQUIRE( convertPropertyComponentTypeToString(PropertyComponentType::Int32) == - ExtensionExtStructuralMetadataClassProperty::ComponentType::INT32); + ClassProperty::ComponentType::INT32); REQUIRE( convertPropertyComponentTypeToString(PropertyComponentType::Uint64) == - ExtensionExtStructuralMetadataClassProperty::ComponentType::UINT64); + ClassProperty::ComponentType::UINT64); REQUIRE( convertPropertyComponentTypeToString(PropertyComponentType::Int64) == - ExtensionExtStructuralMetadataClassProperty::ComponentType::INT64); + ClassProperty::ComponentType::INT64); REQUIRE( convertPropertyComponentTypeToString(PropertyComponentType::Float32) == - ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT32); + ClassProperty::ComponentType::FLOAT32); REQUIRE( convertPropertyComponentTypeToString(PropertyComponentType::Float64) == - ExtensionExtStructuralMetadataClassProperty::ComponentType::FLOAT64); + ClassProperty::ComponentType::FLOAT64); } SECTION("Convert array offset type string to PropertyComponentType") { REQUIRE( convertArrayOffsetTypeStringToPropertyComponentType( - ExtensionExtStructuralMetadataPropertyTableProperty:: - ArrayOffsetType::UINT8) == PropertyComponentType::Uint8); + PropertyTableProperty::ArrayOffsetType::UINT8) == + PropertyComponentType::Uint8); REQUIRE( convertArrayOffsetTypeStringToPropertyComponentType( - ExtensionExtStructuralMetadataPropertyTableProperty:: - ArrayOffsetType::UINT16) == PropertyComponentType::Uint16); + PropertyTableProperty::ArrayOffsetType::UINT16) == + PropertyComponentType::Uint16); REQUIRE( convertArrayOffsetTypeStringToPropertyComponentType( - ExtensionExtStructuralMetadataPropertyTableProperty:: - ArrayOffsetType::UINT32) == PropertyComponentType::Uint32); + PropertyTableProperty::ArrayOffsetType::UINT32) == + PropertyComponentType::Uint32); REQUIRE( convertArrayOffsetTypeStringToPropertyComponentType( - ExtensionExtStructuralMetadataPropertyTableProperty:: - ArrayOffsetType::UINT64) == PropertyComponentType::Uint64); + PropertyTableProperty::ArrayOffsetType::UINT64) == + PropertyComponentType::Uint64); REQUIRE( convertArrayOffsetTypeStringToPropertyComponentType("invalid") == @@ -227,23 +216,23 @@ TEST_CASE("Test PropertyType utilities function") { SECTION("Convert string offset type string to PropertyComponentType") { REQUIRE( convertStringOffsetTypeStringToPropertyComponentType( - ExtensionExtStructuralMetadataPropertyTableProperty:: - StringOffsetType::UINT8) == PropertyComponentType::Uint8); + PropertyTableProperty::StringOffsetType::UINT8) == + PropertyComponentType::Uint8); REQUIRE( convertStringOffsetTypeStringToPropertyComponentType( - ExtensionExtStructuralMetadataPropertyTableProperty:: - StringOffsetType::UINT16) == PropertyComponentType::Uint16); + PropertyTableProperty::StringOffsetType::UINT16) == + PropertyComponentType::Uint16); REQUIRE( convertStringOffsetTypeStringToPropertyComponentType( - ExtensionExtStructuralMetadataPropertyTableProperty:: - StringOffsetType::UINT32) == PropertyComponentType::Uint32); + PropertyTableProperty::StringOffsetType::UINT32) == + PropertyComponentType::Uint32); REQUIRE( convertStringOffsetTypeStringToPropertyComponentType( - ExtensionExtStructuralMetadataPropertyTableProperty:: - StringOffsetType::UINT64) == PropertyComponentType::Uint64); + PropertyTableProperty::StringOffsetType::UINT64) == + PropertyComponentType::Uint64); REQUIRE( convertStringOffsetTypeStringToPropertyComponentType("invalid") == From 0b8aecf710f0245059cda7a09a50fd69777f4e15 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Fri, 16 Jun 2023 16:41:07 -0400 Subject: [PATCH 079/421] Update changelog --- CHANGES.md | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 25ce81708..307298603 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -4,19 +4,20 @@ ##### Breaking Changes :mega: -- In `CesiumGltf`, all generated classes related to `EXT_feature_metadata` are now prefixed with `ExtensionExtFeatureMetadata`. For example, `ClassProperty` becomes `ExtensionExtFeatureMetadataClassProperty`. This also extends to the glTF reader and writer. +- In `CesiumGltf`, all generated classes related to `EXT_feature_metadata` are now prefixed with `ExtensionExtFeatureMetadata`. For example, `ClassProperty` has become `ExtensionExtFeatureMetadataClassProperty`. This also extends to the glTF reader and writer. +- In `CesiumGltf`, all generated classes related to `EXT_structural_metadata` have had their `ExtensionExtStructuralMetadata` prefix removed. For example, `ExtensionExtStructuralMetadataClassProperty` has become `ClassProperty`. This also extends to the glTF reader and writer. - In `CesiumGltf`, `ExtensionExtMeshFeaturesFeatureId` and `ExtensionExtMeshFeaturesFeatureIdTexture` have been renamed to `FeatureId` and `FeatureIdTexture` respectively. - Replaced `FeatureIDTextureView` with `FeatureIdTextureView`, which views a `FeatureIdTexture` in `EXT_mesh_features`. Feature ID textures from `EXT_feature_metadata` are no longer supported. - Renamed `FeatureIDTextureViewStatus` to `FeatureIdTextureViewStatus` for consistency. -- Replaced `MetadataFeatureTableView` with `PropertyTableView`, which views a `ExtensionExtStructuralMetadataPropertyTable`. - - Added `PropertyTableViewStatus` to indicate whether or not `PropertyTableView` is valid. +- Replaced `MetadataFeatureTableView` with `PropertyTableView`, which views a `PropertyTable` in `EXT_structural_metadata`. +- Added `PropertyTableViewStatus` to indicate whether a `PropertyTableView` is valid. - Renamed `MetadataArrayView` to `PropertyArrayView`. -- Replaced `FeatureTextureView` with `PropertyTextureView`, which views a `ExtensionExtMeshFeaturesPropertyTexture`. - - Replaced `FeatureTextureViewStatus` with `PropertyTextureViewStatus`. -- Replaced `FeatureTexturePropertyView` with `PropertyTexturePropertyView`, which views a `ExtensionExtMeshFeaturesPropertyTextureProperty`. - - Replaced `FeatureTexturePropertyViewStatus` with `PropertyTexturePropertyViewStatus`. -- Refactored the `PropertyType` enum to reflect the values of `type` in an `ExtensionExtStructuralMetadataClassProperty`. -- Added the `PropertyComponentType` enum to reflect the values of `componentType` in an `ExtensionExtStructuralMetadataClassProperty`. +- Replaced `FeatureTextureView` with `PropertyTextureView`, which views a `PropertyTexture` in `EXT_structural_metadata`. +- Renamed `FeatureTextureViewStatus` to `PropertyTextureViewStatus`. +- Replaced `FeatureTexturePropertyView` with `PropertyTexturePropertyView`, which views a `PropertyTextureProperty` in `EXT_structural_metadata`. +- Renamed `FeatureTexturePropertyViewStatus` to `PropertyTexturePropertyViewStatus`. +- Refactored `PropertyType` to reflect the values of `type` in a `ClassProperty` from `EXT_structural_metadata`. +- Added `PropertyComponentType` to reflect the values of `componentType` in a `ClassProperty` from `EXT_structural_metadata`. Additionally, views of the data contained by `EXT_feature_metadata` will no longer supported by Cesium Native. The extension will still be parsed, but it will log a warning. Batch tables will also be converted to `EXT_structural_metadata` instead of `EXT_feature_metadata`. From 037a718eb830d7b5224c189fd70f51bea26f18c9 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Fri, 16 Jun 2023 16:44:43 -0400 Subject: [PATCH 080/421] Formatting --- .../generated/src/registerExtensions.cpp | 123 ++++++++++++------ 1 file changed, 80 insertions(+), 43 deletions(-) diff --git a/CesiumGltfReader/generated/src/registerExtensions.cpp b/CesiumGltfReader/generated/src/registerExtensions.cpp index 1c46f4ba9..15deefecc 100644 --- a/CesiumGltfReader/generated/src/registerExtensions.cpp +++ b/CesiumGltfReader/generated/src/registerExtensions.cpp @@ -3,61 +3,98 @@ #include "registerExtensions.h" -#include - -#include -#include -#include -#include -#include -#include -#include -#include - +#include "ExtensionBufferExtMeshoptCompressionJsonHandler.h" +#include "ExtensionBufferViewExtMeshoptCompressionJsonHandler.h" #include "ExtensionCesiumRTCJsonHandler.h" -#include "ExtensionModelExtFeatureMetadataJsonHandler.h" -#include "ExtensionModelExtStructuralMetadataJsonHandler.h" -#include "ExtensionModelKhrMaterialsVariantsJsonHandler.h" -#include "ExtensionModelMaxarMeshVariantsJsonHandler.h" #include "ExtensionCesiumTileEdgesJsonHandler.h" -#include "ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler.h" -#include "ExtensionExtMeshFeaturesJsonHandler.h" -#include "ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler.h" -#include "ExtensionKhrDracoMeshCompressionJsonHandler.h" -#include "ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler.h" #include "ExtensionExtInstanceFeaturesJsonHandler.h" +#include "ExtensionExtMeshFeaturesJsonHandler.h" #include "ExtensionExtMeshGpuInstancingJsonHandler.h" -#include "ExtensionNodeMaxarMeshVariantsJsonHandler.h" -#include "ExtensionBufferExtMeshoptCompressionJsonHandler.h" -#include "ExtensionBufferViewExtMeshoptCompressionJsonHandler.h" +#include "ExtensionKhrDracoMeshCompressionJsonHandler.h" #include "ExtensionKhrMaterialsUnlitJsonHandler.h" #include "ExtensionKhrTextureBasisuJsonHandler.h" +#include "ExtensionKhrTextureTransformJsonHandler.h" +#include "ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler.h" +#include "ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler.h" +#include "ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler.h" +#include "ExtensionModelExtFeatureMetadataJsonHandler.h" +#include "ExtensionModelExtStructuralMetadataJsonHandler.h" +#include "ExtensionModelKhrMaterialsVariantsJsonHandler.h" +#include "ExtensionModelMaxarMeshVariantsJsonHandler.h" +#include "ExtensionNodeMaxarMeshVariantsJsonHandler.h" #include "ExtensionTextureWebpJsonHandler.h" -#include "ExtensionKhrTextureTransformJsonHandler.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include namespace CesiumGltfReader { void registerExtensions(CesiumJsonReader::ExtensionReaderContext& context) { (void)context; context.registerExtension(); -context.registerExtension(); -context.registerExtension(); -context.registerExtension(); -context.registerExtension(); -context.registerExtension(); -context.registerExtension(); -context.registerExtension(); -context.registerExtension(); -context.registerExtension(); -context.registerExtension(); -context.registerExtension(); -context.registerExtension(); -context.registerExtension(); -context.registerExtension(); -context.registerExtension(); -context.registerExtension(); -context.registerExtension(); -context.registerExtension(); -context.registerExtension(); + context.registerExtension< + CesiumGltf::Model, + ExtensionModelExtFeatureMetadataJsonHandler>(); + context.registerExtension< + CesiumGltf::Model, + ExtensionModelExtStructuralMetadataJsonHandler>(); + context.registerExtension< + CesiumGltf::Model, + ExtensionModelKhrMaterialsVariantsJsonHandler>(); + context.registerExtension< + CesiumGltf::Model, + ExtensionModelMaxarMeshVariantsJsonHandler>(); + context.registerExtension< + CesiumGltf::MeshPrimitive, + ExtensionCesiumTileEdgesJsonHandler>(); + context.registerExtension< + CesiumGltf::MeshPrimitive, + ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler>(); + context.registerExtension< + CesiumGltf::MeshPrimitive, + ExtensionExtMeshFeaturesJsonHandler>(); + context.registerExtension< + CesiumGltf::MeshPrimitive, + ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler>(); + context.registerExtension< + CesiumGltf::MeshPrimitive, + ExtensionKhrDracoMeshCompressionJsonHandler>(); + context.registerExtension< + CesiumGltf::MeshPrimitive, + ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler>(); + context.registerExtension< + CesiumGltf::Node, + ExtensionExtInstanceFeaturesJsonHandler>(); + context.registerExtension< + CesiumGltf::Node, + ExtensionExtMeshGpuInstancingJsonHandler>(); + context.registerExtension< + CesiumGltf::Node, + ExtensionNodeMaxarMeshVariantsJsonHandler>(); + context.registerExtension< + CesiumGltf::Buffer, + ExtensionBufferExtMeshoptCompressionJsonHandler>(); + context.registerExtension< + CesiumGltf::BufferView, + ExtensionBufferViewExtMeshoptCompressionJsonHandler>(); + context.registerExtension< + CesiumGltf::Material, + ExtensionKhrMaterialsUnlitJsonHandler>(); + context.registerExtension< + CesiumGltf::Texture, + ExtensionKhrTextureBasisuJsonHandler>(); + context.registerExtension< + CesiumGltf::Texture, + ExtensionTextureWebpJsonHandler>(); + context.registerExtension< + CesiumGltf::TextureInfo, + ExtensionKhrTextureTransformJsonHandler>(); } } // namespace CesiumGltfReader From ffc1ca8f65b9dd7034e9b53f33a125b6144acbae Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Fri, 16 Jun 2023 17:33:40 -0400 Subject: [PATCH 081/421] Use helper function in PropertyTableView tests --- CesiumGltf/test/TestPropertyTableView.cpp | 654 ++++------------------ 1 file changed, 94 insertions(+), 560 deletions(-) diff --git a/CesiumGltf/test/TestPropertyTableView.cpp b/CesiumGltf/test/TestPropertyTableView.cpp index d1984cd67..9516c72a6 100644 --- a/CesiumGltf/test/TestPropertyTableView.cpp +++ b/CesiumGltf/test/TestPropertyTableView.cpp @@ -6,6 +6,22 @@ using namespace CesiumGltf; +template +void addBufferToModel(Model& model, std::vector& values) { + Buffer& valueBuffer = model.buffers.emplace_back(); + valueBuffer.cesium.data.resize(values.size() * sizeof(T)); + valueBuffer.byteLength = static_cast(valueBuffer.cesium.data.size()); + std::memcpy( + valueBuffer.cesium.data.data(), + values.data(), + valueBuffer.cesium.data.size()); + + BufferView& valueBufferView = model.bufferViews.emplace_back(); + valueBufferView.buffer = static_cast(model.buffers.size() - 1); + valueBufferView.byteOffset = 0; + valueBufferView.byteLength = valueBuffer.byteLength; +} + TEST_CASE("Test PropertyTableView on model without EXT_structural_metadata " "extension") { Model model; @@ -83,31 +99,11 @@ TEST_CASE("Test property table with nonexistent class") { TEST_CASE("Test scalar property") { Model model; - std::vector values = {12, 34, 30, 11, 34, 34, 11, 33, 122, 33}; - size_t valueBufferIndex = 0; - size_t valueBufferViewIndex = 0; - - // Buffers are constructed in scope to ensure that the tests don't use their - // temporary variables. - { - Buffer& valueBuffer = model.buffers.emplace_back(); - valueBuffer.cesium.data.resize(values.size() * sizeof(uint32_t)); - valueBuffer.byteLength = - static_cast(valueBuffer.cesium.data.size()); - std::memcpy( - valueBuffer.cesium.data.data(), - values.data(), - valueBuffer.cesium.data.size()); - valueBufferIndex = model.buffers.size() - 1; - - BufferView& valueBufferView = model.bufferViews.emplace_back(); - valueBufferView.buffer = static_cast(model.buffers.size() - 1); - valueBufferView.byteOffset = 0; - valueBufferView.byteLength = valueBuffer.byteLength; - valueBufferViewIndex = model.bufferViews.size() - 1; - } + addBufferToModel(model, values); + size_t valueBufferIndex = model.buffers.size() - 1; + size_t valueBufferViewIndex = model.bufferViews.size() - 1; ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); @@ -253,35 +249,15 @@ TEST_CASE("Test scalar property") { TEST_CASE("Test vecN property") { Model model; - std::vector values = { glm::ivec3(-12, 34, 30), glm::ivec3(11, 73, 0), glm::ivec3(-2, 6, 12), glm::ivec3(-4, 8, -13)}; - size_t valueBufferIndex = 0; - size_t valueBufferViewIndex = 0; - - // Buffers are constructed in scope to ensure that the tests don't use their - // temporary variables. - { - Buffer& valueBuffer = model.buffers.emplace_back(); - valueBuffer.cesium.data.resize(values.size() * sizeof(glm::ivec3)); - valueBuffer.byteLength = - static_cast(valueBuffer.cesium.data.size()); - std::memcpy( - valueBuffer.cesium.data.data(), - values.data(), - valueBuffer.cesium.data.size()); - valueBufferIndex = model.buffers.size() - 1; - - BufferView& valueBufferView = model.bufferViews.emplace_back(); - valueBufferView.buffer = static_cast(model.buffers.size() - 1); - valueBufferView.byteOffset = 0; - valueBufferView.byteLength = valueBuffer.byteLength; - valueBufferViewIndex = model.bufferViews.size() - 1; - } + addBufferToModel(model, values); + size_t valueBufferIndex = model.buffers.size() - 1; + size_t valueBufferViewIndex = model.bufferViews.size() - 1; ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); @@ -434,7 +410,6 @@ TEST_CASE("Test vecN property") { TEST_CASE("Test matN property") { Model model; - // clang-format off std::vector values = { glm::u32mat2x2( @@ -451,28 +426,9 @@ TEST_CASE("Test matN property") { 3, 23)}; // clang-format on - size_t valueBufferIndex = 0; - size_t valueBufferViewIndex = 0; - - // Buffers are constructed in scope to ensure that the tests don't use their - // temporary variables. - { - Buffer& valueBuffer = model.buffers.emplace_back(); - valueBuffer.cesium.data.resize(values.size() * sizeof(glm::u32mat2x2)); - valueBuffer.byteLength = - static_cast(valueBuffer.cesium.data.size()); - std::memcpy( - valueBuffer.cesium.data.data(), - values.data(), - valueBuffer.cesium.data.size()); - valueBufferIndex = model.buffers.size() - 1; - - BufferView& valueBufferView = model.bufferViews.emplace_back(); - valueBufferView.buffer = static_cast(model.buffers.size() - 1); - valueBufferView.byteOffset = 0; - valueBufferView.byteLength = valueBuffer.byteLength; - valueBufferViewIndex = model.bufferViews.size() - 1; - } + addBufferToModel(model, values); + size_t valueBufferIndex = model.buffers.size() - 1; + size_t valueBufferViewIndex = model.bufferViews.size() - 1; ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); @@ -647,24 +603,7 @@ TEST_CASE("Test boolean property") { values[static_cast(byteIndex)] = static_cast( (expectedValue << bitIndex) | values[static_cast(byteIndex)]); } - - // Buffers are constructed in scope to ensure that the tests don't use their - // temporary variables. - { - Buffer& valueBuffer = model.buffers.emplace_back(); - valueBuffer.cesium.data.resize(values.size()); - valueBuffer.byteLength = - static_cast(valueBuffer.cesium.data.size()); - std::memcpy( - valueBuffer.cesium.data.data(), - values.data(), - valueBuffer.cesium.data.size()); - - BufferView& valueBufferView = model.bufferViews.emplace_back(); - valueBufferView.buffer = static_cast(model.buffers.size() - 1); - valueBufferView.byteOffset = 0; - valueBufferView.byteLength = valueBuffer.byteLength; - } + addBufferToModel(model, values); ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); @@ -740,37 +679,13 @@ TEST_CASE("Test string property") { offsetValue[i] + static_cast(expectedValue.size()); } - // Buffers are constructed in scope to ensure that the tests don't use their - // temporary variables. - size_t valueBufferIndex = 0; - size_t valueBufferViewIndex = 0; - { - Buffer& valueBuffer = model.buffers.emplace_back(); - valueBuffer.byteLength = static_cast(values.size()); - valueBuffer.cesium.data = std::move(values); - valueBufferIndex = model.buffers.size() - 1; + addBufferToModel(model, values); + size_t valueBufferIndex = model.buffers.size() - 1; + size_t valueBufferViewIndex = model.bufferViews.size() - 1; - BufferView& valueBufferView = model.bufferViews.emplace_back(); - valueBufferView.buffer = static_cast(valueBufferIndex); - valueBufferView.byteOffset = 0; - valueBufferView.byteLength = valueBuffer.byteLength; - valueBufferViewIndex = model.bufferViews.size() - 1; - } - - size_t offsetBufferIndex = 0; - size_t offsetBufferViewIndex = 0; - { - Buffer& offsetBuffer = model.buffers.emplace_back(); - offsetBuffer.byteLength = static_cast(stringOffsets.size()); - offsetBuffer.cesium.data = std::move(stringOffsets); - offsetBufferIndex = model.buffers.size() - 1; - - BufferView& offsetBufferView = model.bufferViews.emplace_back(); - offsetBufferView.buffer = static_cast(offsetBufferIndex); - offsetBufferView.byteOffset = 0; - offsetBufferView.byteLength = offsetBuffer.byteLength; - offsetBufferViewIndex = model.bufferViews.size() - 1; - } + addBufferToModel(model, stringOffsets); + size_t offsetBufferIndex = model.buffers.size() - 1; + size_t offsetBufferViewIndex = model.bufferViews.size() - 1; ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); @@ -886,27 +801,11 @@ TEST_CASE("Test string property") { TEST_CASE("Test fixed-length scalar array") { Model model; - std::vector values = {12, 34, 30, 11, 34, 34, 11, 33, 122, 33, 223, 11}; - size_t valueBufferViewIndex = 0; - { - Buffer& valueBuffer = model.buffers.emplace_back(); - valueBuffer.cesium.data.resize(values.size() * sizeof(uint32_t)); - valueBuffer.byteLength = - static_cast(valueBuffer.cesium.data.size()); - std::memcpy( - valueBuffer.cesium.data.data(), - values.data(), - valueBuffer.cesium.data.size()); - - BufferView& valueBufferView = model.bufferViews.emplace_back(); - valueBufferView.buffer = static_cast(model.buffers.size() - 1); - valueBufferView.byteOffset = 0; - valueBufferView.byteLength = valueBuffer.byteLength; - valueBufferViewIndex = model.bufferViews.size() - 1; - } + addBufferToModel(model, values); + size_t valueBufferViewIndex = model.bufferViews.size() - 1; ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); @@ -1043,35 +942,13 @@ TEST_CASE("Test variable-length scalar array") { offsetValue[i + 1] = offsetValue[i] + expected[i].size() * sizeof(uint16_t); } - size_t valueBufferIndex = 0; - size_t valueBufferViewIndex = 0; - { - Buffer& valueBuffer = model.buffers.emplace_back(); - valueBuffer.byteLength = static_cast(values.size()); - valueBuffer.cesium.data = std::move(values); - valueBufferIndex = model.buffers.size() - 1; - - BufferView& valueBufferView = model.bufferViews.emplace_back(); - valueBufferView.buffer = static_cast(model.buffers.size() - 1); - valueBufferView.byteOffset = 0; - valueBufferView.byteLength = valueBuffer.byteLength; - valueBufferViewIndex = model.bufferViews.size() - 1; - } + addBufferToModel(model, values); + size_t valueBufferIndex = model.buffers.size() - 1; + size_t valueBufferViewIndex = model.bufferViews.size() - 1; - size_t offsetBufferIndex = 0; - size_t offsetBufferViewIndex = 0; - { - Buffer& offsetBuffer = model.buffers.emplace_back(); - offsetBuffer.byteLength = static_cast(offsets.size()); - offsetBuffer.cesium.data = std::move(offsets); - offsetBufferIndex = model.buffers.size() - 1; - - BufferView& offsetBufferView = model.bufferViews.emplace_back(); - offsetBufferView.buffer = static_cast(model.buffers.size() - 1); - offsetBufferView.byteOffset = 0; - offsetBufferView.byteLength = offsetBuffer.byteLength; - offsetBufferViewIndex = model.bufferViews.size() - 1; - } + addBufferToModel(model, offsets); + size_t offsetBufferIndex = model.buffers.size() - 1; + size_t offsetBufferViewIndex = model.bufferViews.size() - 1; ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); @@ -1193,7 +1070,6 @@ TEST_CASE("Test variable-length scalar array") { TEST_CASE("Test fixed-length vecN array") { Model model; - std::vector values = { glm::ivec3(12, 34, -30), glm::ivec3(-2, 0, 1), @@ -1203,23 +1079,8 @@ TEST_CASE("Test fixed-length vecN array") { glm::ivec3(40, 61, 3), }; - size_t valueBufferViewIndex = 0; - { - Buffer& valueBuffer = model.buffers.emplace_back(); - valueBuffer.cesium.data.resize(values.size() * sizeof(glm::ivec3)); - valueBuffer.byteLength = - static_cast(valueBuffer.cesium.data.size()); - std::memcpy( - valueBuffer.cesium.data.data(), - values.data(), - valueBuffer.cesium.data.size()); - - BufferView& valueBufferView = model.bufferViews.emplace_back(); - valueBufferView.buffer = static_cast(model.buffers.size() - 1); - valueBufferView.byteOffset = 0; - valueBufferView.byteLength = valueBuffer.byteLength; - valueBufferViewIndex = model.bufferViews.size() - 1; - } + addBufferToModel(model, values); + size_t valueBufferViewIndex = model.bufferViews.size() - 1; ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); @@ -1335,7 +1196,6 @@ TEST_CASE("Test fixed-length vecN array") { TEST_CASE("Test variable-length vecN array") { Model model; - // clang-format off std::vector> expected{ { glm::ivec3(12, 34, -30), glm::ivec3(-2, 0, 1) }, @@ -1363,35 +1223,13 @@ TEST_CASE("Test variable-length vecN array") { offsetValue[i] + expected[i].size() * sizeof(glm::ivec3); } - size_t valueBufferIndex = 0; - size_t valueBufferViewIndex = 0; - { - Buffer& valueBuffer = model.buffers.emplace_back(); - valueBuffer.byteLength = static_cast(values.size()); - valueBuffer.cesium.data = std::move(values); - valueBufferIndex = model.buffers.size() - 1; + addBufferToModel(model, values); + size_t valueBufferIndex = model.buffers.size() - 1; + size_t valueBufferViewIndex = model.bufferViews.size() - 1; - BufferView& valueBufferView = model.bufferViews.emplace_back(); - valueBufferView.buffer = static_cast(model.buffers.size() - 1); - valueBufferView.byteOffset = 0; - valueBufferView.byteLength = valueBuffer.byteLength; - valueBufferViewIndex = model.bufferViews.size() - 1; - } - - size_t offsetBufferIndex = 0; - size_t offsetBufferViewIndex = 0; - { - Buffer& offsetBuffer = model.buffers.emplace_back(); - offsetBuffer.byteLength = static_cast(offsets.size()); - offsetBuffer.cesium.data = std::move(offsets); - offsetBufferIndex = model.buffers.size() - 1; - - BufferView& offsetBufferView = model.bufferViews.emplace_back(); - offsetBufferView.buffer = static_cast(model.buffers.size() - 1); - offsetBufferView.byteOffset = 0; - offsetBufferView.byteLength = offsetBuffer.byteLength; - offsetBufferViewIndex = model.bufferViews.size() - 1; - } + addBufferToModel(model, offsets); + size_t offsetBufferIndex = model.buffers.size() - 1; + size_t offsetBufferViewIndex = model.bufferViews.size() - 1; ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); @@ -1519,7 +1357,6 @@ TEST_CASE("Test variable-length vecN array") { TEST_CASE("Test fixed-length matN array") { Model model; - // clang-format off std::vector values = { glm::i32mat2x2( @@ -1543,23 +1380,8 @@ TEST_CASE("Test fixed-length matN array") { }; // clang-format on - size_t valueBufferViewIndex = 0; - { - Buffer& valueBuffer = model.buffers.emplace_back(); - valueBuffer.cesium.data.resize(values.size() * sizeof(glm::i32mat2x2)); - valueBuffer.byteLength = - static_cast(valueBuffer.cesium.data.size()); - std::memcpy( - valueBuffer.cesium.data.data(), - values.data(), - valueBuffer.cesium.data.size()); - - BufferView& valueBufferView = model.bufferViews.emplace_back(); - valueBufferView.buffer = static_cast(model.buffers.size() - 1); - valueBufferView.byteOffset = 0; - valueBufferView.byteLength = valueBuffer.byteLength; - valueBufferViewIndex = model.bufferViews.size() - 1; - } + addBufferToModel(model, values); + size_t valueBufferViewIndex = model.bufferViews.size() - 1; ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); @@ -1676,7 +1498,6 @@ TEST_CASE("Test fixed-length matN array") { TEST_CASE("Test variable-length matN array") { Model model; - // clang-format off std::vector data0{ glm::i32mat2x2( @@ -1724,35 +1545,13 @@ TEST_CASE("Test variable-length matN array") { offsetValue[i] + expected[i].size() * sizeof(glm::i32mat2x2); } - size_t valueBufferIndex = 0; - size_t valueBufferViewIndex = 0; - { - Buffer& valueBuffer = model.buffers.emplace_back(); - valueBuffer.byteLength = static_cast(values.size()); - valueBuffer.cesium.data = std::move(values); - valueBufferIndex = model.buffers.size() - 1; + addBufferToModel(model, values); + size_t valueBufferIndex = model.buffers.size() - 1; + size_t valueBufferViewIndex = model.bufferViews.size() - 1; - BufferView& valueBufferView = model.bufferViews.emplace_back(); - valueBufferView.buffer = static_cast(model.buffers.size() - 1); - valueBufferView.byteOffset = 0; - valueBufferView.byteLength = valueBuffer.byteLength; - valueBufferViewIndex = model.bufferViews.size() - 1; - } - - size_t offsetBufferIndex = 0; - size_t offsetBufferViewIndex = 0; - { - Buffer& offsetBuffer = model.buffers.emplace_back(); - offsetBuffer.byteLength = static_cast(offsets.size()); - offsetBuffer.cesium.data = std::move(offsets); - offsetBufferIndex = model.buffers.size() - 1; - - BufferView& offsetBufferView = model.bufferViews.emplace_back(); - offsetBufferView.buffer = static_cast(model.buffers.size() - 1); - offsetBufferView.byteOffset = 0; - offsetBufferView.byteLength = offsetBuffer.byteLength; - offsetBufferViewIndex = model.bufferViews.size() - 1; - } + addBufferToModel(model, offsets); + size_t offsetBufferIndex = model.buffers.size() - 1; + size_t offsetBufferViewIndex = model.bufferViews.size() - 1; ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); @@ -1906,21 +1705,7 @@ TEST_CASE("Test fixed-length boolean array") { static_cast((expectedValue << bitIndex) | values[byteIndex]); } - { - Buffer& valueBuffer = model.buffers.emplace_back(); - valueBuffer.cesium.data.resize(values.size()); - valueBuffer.byteLength = - static_cast(valueBuffer.cesium.data.size()); - std::memcpy( - valueBuffer.cesium.data.data(), - values.data(), - valueBuffer.cesium.data.size()); - - BufferView& valueBufferView = model.bufferViews.emplace_back(); - valueBufferView.buffer = static_cast(model.buffers.size() - 1); - valueBufferView.byteOffset = 0; - valueBufferView.byteLength = valueBuffer.byteLength; - } + addBufferToModel(model, values); ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); @@ -2040,35 +1825,13 @@ TEST_CASE("Test variable-length boolean array") { offsetValue[i + 1] = offsetValue[i] + expected[i].size(); } - size_t valueBufferViewIndex = 0; - size_t valueBufferIndex = 0; - { - Buffer& valueBuffer = model.buffers.emplace_back(); - valueBuffer.byteLength = static_cast(values.size()); - valueBuffer.cesium.data = std::move(values); - valueBufferIndex = model.buffers.size() - 1; + addBufferToModel(model, values); + size_t valueBufferIndex = model.buffers.size() - 1; + size_t valueBufferViewIndex = model.bufferViews.size() - 1; - BufferView& valueBufferView = model.bufferViews.emplace_back(); - valueBufferView.buffer = static_cast(valueBufferIndex); - valueBufferView.byteOffset = 0; - valueBufferView.byteLength = valueBuffer.byteLength; - valueBufferViewIndex = model.bufferViews.size() - 1; - } - - size_t offsetBufferIndex = 0; - size_t offsetBufferViewIndex = 0; - { - Buffer& offsetBuffer = model.buffers.emplace_back(); - offsetBuffer.byteLength = static_cast(offsets.size()); - offsetBuffer.cesium.data = std::move(offsets); - offsetBufferIndex = model.buffers.size() - 1; - - BufferView& offsetBufferView = model.bufferViews.emplace_back(); - offsetBufferView.buffer = static_cast(offsetBufferIndex); - offsetBufferView.byteOffset = 0; - offsetBufferView.byteLength = offsetBuffer.byteLength; - offsetBufferViewIndex = model.bufferViews.size() - 1; - } + addBufferToModel(model, offsets); + size_t offsetBufferIndex = model.buffers.size() - 1; + size_t offsetBufferViewIndex = model.bufferViews.size() - 1; ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); @@ -2217,31 +1980,11 @@ TEST_CASE("Test fixed-length arrays of strings") { offsetValue[i] + static_cast(expectedValue.size()); } - size_t valueBufferViewIndex = 0; - { - Buffer& valueBuffer = model.buffers.emplace_back(); - valueBuffer.byteLength = static_cast(values.size()); - valueBuffer.cesium.data = std::move(values); - - BufferView& valueBufferView = model.bufferViews.emplace_back(); - valueBufferView.buffer = static_cast(model.buffers.size() - 1); - valueBufferView.byteOffset = 0; - valueBufferView.byteLength = valueBuffer.byteLength; - valueBufferViewIndex = model.bufferViews.size() - 1; - } - - size_t offsetBufferViewIndex = 0; - { - Buffer& offsetBuffer = model.buffers.emplace_back(); - offsetBuffer.byteLength = static_cast(offsets.size()); - offsetBuffer.cesium.data = std::move(offsets); + addBufferToModel(model, values); + size_t valueBufferViewIndex = model.bufferViews.size() - 1; - BufferView& offsetBufferView = model.bufferViews.emplace_back(); - offsetBufferView.buffer = static_cast(model.buffers.size() - 1); - offsetBufferView.byteOffset = 0; - offsetBufferView.byteLength = offsetBuffer.byteLength; - offsetBufferViewIndex = model.bufferViews.size() - 1; - } + addBufferToModel(model, offsets); + size_t offsetBufferViewIndex = model.bufferViews.size() - 1; ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); @@ -2399,48 +2142,16 @@ TEST_CASE("Test variable-length arrays of strings") { static_cast(expected[i].size() * sizeof(uint32_t)); } - size_t valueBufferViewIndex = 0; - { - Buffer& valueBuffer = model.buffers.emplace_back(); - valueBuffer.byteLength = static_cast(values.size()); - valueBuffer.cesium.data = std::move(values); - - BufferView& valueBufferView = model.bufferViews.emplace_back(); - valueBufferView.buffer = static_cast(model.buffers.size() - 1); - valueBufferView.byteOffset = 0; - valueBufferView.byteLength = valueBuffer.byteLength; - valueBufferViewIndex = model.bufferViews.size() - 1; - } + addBufferToModel(model, values); + size_t valueBufferViewIndex = model.bufferViews.size() - 1; - size_t arrayOffsetBuffer = 0; - size_t arrayOffsetBufferView = 0; - { - Buffer& offsetBuffer = model.buffers.emplace_back(); - offsetBuffer.byteLength = static_cast(offsets.size()); - offsetBuffer.cesium.data = std::move(offsets); - arrayOffsetBuffer = model.buffers.size() - 1; + addBufferToModel(model, offsets); + size_t arrayOffsetBuffer = model.buffers.size() - 1; + size_t arrayOffsetBufferView = model.bufferViews.size() - 1; - BufferView& offsetBufferView = model.bufferViews.emplace_back(); - offsetBufferView.buffer = static_cast(arrayOffsetBuffer); - offsetBufferView.byteOffset = 0; - offsetBufferView.byteLength = offsetBuffer.byteLength; - arrayOffsetBufferView = model.bufferViews.size() - 1; - } - - size_t stringOffsetBuffer = 0; - size_t stringOffsetBufferView = 0; - { - Buffer& strOffsetBuffer = model.buffers.emplace_back(); - strOffsetBuffer.byteLength = static_cast(stringOffsets.size()); - strOffsetBuffer.cesium.data = std::move(stringOffsets); - stringOffsetBuffer = model.buffers.size() - 1; - - BufferView& strOffsetBufferView = model.bufferViews.emplace_back(); - strOffsetBufferView.buffer = static_cast(stringOffsetBuffer); - strOffsetBufferView.byteOffset = 0; - strOffsetBufferView.byteLength = strOffsetBuffer.byteLength; - stringOffsetBufferView = model.bufferViews.size() - 1; - } + addBufferToModel(model, stringOffsets); + size_t stringOffsetBuffer = model.buffers.size() - 1; + size_t stringOffsetBufferView = model.bufferViews.size() - 1; ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); @@ -2713,26 +2424,8 @@ TEST_CASE("Test callback for scalar property") { Model model; std::vector values = {12, 34, 30, 11, 34, 34, 11, 33, 122, 33}; - size_t valueBufferViewIndex = 0; - - // Buffers are constructed in scope to ensure that the tests don't use their - // temporary variables. - { - Buffer& valueBuffer = model.buffers.emplace_back(); - valueBuffer.cesium.data.resize(values.size() * sizeof(uint32_t)); - valueBuffer.byteLength = - static_cast(valueBuffer.cesium.data.size()); - std::memcpy( - valueBuffer.cesium.data.data(), - values.data(), - valueBuffer.cesium.data.size()); - - BufferView& valueBufferView = model.bufferViews.emplace_back(); - valueBufferView.buffer = static_cast(model.buffers.size() - 1); - valueBufferView.byteOffset = 0; - valueBufferView.byteLength = valueBuffer.byteLength; - valueBufferViewIndex = model.bufferViews.size() - 1; - } + addBufferToModel(model, values); + size_t valueBufferViewIndex = model.bufferViews.size() - 1; ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); @@ -2794,33 +2487,14 @@ TEST_CASE("Test callback for scalar property") { TEST_CASE("Test callback for vecN property") { Model model; - std::vector values = { glm::ivec3(-12, 34, 30), glm::ivec3(11, 73, 0), glm::ivec3(-2, 6, 12), glm::ivec3(-4, 8, -13)}; - size_t valueBufferViewIndex = 0; - - // Buffers are constructed in scope to ensure that the tests don't use their - // temporary variables. - { - Buffer& valueBuffer = model.buffers.emplace_back(); - valueBuffer.cesium.data.resize(values.size() * sizeof(glm::ivec3)); - valueBuffer.byteLength = - static_cast(valueBuffer.cesium.data.size()); - std::memcpy( - valueBuffer.cesium.data.data(), - values.data(), - valueBuffer.cesium.data.size()); - - BufferView& valueBufferView = model.bufferViews.emplace_back(); - valueBufferView.buffer = static_cast(model.buffers.size() - 1); - valueBufferView.byteOffset = 0; - valueBufferView.byteLength = valueBuffer.byteLength; - valueBufferViewIndex = model.bufferViews.size() - 1; - } + addBufferToModel(model, values); + size_t valueBufferViewIndex = model.bufferViews.size() - 1; ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); @@ -2882,7 +2556,6 @@ TEST_CASE("Test callback for vecN property") { TEST_CASE("Test callback for matN property") { Model model; - // clang-format off std::vector values = { glm::u32mat2x2( @@ -2899,26 +2572,8 @@ TEST_CASE("Test callback for matN property") { 3, 23)}; // clang-format on - size_t valueBufferViewIndex = 0; - - // Buffers are constructed in scope to ensure that the tests don't use their - // temporary variables. - { - Buffer& valueBuffer = model.buffers.emplace_back(); - valueBuffer.cesium.data.resize(values.size() * sizeof(glm::u32mat2x2)); - valueBuffer.byteLength = - static_cast(valueBuffer.cesium.data.size()); - std::memcpy( - valueBuffer.cesium.data.data(), - values.data(), - valueBuffer.cesium.data.size()); - - BufferView& valueBufferView = model.bufferViews.emplace_back(); - valueBufferView.buffer = static_cast(model.buffers.size() - 1); - valueBufferView.byteOffset = 0; - valueBufferView.byteLength = valueBuffer.byteLength; - valueBufferViewIndex = model.bufferViews.size() - 1; - } + addBufferToModel(model, values); + size_t valueBufferViewIndex = model.bufferViews.size() - 1; ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); @@ -2999,23 +2654,7 @@ TEST_CASE("Test callback for boolean property") { (expectedValue << bitIndex) | values[static_cast(byteIndex)]); } - // Buffers are constructed in scope to ensure that the tests don't use their - // temporary variables. - { - Buffer& valueBuffer = model.buffers.emplace_back(); - valueBuffer.cesium.data.resize(values.size()); - valueBuffer.byteLength = - static_cast(valueBuffer.cesium.data.size()); - std::memcpy( - valueBuffer.cesium.data.data(), - values.data(), - valueBuffer.cesium.data.size()); - - BufferView& valueBufferView = model.bufferViews.emplace_back(); - valueBufferView.buffer = static_cast(model.buffers.size() - 1); - valueBufferView.byteOffset = 0; - valueBufferView.byteLength = valueBuffer.byteLength; - } + addBufferToModel(model, values); ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); @@ -3099,37 +2738,11 @@ TEST_CASE("Test callback for string property") { offsetValue[i] + static_cast(expectedValue.size()); } - // Buffers are constructed in scope to ensure that the tests don't use their - // temporary variables. - size_t valueBufferIndex = 0; - size_t valueBufferViewIndex = 0; - { - Buffer& valueBuffer = model.buffers.emplace_back(); - valueBuffer.byteLength = static_cast(values.size()); - valueBuffer.cesium.data = std::move(values); - valueBufferIndex = model.buffers.size() - 1; - - BufferView& valueBufferView = model.bufferViews.emplace_back(); - valueBufferView.buffer = static_cast(valueBufferIndex); - valueBufferView.byteOffset = 0; - valueBufferView.byteLength = valueBuffer.byteLength; - valueBufferViewIndex = model.bufferViews.size() - 1; - } - - size_t offsetBufferIndex = 0; - size_t offsetBufferViewIndex = 0; - { - Buffer& offsetBuffer = model.buffers.emplace_back(); - offsetBuffer.byteLength = static_cast(stringOffsets.size()); - offsetBuffer.cesium.data = std::move(stringOffsets); - offsetBufferIndex = model.buffers.size() - 1; + addBufferToModel(model, values); + size_t valueBufferViewIndex = model.bufferViews.size() - 1; - BufferView& offsetBufferView = model.bufferViews.emplace_back(); - offsetBufferView.buffer = static_cast(offsetBufferIndex); - offsetBufferView.byteOffset = 0; - offsetBufferView.byteLength = offsetBuffer.byteLength; - offsetBufferViewIndex = model.bufferViews.size() - 1; - } + addBufferToModel(model, stringOffsets); + size_t offsetBufferViewIndex = model.bufferViews.size() - 1; ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); @@ -3194,25 +2807,10 @@ TEST_CASE("Test callback for string property") { TEST_CASE("Test callback for scalar array") { Model model; - std::vector values = {12, 34, 30, 11, 34, 34, 11, 33, 122, 33, 223, 11}; - { - Buffer& valueBuffer = model.buffers.emplace_back(); - valueBuffer.cesium.data.resize(values.size() * sizeof(uint32_t)); - valueBuffer.byteLength = - static_cast(valueBuffer.cesium.data.size()); - std::memcpy( - valueBuffer.cesium.data.data(), - values.data(), - valueBuffer.cesium.data.size()); - - BufferView& valueBufferView = model.bufferViews.emplace_back(); - valueBufferView.buffer = static_cast(model.buffers.size() - 1); - valueBufferView.byteOffset = 0; - valueBufferView.byteLength = valueBuffer.byteLength; - } + addBufferToModel(model, values); ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); @@ -3280,7 +2878,6 @@ TEST_CASE("Test callback for scalar array") { TEST_CASE("Test callback for vecN array") { Model model; - std::vector values = { glm::ivec3(12, 34, -30), glm::ivec3(-2, 0, 1), @@ -3290,21 +2887,7 @@ TEST_CASE("Test callback for vecN array") { glm::ivec3(40, 61, 3), }; - { - Buffer& valueBuffer = model.buffers.emplace_back(); - valueBuffer.cesium.data.resize(values.size() * sizeof(glm::ivec3)); - valueBuffer.byteLength = - static_cast(valueBuffer.cesium.data.size()); - std::memcpy( - valueBuffer.cesium.data.data(), - values.data(), - valueBuffer.cesium.data.size()); - - BufferView& valueBufferView = model.bufferViews.emplace_back(); - valueBufferView.buffer = static_cast(model.buffers.size() - 1); - valueBufferView.byteOffset = 0; - valueBufferView.byteLength = valueBuffer.byteLength; - } + addBufferToModel(model, values); ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); @@ -3372,7 +2955,6 @@ TEST_CASE("Test callback for vecN array") { TEST_CASE("Test callback for matN array") { Model model; - // clang-format off std::vector values = { glm::i32mat2x2( @@ -3396,21 +2978,7 @@ TEST_CASE("Test callback for matN array") { }; // clang-format on - { - Buffer& valueBuffer = model.buffers.emplace_back(); - valueBuffer.cesium.data.resize(values.size() * sizeof(glm::i32mat2x2)); - valueBuffer.byteLength = - static_cast(valueBuffer.cesium.data.size()); - std::memcpy( - valueBuffer.cesium.data.data(), - values.data(), - valueBuffer.cesium.data.size()); - - BufferView& valueBufferView = model.bufferViews.emplace_back(); - valueBufferView.buffer = static_cast(model.buffers.size() - 1); - valueBufferView.byteOffset = 0; - valueBufferView.byteLength = valueBuffer.byteLength; - } + addBufferToModel(model, values); ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); @@ -3504,21 +3072,7 @@ TEST_CASE("Test callback for boolean array") { static_cast((expectedValue << bitIndex) | values[byteIndex]); } - { - Buffer& valueBuffer = model.buffers.emplace_back(); - valueBuffer.cesium.data.resize(values.size()); - valueBuffer.byteLength = - static_cast(valueBuffer.cesium.data.size()); - std::memcpy( - valueBuffer.cesium.data.data(), - values.data(), - valueBuffer.cesium.data.size()); - - BufferView& valueBufferView = model.bufferViews.emplace_back(); - valueBufferView.buffer = static_cast(model.buffers.size() - 1); - valueBufferView.byteOffset = 0; - valueBufferView.byteLength = valueBuffer.byteLength; - } + addBufferToModel(model, values); ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); @@ -3610,31 +3164,11 @@ TEST_CASE("Test callback for array of strings") { offsetValue[i] + static_cast(expectedValue.size()); } - size_t valueBufferViewIndex = 0; - { - Buffer& valueBuffer = model.buffers.emplace_back(); - valueBuffer.byteLength = static_cast(values.size()); - valueBuffer.cesium.data = std::move(values); - - BufferView& valueBufferView = model.bufferViews.emplace_back(); - valueBufferView.buffer = static_cast(model.buffers.size() - 1); - valueBufferView.byteOffset = 0; - valueBufferView.byteLength = valueBuffer.byteLength; - valueBufferViewIndex = model.bufferViews.size() - 1; - } - - size_t offsetBufferViewIndex = 0; - { - Buffer& offsetBuffer = model.buffers.emplace_back(); - offsetBuffer.byteLength = static_cast(offsets.size()); - offsetBuffer.cesium.data = std::move(offsets); + addBufferToModel(model, values); + size_t valueBufferViewIndex = model.bufferViews.size() - 1; - BufferView& offsetBufferView = model.bufferViews.emplace_back(); - offsetBufferView.buffer = static_cast(model.buffers.size() - 1); - offsetBufferView.byteOffset = 0; - offsetBufferView.byteLength = offsetBuffer.byteLength; - offsetBufferViewIndex = model.bufferViews.size() - 1; - } + addBufferToModel(model, offsets); + size_t offsetBufferViewIndex = model.bufferViews.size() - 1; ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); From 59549893c7038b343008950e9362774e1476da31 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Fri, 16 Jun 2023 17:35:26 -0400 Subject: [PATCH 082/421] Formatting --- CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h index 425ad1f58..eea7425e4 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h @@ -89,9 +89,7 @@ enum class PropertyTexturePropertyComponentType { * @tparam T The component type, must correspond to a valid * {@link PropertyTexturePropertyComponentType}. */ -template struct PropertyTexturePropertyValue { - T components[4]; -}; +template struct PropertyTexturePropertyValue { T components[4]; }; /** * @brief A view of the data specified by a {@link PropertyTextureProperty}. From 1694a9ad3e731f4e3d047c85996206e57e0689de Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Tue, 20 Jun 2023 10:26:32 -0400 Subject: [PATCH 083/421] Fix typo --- CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h index 05b1adcd5..e1987a5a2 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h @@ -155,12 +155,12 @@ enum class PropertyTablePropertyViewStatus { }; /** - * @brief A view on the data of the {@link PropertyTableProperty that is created + * @brief A view on the data of the {@link PropertyTableProperty} that is created * by a {@link PropertyTableView}. * * It provides utility to retrieve the actual data stored in the * {@link PropertyTableProperty::values} like an array of elements. Data of each - * instance can be accessed through the {@link get(int64_t instance)} method. + * instance can be accessed through the {@link PropertyTablePropertyView::get} method. * * @param ElementType must be one of the following: a scalar (uint8_t, int8_t, * uint16_t, int16_t, uint32_t, int32_t, uint64_t, int64_t, float, double), a From 76d5d260513388678f98a6bea2a879d982ef449d Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Fri, 23 Jun 2023 12:06:25 -0400 Subject: [PATCH 084/421] Rework PropertyTexturePropertyView, begin reworking PropertyTextureView --- .../include/CesiumGltf/FeatureIdTextureView.h | 3 +- .../include/CesiumGltf/PropertyArrayView.h | 60 +- .../CesiumGltf/PropertyTablePropertyView.h | 28 +- .../include/CesiumGltf/PropertyTableView.h | 108 +- .../CesiumGltf/PropertyTexturePropertyView.h | 466 ++++-- .../include/CesiumGltf/PropertyTextureView.h | 289 +++- CesiumGltf/include/CesiumGltf/PropertyType.h | 7 + CesiumGltf/src/PropertyTableView.cpp | 48 +- .../src/PropertyTexturePropertyView.cpp | 145 -- CesiumGltf/src/PropertyTextureView.cpp | 95 +- CesiumGltf/src/PropertyType.cpp | 39 + .../test/TestPropertyTablePropertyView.cpp | 37 +- CesiumGltf/test/TestPropertyTableView.cpp | 2 +- .../test/TestPropertyTexturePropertyView.cpp | 1428 ++++++++++++----- CesiumGltf/test/TestPropertyTextureView.cpp | 461 +++++- CesiumGltf/test/TestPropertyType.cpp | 501 +++--- 16 files changed, 2594 insertions(+), 1123 deletions(-) delete mode 100644 CesiumGltf/src/PropertyTexturePropertyView.cpp diff --git a/CesiumGltf/include/CesiumGltf/FeatureIdTextureView.h b/CesiumGltf/include/CesiumGltf/FeatureIdTextureView.h index 8b6e28eef..9834c0f3c 100644 --- a/CesiumGltf/include/CesiumGltf/FeatureIdTextureView.h +++ b/CesiumGltf/include/CesiumGltf/FeatureIdTextureView.h @@ -49,8 +49,7 @@ enum class FeatureIdTextureViewStatus { /** * @brief The image for this feature ID texture has channels that take up more - * than a byte. The feature ID texture's channels should represent the bytes - * of the actual feature ID. + * than a byte. Only single-byte channels are supported. */ ErrorInvalidImageBytesPerChannel, diff --git a/CesiumGltf/include/CesiumGltf/PropertyArrayView.h b/CesiumGltf/include/CesiumGltf/PropertyArrayView.h index ef89f5766..b921c13f3 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyArrayView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyArrayView.h @@ -9,35 +9,76 @@ #include #include +#include +#include namespace CesiumGltf { /** - * @brief A view on an array element of a {@link PropertyTableProperty}. + * @brief A view on an array element of a {@link PropertyTableProperty} + * or {@link PropertyTextureProperty}. * * Provides utility to retrieve the data stored in the array of * elements via the array index operator. */ template class PropertyArrayView { public: + /** + * @brief Constructs an empty array view. + */ PropertyArrayView() : _values{} {} + /** + * @brief Constructs an array view from a buffer. + * + * @param buffer The buffer containing the values. + */ PropertyArrayView(const gsl::span& buffer) noexcept : _values{CesiumUtility::reintepretCastSpan(buffer)} {} + /** + * @brief Constructs an array view from a vector of values. This is mainly + * used by PropertyTextureProperty -- since the values are swizzled from the + * texture, the values cannot be viewed in place, and must passed in through a + * correctly ordered vector. + * + * @param values The vector containing the values. + */ + PropertyArrayView(const std::vector&& values) + : _values{std::move(values)} {} + const ElementType& operator[](int64_t index) const noexcept { - return _values[index]; + return std::visit( + [index](auto const& values) -> auto const& { return values[index]; }, + _values); } - int64_t size() const noexcept { return static_cast(_values.size()); } + int64_t size() const noexcept { + return std::visit( + [](auto const& values) { return static_cast(values.size()); }, + _values); + } private: - gsl::span _values; + using ArrayType = + std::variant, std::vector>; + ArrayType _values; }; template <> class PropertyArrayView { public: + /** + * @brief Constructs an empty array view. + */ PropertyArrayView() : _values{}, _bitOffset{0}, _size{0} {} + /** + * @brief Constructs an array view from a buffer. + * + * @param buffer The buffer containing the values. + * @param bitOffset The offset into the buffer where the values actually + * begin. + * @param size The number of values in the array. + */ PropertyArrayView( const gsl::span& buffer, int64_t bitOffset, @@ -62,9 +103,20 @@ template <> class PropertyArrayView { template <> class PropertyArrayView { public: + /** + * @brief Constructs an empty array view. + */ PropertyArrayView() : _values{}, _stringOffsets{}, _stringOffsetType{}, _size{0} {} + /** + * @brief Constructs an array view from buffers and their information. + * + * @param values The buffer containing the values. + * @param stringOffsets The buffer containing the string offsets. + * @param stringOffsetType The component type of the string offsets. + * @param size The number of values in the array. + */ PropertyArrayView( const gsl::span& values, const gsl::span& stringOffsets, diff --git a/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h index 912dba62f..977ccc995 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h @@ -171,7 +171,7 @@ enum class PropertyTablePropertyViewStatus { template class PropertyTablePropertyView { public: /** - * @brief Constructs a new instance with a non-existent property. + * @brief Constructs an invalid instance for a non-existent property. */ PropertyTablePropertyView() : _status{PropertyTablePropertyViewStatus::ErrorPropertyDoesNotExist}, @@ -181,18 +181,29 @@ template class PropertyTablePropertyView { _normalized{} {} /** - * @brief Construct a new instance pointing to non-array data specified by - * {@link PropertyTableProperty}. + * @brief Constructs an invalid instance for an erroneous property. + * + * @param status The {@link PropertyTablePropertyViewStatus} indicating the error with the property. + */ + PropertyTablePropertyView(PropertyTablePropertyViewStatus status) + : _status{status}, _values{}, _arrayCount{}, _size{}, _normalized{} { + assert( + _status != PropertyTablePropertyViewStatus::Valid && + "An empty property view should not be constructed with a valid status"); + } + + /** + * @brief Construct a valid instance pointing to non-array data specified by + * a {@link PropertyTableProperty}. * @param values The raw buffer specified by {@link PropertyTableProperty::values} * @param size The number of elements in the property table specified by {@link PropertyTable::count} * @param normalized Whether this property has a normalized integer type. */ PropertyTablePropertyView( - PropertyTablePropertyViewStatus status, gsl::span values, int64_t size, bool normalized) noexcept - : _status{status}, + : _status{PropertyTablePropertyViewStatus::Valid}, _values{values}, _arrayOffsets{}, _arrayOffsetType{PropertyComponentType::None}, @@ -205,8 +216,8 @@ template class PropertyTablePropertyView { _normalized{normalized} {} /** - * @brief Construct a new instance pointing to the data specified by - * {@link PropertyTableProperty}. + * @brief Construct a valid instance pointing to the data specified by + * a {@link PropertyTableProperty}. * @param values The raw buffer specified by {@link PropertyTableProperty::values} * @param arrayOffsets The raw buffer specified by {@link PropertyTableProperty::arrayOffsets} * @param stringOffsets The raw buffer specified by {@link PropertyTableProperty::stringOffsets} @@ -217,7 +228,6 @@ template class PropertyTablePropertyView { * @param normalized Whether this property has a normalized integer type. */ PropertyTablePropertyView( - PropertyTablePropertyViewStatus status, gsl::span values, gsl::span arrayOffsets, gsl::span stringOffsets, @@ -226,7 +236,7 @@ template class PropertyTablePropertyView { int64_t arrayCount, int64_t size, bool normalized) noexcept - : _status{status}, + : _status{PropertyTablePropertyViewStatus::Valid}, _values{values}, _arrayOffsets{arrayOffsets}, _arrayOffsetType{arrayOffsetType}, diff --git a/CesiumGltf/include/CesiumGltf/PropertyTableView.h b/CesiumGltf/include/CesiumGltf/PropertyTableView.h index 4c255439e..6af76ce7f 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTableView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTableView.h @@ -54,7 +54,7 @@ class PropertyTableView { public: /** * @brief Creates an instance of PropertyTableView. - * @param model The Gltf Model that contains property table data. + * @param model The glTF Model that contains the property table data. * @param propertyTable The {@link PropertyTable} * from which the view will retrieve data. */ @@ -111,15 +111,14 @@ class PropertyTableView { PropertyTablePropertyView getPropertyView(const std::string& propertyName) const { if (this->size() <= 0) { - return createInvalidPropertyView( + return PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorInvalidPropertyTable); } const ClassProperty* pClassProperty = getClassProperty(propertyName); if (!pClassProperty) { - return createInvalidPropertyView( - StructuralMetadata::PropertyTablePropertyViewStatus:: - ErrorNonexistentProperty); + return PropertyTablePropertyView( + PropertyTablePropertyViewStatus::ErrorNonexistentProperty); } return getPropertyViewImpl(propertyName, *pClassProperty); @@ -150,7 +149,7 @@ class PropertyTableView { if (this->size() <= 0) { callback( propertyName, - createInvalidPropertyView( + PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorInvalidPropertyTable)); return; } @@ -159,7 +158,7 @@ class PropertyTableView { if (!pClassProperty) { callback( propertyName, - createInvalidPropertyView( + PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorNonexistentProperty)); return; } @@ -209,7 +208,7 @@ class PropertyTableView { } else { callback( propertyName, - createInvalidPropertyView( + PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorTypeMismatch)); } } @@ -241,22 +240,6 @@ class PropertyTableView { } private: - glm::length_t getDimensionsFromType(PropertyType type) const { - switch (type) { - case PropertyType::Vec2: - case PropertyType::Mat2: - return 2; - case PropertyType::Vec3: - case PropertyType::Mat3: - return 3; - case PropertyType::Vec4: - case PropertyType::Mat4: - return 4; - default: - return 0; - } - } - template void getScalarArrayPropertyViewImpl( const std::string& propertyName, @@ -337,7 +320,7 @@ class PropertyTableView { default: callback( propertyName, - createInvalidPropertyView( + PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch)); break; } @@ -423,7 +406,7 @@ class PropertyTableView { default: callback( propertyName, - createInvalidPropertyView( + PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch)); break; } @@ -436,7 +419,7 @@ class PropertyTableView { PropertyType type, PropertyComponentType componentType, Callback&& callback) const { - glm::length_t N = getDimensionsFromType(type); + glm::length_t N = getDimensionsFromPropertyType(type); switch (N) { case 2: getVecNArrayPropertyViewImpl( @@ -462,7 +445,7 @@ class PropertyTableView { default: callback( propertyName, - createInvalidPropertyView( + PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorTypeMismatch)); break; } @@ -548,7 +531,7 @@ class PropertyTableView { default: callback( propertyName, - createInvalidPropertyView( + PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch)); break; } @@ -561,7 +544,7 @@ class PropertyTableView { PropertyType type, PropertyComponentType componentType, Callback&& callback) const { - const glm::length_t N = getDimensionsFromType(type); + const glm::length_t N = getDimensionsFromPropertyType(type); switch (N) { case 2: getMatNArrayPropertyViewImpl( @@ -587,7 +570,7 @@ class PropertyTableView { default: callback( propertyName, - createInvalidPropertyView( + PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorTypeMismatch)); break; } @@ -636,7 +619,7 @@ class PropertyTableView { } else { callback( propertyName, - createInvalidPropertyView( + PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorTypeMismatch)); } } @@ -720,7 +703,7 @@ class PropertyTableView { default: callback( propertyName, - createInvalidPropertyView( + PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch)); break; } @@ -733,7 +716,7 @@ class PropertyTableView { PropertyType type, PropertyComponentType componentType, Callback&& callback) const { - const glm::length_t N = getDimensionsFromType(type); + const glm::length_t N = getDimensionsFromPropertyType(type); switch (N) { case 2: getVecNPropertyViewImpl( @@ -759,7 +742,7 @@ class PropertyTableView { default: callback( propertyName, - createInvalidPropertyView( + PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorTypeMismatch)); break; } @@ -845,7 +828,7 @@ class PropertyTableView { default: callback( propertyName, - createInvalidPropertyView( + PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch)); break; } @@ -858,7 +841,7 @@ class PropertyTableView { PropertyType type, PropertyComponentType componentType, Callback&& callback) const { - glm::length_t N = getDimensionsFromType(type); + glm::length_t N = getDimensionsFromPropertyType(type); switch (N) { case 2: getMatNPropertyViewImpl( @@ -884,7 +867,7 @@ class PropertyTableView { default: callback( propertyName, - createInvalidPropertyView( + PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorTypeMismatch)); break; } @@ -950,7 +933,7 @@ class PropertyTableView { default: callback( propertyName, - createInvalidPropertyView( + PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch)); break; } @@ -963,7 +946,7 @@ class PropertyTableView { auto propertyTablePropertyIter = _pPropertyTable->properties.find(propertyName); if (propertyTablePropertyIter == _pPropertyTable->properties.end()) { - return createInvalidPropertyView( + return PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorNonexistentProperty); } @@ -998,31 +981,31 @@ class PropertyTableView { const ClassProperty& classProperty, const PropertyTableProperty& propertyTableProperty) const { if (classProperty.array) { - return createInvalidPropertyView( + return PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); } const PropertyType type = convertStringToPropertyType(classProperty.type); if (TypeToPropertyType::value != type) { - return createInvalidPropertyView( + return PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorTypeMismatch); } const PropertyComponentType componentType = convertStringToPropertyComponentType( classProperty.componentType.value_or("")); if (TypeToPropertyType::component != componentType) { - return createInvalidPropertyView( + return PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); } gsl::span values; const auto status = getBufferSafe(propertyTableProperty.values, values); if (status != PropertyTablePropertyViewStatus::Valid) { - return createInvalidPropertyView(status); + return PropertyTablePropertyView(status); } if (values.size() % sizeof(T) != 0) { - return createInvalidPropertyView( + return PropertyTablePropertyView( PropertyTablePropertyViewStatus:: ErrorBufferViewSizeNotDivisibleByTypeSize); } @@ -1036,13 +1019,12 @@ class PropertyTableView { } if (values.size() < maxRequiredBytes) { - return createInvalidPropertyView( + return PropertyTablePropertyView( PropertyTablePropertyViewStatus:: ErrorBufferViewSizeDoesNotMatchPropertyTableCount); } return PropertyTablePropertyView( - PropertyTablePropertyViewStatus::Valid, values, _pPropertyTable->count, classProperty.normalized); @@ -1058,13 +1040,13 @@ class PropertyTableView { const ClassProperty& classProperty, const PropertyTableProperty& propertyTableProperty) const { if (!classProperty.array) { - return createInvalidPropertyView>( + return PropertyTablePropertyView>( PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); } const PropertyType type = convertStringToPropertyType(classProperty.type); if (TypeToPropertyType::value != type) { - return createInvalidPropertyView>( + return PropertyTablePropertyView>( PropertyTablePropertyViewStatus::ErrorTypeMismatch); } @@ -1072,31 +1054,31 @@ class PropertyTableView { convertStringToPropertyComponentType( classProperty.componentType.value_or("")); if (TypeToPropertyType::component != componentType) { - return createInvalidPropertyView>( + return PropertyTablePropertyView>( PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); } gsl::span values; auto status = getBufferSafe(propertyTableProperty.values, values); if (status != PropertyTablePropertyViewStatus::Valid) { - return createInvalidPropertyView>(status); + return PropertyTablePropertyView>(status); } if (values.size() % sizeof(T) != 0) { - return createInvalidPropertyView>( + return PropertyTablePropertyView>( PropertyTablePropertyViewStatus:: ErrorBufferViewSizeNotDivisibleByTypeSize); } const int64_t fixedLengthArrayCount = classProperty.count.value_or(0); if (fixedLengthArrayCount > 0 && propertyTableProperty.arrayOffsets >= 0) { - return createInvalidPropertyView>( + return PropertyTablePropertyView>( PropertyTablePropertyViewStatus:: ErrorArrayCountAndOffsetBufferCoexist); } if (fixedLengthArrayCount <= 0 && propertyTableProperty.arrayOffsets < 0) { - return createInvalidPropertyView>( + return PropertyTablePropertyView>( PropertyTablePropertyViewStatus:: ErrorArrayCountAndOffsetBufferDontExist); } @@ -1115,13 +1097,12 @@ class PropertyTableView { } if (values.size() < maxRequiredBytes) { - return createInvalidPropertyView>( + return PropertyTablePropertyView>( PropertyTablePropertyViewStatus:: ErrorBufferViewSizeDoesNotMatchPropertyTableCount); } return PropertyTablePropertyView>( - PropertyTablePropertyViewStatus::Valid, values, gsl::span(), gsl::span(), @@ -1137,7 +1118,7 @@ class PropertyTableView { convertArrayOffsetTypeStringToPropertyComponentType( propertyTableProperty.arrayOffsetType); if (arrayOffsetType == PropertyComponentType::None) { - return createInvalidPropertyView>( + return PropertyTablePropertyView>( PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); } @@ -1151,11 +1132,10 @@ class PropertyTableView { checkBitsSize, arrayOffsets); if (status != PropertyTablePropertyViewStatus::Valid) { - return createInvalidPropertyView>(status); + return PropertyTablePropertyView>(status); } return PropertyTablePropertyView>( - PropertyTablePropertyViewStatus::Valid, values, arrayOffsets, gsl::span(), @@ -1190,16 +1170,6 @@ class PropertyTableView { size_t propertyTableCount, gsl::span& stringOffsetsBuffer) const noexcept; - template - static PropertyTablePropertyView createInvalidPropertyView( - PropertyTablePropertyViewStatus invalidStatus) noexcept { - return PropertyTablePropertyView( - invalidStatus, - gsl::span(), - 0, - false); - } - const Model* _pModel; const PropertyTable* _pPropertyTable; const Class* _pClass; diff --git a/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h index f388d2c6e..c843781d2 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h @@ -1,17 +1,12 @@ #pragma once -#include "CesiumGltf/ClassProperty.h" -#include "CesiumGltf/ExtensionModelExtStructuralMetadata.h" -#include "CesiumGltf/Image.h" #include "CesiumGltf/ImageCesium.h" -#include "CesiumGltf/Model.h" -#include "CesiumGltf/PropertyTexture.h" +#include "CesiumGltf/PropertyTextureProperty.h" +#include "CesiumGltf/PropertyTypeTraits.h" #include "CesiumGltf/Sampler.h" -#include "CesiumGltf/Texture.h" #include #include -#include namespace CesiumGltf { /** @@ -24,74 +19,93 @@ namespace CesiumGltf { */ enum class PropertyTexturePropertyViewStatus { /** - * @brief This view is valid and ready to use. + * @brief This property view is valid and ready to use. */ Valid, /** - * @brief This view has not been initialized. + * @brief This property view was initialized from an invalid + * {@link PropertyTexture}. */ - ErrorUninitialized, + ErrorInvalidPropertyTexture, /** - * @brief This property texture property is associated with a class property - * with an invalid or unsupported type. + * @brief This property view is trying to view a property that does not exist + * in the {@link PropertyTexture}. */ - ErrorInvalidClassProperty, + ErrorNonexistentProperty, /** - * @brief This property texture property has a texture index that does not - * exist in the glTF. + * @brief This property view is associated with a {@link ClassProperty} of an + * unsupported type. + */ + ErrorUnsupportedProperty, + + /** + * @brief This property view's type does not match what is + * specified in {@link ClassProperty::type}. + */ + ErrorTypeMismatch, + + /** + * @brief This property view's component type does not match what + * is specified in {@link ClassProperty::componentType}. + */ + ErrorComponentTypeMismatch, + + /** + * @brief This property view differs from what is specified in + * {@link ClassProperty::array}. + */ + ErrorArrayTypeMismatch, + + /** + * @brief This property view does not have a valid texture index. */ ErrorInvalidTexture, /** - * @brief This property texture property has a texture sampler index that does - * not exist in the glTF. + * @brief This property view does not have a valid sampler index. */ - ErrorInvalidTextureSampler, + ErrorInvalidSampler, /** - * @brief This property texture property has an image index that does not - * exist in the glTF. + * @brief This property view does not have a valid image index. */ ErrorInvalidImage, /** - * @brief This property texture property points to an empty image. + * @brief This property is viewing an empty image. */ ErrorEmptyImage, + /** + * @brief This property uses an image with multi-byte channels. Only + * single-byte channels are supported. + */ + ErrorInvalidBytesPerChannel, + /** * @brief The channels of this property texture property are invalid. * Channels must be in the range 0-3, with a minimum of one channel. Although * more than four channels can be defined for specialized texture * formats, this view only supports a maximum of four channels. */ - ErrorInvalidChannels -}; + ErrorInvalidChannels, -/** - * @brief The supported component types that can exist in property id textures. - */ -enum class PropertyTexturePropertyComponentType { - Uint8 - // TODO: add more types. Currently this is the only one outputted by stb, - // so change stb call to output more of the original types. -}; + /** + * @brief This property texture property is trying to sample channels that + * don't exist in the image. + */ + ErrorTooManyChannels, -/** - * @brief The property texture property value for a pixel. This will contain - * four channels of the specified type. - * - * Only the first n components will be valid, where n is the number of channels - * in this property texture property. - * - * @tparam T The component type, must correspond to a valid - * {@link PropertyTexturePropertyComponentType}. - */ -template struct PropertyTexturePropertyValue { - T components[4]; + /** + * @brief The channels of this property texture property do not provide the + * exact number of bytes required by the property type. This may be because + * an incorrect number of channels was provided, or because the image itself + * has a different channel count / byte size than expected. + */ + ErrorChannelsAndTypeMismatch, }; /** @@ -100,144 +114,142 @@ template struct PropertyTexturePropertyValue { * Provides utilities to sample the property texture property using texture * coordinates. */ -class PropertyTexturePropertyView { +template class PropertyTexturePropertyView { public: /** - * @brief Construct an uninitialized, invalid view. + * @brief Constructs an invalid instance for a non-existent property. */ - PropertyTexturePropertyView() noexcept; + PropertyTexturePropertyView() noexcept + : _status(PropertyTexturePropertyViewStatus::ErrorUninitialized), + _pSampler(nullptr), + _pImage(nullptr), + _texCoordSetIndex(0), + _channels(), + _swizzle(""), + _normalized(false) {} /** - * @brief Construct a view of the data specified by the given property texture - * property. Assumes the model has already been validated by the - * {@link PropertyTextureView} that invoked this constructor. + * @brief Constructs an invalid instance for an erroneous property. * - * @param model The glTF in which to look for the data specified by the - * property texture property. - * @param classProperty The property description. - * @param propertyTextureProperty The property texture property + * @param status The {@link PropertyTexturePropertyViewStatus} indicating the error with the property. + */ + PropertyTexturePropertyView(PropertyTexturePropertyViewStatus status) noexcept + : _status(status), + _pSampler(nullptr), + _pImage(nullptr), + _texCoordSetIndex(0), + _channels(), + _swizzle(""), + _normalized(false) {} + + /** + * @brief Construct a valid view of the data specified by a {@link PropertyTextureProperty}. + * + * @param pSampler A pointer to the sampler used by the property. + * @param pImage A pointer to the image used by the property. + * @param texCoordSetIndex The value of {@link PropertyTextureProperty::texcoord}. + * @param channels The value of {@link PropertyTextureProperty::channels}. + * @param normalized Whether this property has a normalized integer type. */ PropertyTexturePropertyView( - const Model& model, - const ClassProperty& classProperty, - const PropertyTextureProperty& propertyTextureProperty) noexcept; + const Sampler& sampler, + const ImageCesium& image, + int64_t texCoordSetIndex, + const std::vector& channels, + bool normalized) noexcept + : _status(PropertyTexturePropertyViewStatus::Valid), + _pSampler(&sampler), + _pImage(&image), + _texCoordSetIndex(texCoordSetIndex), + _channels(channels), + _swizzle(""), + _normalized(normalized) { + for (size_t i = 0; i < _channels.size(); ++i) { + switch (_channels[i]) { + case 0: + _swizzle += "r"; + break; + case 1: + _swizzle += "g"; + break; + case 2: + _swizzle += "b"; + break; + case 3: + _swizzle += "a"; + break; + default: + assert(false && "A valid channels vector must be passed to the view."); + } + } + } /** - * @brief Gets the unswizzled property value for the given texture - * coordinates. + * @brief Gets the property value for the given texture coordinates. The + * sampler's wrapping mode will be used when sampling the texture. * - * Will return 0s when the status is not Valid or when the templated - * component type doesn't match the image's channel byte-size. + * @param u The u-component of the texture coordinates. + * @param v The v-component of the texture coordinates. * - * @tparam T The component type to use when interpreting the channels of the - * property's pixel value. - * @param u The u-component of the texture coordinates. Must be within - * [0.0, 1.0]. - * @param v The v-component of the texture coordinates. Must be within - * [0.0, 1.0]. - * @return The property at the nearest pixel to the texture coordinates. + * @return The value at the nearest pixel to the texture coordinates. */ - template - PropertyTexturePropertyValue get(double u, double v) const noexcept { - PropertyTexturePropertyValue property; - property.components[0] = 0; - property.components[1] = 0; - property.components[2] = 0; - property.components[3] = 0; - - if (this->_status != PropertyTexturePropertyViewStatus::Valid || - sizeof(T) != this->_pImage->bytesPerChannel) { - return property; - } - double fraction = 0, integral = 0; - int64_t integer = 0; - switch (this->_pSampler->wrapS) { - case Sampler::WrapS::REPEAT: - fraction = std::modf(u, &integral); - // Wrap negative values. - u = fraction < 0 ? 1.0 - fraction : fraction; - break; - case Sampler::WrapS::MIRRORED_REPEAT: - fraction = std::abs(std::modf(u, &integral)); - integer = static_cast(std::abs(integral)); - // If the integer part is odd, the direction is reversed. - u = integer % 2 == 1 ? 1.0 - fraction : fraction; - break; - case Sampler::WrapS::CLAMP_TO_EDGE: - default: - u = std::clamp(u, 0.0, 1.0); - break; - } + ElementType get(double u, double v) const noexcept { + assert( + _status == PropertyTexturePropertyViewStatus::Valid && + "Check the status() first to make sure view is valid"); - switch (this->_pSampler->wrapT) { - case Sampler::WrapT::REPEAT: - fraction = std::modf(v, &integral); - // Wrap negative values. - v = fraction < 0 ? 1.0 - fraction : fraction; - break; - case Sampler::WrapT::MIRRORED_REPEAT: - fraction = std::abs(std::modf(v, &integral)); - integer = static_cast(std::abs(integral)); - // If the integer part is odd, the direction is reversed. - v = integer % 2 == 1 ? 1.0 - fraction : fraction; - break; - case Sampler::WrapT::CLAMP_TO_EDGE: - default: - v = std::clamp(u, 0.0, 1.0); - break; - } + double wrappedU = applySamplerWrapS(u, this->_pSampler->wrapS); + double wrappedV = applySamplerWrapT(v, this->_pSampler->wrapT); - // Clamp here to ensure no out-of-bounds data access. + // TODO: account for sampler's filter (can be nearest or linear) + + // For nearest filtering, std::floor is used instead of std::round. + // This is because filtering is supposed to consider the pixel centers. But + // memory access here acts as sampling the beginning of the pixel. Example: + // 0.4 * 2 = 0.8. In a 2x1 pixel image, that should be closer to the left + // pixel's center. But it will round to 1.0 which corresponds to the right + // pixel. So the right pixel has a bigger range than the left one, which is + // incorrect. + double xCoord = std::floor(wrappedU * this->_pImage->width); + double yCoord = std::floor(wrappedV * this->_pImage->height); + + // Clamp to ensure no out-of-bounds data access int64_t x = std::clamp( - std::llround(u * this->_pImage->width), + static_cast(xCoord), 0LL, static_cast(this->_pImage->width) - 1); int64_t y = std::clamp( - std::llround(v * this->_pImage->height), + static_cast(yCoord), 0LL, static_cast(this->_pImage->height) - 1); - int64_t pixelOffset = this->_pImage->bytesPerChannel * - this->_pImage->channels * - (y * this->_pImage->width + x); - const T* pRedChannel = reinterpret_cast( - this->_pImage->pixelData.data() + pixelOffset); + int64_t pixelIndex = this->_pImage->bytesPerChannel * + this->_pImage->channels * + (y * this->_pImage->width + x); + + // TODO: Currently stb only outputs uint8 pixel types. If that + // changes this should account for additional pixel byte sizes. + const uint8_t* pValue = reinterpret_cast( + this->_pImage->pixelData.data() + pixelIndex); - // TODO: account for the sampler filter. - // TODO: it is possible for channels to represent multi-byte values for a - // property. But we only support uint8 property types at the moment. + std::vector channelValues(this->_channels.size()); for (size_t i = 0; i < this->_channels.size(); i++) { - const size_t channel = static_cast(this->_channels[i]); - property.components[channel] = *(pRedChannel + channel); + channelValues[i] = *(pValue + this->_channels[i]); } - return property; + return assembleValueFromChannels(channelValues); } /** * @brief Get the status of this view. * - * If invalid, it will not be safe to sample from this view. + * If invalid, this view cannot be sampled. */ PropertyTexturePropertyViewStatus status() const noexcept { return this->_status; } - /** - * @brief Get the component type for this property. - */ - PropertyTexturePropertyComponentType - getPropertyComponentType() const noexcept { - return this->_componentType; - } - - /** - * @brief Get the component count of this property. This is equivalent to how - * many channels a pixel value for this property will use. - */ - int64_t getComponentCount() const noexcept { return this->_componentCount; } - /** * @brief Get the texture coordinate set index for this property. */ @@ -271,16 +283,176 @@ class PropertyTexturePropertyView { const std::string& getSwizzle() const noexcept { return this->_swizzle; } private: + ElementType + assembleValueFromChannels(const std::vector& bytes) const noexcept { + assert(bytes.size() > 0 && "Channel input must have at least one value."); + + if constexpr (IsMetadataScalar::value) { + return assembleScalarValue(bytes); + } + + if constexpr (IsMetadataVecN::value) { + return assembleVecNValue(bytes); + } + + if constexpr (IsMetadataArray::value) { + return assembleArrayValue::type>( + bytes); + } + } + + ElementType + assembleScalarValue(const std::vector& bytes) const noexcept { + if constexpr (std::is_same_v) { + assert( + bytes.size() == sizeof(float) && + "Not enough channel inputs to construct a float."); + uint32_t resultAsUint = 0; + for (size_t i = 0; i < bytes.size(); i++) { + resultAsUint |= static_cast(bytes[i]) << i * 8; + } + + // Reinterpret the bits as a float. + return *reinterpret_cast(&resultAsUint); + } + + if constexpr ( + IsMetadataInteger::value && + std::is_signed_v) { + using UintType = std::make_unsigned_t; + UintType resultAsUint = 0; + for (size_t i = 0; i < bytes.size(); i++) { + resultAsUint |= static_cast(bytes[i]) << i * 8; + } + // Reinterpret the bits as a signed integer. + return *reinterpret_cast(&resultAsUint); + } else if constexpr (IsMetadataInteger::value) { + ElementType result = 0; + for (size_t i = 0; i < bytes.size(); i++) { + result |= static_cast(bytes[i]) << i * 8; + } + return result; + } + } + + ElementType + assembleVecNValue(const std::vector& bytes) const noexcept { + ElementType result = ElementType(); + + PropertyComponentType componentType = + TypeToPropertyType::component; + size_t componentSize = getSizeOfComponentType(componentType); + assert( + componentSize <= 2 && + "Components cannot be larger than two bytes in size."); + + if (componentSize == 2) { + assert( + TypeToPropertyType::value == PropertyType::Vec2 && + "Only vec2s can contain two-byte integer components."); + uint16_t x = static_cast(bytes[0]) | + (static_cast(bytes[1]) << 8); + uint16_t y = static_cast(bytes[2]) | + (static_cast(bytes[3]) << 8); + + if (componentType == PropertyComponentType::Int16) { + result[0] = *reinterpret_cast(&x); + result[1] = *reinterpret_cast(&y); + } else { + result[0] = x; + result[1] = y; + } + + return result; + } + + if (componentType == PropertyComponentType::Int8) { + for (size_t i = 0; i < bytes.size(); i++) { + result[i] = *reinterpret_cast(&bytes[i]); + } + } else { + for (size_t i = 0; i < bytes.size(); i++) { + result[i] = bytes[i]; + } + } + + return result; + } + + template + PropertyArrayView + assembleArrayValue(const std::vector& bytes) const noexcept { + if (sizeof(T) == 2) { + std::vector result(bytes.size() / sizeof(T)); + for (int i = 0, b = 0; i < result.size(); i++, b += 2) { + if constexpr (std::is_signed_v) { + using UintType = std::make_unsigned_t; + UintType resultAsUint = static_cast(bytes[b]) | + (static_cast(bytes[b + 1]) << 8); + result[i] = *reinterpret_cast(&resultAsUint); + } else { + result[i] = + static_cast(bytes[b]) | (static_cast(bytes[b + 1]) << 8); + } + } + + return PropertyArrayView(std::move(result)); + } + + std::vector result(bytes.size()); + for (size_t i = 0; i < bytes.size(); i++) { + if constexpr (std::is_signed_v) { + result[i] = *reinterpret_cast(&bytes[i]); + } else { + result[i] = bytes[i]; + } + } + return PropertyArrayView(std::move(result)); + } + + double applySamplerWrapS(const double u, const int32_t wrapS) const noexcept { + if (wrapS == Sampler::WrapS::REPEAT) { + double integral = 0; + double fraction = std::modf(u, &integral); + return fraction < 0 ? 1.0 - fraction : fraction; + } + + if (wrapS == Sampler::WrapS::MIRRORED_REPEAT) { + double integral = 0; + double fraction = std::abs(std::modf(u, &integral)); + int64_t integer = static_cast(std::abs(integral)); + // If the integer part is odd, the direction is reversed. + return integer % 2 == 1 ? 1.0 - fraction : fraction; + } + + return glm::clamp(u, 0.0, 1.0); + } + + double applySamplerWrapT(const double v, const int32_t wrapT) const noexcept { + if (wrapT == Sampler::WrapT::REPEAT) { + double integral = 0; + double fraction = std::modf(v, &integral); + return fraction < 0 ? 1.0 - fraction : fraction; + } + + if (wrapT == Sampler::WrapT::MIRRORED_REPEAT) { + double integral = 0; + double fraction = std::abs(std::modf(v, &integral)); + int64_t integer = static_cast(std::abs(integral)); + // If the integer part is odd, the direction is reversed. + return integer % 2 == 1 ? 1.0 - fraction : fraction; + } + + return glm::clamp(v, 0.0, 1.0); + } + PropertyTexturePropertyViewStatus _status; - const ClassProperty* _pClassProperty; const Sampler* _pSampler; const ImageCesium* _pImage; int64_t _texCoordSetIndex; std::vector _channels; std::string _swizzle; - PropertyTexturePropertyComponentType _componentType; - int64_t _componentCount; bool _normalized; }; } // namespace CesiumGltf diff --git a/CesiumGltf/include/CesiumGltf/PropertyTextureView.h b/CesiumGltf/include/CesiumGltf/PropertyTextureView.h index 22eeeb1c4..1ac63f84d 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTextureView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTextureView.h @@ -6,8 +6,6 @@ #include "CesiumGltf/PropertyTexture.h" #include "CesiumGltf/PropertyTexturePropertyView.h" #include "CesiumGltf/Texture.h" -#include "Image.h" -#include "ImageCesium.h" #include "Model.h" namespace CesiumGltf { @@ -24,11 +22,6 @@ enum class PropertyTextureViewStatus { */ Valid, - /** - * @brief This property texture view is not initialized. - */ - ErrorUninitialized, - /** * @brief The glTF is missing the EXT_structural_metadata extension. */ @@ -43,32 +36,24 @@ enum class PropertyTextureViewStatus { * @brief The property texture's specified class could not be found in the * extension. */ - ErrorClassNotFound, - - /** - * @brief A property name specified in the property texture could not be found - * in the class. - */ - ErrorClassPropertyNotFound + ErrorClassNotFound }; /** - * @brief A view on the {@link PropertyTexture}. + * @brief A view on a {@link PropertyTexture}. * - * Provides access to views on the property texture properties. + * This should be used to get a {@link PropertyTexturePropertyView} of a property in the property texture. + * It will validate the EXT_structural_metadata format and ensure {@link PropertyTexturePropertyView} + * does not access out of bounds. */ class PropertyTextureView { public: /** - * @brief Construct an uninitialized, invalid property texture view. - */ - PropertyTextureView() noexcept; - - /** - * @brief Construct a view for the property texture. + * @brief Construct a PropertyTextureView. * - * @param model The glTF in which to look for the property texture's data. - * @param propertyTexture The property texture to create a view for. + * @param model The glTF that contains the property texture's data. + * @param propertyTexture The {@link PropertyTexture} + * from which the view will retrieve data. */ PropertyTextureView( const Model& model, @@ -93,18 +78,264 @@ class PropertyTextureView { const ClassProperty* getClassProperty(const std::string& propertyName) const; /** - * @brief Get the views for this property texture's properties. + * @brief Gets a {@link PropertyTexturePropertyView} that views the data of a + * property stored in the {@link PropertyTexture}. + * + * This method will validate the EXT_structural_metadata format to ensure + * {@link PropertyTexturePropertyView} retrieves the correct data. T must be + * a scalar with a supported component type (uint8_t, uint16_t, uint32_t, + * float), or a glm vecN composed of one of the scalar types. + * PropertyArrayViews are unsupported; if the property describes a + * fixed-length array of scalars, T must be a glm vecN of the same length. + * + * @param propertyName The name of the property to retrieve data from + * @return A {@link PropertyTablePropertyView} of the property. If no valid property is + * found, the property view will be invalid. */ - const std::unordered_map& - getProperties() const noexcept { - return this->_propertyViews; + template + PropertyTexturePropertyView + getPropertyView(const std::string& propertyName) const { + if (this->_status != PropertyTextureViewStatus::Valid) { + return PropertyTexturePropertyView( + PropertyTexturePropertyViewStatus::ErrorInvalidPropertyTexture); + } + + const ClassProperty* pClassProperty = getClassProperty(propertyName); + if (!pClassProperty) { + return PropertyTexturePropertyView( + PropertyTexturePropertyViewStatus::ErrorNonexistentProperty); + } + + return getPropertyViewImpl(propertyName, *pClassProperty); } private: + template + PropertyTexturePropertyView getPropertyViewImpl( + const std::string& propertyName, + const ClassProperty& classProperty) const { + auto propertyTexturePropertyIter = + _pPropertyTexture->properties.find(propertyName); + if (propertyTexturePropertyIter == _pPropertyTexture->properties.end()) { + return PropertyTexturePropertyView( + PropertyTexturePropertyViewStatus::ErrorNonexistentProperty); + } + + const PropertyTextureProperty& propertyTextureProperty = + propertyTexturePropertyIter->second; + + if constexpr (IsMetadataScalar::value) { + return createScalarPropertyView( + classProperty, + propertyTextureProperty); + } else if constexpr (IsMetadataVecN::value) { + } else if constexpr (IsMetadataArray::value) { + return createArrayPropertyView::type>( + classProperty, + propertyTextureProperty); + } else { + return PropertyTexturePropertyView( + PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty); + } + } + + template + PropertyTexturePropertyView createScalarPropertyView( + const ClassProperty& classProperty, + const PropertyTextureProperty& propertyTextureProperty) const { + if (classProperty.array) { + return PropertyTexturePropertyView( + PropertyTexturePropertyViewStatus::ErrorArrayTypeMismatch); + } + + const PropertyType type = convertStringToPropertyType(classProperty.type); + if (TypeToPropertyType::value != type) { + return PropertyTexturePropertyView( + PropertyTexturePropertyViewStatus::ErrorTypeMismatch); + } + + const PropertyComponentType componentType = + convertStringToPropertyComponentType( + classProperty.componentType.value_or("")); + if (TypeToPropertyType::component != componentType) { + return PropertyTexturePropertyView( + PropertyTexturePropertyViewStatus::ErrorComponentTypeMismatch); + } + + // Eight-byte scalar types are unsupported. + if constexpr ( + std::is_same_v || std::is_same_v || + std::is_same_v) { + return PropertyTexturePropertyView( + PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty); + } + + return createPropertyViewImpl( + classProperty, + propertyTextureProperty, + sizeof(T)); + } + + template + PropertyTexturePropertyView createVecNPropertyView( + const ClassProperty& classProperty, + const PropertyTextureProperty& propertyTextureProperty) const { + if (classProperty.array) { + return PropertyTexturePropertyView( + PropertyTexturePropertyViewStatus::ErrorArrayTypeMismatch); + } + + const PropertyType type = convertStringToPropertyType(classProperty.type); + if (TypeToPropertyType::value != type) { + return PropertyTexturePropertyView( + PropertyTexturePropertyViewStatus::ErrorTypeMismatch); + } + + const PropertyComponentType componentType = + convertStringToPropertyComponentType( + classProperty.componentType.value_or("")); + if (TypeToPropertyType::component != componentType) { + return PropertyTexturePropertyView( + PropertyTexturePropertyViewStatus::ErrorComponentTypeMismatch); + } + + // Only uint8 and uint16s are supported. + if (componentType != PropertyComponentType::Uint8 && + componentType != PropertyComponentType::Uint16) { + return PropertyTexturePropertyView( + PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty); + } + + // Only up to four bytes of image data are supported. + if (sizeof(T) > 4) { + return PropertyTexturePropertyView( + PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty); + } + + return createPropertyViewImpl( + classProperty, + propertyTextureProperty, + sizeof(T)); + } + + template + PropertyTexturePropertyView> createArrayPropertyView( + const ClassProperty& classProperty, + const PropertyTextureProperty& propertyTextureProperty) const { + if (!classProperty.array) { + return PropertyTexturePropertyView>( + PropertyTexturePropertyViewStatus::ErrorArrayTypeMismatch); + } + + const PropertyType type = convertStringToPropertyType(classProperty.type); + if (TypeToPropertyType::value != type) { + return PropertyTexturePropertyView>( + PropertyTexturePropertyViewStatus::ErrorTypeMismatch); + } + + // Only scalar arrays are supported. + if (type != PropertyType::Scalar) { + return PropertyTexturePropertyView>( + PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty); + } + + // Only up to four elements are supported. + int64_t count = classProperty.count.value_or(0); + if (count <= 0 || count > 4) { + return PropertyTexturePropertyView>( + PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty); + } + + const PropertyComponentType componentType = + convertStringToPropertyComponentType( + classProperty.componentType.value_or("")); + if (TypeToPropertyType::component != componentType) { + return PropertyTexturePropertyView>( + PropertyTexturePropertyViewStatus::ErrorComponentTypeMismatch); + } + + // Only uint8 and uint16s are supported. + if (componentType != PropertyComponentType::Uint8 && + componentType != PropertyComponentType::Uint16) { + return PropertyTexturePropertyView>( + PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty); + } + + if (componentType == PropertyComponentType::Uint16 && count > 2) { + return PropertyTexturePropertyView>( + PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty); + } + + return createPropertyViewImpl>( + classProperty, + propertyTextureProperty, + count * sizeof(T)); + } + + template + PropertyTexturePropertyView createPropertyViewImpl( + const ClassProperty& classProperty, + const PropertyTextureProperty& propertyTextureProperty, + size_t elementSize) const { + int32_t samplerIndex; + int32_t imageIndex; + + auto status = + getTextureSafe(propertyTextureProperty.index, samplerIndex, imageIndex); + + if (status != PropertyTexturePropertyViewStatus::Valid) { + return PropertyTexturePropertyView(status); + } + + status = checkSampler(samplerIndex); + if (status != PropertyTexturePropertyViewStatus::Valid) { + return PropertyTexturePropertyView(status); + } + + status = checkImage(samplerIndex); + if (status != PropertyTexturePropertyViewStatus::Valid) { + return PropertyTexturePropertyView(status); + } + + const ImageCesium& image = _pModel->images[imageIndex].cesium; + const std::vector& channels = propertyTextureProperty.channels; + + status = checkChannels(channels, image); + if (status != PropertyTexturePropertyViewStatus::Valid) { + return PropertyTexturePropertyView(status); + } + + if (channels.size() * image.bytesPerChannel != elementSize) { + return PropertyTexturePropertyViewStatus::ErrorChannelsAndTypeMismatch; + } + + return PropertyTexturePropertyView( + _pModel->samplers[samplerIndex], + image, + propertyTextureProperty.texCoord, + channels, + classProperty.normalized); + } + + PropertyTexturePropertyViewStatus getTextureSafe( + const int32_t textureIndex, + int32_t& samplerIndex, + int32_t& imageIndex) const noexcept; + + PropertyTexturePropertyViewStatus + checkSampler(const int32_t samplerIndex) const noexcept; + + PropertyTexturePropertyViewStatus + checkImage(const int32_t imageIndex) const noexcept; + + PropertyTexturePropertyViewStatus checkChannels( + const std::vector& channels, + const ImageCesium& image) const noexcept; + const Model* _pModel; const PropertyTexture* _pPropertyTexture; const Class* _pClass; - std::unordered_map _propertyViews; + PropertyTextureViewStatus _status; }; } // namespace CesiumGltf diff --git a/CesiumGltf/include/CesiumGltf/PropertyType.h b/CesiumGltf/include/CesiumGltf/PropertyType.h index e5771a015..e5c964ec8 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyType.h +++ b/CesiumGltf/include/CesiumGltf/PropertyType.h @@ -4,6 +4,8 @@ #include #include +#include + namespace CesiumGltf { enum class PropertyType { Invalid, @@ -52,4 +54,9 @@ convertStringOffsetTypeStringToPropertyComponentType(const std::string& str); bool isPropertyTypeVecN(PropertyType type); bool isPropertyTypeMatN(PropertyType type); + +glm::length_t getDimensionsFromPropertyType(PropertyType type); + +size_t getSizeOfComponentType(PropertyComponentType componentType); + } // namespace CesiumGltf diff --git a/CesiumGltf/src/PropertyTableView.cpp b/CesiumGltf/src/PropertyTableView.cpp index a280527f6..0e861151e 100644 --- a/CesiumGltf/src/PropertyTableView.cpp +++ b/CesiumGltf/src/PropertyTableView.cpp @@ -125,13 +125,12 @@ PropertyTableView::PropertyTableView( } auto classIter = schema->classes.find(_pPropertyTable->classProperty); - if (classIter != schema->classes.end()) { - _pClass = &classIter->second; - } - - if (!_pClass) { + if (classIter == schema->classes.end()) { _status = PropertyTableViewStatus::ErrorClassNotFound; + return; } + + _pClass = &classIter->second; } const ClassProperty* @@ -282,26 +281,26 @@ PropertyTableView::getStringPropertyValues( const ClassProperty& classProperty, const PropertyTableProperty& propertyTableProperty) const { if (classProperty.array) { - return createInvalidPropertyView( + return PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); } if (classProperty.type != ClassProperty::Type::STRING) { - return createInvalidPropertyView( + return PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorTypeMismatch); } gsl::span values; auto status = getBufferSafe(propertyTableProperty.values, values); if (status != PropertyTablePropertyViewStatus::Valid) { - return createInvalidPropertyView(status); + return PropertyTablePropertyView(status); } const PropertyComponentType offsetType = convertStringOffsetTypeStringToPropertyComponentType( propertyTableProperty.stringOffsetType); if (offsetType == PropertyComponentType::None) { - return createInvalidPropertyView( + return PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorInvalidStringOffsetType); } @@ -313,11 +312,10 @@ PropertyTableView::getStringPropertyValues( static_cast(_pPropertyTable->count), stringOffsets); if (status != PropertyTablePropertyViewStatus::Valid) { - return createInvalidPropertyView(status); + return PropertyTablePropertyView(status); } return PropertyTablePropertyView( - PropertyTablePropertyViewStatus::Valid, values, gsl::span(), stringOffsets, @@ -333,31 +331,31 @@ PropertyTableView::getStringArrayPropertyValues( const ClassProperty& classProperty, const PropertyTableProperty& propertyTableProperty) const { if (!classProperty.array) { - return createInvalidPropertyView>( + return PropertyTablePropertyView>( PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); } if (classProperty.type != ClassProperty::Type::STRING) { - return createInvalidPropertyView>( + return PropertyTablePropertyView>( PropertyTablePropertyViewStatus::ErrorTypeMismatch); } gsl::span values; auto status = getBufferSafe(propertyTableProperty.values, values); if (status != PropertyTablePropertyViewStatus::Valid) { - return createInvalidPropertyView>( + return PropertyTablePropertyView>( status); } // Check if array is fixed or variable length const int64_t fixedLengthArrayCount = classProperty.count.value_or(0); if (fixedLengthArrayCount > 0 && propertyTableProperty.arrayOffsets >= 0) { - return createInvalidPropertyView>( + return PropertyTablePropertyView>( PropertyTablePropertyViewStatus::ErrorArrayCountAndOffsetBufferCoexist); } if (fixedLengthArrayCount <= 0 && propertyTableProperty.arrayOffsets < 0) { - return createInvalidPropertyView>( + return PropertyTablePropertyView>( PropertyTablePropertyViewStatus:: ErrorArrayCountAndOffsetBufferDontExist); } @@ -367,12 +365,12 @@ PropertyTableView::getStringArrayPropertyValues( convertStringOffsetTypeStringToPropertyComponentType( propertyTableProperty.stringOffsetType); if (stringOffsetType == PropertyComponentType::None) { - return createInvalidPropertyView>( + return PropertyTablePropertyView>( PropertyTablePropertyViewStatus::ErrorInvalidStringOffsetType); } if (propertyTableProperty.stringOffsets < 0) { - return createInvalidPropertyView>( + return PropertyTablePropertyView>( PropertyTablePropertyViewStatus::ErrorInvalidStringOffsetBufferView); } @@ -386,12 +384,11 @@ PropertyTableView::getStringArrayPropertyValues( static_cast(_pPropertyTable->count * fixedLengthArrayCount), stringOffsets); if (status != PropertyTablePropertyViewStatus::Valid) { - return createInvalidPropertyView>( + return PropertyTablePropertyView>( status); } return PropertyTablePropertyView>( - PropertyTablePropertyViewStatus::Valid, values, gsl::span(), stringOffsets, @@ -407,12 +404,12 @@ PropertyTableView::getStringArrayPropertyValues( convertArrayOffsetTypeStringToPropertyComponentType( propertyTableProperty.arrayOffsetType); if (arrayOffsetType == PropertyComponentType::None) { - return createInvalidPropertyView>( + return PropertyTablePropertyView>( PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); } if (propertyTableProperty.arrayOffsets < 0) { - return createInvalidPropertyView>( + return PropertyTablePropertyView>( PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetBufferView); } @@ -420,14 +417,14 @@ PropertyTableView::getStringArrayPropertyValues( gsl::span stringOffsets; status = getBufferSafe(propertyTableProperty.stringOffsets, stringOffsets); if (status != PropertyTablePropertyViewStatus::Valid) { - return createInvalidPropertyView>( + return PropertyTablePropertyView>( status); } gsl::span arrayOffsets; status = getBufferSafe(propertyTableProperty.arrayOffsets, arrayOffsets); if (status != PropertyTablePropertyViewStatus::Valid) { - return createInvalidPropertyView>( + return PropertyTablePropertyView>( status); } @@ -470,12 +467,11 @@ PropertyTableView::getStringArrayPropertyValues( } if (status != PropertyTablePropertyViewStatus::Valid) { - return createInvalidPropertyView>( + return PropertyTablePropertyView>( status); } return PropertyTablePropertyView>( - PropertyTablePropertyViewStatus::Valid, values, arrayOffsets, stringOffsets, diff --git a/CesiumGltf/src/PropertyTexturePropertyView.cpp b/CesiumGltf/src/PropertyTexturePropertyView.cpp deleted file mode 100644 index a779a73ee..000000000 --- a/CesiumGltf/src/PropertyTexturePropertyView.cpp +++ /dev/null @@ -1,145 +0,0 @@ - -#include "CesiumGltf/PropertyTexturePropertyView.h" - -#include - -namespace CesiumGltf { -namespace { -static std::unordered_set supportedTypes{ - ClassProperty::Type::SCALAR, - ClassProperty::Type::VEC2, - ClassProperty::Type::VEC3, - ClassProperty::Type::VEC4}; - -bool isValidClassProperty(const ClassProperty& classProperty) { - if (supportedTypes.find(classProperty.type) == supportedTypes.end()) { - return false; - } - - // Non-arrays don't need further validation. - if (!classProperty.array) { - return true; - } - - if (classProperty.type != ClassProperty::Type::SCALAR) { - return false; - } - - int64_t count = classProperty.count.value_or(0); - return count > 0 && count <= 4; -} -} // namespace - -PropertyTexturePropertyView::PropertyTexturePropertyView() noexcept - : _status(PropertyTexturePropertyViewStatus::ErrorUninitialized), - _pClassProperty(nullptr), - _pSampler(nullptr), - _pImage(nullptr), - _texCoordSetIndex(0), - _channels(), - _swizzle(""), - _componentType(PropertyTexturePropertyComponentType::Uint8), - _componentCount(0), - _normalized(false) {} - -PropertyTexturePropertyView::PropertyTexturePropertyView( - const Model& model, - const ClassProperty& classProperty, - const PropertyTextureProperty& propertyTextureProperty) noexcept - : _status(PropertyTexturePropertyViewStatus::ErrorUninitialized), - _pClassProperty(&classProperty), - _pSampler(nullptr), - _pImage(nullptr), - _texCoordSetIndex(propertyTextureProperty.texCoord), - _channels(), - _swizzle(""), - _componentType(PropertyTexturePropertyComponentType::Uint8), - _componentCount(0), - _normalized(false) { - - if (!isValidClassProperty(classProperty)) { - this->_status = - PropertyTexturePropertyViewStatus::ErrorInvalidClassProperty; - return; - } - - if (classProperty.array) { - this->_componentCount = *classProperty.count; - } else if (classProperty.type == ClassProperty::Type::SCALAR) { - this->_componentCount = 1; - } else if (classProperty.type == ClassProperty::Type::VEC2) { - this->_componentCount = 2; - } else if (classProperty.type == ClassProperty::Type::VEC3) { - this->_componentCount = 3; - } else if (classProperty.type == ClassProperty::Type::VEC4) { - this->_componentCount = 4; - } - - const int64_t index = propertyTextureProperty.index; - if (index < 0 || static_cast(index) >= model.textures.size()) { - this->_status = PropertyTexturePropertyViewStatus::ErrorInvalidTexture; - return; - } - - const Texture& texture = model.textures[static_cast(index)]; - if (texture.sampler < 0 || - static_cast(texture.sampler) >= model.samplers.size()) { - this->_status = - PropertyTexturePropertyViewStatus::ErrorInvalidTextureSampler; - return; - } - - this->_pSampler = &model.samplers[static_cast(texture.sampler)]; - - if (texture.source < 0 || - static_cast(texture.source) >= model.images.size()) { - this->_status = PropertyTexturePropertyViewStatus::ErrorInvalidImage; - return; - } - - this->_pImage = &model.images[static_cast(texture.source)].cesium; - if (this->_pImage->width < 1 || this->_pImage->height < 1) { - this->_status = PropertyTexturePropertyViewStatus::ErrorEmptyImage; - return; - } - - // TODO: support more component types - // this->_componentType = ... - - this->_normalized = this->_pClassProperty->normalized; - - // TODO: channels can represent a multi-byte value, so the last check will - // need to change. - const std::vector& channels = propertyTextureProperty.channels; - if (channels.size() == 0 || channels.size() > 4 || - channels.size() > static_cast(this->_pImage->channels) || - channels.size() != static_cast(this->_componentCount)) { - this->_status = PropertyTexturePropertyViewStatus::ErrorInvalidChannels; - return; - } - - for (size_t i = 0; i < channels.size(); ++i) { - switch (channels[i]) { - case 0: - this->_swizzle += "r"; - break; - case 1: - this->_swizzle += "g"; - break; - case 2: - this->_swizzle += "b"; - break; - case 3: - this->_swizzle += "a"; - break; - default: - this->_status = PropertyTexturePropertyViewStatus::ErrorInvalidChannels; - return; - } - } - - this->_channels = channels; - - this->_status = PropertyTexturePropertyViewStatus::Valid; -} -} // namespace CesiumGltf diff --git a/CesiumGltf/src/PropertyTextureView.cpp b/CesiumGltf/src/PropertyTextureView.cpp index c5c67de05..24c9d10ff 100644 --- a/CesiumGltf/src/PropertyTextureView.cpp +++ b/CesiumGltf/src/PropertyTextureView.cpp @@ -1,22 +1,13 @@ #include "CesiumGltf/PropertyTextureView.h" namespace CesiumGltf { -PropertyTextureView::PropertyTextureView() noexcept - : _pModel(nullptr), - _pPropertyTexture(nullptr), - _pClass(nullptr), - _propertyViews(), - _status(PropertyTextureViewStatus::ErrorUninitialized) {} - PropertyTextureView::PropertyTextureView( const Model& model, const PropertyTexture& propertyTexture) noexcept : _pModel(&model), _pPropertyTexture(&propertyTexture), _pClass(nullptr), - _propertyViews(), - _status(PropertyTextureViewStatus::ErrorUninitialized) { - + _status() { const ExtensionModelExtStructuralMetadata* pMetadata = model.getExtension(); @@ -38,23 +29,6 @@ PropertyTextureView::PropertyTextureView( } this->_pClass = &classIt->second; - - this->_propertyViews.reserve(propertyTexture.properties.size()); - for (const auto& property : propertyTexture.properties) { - auto classPropertyIt = this->_pClass->properties.find(property.first); - - if (classPropertyIt == this->_pClass->properties.end()) { - this->_status = PropertyTextureViewStatus::ErrorClassPropertyNotFound; - return; - } - - this->_propertyViews[property.first] = PropertyTexturePropertyView( - model, - classPropertyIt->second, - property.second); - } - - this->_status = PropertyTextureViewStatus::Valid; } const ClassProperty* @@ -70,4 +44,71 @@ PropertyTextureView::getClassProperty(const std::string& propertyName) const { return &propertyIter->second; } + +PropertyTexturePropertyViewStatus PropertyTextureView::getTextureSafe( + const int32_t textureIndex, + int32_t& samplerIndex, + int32_t& imageIndex) const noexcept { + if (textureIndex < 0 || + static_cast(textureIndex) >= _pModel->textures.size()) { + return PropertyTexturePropertyViewStatus::ErrorInvalidTexture; + } + + const Texture& texture = _pModel->textures[static_cast(textureIndex)]; + samplerIndex = texture.sampler; + imageIndex = texture.source; + + return PropertyTexturePropertyViewStatus::Valid; +} + +PropertyTexturePropertyViewStatus +PropertyTextureView::checkSampler(const int32_t samplerIndex) const noexcept { + if (samplerIndex < 0 || + static_cast(samplerIndex) >= _pModel->samplers.size()) { + return PropertyTexturePropertyViewStatus::ErrorInvalidSampler; + } + + // TODO: check if sampler filter values are supported + + return PropertyTexturePropertyViewStatus::Valid; +} + +PropertyTexturePropertyViewStatus +PropertyTextureView::checkImage(const int32_t imageIndex) const noexcept { + if (imageIndex < 0 || + static_cast(imageIndex) >= _pModel->images.size()) { + return PropertyTexturePropertyViewStatus::ErrorInvalidImage; + } + + const ImageCesium& image = + _pModel->images[static_cast(imageIndex)].cesium; + + if (image.width < 1 || image.height < 1) { + return PropertyTexturePropertyViewStatus::ErrorEmptyImage; + } + + if (image.bytesPerChannel > 1) { + return PropertyTexturePropertyViewStatus::ErrorInvalidBytesPerChannel; + } + + return PropertyTexturePropertyViewStatus::Valid; +} + +PropertyTexturePropertyViewStatus PropertyTextureView::checkChannels( + const std::vector& channels, + const ImageCesium& image) const noexcept { + int64_t imageChannelCount = static_cast(image.channels); + for (size_t i = 0; i < channels.size(); i++) { + if (channels[i] < 0 || channels[i] >= imageChannelCount) { + return PropertyTexturePropertyViewStatus::ErrorInvalidChannels; + } + } + + if (static_cast(imageChannelCount) < channels.size()) { + return PropertyTexturePropertyViewStatus::ErrorTooManyChannels; + } + + return PropertyTexturePropertyViewStatus::Valid; +} + } // namespace CesiumGltf diff --git a/CesiumGltf/src/PropertyType.cpp b/CesiumGltf/src/PropertyType.cpp index 1e5e4ed62..f30be7931 100644 --- a/CesiumGltf/src/PropertyType.cpp +++ b/CesiumGltf/src/PropertyType.cpp @@ -200,4 +200,43 @@ bool isPropertyTypeMatN(PropertyType type) { return type == PropertyType::Mat2 || type == PropertyType::Mat3 || type == PropertyType::Mat4; } + +glm::length_t getDimensionsFromPropertyType(PropertyType type) { + switch (type) { + case PropertyType::Scalar: + return 1; + case PropertyType::Vec2: + case PropertyType::Mat2: + return 2; + case PropertyType::Vec3: + case PropertyType::Mat3: + return 3; + case PropertyType::Vec4: + case PropertyType::Mat4: + return 4; + default: + return 0; + } +} + +size_t getSizeOfComponentType(PropertyComponentType componentType) { + switch (componentType) { + case PropertyComponentType::Int8: + case PropertyComponentType::Uint8: + return 1; + case PropertyComponentType::Int16: + case PropertyComponentType::Uint16: + return 2; + case PropertyComponentType::Int32: + case PropertyComponentType::Uint32: + case PropertyComponentType::Float32: + return 4; + case PropertyComponentType::Int64: + case PropertyComponentType::Uint64: + case PropertyComponentType::Float64: + return 8; + default: + return 0; + } +} } // namespace CesiumGltf diff --git a/CesiumGltf/test/TestPropertyTablePropertyView.cpp b/CesiumGltf/test/TestPropertyTablePropertyView.cpp index ae1075ec3..8fc0a28c1 100644 --- a/CesiumGltf/test/TestPropertyTablePropertyView.cpp +++ b/CesiumGltf/test/TestPropertyTablePropertyView.cpp @@ -17,7 +17,6 @@ template static void checkNumeric(const std::vector& expected) { std::memcpy(data.data(), expected.data(), data.size()); PropertyTablePropertyView property( - PropertyTablePropertyViewStatus::Valid, gsl::span(data.data(), data.size()), static_cast(expected.size()), false); @@ -47,7 +46,6 @@ static void checkVariableLengthArray( offsets.size() * sizeof(OffsetType)); PropertyTablePropertyView> property( - PropertyTablePropertyViewStatus::Valid, gsl::span(buffer.data(), buffer.size()), gsl::span(offsetBuffer.data(), offsetBuffer.size()), gsl::span(), @@ -79,7 +77,6 @@ static void checkFixedLengthArray( std::memcpy(buffer.data(), data.data(), data.size() * sizeof(T)); PropertyTablePropertyView> property( - PropertyTablePropertyViewStatus::Valid, gsl::span(buffer.data(), buffer.size()), gsl::span(), gsl::span(), @@ -101,7 +98,7 @@ static void checkFixedLengthArray( REQUIRE(expectedIdx == data.size()); } -TEST_CASE("Check scalar numeric property view") { +TEST_CASE("Check scalar PropertyTablePropertyView") { SECTION("Uint8 Scalar") { std::vector data{12, 33, 56, 67}; checkNumeric(data); @@ -127,7 +124,7 @@ TEST_CASE("Check scalar numeric property view") { } } -TEST_CASE("Check vecN numeric property view") { +TEST_CASE("Check vecN PropertyTablePropertyView") { SECTION("Float Vec2") { std::vector data{ glm::vec2(10.001f, 0.005f), @@ -156,7 +153,7 @@ TEST_CASE("Check vecN numeric property view") { } } -TEST_CASE("Check matN numeric property view") { +TEST_CASE("Check matN PropertyTablePropertyView") { SECTION("Float Mat2") { // clang-format off std::vector data{ @@ -218,7 +215,7 @@ TEST_CASE("Check matN numeric property view") { } } -TEST_CASE("Check boolean property") { +TEST_CASE("Check boolean PropertyTablePropertyView") { std::bitset bits = 0b11110101; unsigned long val = bits.to_ulong(); std::vector data(sizeof(val)); @@ -226,7 +223,6 @@ TEST_CASE("Check boolean property") { size_t instanceCount = sizeof(unsigned long) * CHAR_BIT; PropertyTablePropertyView property( - PropertyTablePropertyViewStatus::Valid, gsl::span(data.data(), data.size()), static_cast(instanceCount), false); @@ -235,7 +231,7 @@ TEST_CASE("Check boolean property") { } } -TEST_CASE("Check string property") { +TEST_CASE("Check string PropertyTablePropertyView") { std::vector strings{ "This is a fine test", "What's going on", @@ -273,7 +269,6 @@ TEST_CASE("Check string property") { sizeof(uint32_t)); PropertyTablePropertyView property( - PropertyTablePropertyViewStatus::Valid, gsl::span(buffer.data(), buffer.size()), gsl::span(), gsl::span(offsetBuffer.data(), offsetBuffer.size()), @@ -287,7 +282,7 @@ TEST_CASE("Check string property") { } } -TEST_CASE("Check fixed-length scalar array property") { +TEST_CASE("Check fixed-length scalar array PropertyTablePropertyView") { SECTION("Fixed-length array of 4 uint8_ts") { // clang-format off std::vector data{ @@ -376,7 +371,7 @@ TEST_CASE("Check fixed-length scalar array property") { } } -TEST_CASE("Check fixed-length vecN array property") { +TEST_CASE("Check fixed-length vecN array PropertyTablePropertyView") { SECTION("Fixed-length array of 4 u8vec2s") { // clang-format off std::vector data{ @@ -419,7 +414,7 @@ TEST_CASE("Check fixed-length vecN array property") { } } -TEST_CASE("Check fixed-length matN array property") { +TEST_CASE("Check fixed-length matN array PropertyTablePropertyView") { SECTION("Fixed-length array of 4 i8mat2x2") { // clang-format off std::vector data{ @@ -520,7 +515,7 @@ TEST_CASE("Check fixed-length matN array property") { } } -TEST_CASE("Check variable-length scalar array property") { +TEST_CASE("Check variable-length scalar array PropertyTablePropertyView") { SECTION("Variable-length array of uint8_t") { // clang-format off std::vector data{ @@ -572,7 +567,7 @@ TEST_CASE("Check variable-length scalar array property") { } } -TEST_CASE("Check variable-length vecN array property") { +TEST_CASE("Check variable-length vecN array PropertyTablePropertyView") { SECTION("Variable-length array of ivec2") { // clang-format off std::vector data{ @@ -636,7 +631,7 @@ TEST_CASE("Check variable-length vecN array property") { } } -TEST_CASE("Check variable-length matN array property") { +TEST_CASE("Check variable-length matN array PropertyTablePropertyView") { SECTION("Variable-length array of dmat2") { // clang-format off std::vector data0{ @@ -832,7 +827,6 @@ TEST_CASE("Check fixed-length array of string") { sizeof(uint32_t)); PropertyTablePropertyView> property( - PropertyTablePropertyViewStatus::Valid, gsl::span(buffer.data(), buffer.size()), gsl::span(), gsl::span(stringOffsets.data(), stringOffsets.size()), @@ -855,7 +849,7 @@ TEST_CASE("Check fixed-length array of string") { REQUIRE(expectedIdx == stringCount); } -TEST_CASE("Check variable-length array of strings property") { +TEST_CASE("Check variable-length string array PropertyTablePropertyView") { // clang-format off std::vector arrayOffsets{ 0, @@ -904,7 +898,6 @@ TEST_CASE("Check variable-length array of strings property") { sizeof(uint32_t)); PropertyTablePropertyView> property( - PropertyTablePropertyViewStatus::Valid, gsl::span(buffer.data(), buffer.size()), gsl::span( reinterpret_cast(arrayOffsets.data()), @@ -929,14 +922,13 @@ TEST_CASE("Check variable-length array of strings property") { REQUIRE(expectedIdx == stringCount); } -TEST_CASE("Check fixed-length boolean array property") { +TEST_CASE("Check fixed-length boolean array PropertyTablePropertyView") { std::vector buffer{ static_cast(0b10101111), static_cast(0b11111010), static_cast(0b11100111)}; PropertyTablePropertyView> property( - PropertyTablePropertyViewStatus::Valid, gsl::span(buffer.data(), buffer.size()), gsl::span(), gsl::span(), @@ -978,7 +970,7 @@ TEST_CASE("Check fixed-length boolean array property") { REQUIRE(static_cast(val1[11]) == 1); } -TEST_CASE("Check variable-length boolean array property") { +TEST_CASE("Check variable-length boolean array PropertyTablePropertyView") { std::vector buffer{ static_cast(0b10101111), static_cast(0b11111010), @@ -988,7 +980,6 @@ TEST_CASE("Check variable-length boolean array property") { std::vector offsetBuffer{0, 3, 12, 28}; PropertyTablePropertyView> property( - PropertyTablePropertyViewStatus::Valid, gsl::span(buffer.data(), buffer.size()), gsl::span( reinterpret_cast(offsetBuffer.data()), diff --git a/CesiumGltf/test/TestPropertyTableView.cpp b/CesiumGltf/test/TestPropertyTableView.cpp index 9516c72a6..df8fc0a28 100644 --- a/CesiumGltf/test/TestPropertyTableView.cpp +++ b/CesiumGltf/test/TestPropertyTableView.cpp @@ -7,7 +7,7 @@ using namespace CesiumGltf; template -void addBufferToModel(Model& model, std::vector& values) { +void addBufferToModel(Model& model, const std::vector& values) { Buffer& valueBuffer = model.buffers.emplace_back(); valueBuffer.cesium.data.resize(values.size() * sizeof(T)); valueBuffer.byteLength = static_cast(valueBuffer.cesium.data.size()); diff --git a/CesiumGltf/test/TestPropertyTexturePropertyView.cpp b/CesiumGltf/test/TestPropertyTexturePropertyView.cpp index 60035721a..4b54c49b8 100644 --- a/CesiumGltf/test/TestPropertyTexturePropertyView.cpp +++ b/CesiumGltf/test/TestPropertyTexturePropertyView.cpp @@ -11,401 +11,1063 @@ using namespace CesiumGltf; -TEST_CASE( - "Test PropertyTexturePropertyView on property with invalid texture index") { - Model model; - ExtensionModelExtStructuralMetadata& metadata = - model.addExtension(); - - Schema& schema = metadata.schema.emplace(); - Class& testClass = schema.classes["TestClass"]; - ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; - testClassProperty.type = ClassProperty::Type::SCALAR; - testClassProperty.componentType = ClassProperty::ComponentType::UINT8; - - PropertyTexture propertyTexture; - propertyTexture.classProperty = "TestClass"; - - PropertyTextureProperty& propertyTextureProperty = - propertyTexture.properties["TestClassProperty"]; - propertyTextureProperty.index = -1; - propertyTextureProperty.texCoord = 0; - propertyTextureProperty.channels = {0}; - - PropertyTexturePropertyView view( - model, - testClassProperty, - propertyTextureProperty); - REQUIRE( - view.status() == PropertyTexturePropertyViewStatus::ErrorInvalidTexture); -} +TEST_CASE("Check scalar PropertyTexturePropertyView") { + SECTION("uint8_t") { + Sampler sampler; + ImageCesium image; + image.width = 2; + image.height = 2; + image.channels = 1; + image.bytesPerChannel = 1; + + std::vector data{12, 33, 56, 67}; + std::vector& imageData = image.pixelData; + imageData.resize(data.size()); + std::memcpy(imageData.data(), data.data(), data.size()); + + std::vector channels{0}; + + PropertyTexturePropertyView + view(sampler, image, 0, channels, false); + CHECK(view.getSwizzle() == "r"); + + std::vector values{ + view.get(0, 0), + view.get(0.5, 0), + view.get(0, 0.5), + view.get(0.5, 0.5)}; + for (size_t i = 0; i < values.size(); i++) { + REQUIRE(values[i] == data[i]); + } + } -TEST_CASE( - "Test PropertyTexturePropertyView on property with invalid sampler index") { - Model model; - ExtensionModelExtStructuralMetadata& metadata = - model.addExtension(); - - Schema& schema = metadata.schema.emplace(); - Class& testClass = schema.classes["TestClass"]; - ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; - testClassProperty.type = ClassProperty::Type::SCALAR; - testClassProperty.componentType = ClassProperty::ComponentType::UINT8; - - Image& image = model.images.emplace_back(); - image.cesium.width = 1; - image.cesium.height = 1; - Texture& texture = model.textures.emplace_back(); - texture.sampler = -1; - texture.source = 0; - - PropertyTexture propertyTexture; - propertyTexture.classProperty = "TestClass"; - - PropertyTextureProperty& propertyTextureProperty = - propertyTexture.properties["TestClassProperty"]; - propertyTextureProperty.index = 0; - propertyTextureProperty.texCoord = 0; - propertyTextureProperty.channels = {0}; - - PropertyTexturePropertyView view( - model, - testClassProperty, - propertyTextureProperty); - REQUIRE( - view.status() == - PropertyTexturePropertyViewStatus::ErrorInvalidTextureSampler); -} + SECTION("int8_t") { + Sampler sampler; + ImageCesium image; + image.width = 2; + image.height = 2; + image.channels = 1; + image.bytesPerChannel = 1; + + std::vector data{255, 0, 223, 67}; + std::vector& imageData = image.pixelData; + imageData.resize(data.size()); + std::memcpy(imageData.data(), data.data(), data.size()); + + std::vector channels{0}; + + PropertyTexturePropertyView + view(sampler, image, 0, channels, false); + CHECK(view.getSwizzle() == "r"); + + std::vector expected{-1, 0, -33, 67}; + std::vector values{ + view.get(0, 0), + view.get(0.5, 0), + view.get(0, 0.5), + view.get(0.5, 0.5)}; + for (size_t i = 0; i < values.size(); i++) { + REQUIRE(values[i] == expected[i]); + } + } -TEST_CASE( - "Test PropertyTexturePropertyView on property with invalid image index") { - Model model; - ExtensionModelExtStructuralMetadata& metadata = - model.addExtension(); - - Schema& schema = metadata.schema.emplace(); - Class& testClass = schema.classes["TestClass"]; - ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; - testClassProperty.type = ClassProperty::Type::SCALAR; - testClassProperty.componentType = ClassProperty::ComponentType::UINT8; - - model.samplers.emplace_back(); - - Texture& texture = model.textures.emplace_back(); - texture.sampler = 0; - texture.source = -1; - - PropertyTexture propertyTexture; - propertyTexture.classProperty = "TestClass"; - - PropertyTextureProperty& propertyTextureProperty = - propertyTexture.properties["TestClassProperty"]; - propertyTextureProperty.index = 0; - propertyTextureProperty.texCoord = 0; - propertyTextureProperty.channels = {0}; - - PropertyTexturePropertyView view( - model, - testClassProperty, - propertyTextureProperty); - REQUIRE( - view.status() == PropertyTexturePropertyViewStatus::ErrorInvalidImage); -} + SECTION("uint16_t") { + Sampler sampler; + ImageCesium image; + image.width = 2; + image.height = 2; + image.channels = 2; + image.bytesPerChannel = 1; + + // clang-format off + std::vector data{ + 28, 0, + 1, 1, + 0, 3, + 182, 1}; + // clang-format on + + std::vector& imageData = image.pixelData; + imageData.resize(data.size()); + std::memcpy(imageData.data(), data.data(), data.size()); + + std::vector channels{0, 1}; + + PropertyTexturePropertyView + view(sampler, image, 0, channels, false); + CHECK(view.getSwizzle() == "rg"); + + std::vector expected{28, 257, 768, 438}; + std::vector values{ + view.get(0, 0), + view.get(0.5, 0), + view.get(0, 0.5), + view.get(0.5, 0.5)}; + for (size_t i = 0; i < values.size(); i++) { + REQUIRE(values[i] == expected[i]); + } + } -TEST_CASE("Test PropertyTexturePropertyView on property with empty image") { - Model model; - ExtensionModelExtStructuralMetadata& metadata = - model.addExtension(); - - Schema& schema = metadata.schema.emplace(); - Class& testClass = schema.classes["TestClass"]; - ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; - testClassProperty.type = ClassProperty::Type::SCALAR; - testClassProperty.componentType = ClassProperty::ComponentType::UINT8; - - Image& image = model.images.emplace_back(); - image.cesium.width = 0; - image.cesium.height = 0; - - model.samplers.emplace_back(); - - Texture& texture = model.textures.emplace_back(); - texture.sampler = 0; - texture.source = 0; - - PropertyTexture propertyTexture; - propertyTexture.classProperty = "TestClass"; - - PropertyTextureProperty& propertyTextureProperty = - propertyTexture.properties["TestClassProperty"]; - propertyTextureProperty.index = 0; - propertyTextureProperty.texCoord = 0; - propertyTextureProperty.channels = {0}; - - PropertyTexturePropertyView view( - model, - testClassProperty, - propertyTextureProperty); - REQUIRE(view.status() == PropertyTexturePropertyViewStatus::ErrorEmptyImage); -} + SECTION("int16_t") { + Sampler sampler; + ImageCesium image; + image.width = 2; + image.height = 2; + image.channels = 2; + image.bytesPerChannel = 1; + + // clang-format off + std::vector data{ + 255, 255, + 1, 129, + 0, 3, + 182, 1}; + // clang-format on + + std::vector& imageData = image.pixelData; + imageData.resize(data.size()); + std::memcpy(imageData.data(), data.data(), data.size()); + + std::vector channels{0, 1}; + + PropertyTexturePropertyView + view(sampler, image, 0, channels, false); + CHECK(view.getSwizzle() == "rg"); + + std::vector expected{-1, -32511, 768, 438}; + std::vector values{ + view.get(0, 0), + view.get(0.5, 0), + view.get(0, 0.5), + view.get(0.5, 0.5)}; + for (size_t i = 0; i < values.size(); i++) { + REQUIRE(values[i] == expected[i]); + } + } -TEST_CASE("Test PropertyTextureView on property texture property with zero " - "channels") { - Model model; - ExtensionModelExtStructuralMetadata& metadata = - model.addExtension(); - - Schema& schema = metadata.schema.emplace(); - Class& testClass = schema.classes["TestClass"]; - ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; - testClassProperty.type = ClassProperty::Type::SCALAR; - testClassProperty.componentType = ClassProperty::ComponentType::UINT8; - - Image& image = model.images.emplace_back(); - image.cesium.width = 1; - image.cesium.height = 1; - image.cesium.channels = 1; - - model.samplers.emplace_back(); - - Texture& texture = model.textures.emplace_back(); - texture.sampler = 0; - texture.source = 0; - - PropertyTexture propertyTexture; - propertyTexture.classProperty = "TestClass"; - - PropertyTextureProperty& propertyTextureProperty = - propertyTexture.properties["TestClassProperty"]; - propertyTextureProperty.index = 0; - propertyTextureProperty.texCoord = 0; - propertyTextureProperty.channels = {}; - - PropertyTexturePropertyView view( - model, - testClassProperty, - propertyTextureProperty); - REQUIRE( - view.status() == PropertyTexturePropertyViewStatus::ErrorInvalidChannels); -} + SECTION("uint32_t") { + Sampler sampler; + ImageCesium image; + image.width = 2; + image.height = 2; + image.channels = 4; + image.bytesPerChannel = 1; + + // clang-format off + std::vector data{ + 0, 0, 0, 1, + 9, 0, 1, 0, + 20, 2, 2, 0, + 8, 1, 0, 1}; + // clang-format on + + std::vector& imageData = image.pixelData; + imageData.resize(data.size()); + std::memcpy(imageData.data(), data.data(), data.size()); + + std::vector channels{0, 1, 2, 3}; + + PropertyTexturePropertyView + view(sampler, image, 0, channels, false); + CHECK(view.getSwizzle() == "rgba"); + + std::vector expected{16777216, 65545, 131604, 16777480}; + std::vector values{ + view.get(0, 0), + view.get(0.5, 0), + view.get(0, 0.5), + view.get(0.5, 0.5)}; + for (size_t i = 0; i < values.size(); i++) { + REQUIRE(values[i] == expected[i]); + } + } + + SECTION("int32_t") { + Sampler sampler; + ImageCesium image; + image.width = 2; + image.height = 2; + image.channels = 4; + image.bytesPerChannel = 1; + + // clang-format off + std::vector data{ + 255, 255, 255, 255, + 9, 0, 1, 0, + 20, 2, 2, 255, + 8, 1, 0, 1}; + // clang-format on + + std::vector& imageData = image.pixelData; + imageData.resize(data.size()); + std::memcpy(imageData.data(), data.data(), data.size()); + + std::vector channels{0, 1, 2, 3}; + + PropertyTexturePropertyView + view(sampler, image, 0, channels, false); + CHECK(view.getSwizzle() == "rgba"); + std::vector expected{-1, 65545, -16645612, 16777480}; + std::vector values{ + view.get(0, 0), + view.get(0.5, 0), + view.get(0, 0.5), + view.get(0.5, 0.5)}; + for (size_t i = 0; i < values.size(); i++) { + REQUIRE(values[i] == expected[i]); + } + } -TEST_CASE("Test PropertyTextureView on property texture property with too many " - "channels") { - Model model; - ExtensionModelExtStructuralMetadata& metadata = - model.addExtension(); - - Schema& schema = metadata.schema.emplace(); - Class& testClass = schema.classes["TestClass"]; - ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; - testClassProperty.type = ClassProperty::Type::SCALAR; - testClassProperty.componentType = ClassProperty::ComponentType::UINT8; - - Image& image = model.images.emplace_back(); - image.cesium.width = 1; - image.cesium.height = 1; - image.cesium.channels = 1; - - model.samplers.emplace_back(); - - Texture& texture = model.textures.emplace_back(); - texture.sampler = 0; - texture.source = 0; - - PropertyTexture propertyTexture; - propertyTexture.classProperty = "TestClass"; - - PropertyTextureProperty& propertyTextureProperty = - propertyTexture.properties["TestClassProperty"]; - propertyTextureProperty.index = 0; - propertyTextureProperty.texCoord = 0; - propertyTextureProperty.channels = {0, 1}; - - PropertyTexturePropertyView view( - model, - testClassProperty, - propertyTextureProperty); - REQUIRE( - view.status() == PropertyTexturePropertyViewStatus::ErrorInvalidChannels); + SECTION("float") { + Sampler sampler; + ImageCesium image; + image.width = 2; + image.height = 2; + image.channels = 4; + image.bytesPerChannel = 1; + + // clang-format off + std::vector data{ + 0, 0, 0, 1, + 9, 0, 1, 0, + 20, 2, 2, 0, + 8, 1, 0, 1}; + // clang-format on + + std::vector& imageData = image.pixelData; + imageData.resize(data.size()); + std::memcpy(imageData.data(), data.data(), data.size()); + + std::vector channels{0, 1, 2, 3}; + + PropertyTexturePropertyView view(sampler, image, 0, channels, false); + CHECK(view.getSwizzle() == "rgba"); + + std::vector expectedUint{16777216, 65545, 131604, 16777480}; + std::vector expected(expectedUint.size()); + for (size_t i = 0; i < expectedUint.size(); i++) { + float value = *reinterpret_cast(&expectedUint[i]); + expected[i] = value; + } + + std::vector values{ + view.get(0, 0), + view.get(0.5, 0), + view.get(0, 0.5), + view.get(0.5, 0.5)}; + for (size_t i = 0; i < values.size(); i++) { + REQUIRE(values[i] == expected[i]); + } + } } -TEST_CASE("Test PropertyTexturePropertyView on valid property texture") { - Model model; - ExtensionModelExtStructuralMetadata& metadata = - model.addExtension(); - - Schema& schema = metadata.schema.emplace(); - Class& testClass = schema.classes["TestClass"]; - ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; - testClassProperty.type = ClassProperty::Type::SCALAR; - testClassProperty.componentType = ClassProperty::ComponentType::UINT8; - - Image& image = model.images.emplace_back(); - image.cesium.width = 1; - image.cesium.height = 1; - image.cesium.channels = 1; - - model.samplers.emplace_back(); - - Texture& texture = model.textures.emplace_back(); - texture.sampler = 0; - texture.source = 0; - - PropertyTexture propertyTexture; - propertyTexture.classProperty = "TestClass"; - - PropertyTextureProperty& propertyTextureProperty = - propertyTexture.properties["TestClassProperty"]; - propertyTextureProperty.index = 0; - propertyTextureProperty.texCoord = 0; - propertyTextureProperty.channels = {0}; - - PropertyTexturePropertyView view( - model, - testClassProperty, - propertyTextureProperty); - REQUIRE(view.status() == PropertyTexturePropertyViewStatus::Valid); +TEST_CASE("Check vecN PropertyTexturePropertyView") { + SECTION("glm::u8vec2") { + Sampler sampler; + ImageCesium image; + image.width = 2; + image.height = 2; + image.channels = 2; + image.bytesPerChannel = 1; + + // clang-format off + std::vector data{ + 28, 0, + 1, 1, + 0, 3, + 182, 1}; + // clang-format on + + std::vector& imageData = image.pixelData; + imageData.resize(data.size()); + std::memcpy(imageData.data(), data.data(), data.size()); + + std::vector channels{0, 1}; + + PropertyTexturePropertyView + view(sampler, image, 0, channels, false); + CHECK(view.getSwizzle() == "rg"); + + std::vector expected{ + glm::u8vec2(28, 0), + glm::u8vec2(1, 1), + glm::u8vec2(0, 3), + glm::u8vec2(182, 1)}; + std::vector values{ + view.get(0, 0), + view.get(0.5, 0), + view.get(0, 0.5), + view.get(0.5, 0.5)}; + for (size_t i = 0; i < values.size(); i++) { + REQUIRE(values[i] == expected[i]); + } + } + + SECTION("glm::i8vec2") { + Sampler sampler; + ImageCesium image; + image.width = 2; + image.height = 2; + image.channels = 2; + image.bytesPerChannel = 1; + + // clang-format off + std::vector data{ + 28, 255, + 254, 1, + 0, 3, + 182, 1}; + // clang-format on + + std::vector& imageData = image.pixelData; + imageData.resize(data.size()); + std::memcpy(imageData.data(), data.data(), data.size()); + + std::vector channels{0, 1}; + + PropertyTexturePropertyView + view(sampler, image, 0, channels, false); + CHECK(view.getSwizzle() == "rg"); + + std::vector expected{ + glm::i8vec2(28, -1), + glm::i8vec2(-2, 1), + glm::i8vec2(0, 3), + glm::i8vec2(-74, 1)}; + std::vector values{ + view.get(0, 0), + view.get(0.5, 0), + view.get(0, 0.5), + view.get(0.5, 0.5)}; + for (size_t i = 0; i < values.size(); i++) { + REQUIRE(values[i] == expected[i]); + } + } + + SECTION("glm::u8vec3") { + Sampler sampler; + ImageCesium image; + image.width = 2; + image.height = 2; + image.channels = 3; + image.bytesPerChannel = 1; + + // clang-format off + std::vector data{ + 1, 2, 3, + 4, 5, 6, + 7, 8, 9, + 0, 5, 2}; + // clang-format on + + std::vector& imageData = image.pixelData; + imageData.resize(data.size()); + std::memcpy(imageData.data(), data.data(), data.size()); + + std::vector channels{0, 1, 2}; + + PropertyTexturePropertyView + view(sampler, image, 0, channels, false); + CHECK(view.getSwizzle() == "rgb"); + + std::vector expected{ + glm::u8vec3(1, 2, 3), + glm::u8vec3(4, 5, 6), + glm::u8vec3(7, 8, 9), + glm::u8vec3(0, 5, 2)}; + std::vector values{ + view.get(0, 0), + view.get(0.5, 0), + view.get(0, 0.5), + view.get(0.5, 0.5)}; + for (size_t i = 0; i < values.size(); i++) { + REQUIRE(values[i] == expected[i]); + } + } + + SECTION("glm::i8vec3") { + Sampler sampler; + ImageCesium image; + image.width = 2; + image.height = 2; + image.channels = 3; + image.bytesPerChannel = 1; + + // clang-format off + std::vector data{ + 255, 2, 3, + 4, 254, 6, + 7, 8, 159, + 0, 5, 2}; + // clang-format on + + std::vector& imageData = image.pixelData; + imageData.resize(data.size()); + std::memcpy(imageData.data(), data.data(), data.size()); + + std::vector channels{0, 1, 2}; + + PropertyTexturePropertyView + view(sampler, image, 0, channels, false); + CHECK(view.getSwizzle() == "rgb"); + + std::vector expected{ + glm::i8vec3(-1, 2, 3), + glm::i8vec3(4, -2, 6), + glm::i8vec3(7, 8, -97), + glm::i8vec3(0, 5, 2)}; + std::vector values{ + view.get(0, 0), + view.get(0.5, 0), + view.get(0, 0.5), + view.get(0.5, 0.5)}; + for (size_t i = 0; i < values.size(); i++) { + REQUIRE(values[i] == expected[i]); + } + } + + SECTION("glm::u8vec4") { + Sampler sampler; + ImageCesium image; + image.width = 2; + image.height = 2; + image.channels = 4; + image.bytesPerChannel = 1; + + // clang-format off + std::vector data{ + 1, 2, 3, 0, + 4, 5, 6, 11, + 7, 8, 9, 3, + 0, 5, 2, 27}; + // clang-format on + + std::vector& imageData = image.pixelData; + imageData.resize(data.size()); + std::memcpy(imageData.data(), data.data(), data.size()); + + std::vector channels{0, 1, 2, 3}; + + PropertyTexturePropertyView + view(sampler, image, 0, channels, false); + CHECK(view.getSwizzle() == "rgba"); + + std::vector expected{ + glm::u8vec4(1, 2, 3, 0), + glm::u8vec4(4, 5, 6, 11), + glm::u8vec4(7, 8, 9, 3), + glm::u8vec4(0, 5, 2, 27)}; + std::vector values{ + view.get(0, 0), + view.get(0.5, 0), + view.get(0, 0.5), + view.get(0.5, 0.5)}; + for (size_t i = 0; i < values.size(); i++) { + REQUIRE(values[i] == expected[i]); + } + } + + SECTION("glm::i8vec4") { + Sampler sampler; + ImageCesium image; + image.width = 2; + image.height = 2; + image.channels = 4; + image.bytesPerChannel = 1; + + // clang-format off + std::vector data{ + 1, 200, 3, 0, + 4, 5, 6, 251, + 129, 8, 9, 3, + 0, 155, 2, 27}; + // clang-format on + + std::vector& imageData = image.pixelData; + imageData.resize(data.size()); + std::memcpy(imageData.data(), data.data(), data.size()); + + std::vector channels{0, 1, 2, 3}; + + PropertyTexturePropertyView + view(sampler, image, 0, channels, false); + CHECK(view.getSwizzle() == "rgba"); + + std::vector expected{ + glm::i8vec4(1, -56, 3, 0), + glm::i8vec4(4, 5, 6, -5), + glm::i8vec4(-127, 8, 9, 3), + glm::i8vec4(0, -101, 2, 27)}; + std::vector values{ + view.get(0, 0), + view.get(0.5, 0), + view.get(0, 0.5), + view.get(0.5, 0.5)}; + for (size_t i = 0; i < values.size(); i++) { + REQUIRE(values[i] == expected[i]); + } + } + + SECTION("glm::u16vec2") { + Sampler sampler; + ImageCesium image; + image.width = 2; + image.height = 2; + image.channels = 4; + image.bytesPerChannel = 1; + + // clang-format off + std::vector data{ + 0, 0, 0, 1, + 9, 0, 1, 0, + 20, 2, 2, 0, + 8, 1, 0, 1}; + // clang-format on + + std::vector& imageData = image.pixelData; + imageData.resize(data.size()); + std::memcpy(imageData.data(), data.data(), data.size()); + + std::vector channels{0, 1, 2, 3}; + + PropertyTexturePropertyView + view(sampler, image, 0, channels, false); + CHECK(view.getSwizzle() == "rgba"); + + std::vector expected{ + glm::u16vec2(0, 256), + glm::u16vec2(9, 1), + glm::u16vec2(532, 2), + glm::u16vec2(264, 256)}; + std::vector values{ + view.get(0, 0), + view.get(0.5, 0), + view.get(0, 0.5), + view.get(0.5, 0.5)}; + for (size_t i = 0; i < values.size(); i++) { + REQUIRE(values[i] == expected[i]); + } + } + + SECTION("glm::i16vec2") { + Sampler sampler; + ImageCesium image; + image.width = 2; + image.height = 2; + image.channels = 4; + image.bytesPerChannel = 1; + + // clang-format off + std::vector data{ + 255, 255, 0, 1, + 9, 0, 146, 195, + 20, 2, 2, 0, + 8, 1, 255, 1}; + // clang-format on + + std::vector& imageData = image.pixelData; + imageData.resize(data.size()); + std::memcpy(imageData.data(), data.data(), data.size()); + + std::vector channels{0, 1, 2, 3}; + + PropertyTexturePropertyView + view(sampler, image, 0, channels, false); + CHECK(view.getSwizzle() == "rgba"); + + std::vector expected{ + glm::i16vec2(-1, 256), + glm::i16vec2(9, -15470), + glm::i16vec2(532, 2), + glm::i16vec2(264, 511)}; + std::vector values{ + view.get(0, 0), + view.get(0.5, 0), + view.get(0, 0.5), + view.get(0.5, 0.5)}; + for (size_t i = 0; i < values.size(); i++) { + REQUIRE(values[i] == expected[i]); + } + } } -TEST_CASE("Test getSwizzle") { - Model model; - ExtensionModelExtStructuralMetadata& metadata = - model.addExtension(); - - Schema& schema = metadata.schema.emplace(); - Class& testClass = schema.classes["TestClass"]; - ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; - testClassProperty.type = ClassProperty::Type::SCALAR; - testClassProperty.componentType = ClassProperty::ComponentType::UINT8; - testClassProperty.count = 4; - - Image& image = model.images.emplace_back(); - image.cesium.width = 1; - image.cesium.height = 1; - image.cesium.channels = 4; - - model.samplers.emplace_back(); - - Texture& texture = model.textures.emplace_back(); - texture.sampler = 0; - texture.source = 0; - - PropertyTexture propertyTexture; - propertyTexture.classProperty = "TestClass"; - - PropertyTextureProperty& propertyTextureProperty = - propertyTexture.properties["TestClassProperty"]; - propertyTextureProperty.index = 0; - propertyTextureProperty.texCoord = 0; - propertyTextureProperty.channels = {0, 2, 3, 1}; - - PropertyTexturePropertyView view( - model, - testClassProperty, - propertyTextureProperty); - REQUIRE(view.status() == PropertyTexturePropertyViewStatus::Valid); - REQUIRE(view.getCount() == 4); - REQUIRE(view.getSwizzle() == "rbag"); +TEST_CASE("Check array PropertyTexturePropertyView") { + SECTION("uint8_t array") { + Sampler sampler; + ImageCesium image; + image.width = 2; + image.height = 2; + image.channels = 4; + image.bytesPerChannel = 1; + + // clang-format off + std::vector data{ + 1, 2, 3, 0, + 4, 5, 6, 11, + 7, 8, 9, 3, + 0, 5, 2, 27}; + // clang-format on + + std::vector& imageData = image.pixelData; + imageData.resize(data.size()); + std::memcpy(imageData.data(), data.data(), data.size()); + + std::vector channels{0, 1, 2, 3}; + + PropertyTexturePropertyView> + view(sampler, image, 0, channels, false); + CHECK(view.getSwizzle() == "rgba"); + + std::vector> values{ + view.get(0, 0), + view.get(0.5, 0), + view.get(0, 0.5), + view.get(0.5, 0.5)}; + + for (size_t i = 0; i < 4; i++) { + auto dataStart = data.begin() + i * 4; + std::vector expected(dataStart, dataStart + 4); + const PropertyArrayView& value = values[i]; + REQUIRE(static_cast(value.size()) == expected.size()); + for (size_t j = 0; j < expected.size(); j++) { + REQUIRE(value[j] == expected[j]); + } + } + } + + SECTION("int8_t array") { + Sampler sampler; + ImageCesium image; + image.width = 2; + image.height = 2; + image.channels = 4; + image.bytesPerChannel = 1; + + // clang-format off + std::vector data{ + 1, 200, 3, 0, + 4, 5, 6, 251, + 129, 8, 9, 3, + 0, 155, 2, 27}; + // clang-format on + + std::vector& imageData = image.pixelData; + imageData.resize(data.size()); + std::memcpy(imageData.data(), data.data(), data.size()); + + std::vector channels{0, 1, 2, 3}; + + PropertyTexturePropertyView> + view(sampler, image, 0, channels, false); + CHECK(view.getSwizzle() == "rgba"); + + std::vector> expected{ + {1, -56, 3, 0}, + {4, 5, 6, -5}, + {-127, 8, 9, 3}, + {0, -101, 2, 27}}; + std::vector> values{ + view.get(0, 0), + view.get(0.5, 0), + view.get(0, 0.5), + view.get(0.5, 0.5)}; + for (size_t i = 0; i < values.size(); i++) { + std::vector& expectedValue = expected[i]; + const PropertyArrayView& value = values[i]; + REQUIRE(static_cast(value.size()) == expected.size()); + for (size_t j = 0; j < expected.size(); j++) { + REQUIRE(value[j] == expectedValue[j]); + } + } + } + + SECTION("uint16_t array") { + Sampler sampler; + ImageCesium image; + image.width = 2; + image.height = 2; + image.channels = 4; + image.bytesPerChannel = 1; + + // clang-format off + std::vector data{ + 0, 0, 0, 1, + 9, 0, 1, 0, + 20, 2, 2, 0, + 8, 1, 0, 1}; + // clang-format on + + std::vector& imageData = image.pixelData; + imageData.resize(data.size()); + std::memcpy(imageData.data(), data.data(), data.size()); + + std::vector channels{0, 1, 2, 3}; + + PropertyTexturePropertyView> + view(sampler, image, 0, channels, false); + CHECK(view.getSwizzle() == "rgba"); + + std::vector> values{ + view.get(0, 0), + view.get(0.5, 0), + view.get(0, 0.5), + view.get(0.5, 0.5)}; + + std::vector> expected{ + {0, 256}, + {9, 1}, + {532, 2}, + {264, 256}}; + + for (size_t i = 0; i < expected.size(); i++) { + std::vector& expectedValue = expected[i]; + const PropertyArrayView& value = values[i]; + REQUIRE(static_cast(value.size()) == expectedValue.size()); + for (size_t j = 0; j < expectedValue.size(); j++) { + REQUIRE(value[j] == expectedValue[j]); + } + } + } + + SECTION("int16_t array") { + Sampler sampler; + ImageCesium image; + image.width = 2; + image.height = 2; + image.channels = 4; + image.bytesPerChannel = 1; + + // clang-format off + std::vector data{ + 255, 255, 0, 1, + 9, 0, 146, 195, + 20, 2, 2, 0, + 8, 255, 0, 1}; + // clang-format on + + std::vector& imageData = image.pixelData; + imageData.resize(data.size()); + std::memcpy(imageData.data(), data.data(), data.size()); + + std::vector channels{0, 1, 2, 3}; + + PropertyTexturePropertyView> + view(sampler, image, 0, channels, false); + CHECK(view.getSwizzle() == "rgba"); + + std::vector> expected{ + {-1, 256}, + {9, -15470}, + {532, 2}, + {-248, 256}}; + std::vector> values{ + view.get(0, 0), + view.get(0.5, 0), + view.get(0, 0.5), + view.get(0.5, 0.5)}; + for (size_t i = 0; i < values.size(); i++) { + std::vector& expectedValue = expected[i]; + const PropertyArrayView& value = values[i]; + REQUIRE(static_cast(value.size()) == expectedValue.size()); + for (size_t j = 0; j < expectedValue.size(); j++) { + REQUIRE(value[j] == expectedValue[j]); + } + } + } } -TEST_CASE("Test getting value from invalid view") { - Model model; - ExtensionModelExtStructuralMetadata& metadata = - model.addExtension(); - - Schema& schema = metadata.schema.emplace(); - Class& testClass = schema.classes["TestClass"]; - ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; - testClassProperty.type = ClassProperty::Type::SCALAR; - testClassProperty.componentType = ClassProperty::ComponentType::UINT8; - - Image& image = model.images.emplace_back(); - image.cesium.width = 0; - image.cesium.height = 1; - image.cesium.channels = 1; - - model.samplers.emplace_back(); - - Texture& texture = model.textures.emplace_back(); - texture.sampler = 0; - texture.source = 0; - - PropertyTexture propertyTexture; - propertyTexture.classProperty = "TestClass"; - - PropertyTextureProperty& propertyTextureProperty = - propertyTexture.properties["TestClassProperty"]; - propertyTextureProperty.index = 0; - propertyTextureProperty.texCoord = 0; - propertyTextureProperty.channels = {0}; - - PropertyTexturePropertyView view( - model, - testClassProperty, - propertyTextureProperty); - REQUIRE(view.status() != PropertyTexturePropertyViewStatus::Valid); - PropertyTexturePropertyValue value = view.get(0, 0); - REQUIRE(value.components[0] == 0); - REQUIRE(value.components[1] == 0); - REQUIRE(value.components[2] == 0); - REQUIRE(value.components[3] == 0); +TEST_CASE("Check that non-adjacent channels resolve to expected output") { + SECTION("single-byte scalar") { + Sampler sampler; + ImageCesium image; + image.width = 2; + image.height = 2; + image.channels = 4; + image.bytesPerChannel = 1; + + // clang-format off + std::vector data{ + 0, 1, 2, 3, + 1, 2, 3, 4, + 1, 0, 1, 0, + 2, 3, 8, 1}; + // clang-format on + + std::vector& imageData = image.pixelData; + imageData.resize(data.size()); + std::memcpy(imageData.data(), data.data(), data.size()); + + std::vector channels{3}; + + PropertyTexturePropertyView + view(sampler, image, 0, channels, false); + CHECK(view.getSwizzle() == "a"); + + std::vector expected{3, 4, 0, 1}; + std::vector values{ + view.get(0, 0), + view.get(0.5, 0), + view.get(0, 0.5), + view.get(0.5, 0.5)}; + for (size_t i = 0; i < values.size(); i++) { + REQUIRE(values[i] == expected[i]); + } + } + + SECTION("multi-byte scalar") { + Sampler sampler; + ImageCesium image; + image.width = 2; + image.height = 2; + image.channels = 4; + image.bytesPerChannel = 1; + + // clang-format off + std::vector data{ + 0, 1, 2, 3, + 1, 2, 3, 4, + 1, 0, 1, 0, + 2, 3, 8, 1}; + // clang-format on + std::vector& imageData = image.pixelData; + imageData.resize(data.size()); + std::memcpy(imageData.data(), data.data(), data.size()); + + std::vector channels{2, 0}; + + PropertyTexturePropertyView + view(sampler, image, 0, channels, false); + CHECK(view.getSwizzle() == "br"); + + std::vector expected{2, 259, 257, 520}; + std::vector values{ + view.get(0, 0), + view.get(0.5, 0), + view.get(0, 0.5), + view.get(0.5, 0.5)}; + for (size_t i = 0; i < values.size(); i++) { + REQUIRE(values[i] == expected[i]); + } + } + + SECTION("vecN") { + Sampler sampler; + ImageCesium image; + image.width = 2; + image.height = 2; + image.channels = 4; + image.bytesPerChannel = 1; + + // clang-format off + std::vector data{ + 0, 1, 2, 3, + 1, 2, 3, 4, + 1, 0, 1, 0, + 2, 3, 8, 1}; + // clang-format on + std::vector& imageData = image.pixelData; + imageData.resize(data.size()); + std::memcpy(imageData.data(), data.data(), data.size()); + + std::vector channels{3, 2, 1}; + + PropertyTexturePropertyView + view(sampler, image, 0, channels, false); + CHECK(view.getSwizzle() == "abg"); + + std::vector expected{ + glm::u8vec3(3, 2, 1), + glm::u8vec3(4, 3, 2), + glm::u8vec3(0, 1, 0), + glm::u8vec3(1, 8, 3)}; + std::vector values{ + view.get(0, 0), + view.get(0.5, 0), + view.get(0, 0.5), + view.get(0.5, 0.5)}; + for (size_t i = 0; i < values.size(); i++) { + REQUIRE(values[i] == expected[i]); + } + } + + SECTION("array") { + Sampler sampler; + ImageCesium image; + image.width = 2; + image.height = 2; + image.channels = 4; + image.bytesPerChannel = 1; + + // clang-format off + std::vector data{ + 1, 2, 3, 0, + 4, 5, 6, 11, + 7, 8, 9, 3, + 0, 5, 2, 27}; + // clang-format on + + std::vector& imageData = image.pixelData; + imageData.resize(data.size()); + std::memcpy(imageData.data(), data.data(), data.size()); + + std::vector channels{1, 0, 3, 2}; + + PropertyTexturePropertyView> + view(sampler, image, 0, channels, false); + CHECK(view.getSwizzle() == "grab"); + + std::vector> values{ + view.get(0, 0), + view.get(0.5, 0), + view.get(0, 0.5), + view.get(0.5, 0.5)}; + + std::vector> expected{ + {2, 1, 0, 3}, + {5, 4, 11, 6}, + {8, 7, 3, 9}, + {5, 0, 27, 2}}; + + for (size_t i = 0; i < expected.size(); i++) { + std::vector& expectedValue = expected[i]; + const PropertyArrayView& value = values[i]; + REQUIRE(static_cast(value.size()) == expectedValue.size()); + for (size_t j = 0; j < expectedValue.size(); j++) { + REQUIRE(value[j] == expectedValue[j]); + } + } + } } -TEST_CASE("Test getting value from valid view") { - Model model; - ExtensionModelExtStructuralMetadata& metadata = - model.addExtension(); - - Schema& schema = metadata.schema.emplace(); - Class& testClass = schema.classes["TestClass"]; - ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; - testClassProperty.type = ClassProperty::Type::SCALAR; - testClassProperty.componentType = ClassProperty::ComponentType::UINT8; - - std::vector values{10, 8, 4, 22}; - - Image& image = model.images.emplace_back(); - image.cesium.width = 2; - image.cesium.height = 2; - image.cesium.channels = 1; - image.cesium.bytesPerChannel = 1; - image.cesium.pixelData.resize(values.size()); - std::memcpy(image.cesium.pixelData.data(), values.data(), values.size()); - - Sampler& sampler = model.samplers.emplace_back(); - sampler.wrapS = Sampler::WrapS::CLAMP_TO_EDGE; - sampler.wrapT = Sampler::WrapT::CLAMP_TO_EDGE; - - Texture& texture = model.textures.emplace_back(); - texture.sampler = 0; - texture.source = 0; - - PropertyTexture propertyTexture; - propertyTexture.classProperty = "TestClass"; - - PropertyTextureProperty& propertyTextureProperty = - propertyTexture.properties["TestClassProperty"]; - propertyTextureProperty.index = 0; - propertyTextureProperty.texCoord = 0; - propertyTextureProperty.channels = {0}; - - PropertyTexturePropertyView view( - model, - testClassProperty, - propertyTextureProperty); - REQUIRE(view.status() == PropertyTexturePropertyViewStatus::Valid); - - std::vector> viewValues{ - view.get(0, 0), - view.get(1.0, 0), - view.get(0, 1.0), - view.get(1.0, 1.0)}; - - for (size_t i = 0; i < viewValues.size(); i++) { - PropertyTexturePropertyValue& actual = viewValues[i]; - REQUIRE(actual.components[0] == values[i]); - REQUIRE(actual.components[1] == 0); - REQUIRE(actual.components[2] == 0); - REQUIRE(actual.components[3] == 0); +TEST_CASE("Check sampling with different sampler values") { + SECTION("REPEAT") { + Sampler sampler; + sampler.wrapS = Sampler::WrapS::REPEAT; + sampler.wrapT = Sampler::WrapT::REPEAT; + + ImageCesium image; + image.width = 2; + image.height = 2; + image.channels = 1; + image.bytesPerChannel = 1; + + std::vector data{12, 33, 56, 67}; + std::vector& imageData = image.pixelData; + imageData.resize(data.size()); + std::memcpy(imageData.data(), data.data(), data.size()); + + std::vector channels{0}; + + PropertyTexturePropertyView + view(sampler, image, 0, channels, false); + CHECK(view.getSwizzle() == "r"); + + std::vector values{ + view.get(1.0, 0), + view.get(-1.5, 0), + view.get(0, -0.5), + view.get(1.5, -0.5)}; + for (size_t i = 0; i < values.size(); i++) { + REQUIRE(values[i] == data[i]); + } + } + + SECTION("MIRRORED_REPEAT") { + Sampler sampler; + sampler.wrapS = Sampler::WrapS::MIRRORED_REPEAT; + sampler.wrapT = Sampler::WrapT::MIRRORED_REPEAT; + // REPEAT: | 1 2 3 | 1 2 3 | + // MIRRORED: | 1 2 3 | 3 2 1 | + // Sampling 0.6 is equal to sampling 1.4 or -0.6. + + ImageCesium image; + image.width = 2; + image.height = 2; + image.channels = 1; + image.bytesPerChannel = 1; + + std::vector data{12, 33, 56, 67}; + std::vector& imageData = image.pixelData; + imageData.resize(data.size()); + std::memcpy(imageData.data(), data.data(), data.size()); + + std::vector channels{0}; + + PropertyTexturePropertyView + view(sampler, image, 0, channels, false); + CHECK(view.getSwizzle() == "r"); + + std::vector values{ + view.get(2.0, 0), + view.get(-0.75, 0), + view.get(0, 1.25), + view.get(-1.25, 2.75)}; + for (size_t i = 0; i < values.size(); i++) { + REQUIRE(values[i] == data[i]); + } + } + + SECTION("CLAMP_TO_EDGE") { + Sampler sampler; + sampler.wrapS = Sampler::WrapS::CLAMP_TO_EDGE; + sampler.wrapT = Sampler::WrapT::CLAMP_TO_EDGE; + + ImageCesium image; + image.width = 2; + image.height = 2; + image.channels = 1; + image.bytesPerChannel = 1; + + std::vector data{12, 33, 56, 67}; + std::vector& imageData = image.pixelData; + imageData.resize(data.size()); + std::memcpy(imageData.data(), data.data(), data.size()); + + std::vector channels{0}; + + PropertyTexturePropertyView + view(sampler, image, 0, channels, false); + CHECK(view.getSwizzle() == "r"); + + std::vector values{ + view.get(-1.0, 0), + view.get(1.4, 0), + view.get(0, 2.0), + view.get(1.5, 1.5)}; + for (size_t i = 0; i < values.size(); i++) { + REQUIRE(values[i] == data[i]); + } + } + + SECTION("Mismatched wrap values") { + Sampler sampler; + sampler.wrapS = Sampler::WrapS::REPEAT; + sampler.wrapT = Sampler::WrapT::CLAMP_TO_EDGE; + + ImageCesium image; + image.width = 2; + image.height = 2; + image.channels = 1; + image.bytesPerChannel = 1; + + std::vector data{12, 33, 56, 67}; + std::vector& imageData = image.pixelData; + imageData.resize(data.size()); + std::memcpy(imageData.data(), data.data(), data.size()); + + std::vector channels{0}; + + PropertyTexturePropertyView + view(sampler, image, 0, channels, false); + CHECK(view.getSwizzle() == "r"); + + std::vector values{ + view.get(1.0, 0), + view.get(-1.5, -1.0), + view.get(0, 1.5), + view.get(1.5, 1.5)}; + for (size_t i = 0; i < values.size(); i++) { + REQUIRE(values[i] == data[i]); + } } } diff --git a/CesiumGltf/test/TestPropertyTextureView.cpp b/CesiumGltf/test/TestPropertyTextureView.cpp index 34dc8112d..f14306282 100644 --- a/CesiumGltf/test/TestPropertyTextureView.cpp +++ b/CesiumGltf/test/TestPropertyTextureView.cpp @@ -11,6 +11,35 @@ using namespace CesiumGltf; +namespace { +void addTextureToModel( + Model& model, + int32_t wrapS, + int32_t wrapT, + int32_t width, + int32_t height, + int32_t channels, + const std::vector& data) { + Image& image = model.images.emplace_back(); + image.cesium.width = width; + image.cesium.height = height; + image.cesium.channels = channels; + image.cesium.bytesPerChannel = 1; + + std::vector& imageData = image.cesium.pixelData; + imageData.resize(data.size()); + std::memcpy(imageData.data(), data.data(), data.size()); + + Sampler& sampler = model.samplers.emplace_back(); + sampler.wrapS = wrapS; + sampler.wrapT = wrapT; + + Texture& texture = model.textures.emplace_back(); + texture.sampler = static_cast(model.samplers.size() - 1); + texture.source = static_cast(model.images.size() - 1); +} +} // namespace + TEST_CASE("Test PropertyTextureView on model without EXT_structural_metadata " "extension") { Model model; @@ -29,7 +58,6 @@ TEST_CASE("Test PropertyTextureView on model without EXT_structural_metadata " REQUIRE( view.status() == PropertyTextureViewStatus::ErrorMissingMetadataExtension); - REQUIRE(view.getProperties().empty()); const ClassProperty* classProperty = view.getClassProperty("TestClassProperty"); @@ -53,7 +81,6 @@ TEST_CASE("Test PropertyTextureView on model without metadata schema") { PropertyTextureView view(model, propertyTexture); REQUIRE(view.status() == PropertyTextureViewStatus::ErrorMissingSchema); - REQUIRE(view.getProperties().empty()); const ClassProperty* classProperty = view.getClassProperty("TestClassProperty"); @@ -83,15 +110,24 @@ TEST_CASE("Test property texture with nonexistent class") { PropertyTextureView view(model, propertyTexture); REQUIRE(view.status() == PropertyTextureViewStatus::ErrorClassNotFound); - REQUIRE(view.getProperties().empty()); const ClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE(!classProperty); } -TEST_CASE("Test property texture with nonexistent class property") { +TEST_CASE("Test scalar PropertyTextureProperty") { Model model; + std::vector data = {12, 34, 30, 11}; + + addTextureToModel( + model, + Sampler::WrapS::CLAMP_TO_EDGE, + Sampler::WrapS::CLAMP_TO_EDGE, + 2, + 2, + 1, + data); ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); @@ -106,50 +142,423 @@ TEST_CASE("Test property texture with nonexistent class property") { propertyTexture.classProperty = "TestClass"; PropertyTextureProperty& propertyTextureProperty = - propertyTexture.properties["I Don't Exist"]; + propertyTexture.properties["TestClassProperty"]; propertyTextureProperty.index = 0; propertyTextureProperty.texCoord = 0; propertyTextureProperty.channels = {0}; PropertyTextureView view(model, propertyTexture); - REQUIRE( - view.status() == PropertyTextureViewStatus::ErrorClassPropertyNotFound); - REQUIRE(view.getProperties().empty()); + REQUIRE(view.status() == PropertyTextureViewStatus::Valid); const ClassProperty* classProperty = view.getClassProperty("TestClassProperty"); - REQUIRE(!classProperty); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT8); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->array); + + SECTION("Access correct type") { + PropertyTexturePropertyView uint8Property = + view.getPropertyView("TestClassProperty"); + REQUIRE(uint8Property.status() == PropertyTexturePropertyViewStatus::Valid); + + std::vector values{ + uint8Property.get(0.0, 0.0), + uint8Property.get(0.5, 0.0), + uint8Property.get(0.0, 0.5), + uint8Property.get(0.5, 0.5), + }; + + for (size_t i = 0; i < values.size(); ++i) { + REQUIRE(values[i] == data[i]); + } + } + + SECTION("Access wrong component type") { + PropertyTexturePropertyView uint16Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uint16Invalid.status() == + PropertyTexturePropertyViewStatus::ErrorComponentTypeMismatch); + + PropertyTexturePropertyView int32Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + int32Invalid.status() == + PropertyTexturePropertyViewStatus::ErrorComponentTypeMismatch); + + PropertyTexturePropertyView uint64Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uint64Invalid.status() == + PropertyTexturePropertyViewStatus::ErrorComponentTypeMismatch); + } + + SECTION("Access incorrectly as array") { + PropertyTexturePropertyView> arrayInvalid = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + arrayInvalid.status() == + PropertyTexturePropertyViewStatus::ErrorArrayTypeMismatch); + } } -TEST_CASE("Test property texture with invalid property is still valid") { +// TEST_CASE( +// "Test PropertyTexturePropertyView on property with invalid texture +// index") { +// Model model; +// ExtensionModelExtStructuralMetadata& metadata = +// model.addExtension(); +// +// Schema& schema = metadata.schema.emplace(); +// Class& testClass = schema.classes["TestClass"]; +// ClassProperty& testClassProperty = +// testClass.properties["TestClassProperty"]; testClassProperty.type = +// ClassProperty::Type::SCALAR; testClassProperty.componentType = +// ClassProperty::ComponentType::UINT8; +// +// PropertyTexture propertyTexture; +// propertyTexture.classProperty = "TestClass"; +// +// PropertyTextureProperty& propertyTextureProperty = +// propertyTexture.properties["TestClassProperty"]; +// propertyTextureProperty.index = -1; +// propertyTextureProperty.texCoord = 0; +// propertyTextureProperty.channels = {0}; +// +// PropertyTexturePropertyView view( +// model, +// testClassProperty, +// propertyTextureProperty); +// REQUIRE( +// view.status() == +// PropertyTexturePropertyViewStatus::ErrorInvalidTexture); +//} +// +// TEST_CASE( +// "Test PropertyTexturePropertyView on property with invalid sampler index") +// { +// Model model; +// ExtensionModelExtStructuralMetadata& metadata = +// model.addExtension(); +// +// Schema& schema = metadata.schema.emplace(); +// Class& testClass = schema.classes["TestClass"]; +// ClassProperty& testClassProperty = +// testClass.properties["TestClassProperty"]; testClassProperty.type = +// ClassProperty::Type::SCALAR; testClassProperty.componentType = +// ClassProperty::ComponentType::UINT8; +// +// Image& image = model.images.emplace_back(); +// image.cesium.width = 1; +// image.cesium.height = 1; +// Texture& texture = model.textures.emplace_back(); +// texture.sampler = -1; +// texture.source = 0; +// +// PropertyTexture propertyTexture; +// propertyTexture.classProperty = "TestClass"; +// +// PropertyTextureProperty& propertyTextureProperty = +// propertyTexture.properties["TestClassProperty"]; +// propertyTextureProperty.index = 0; +// propertyTextureProperty.texCoord = 0; +// propertyTextureProperty.channels = {0}; +// +// PropertyTexturePropertyView view( +// model, +// testClassProperty, +// propertyTextureProperty); +// REQUIRE( +// view.status() == +// PropertyTexturePropertyViewStatus::ErrorInvalidTextureSampler); +//} +// +// TEST_CASE( +// "Test PropertyTexturePropertyView on property with invalid image index") { +// Model model; +// ExtensionModelExtStructuralMetadata& metadata = +// model.addExtension(); +// +// Schema& schema = metadata.schema.emplace(); +// Class& testClass = schema.classes["TestClass"]; +// ClassProperty& testClassProperty = +// testClass.properties["TestClassProperty"]; testClassProperty.type = +// ClassProperty::Type::SCALAR; testClassProperty.componentType = +// ClassProperty::ComponentType::UINT8; +// +// model.samplers.emplace_back(); +// +// Texture& texture = model.textures.emplace_back(); +// texture.sampler = 0; +// texture.source = -1; +// +// PropertyTexture propertyTexture; +// propertyTexture.classProperty = "TestClass"; +// +// PropertyTextureProperty& propertyTextureProperty = +// propertyTexture.properties["TestClassProperty"]; +// propertyTextureProperty.index = 0; +// propertyTextureProperty.texCoord = 0; +// propertyTextureProperty.channels = {0}; +// +// PropertyTexturePropertyView view( +// model, +// testClassProperty, +// propertyTextureProperty); +// REQUIRE( +// view.status() == PropertyTexturePropertyViewStatus::ErrorInvalidImage); +//} +// +// TEST_CASE("Test PropertyTexturePropertyView on property with empty image") { +// Model model; +// ExtensionModelExtStructuralMetadata& metadata = +// model.addExtension(); +// +// Schema& schema = metadata.schema.emplace(); +// Class& testClass = schema.classes["TestClass"]; +// ClassProperty& testClassProperty = +// testClass.properties["TestClassProperty"]; testClassProperty.type = +// ClassProperty::Type::SCALAR; testClassProperty.componentType = +// ClassProperty::ComponentType::UINT8; +// +// Image& image = model.images.emplace_back(); +// image.cesium.width = 0; +// image.cesium.height = 0; +// +// model.samplers.emplace_back(); +// +// Texture& texture = model.textures.emplace_back(); +// texture.sampler = 0; +// texture.source = 0; +// +// PropertyTexture propertyTexture; +// propertyTexture.classProperty = "TestClass"; +// +// PropertyTextureProperty& propertyTextureProperty = +// propertyTexture.properties["TestClassProperty"]; +// propertyTextureProperty.index = 0; +// propertyTextureProperty.texCoord = 0; +// propertyTextureProperty.channels = {0}; +// +// PropertyTexturePropertyView view( +// model, +// testClassProperty, +// propertyTextureProperty); +// REQUIRE(view.status() == +// PropertyTexturePropertyViewStatus::ErrorEmptyImage); +//} +// +// TEST_CASE("Test PropertyTextureView on property texture property with zero " +// "channels") { +// Model model; +// ExtensionModelExtStructuralMetadata& metadata = +// model.addExtension(); +// +// Schema& schema = metadata.schema.emplace(); +// Class& testClass = schema.classes["TestClass"]; +// ClassProperty& testClassProperty = +// testClass.properties["TestClassProperty"]; testClassProperty.type = +// ClassProperty::Type::SCALAR; testClassProperty.componentType = +// ClassProperty::ComponentType::UINT8; +// +// Image& image = model.images.emplace_back(); +// image.cesium.width = 1; +// image.cesium.height = 1; +// image.cesium.channels = 1; +// +// model.samplers.emplace_back(); +// +// Texture& texture = model.textures.emplace_back(); +// texture.sampler = 0; +// texture.source = 0; +// +// PropertyTexture propertyTexture; +// propertyTexture.classProperty = "TestClass"; +// +// PropertyTextureProperty& propertyTextureProperty = +// propertyTexture.properties["TestClassProperty"]; +// propertyTextureProperty.index = 0; +// propertyTextureProperty.texCoord = 0; +// propertyTextureProperty.channels = {}; +// +// PropertyTexturePropertyView view( +// model, +// testClassProperty, +// propertyTextureProperty); +// REQUIRE( +// view.status() == +// PropertyTexturePropertyViewStatus::ErrorInvalidChannels); +//} +// +// TEST_CASE("Test PropertyTextureView on property texture property with too +// many " +// "channels") { +// Model model; +// ExtensionModelExtStructuralMetadata& metadata = +// model.addExtension(); +// +// Schema& schema = metadata.schema.emplace(); +// Class& testClass = schema.classes["TestClass"]; +// ClassProperty& testClassProperty = +// testClass.properties["TestClassProperty"]; testClassProperty.type = +// ClassProperty::Type::SCALAR; testClassProperty.componentType = +// ClassProperty::ComponentType::UINT8; +// +// Image& image = model.images.emplace_back(); +// image.cesium.width = 1; +// image.cesium.height = 1; +// image.cesium.channels = 1; +// +// model.samplers.emplace_back(); +// +// Texture& texture = model.textures.emplace_back(); +// texture.sampler = 0; +// texture.source = 0; +// +// PropertyTexture propertyTexture; +// propertyTexture.classProperty = "TestClass"; +// +// PropertyTextureProperty& propertyTextureProperty = +// propertyTexture.properties["TestClassProperty"]; +// propertyTextureProperty.index = 0; +// propertyTextureProperty.texCoord = 0; +// propertyTextureProperty.channels = {0, 1}; +// +// PropertyTexturePropertyView view( +// model, +// testClassProperty, +// propertyTextureProperty); +// REQUIRE( +// view.status() == +// PropertyTexturePropertyViewStatus::ErrorInvalidChannels); +//} +// +// TEST_CASE("Test PropertyTextureView on property texture property with " +// "unsupported class property") { +// Model model; +// ExtensionModelExtStructuralMetadata& metadata = +// model.addExtension(); +// +// Schema& schema = metadata.schema.emplace(); +// Class& testClass = schema.classes["TestClass"]; +// ClassProperty& testClassProperty = +// testClass.properties["TestClassProperty"]; testClassProperty.type = +// ClassProperty::Type::SCALAR; testClassProperty.componentType = +// ClassProperty::ComponentType::UINT8; +// +// Image& image = model.images.emplace_back(); +// image.cesium.width = 1; +// image.cesium.height = 1; +// image.cesium.channels = 1; +// +// model.samplers.emplace_back(); +// +// Texture& texture = model.textures.emplace_back(); +// texture.sampler = 0; +// texture.source = 0; +// +// PropertyTexture propertyTexture; +// propertyTexture.classProperty = "TestClass"; +// +// PropertyTextureProperty& propertyTextureProperty = +// propertyTexture.properties["TestClassProperty"]; +// propertyTextureProperty.index = 0; +// propertyTextureProperty.texCoord = 0; +// propertyTextureProperty.channels = {0, 1}; +// +// PropertyTexturePropertyView view( +// model, +// testClassProperty, +// propertyTextureProperty); +// REQUIRE( +// view.status() == +// PropertyTexturePropertyViewStatus::ErrorInvalidChannels); +//} +/* +TEST_CASE("Test PropertyTexturePropertyView on valid property texture") { Model model; - ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); Schema& schema = metadata.schema.emplace(); Class& testClass = schema.classes["TestClass"]; - ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; - testClassProperty.type = ClassProperty::Type::SCALAR; - testClassProperty.componentType = ClassProperty::ComponentType::UINT8; + ClassProperty& testClassProperty = +testClass.properties["TestClassProperty"]; testClassProperty.type = +ClassProperty::Type::SCALAR; testClassProperty.componentType = +ClassProperty::ComponentType::UINT8; - PropertyTexture& propertyTexture = metadata.propertyTextures.emplace_back(); + Image& image = model.images.emplace_back(); + image.cesium.width = 1; + image.cesium.height = 1; + image.cesium.channels = 1; + + model.samplers.emplace_back(); + + Texture& texture = model.textures.emplace_back(); + texture.sampler = 0; + texture.source = 0; + + PropertyTexture propertyTexture; propertyTexture.classProperty = "TestClass"; PropertyTextureProperty& propertyTextureProperty = propertyTexture.properties["TestClassProperty"]; - propertyTextureProperty.index = -1; + propertyTextureProperty.index = 0; + propertyTextureProperty.texCoord = 0; + propertyTextureProperty.channels = {0}; - PropertyTextureView view(model, propertyTexture); - REQUIRE(view.status() == PropertyTextureViewStatus::Valid); + PropertyTexturePropertyView view( + model, + testClassProperty, + propertyTextureProperty); + REQUIRE(view.status() == PropertyTexturePropertyViewStatus::Valid); +} - auto properties = view.getProperties(); - REQUIRE(properties.size() == 1); +TEST_CASE("Test getting value from valid view") { + Model model; + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); - PropertyTexturePropertyView& propertyView = properties["TestClassProperty"]; - REQUIRE(propertyView.status() != PropertyTexturePropertyViewStatus::Valid); + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = +testClass.properties["TestClassProperty"]; testClassProperty.type = +ClassProperty::Type::SCALAR; testClassProperty.componentType = +ClassProperty::ComponentType::UINT8; - const ClassProperty* classProperty = - view.getClassProperty("TestClassProperty"); - REQUIRE(classProperty == &testClassProperty); -} + std::vector values{10, 8, 4, 22}; + + Image& image = model.images.emplace_back(); + image.cesium.width = 2; + image.cesium.height = 2; + image.cesium.channels = 1; + image.cesium.bytesPerChannel = 1; + image.cesium.pixelData.resize(values.size()); + std::memcpy(image.cesium.pixelData.data(), values.data(), values.size()); + + Sampler& sampler = model.samplers.emplace_back(); + sampler.wrapS = Sampler::WrapS::CLAMP_TO_EDGE; + sampler.wrapT = Sampler::WrapT::CLAMP_TO_EDGE; + + Texture& texture = model.textures.emplace_back(); + texture.sampler = 0; + texture.source = 0; + + PropertyTexture propertyTexture; + propertyTexture.classProperty = "TestClass"; + + PropertyTextureProperty& propertyTextureProperty = + propertyTexture.properties["TestClassProperty"]; + propertyTextureProperty.index = 0; + propertyTextureProperty.texCoord = 0; + propertyTextureProperty.channels = {0}; + + PropertyTexturePropertyView view( + model, + testClassProperty, + propertyTextureProperty); + + +}*/ diff --git a/CesiumGltf/test/TestPropertyType.cpp b/CesiumGltf/test/TestPropertyType.cpp index 539d01a3b..acca55dd6 100644 --- a/CesiumGltf/test/TestPropertyType.cpp +++ b/CesiumGltf/test/TestPropertyType.cpp @@ -6,236 +6,273 @@ using namespace CesiumGltf; -TEST_CASE("Test PropertyType utilities function") { - SECTION("Convert string to PropertyType") { - REQUIRE( - convertStringToPropertyType(ClassProperty::Type::SCALAR) == - PropertyType::Scalar); - - REQUIRE( - convertStringToPropertyType(ClassProperty::Type::VEC2) == - PropertyType::Vec2); - - REQUIRE( - convertStringToPropertyType(ClassProperty::Type::VEC3) == - PropertyType::Vec3); - - REQUIRE( - convertStringToPropertyType(ClassProperty::Type::VEC4) == - PropertyType::Vec4); - - REQUIRE( - convertStringToPropertyType(ClassProperty::Type::MAT2) == - PropertyType::Mat2); - - REQUIRE( - convertStringToPropertyType(ClassProperty::Type::MAT3) == - PropertyType::Mat3); - - REQUIRE( - convertStringToPropertyType(ClassProperty::Type::MAT4) == - PropertyType::Mat4); - - REQUIRE( - convertStringToPropertyType(ClassProperty::Type::BOOLEAN) == - PropertyType::Boolean); - - REQUIRE( - convertStringToPropertyType(ClassProperty::Type::STRING) == - PropertyType::String); - - REQUIRE( - convertStringToPropertyType(ClassProperty::Type::ENUM) == - PropertyType::Enum); - - REQUIRE(convertStringToPropertyType("invalid") == PropertyType::Invalid); - } - - SECTION("Convert string to PropertyComponentType") { - REQUIRE( - convertStringToPropertyComponentType( - ClassProperty::ComponentType::UINT8) == - PropertyComponentType::Uint8); - - REQUIRE( - convertStringToPropertyComponentType( - ClassProperty::ComponentType::INT8) == PropertyComponentType::Int8); - - REQUIRE( - convertStringToPropertyComponentType( - ClassProperty::ComponentType::UINT16) == - PropertyComponentType::Uint16); - REQUIRE( - convertStringToPropertyComponentType( - ClassProperty::ComponentType::INT16) == - PropertyComponentType::Int16); - - REQUIRE( - convertStringToPropertyComponentType( - ClassProperty::ComponentType::UINT32) == - PropertyComponentType::Uint32); - - REQUIRE( - convertStringToPropertyComponentType( - ClassProperty::ComponentType::INT32) == - PropertyComponentType::Int32); - REQUIRE( - convertStringToPropertyComponentType( - ClassProperty::ComponentType::UINT64) == - PropertyComponentType::Uint64); - - REQUIRE( - convertStringToPropertyComponentType( - ClassProperty::ComponentType::INT64) == - PropertyComponentType::Int64); - - REQUIRE( - convertStringToPropertyComponentType( - ClassProperty::ComponentType::FLOAT32) == - PropertyComponentType::Float32); - REQUIRE( - convertStringToPropertyComponentType( - ClassProperty::ComponentType::FLOAT64) == - PropertyComponentType::Float64); - - REQUIRE( - convertStringToPropertyComponentType("invalid") == - PropertyComponentType::None); - } - - SECTION("Convert PropertyType to string") { - REQUIRE( - convertPropertyTypeToString(PropertyType::Scalar) == - ClassProperty::Type::SCALAR); - - REQUIRE( - convertPropertyTypeToString(PropertyType::Vec2) == - ClassProperty::Type::VEC2); - - REQUIRE( - convertPropertyTypeToString(PropertyType::Vec3) == - ClassProperty::Type::VEC3); - - REQUIRE( - convertPropertyTypeToString(PropertyType::Vec4) == - ClassProperty::Type::VEC4); - - REQUIRE( - convertPropertyTypeToString(PropertyType::Mat2) == - ClassProperty::Type::MAT2); - - REQUIRE( - convertPropertyTypeToString(PropertyType::Mat3) == - ClassProperty::Type::MAT3); - - REQUIRE( - convertPropertyTypeToString(PropertyType::Mat4) == - ClassProperty::Type::MAT4); - - REQUIRE( - convertPropertyTypeToString(PropertyType::Boolean) == - ClassProperty::Type::BOOLEAN); - - REQUIRE( - convertPropertyTypeToString(PropertyType::String) == - ClassProperty::Type::STRING); - - REQUIRE( - convertPropertyTypeToString(PropertyType::Enum) == - ClassProperty::Type::ENUM); - } - - SECTION("Convert PropertyComponentType to string") { - REQUIRE( - convertPropertyComponentTypeToString(PropertyComponentType::Uint8) == - ClassProperty::ComponentType::UINT8); - - REQUIRE( - convertPropertyComponentTypeToString(PropertyComponentType::Int8) == - ClassProperty::ComponentType::INT8); - - REQUIRE( - convertPropertyComponentTypeToString(PropertyComponentType::Uint16) == - ClassProperty::ComponentType::UINT16); - - REQUIRE( - convertPropertyComponentTypeToString(PropertyComponentType::Int16) == - ClassProperty::ComponentType::INT16); - - REQUIRE( - convertPropertyComponentTypeToString(PropertyComponentType::Uint32) == - ClassProperty::ComponentType::UINT32); - - REQUIRE( - convertPropertyComponentTypeToString(PropertyComponentType::Int32) == - ClassProperty::ComponentType::INT32); - - REQUIRE( - convertPropertyComponentTypeToString(PropertyComponentType::Uint64) == - ClassProperty::ComponentType::UINT64); - - REQUIRE( - convertPropertyComponentTypeToString(PropertyComponentType::Int64) == - ClassProperty::ComponentType::INT64); - - REQUIRE( - convertPropertyComponentTypeToString(PropertyComponentType::Float32) == - ClassProperty::ComponentType::FLOAT32); - - REQUIRE( - convertPropertyComponentTypeToString(PropertyComponentType::Float64) == - ClassProperty::ComponentType::FLOAT64); - } - - SECTION("Convert array offset type string to PropertyComponentType") { - REQUIRE( - convertArrayOffsetTypeStringToPropertyComponentType( - PropertyTableProperty::ArrayOffsetType::UINT8) == - PropertyComponentType::Uint8); - - REQUIRE( - convertArrayOffsetTypeStringToPropertyComponentType( - PropertyTableProperty::ArrayOffsetType::UINT16) == - PropertyComponentType::Uint16); - - REQUIRE( - convertArrayOffsetTypeStringToPropertyComponentType( - PropertyTableProperty::ArrayOffsetType::UINT32) == - PropertyComponentType::Uint32); - - REQUIRE( - convertArrayOffsetTypeStringToPropertyComponentType( - PropertyTableProperty::ArrayOffsetType::UINT64) == - PropertyComponentType::Uint64); - - REQUIRE( - convertArrayOffsetTypeStringToPropertyComponentType("invalid") == - PropertyComponentType::None); - } - - SECTION("Convert string offset type string to PropertyComponentType") { - REQUIRE( - convertStringOffsetTypeStringToPropertyComponentType( - PropertyTableProperty::StringOffsetType::UINT8) == - PropertyComponentType::Uint8); - - REQUIRE( - convertStringOffsetTypeStringToPropertyComponentType( - PropertyTableProperty::StringOffsetType::UINT16) == - PropertyComponentType::Uint16); - - REQUIRE( - convertStringOffsetTypeStringToPropertyComponentType( - PropertyTableProperty::StringOffsetType::UINT32) == - PropertyComponentType::Uint32); - - REQUIRE( - convertStringOffsetTypeStringToPropertyComponentType( - PropertyTableProperty::StringOffsetType::UINT64) == - PropertyComponentType::Uint64); - - REQUIRE( - convertStringOffsetTypeStringToPropertyComponentType("invalid") == - PropertyComponentType::None); - } +TEST_CASE("Test convertStringToPropertyType") { + REQUIRE( + convertStringToPropertyType(ClassProperty::Type::SCALAR) == + PropertyType::Scalar); + + REQUIRE( + convertStringToPropertyType(ClassProperty::Type::VEC2) == + PropertyType::Vec2); + + REQUIRE( + convertStringToPropertyType(ClassProperty::Type::VEC3) == + PropertyType::Vec3); + + REQUIRE( + convertStringToPropertyType(ClassProperty::Type::VEC4) == + PropertyType::Vec4); + + REQUIRE( + convertStringToPropertyType(ClassProperty::Type::MAT2) == + PropertyType::Mat2); + + REQUIRE( + convertStringToPropertyType(ClassProperty::Type::MAT3) == + PropertyType::Mat3); + + REQUIRE( + convertStringToPropertyType(ClassProperty::Type::MAT4) == + PropertyType::Mat4); + + REQUIRE( + convertStringToPropertyType(ClassProperty::Type::BOOLEAN) == + PropertyType::Boolean); + + REQUIRE( + convertStringToPropertyType(ClassProperty::Type::STRING) == + PropertyType::String); + + REQUIRE( + convertStringToPropertyType(ClassProperty::Type::ENUM) == + PropertyType::Enum); + + REQUIRE(convertStringToPropertyType("invalid") == PropertyType::Invalid); +} + +TEST_CASE("Test convertStringToPropertyComponentType") { + REQUIRE( + convertStringToPropertyComponentType( + ClassProperty::ComponentType::UINT8) == PropertyComponentType::Uint8); + + REQUIRE( + convertStringToPropertyComponentType( + ClassProperty::ComponentType::INT8) == PropertyComponentType::Int8); + + REQUIRE( + convertStringToPropertyComponentType( + ClassProperty::ComponentType::UINT16) == + PropertyComponentType::Uint16); + REQUIRE( + convertStringToPropertyComponentType( + ClassProperty::ComponentType::INT16) == PropertyComponentType::Int16); + + REQUIRE( + convertStringToPropertyComponentType( + ClassProperty::ComponentType::UINT32) == + PropertyComponentType::Uint32); + + REQUIRE( + convertStringToPropertyComponentType( + ClassProperty::ComponentType::INT32) == PropertyComponentType::Int32); + REQUIRE( + convertStringToPropertyComponentType( + ClassProperty::ComponentType::UINT64) == + PropertyComponentType::Uint64); + + REQUIRE( + convertStringToPropertyComponentType( + ClassProperty::ComponentType::INT64) == PropertyComponentType::Int64); + + REQUIRE( + convertStringToPropertyComponentType( + ClassProperty::ComponentType::FLOAT32) == + PropertyComponentType::Float32); + REQUIRE( + convertStringToPropertyComponentType( + ClassProperty::ComponentType::FLOAT64) == + PropertyComponentType::Float64); + + REQUIRE( + convertStringToPropertyComponentType("invalid") == + PropertyComponentType::None); +} + +TEST_CASE("Test convertPropertyTypeToString") { + REQUIRE( + convertPropertyTypeToString(PropertyType::Scalar) == + ClassProperty::Type::SCALAR); + + REQUIRE( + convertPropertyTypeToString(PropertyType::Vec2) == + ClassProperty::Type::VEC2); + + REQUIRE( + convertPropertyTypeToString(PropertyType::Vec3) == + ClassProperty::Type::VEC3); + + REQUIRE( + convertPropertyTypeToString(PropertyType::Vec4) == + ClassProperty::Type::VEC4); + + REQUIRE( + convertPropertyTypeToString(PropertyType::Mat2) == + ClassProperty::Type::MAT2); + + REQUIRE( + convertPropertyTypeToString(PropertyType::Mat3) == + ClassProperty::Type::MAT3); + + REQUIRE( + convertPropertyTypeToString(PropertyType::Mat4) == + ClassProperty::Type::MAT4); + + REQUIRE( + convertPropertyTypeToString(PropertyType::Boolean) == + ClassProperty::Type::BOOLEAN); + + REQUIRE( + convertPropertyTypeToString(PropertyType::String) == + ClassProperty::Type::STRING); + + REQUIRE( + convertPropertyTypeToString(PropertyType::Enum) == + ClassProperty::Type::ENUM); +} + +TEST_CASE("Test convertPropertyComponentTypeToString") { + REQUIRE( + convertPropertyComponentTypeToString(PropertyComponentType::Uint8) == + ClassProperty::ComponentType::UINT8); + + REQUIRE( + convertPropertyComponentTypeToString(PropertyComponentType::Int8) == + ClassProperty::ComponentType::INT8); + + REQUIRE( + convertPropertyComponentTypeToString(PropertyComponentType::Uint16) == + ClassProperty::ComponentType::UINT16); + + REQUIRE( + convertPropertyComponentTypeToString(PropertyComponentType::Int16) == + ClassProperty::ComponentType::INT16); + + REQUIRE( + convertPropertyComponentTypeToString(PropertyComponentType::Uint32) == + ClassProperty::ComponentType::UINT32); + + REQUIRE( + convertPropertyComponentTypeToString(PropertyComponentType::Int32) == + ClassProperty::ComponentType::INT32); + + REQUIRE( + convertPropertyComponentTypeToString(PropertyComponentType::Uint64) == + ClassProperty::ComponentType::UINT64); + + REQUIRE( + convertPropertyComponentTypeToString(PropertyComponentType::Int64) == + ClassProperty::ComponentType::INT64); + + REQUIRE( + convertPropertyComponentTypeToString(PropertyComponentType::Float32) == + ClassProperty::ComponentType::FLOAT32); + + REQUIRE( + convertPropertyComponentTypeToString(PropertyComponentType::Float64) == + ClassProperty::ComponentType::FLOAT64); +} + +TEST_CASE("Test convertArrayOffsetTypeStringToPropertyComponentType") { + REQUIRE( + convertArrayOffsetTypeStringToPropertyComponentType( + PropertyTableProperty::ArrayOffsetType::UINT8) == + PropertyComponentType::Uint8); + + REQUIRE( + convertArrayOffsetTypeStringToPropertyComponentType( + PropertyTableProperty::ArrayOffsetType::UINT16) == + PropertyComponentType::Uint16); + + REQUIRE( + convertArrayOffsetTypeStringToPropertyComponentType( + PropertyTableProperty::ArrayOffsetType::UINT32) == + PropertyComponentType::Uint32); + + REQUIRE( + convertArrayOffsetTypeStringToPropertyComponentType( + PropertyTableProperty::ArrayOffsetType::UINT64) == + PropertyComponentType::Uint64); + + REQUIRE( + convertArrayOffsetTypeStringToPropertyComponentType("invalid") == + PropertyComponentType::None); +} + +TEST_CASE("Test convertStringOffsetTypeStringToPropertyComponentType") { + REQUIRE( + convertStringOffsetTypeStringToPropertyComponentType( + PropertyTableProperty::StringOffsetType::UINT8) == + PropertyComponentType::Uint8); + + REQUIRE( + convertStringOffsetTypeStringToPropertyComponentType( + PropertyTableProperty::StringOffsetType::UINT16) == + PropertyComponentType::Uint16); + + REQUIRE( + convertStringOffsetTypeStringToPropertyComponentType( + PropertyTableProperty::StringOffsetType::UINT32) == + PropertyComponentType::Uint32); + + REQUIRE( + convertStringOffsetTypeStringToPropertyComponentType( + PropertyTableProperty::StringOffsetType::UINT64) == + PropertyComponentType::Uint64); + + REQUIRE( + convertStringOffsetTypeStringToPropertyComponentType("invalid") == + PropertyComponentType::None); +} + +TEST_CASE("Test getDimensionsFromPropertyType") { + REQUIRE(getDimensionsFromPropertyType(PropertyType::Scalar) == 1); + + REQUIRE(getDimensionsFromPropertyType(PropertyType::Vec2) == 2); + REQUIRE(getDimensionsFromPropertyType(PropertyType::Vec3) == 3); + REQUIRE(getDimensionsFromPropertyType(PropertyType::Vec4) == 4); + + REQUIRE(getDimensionsFromPropertyType(PropertyType::Mat2) == 2); + REQUIRE(getDimensionsFromPropertyType(PropertyType::Mat3) == 3); + REQUIRE(getDimensionsFromPropertyType(PropertyType::Mat4) == 4); + + REQUIRE(getDimensionsFromPropertyType(PropertyType::Boolean) == 0); + REQUIRE(getDimensionsFromPropertyType(PropertyType::String) == 0); + REQUIRE(getDimensionsFromPropertyType(PropertyType::Enum) == 0); + REQUIRE(getDimensionsFromPropertyType(PropertyType::Invalid) == 0); +} + +TEST_CASE("Test getSizeOfComponentType") { + REQUIRE( + getSizeOfComponentType(PropertyComponentType::Int8) == sizeof(int8_t)); + REQUIRE( + getSizeOfComponentType(PropertyComponentType::Uint8) == sizeof(uint8_t)); + REQUIRE( + getSizeOfComponentType(PropertyComponentType::Int16) == sizeof(int16_t)); + REQUIRE( + getSizeOfComponentType(PropertyComponentType::Uint16) == + sizeof(uint16_t)); + REQUIRE( + getSizeOfComponentType(PropertyComponentType::Int32) == sizeof(int32_t)); + REQUIRE( + getSizeOfComponentType(PropertyComponentType::Uint32) == + sizeof(uint32_t)); + REQUIRE( + getSizeOfComponentType(PropertyComponentType::Int64) == sizeof(int64_t)); + REQUIRE( + getSizeOfComponentType(PropertyComponentType::Uint64) == + sizeof(uint64_t)); + REQUIRE( + getSizeOfComponentType(PropertyComponentType::Float32) == sizeof(float)); + REQUIRE( + getSizeOfComponentType(PropertyComponentType::Float64) == sizeof(double)); } From e49685f0cf139a3c45d6e71bec46946d5dc856a1 Mon Sep 17 00:00:00 2001 From: elser Date: Tue, 27 Jun 2023 12:04:00 -0400 Subject: [PATCH 085/421] include generated headers in doxygen documentation --- doc/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index ec2bd6a83..e6da4b313 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -4,6 +4,7 @@ if (DOXYGEN_FOUND) set( LIB_DIRS ../Cesium3DTiles/include + ../Cesium3DTiles/generated/include ../Cesium3DTilesReader/include ../Cesium3DTilesWriter/include ../Cesium3DTilesSelection/include @@ -11,6 +12,7 @@ if (DOXYGEN_FOUND) ../CesiumGeometry/include ../CesiumGeospatial/include ../CesiumGltf/include + ../CesiumGltf/generated/include ../CesiumGltfReader/include ../CesiumGltfWriter/include ../CesiumUtility/include From 8155617808483a8cdf9239dc420b009d4e79def4 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Thu, 29 Jun 2023 14:49:12 -0400 Subject: [PATCH 086/421] Add tests for templated getPropertyView --- .../CesiumGltf/PropertyTexturePropertyView.h | 11 +- .../include/CesiumGltf/PropertyTextureView.h | 25 +- CesiumGltf/src/PropertyTextureView.cpp | 8 +- CesiumGltf/test/TestPropertyTableView.cpp | 32 +- .../test/TestPropertyTexturePropertyView.cpp | 2 +- CesiumGltf/test/TestPropertyTextureView.cpp | 769 ++++++++++-------- 6 files changed, 481 insertions(+), 366 deletions(-) diff --git a/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h index c843781d2..7adb05fa8 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h @@ -87,18 +87,13 @@ enum class PropertyTexturePropertyViewStatus { /** * @brief The channels of this property texture property are invalid. - * Channels must be in the range 0-3, with a minimum of one channel. Although + * Channels must be in the range 0-N, where N is the number of available + * channels in the image. There must be a minimum of one channel. Although * more than four channels can be defined for specialized texture - * formats, this view only supports a maximum of four channels. + * formats, this implementation only supports four channels max. */ ErrorInvalidChannels, - /** - * @brief This property texture property is trying to sample channels that - * don't exist in the image. - */ - ErrorTooManyChannels, - /** * @brief The channels of this property texture property do not provide the * exact number of bytes required by the property type. This may be because diff --git a/CesiumGltf/include/CesiumGltf/PropertyTextureView.h b/CesiumGltf/include/CesiumGltf/PropertyTextureView.h index 1ac63f84d..f6a7c0ded 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTextureView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTextureView.h @@ -129,6 +129,7 @@ class PropertyTextureView { classProperty, propertyTextureProperty); } else if constexpr (IsMetadataVecN::value) { + return createVecNPropertyView(classProperty, propertyTextureProperty); } else if constexpr (IsMetadataArray::value) { return createArrayPropertyView::type>( classProperty, @@ -168,12 +169,12 @@ class PropertyTextureView { std::is_same_v) { return PropertyTexturePropertyView( PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty); + } else { + return createPropertyViewImpl( + classProperty, + propertyTextureProperty, + sizeof(T)); } - - return createPropertyViewImpl( - classProperty, - propertyTextureProperty, - sizeof(T)); } template @@ -199,15 +200,11 @@ class PropertyTextureView { PropertyTexturePropertyViewStatus::ErrorComponentTypeMismatch); } - // Only uint8 and uint16s are supported. - if (componentType != PropertyComponentType::Uint8 && - componentType != PropertyComponentType::Uint16) { - return PropertyTexturePropertyView( - PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty); - } - // Only up to four bytes of image data are supported. - if (sizeof(T) > 4) { + size_t dimensions = + static_cast(getDimensionsFromPropertyType(type)); + + if (dimensions * getSizeOfComponentType(componentType) > 4) { return PropertyTexturePropertyView( PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty); } @@ -292,7 +289,7 @@ class PropertyTextureView { return PropertyTexturePropertyView(status); } - status = checkImage(samplerIndex); + status = checkImage(imageIndex); if (status != PropertyTexturePropertyViewStatus::Valid) { return PropertyTexturePropertyView(status); } diff --git a/CesiumGltf/src/PropertyTextureView.cpp b/CesiumGltf/src/PropertyTextureView.cpp index 24c9d10ff..d92c00bc2 100644 --- a/CesiumGltf/src/PropertyTextureView.cpp +++ b/CesiumGltf/src/PropertyTextureView.cpp @@ -97,6 +97,10 @@ PropertyTextureView::checkImage(const int32_t imageIndex) const noexcept { PropertyTexturePropertyViewStatus PropertyTextureView::checkChannels( const std::vector& channels, const ImageCesium& image) const noexcept { + if (channels.size() <= 0 || channels.size() > 4) { + return PropertyTexturePropertyViewStatus::ErrorInvalidChannels; + } + int64_t imageChannelCount = static_cast(image.channels); for (size_t i = 0; i < channels.size(); i++) { if (channels[i] < 0 || channels[i] >= imageChannelCount) { @@ -104,10 +108,6 @@ PropertyTexturePropertyViewStatus PropertyTextureView::checkChannels( } } - if (static_cast(imageChannelCount) < channels.size()) { - return PropertyTexturePropertyViewStatus::ErrorTooManyChannels; - } - return PropertyTexturePropertyViewStatus::Valid; } diff --git a/CesiumGltf/test/TestPropertyTableView.cpp b/CesiumGltf/test/TestPropertyTableView.cpp index df8fc0a28..4a625a350 100644 --- a/CesiumGltf/test/TestPropertyTableView.cpp +++ b/CesiumGltf/test/TestPropertyTableView.cpp @@ -97,7 +97,7 @@ TEST_CASE("Test property table with nonexistent class") { REQUIRE(!classProperty); } -TEST_CASE("Test scalar property") { +TEST_CASE("Test scalar PropertyTableProperty") { Model model; std::vector values = {12, 34, 30, 11, 34, 34, 11, 33, 122, 33}; @@ -247,7 +247,7 @@ TEST_CASE("Test scalar property") { } } -TEST_CASE("Test vecN property") { +TEST_CASE("Test vecN PropertyTableProperty") { Model model; std::vector values = { glm::ivec3(-12, 34, 30), @@ -408,7 +408,7 @@ TEST_CASE("Test vecN property") { } } -TEST_CASE("Test matN property") { +TEST_CASE("Test matN PropertyTableProperty") { Model model; // clang-format off std::vector values = { @@ -583,7 +583,7 @@ TEST_CASE("Test matN property") { } } -TEST_CASE("Test boolean property") { +TEST_CASE("Test boolean PropertyTableProperty") { Model model; int64_t instanceCount = 21; @@ -656,7 +656,7 @@ TEST_CASE("Test boolean property") { } } -TEST_CASE("Test string property") { +TEST_CASE("Test string PropertyTableProperty") { Model model; std::vector expected{"What's up", "Test_0", "Test_1", "", "Hi"}; @@ -2376,7 +2376,7 @@ TEST_CASE("Test callback on invalid property table view") { REQUIRE(invokedCallbackCount == 1); } -TEST_CASE("Test callback for invalid property") { +TEST_CASE("Test callback for invalid PropertyTableProperty") { Model model; ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); @@ -2420,7 +2420,7 @@ TEST_CASE("Test callback for invalid property") { REQUIRE(invokedCallbackCount == 2); } -TEST_CASE("Test callback for scalar property") { +TEST_CASE("Test callback for scalar PropertyTableProperty") { Model model; std::vector values = {12, 34, 30, 11, 34, 34, 11, 33, 122, 33}; @@ -2485,7 +2485,7 @@ TEST_CASE("Test callback for scalar property") { REQUIRE(invokedCallbackCount == 1); } -TEST_CASE("Test callback for vecN property") { +TEST_CASE("Test callback for vecN PropertyTableProperty") { Model model; std::vector values = { glm::ivec3(-12, 34, 30), @@ -2554,7 +2554,7 @@ TEST_CASE("Test callback for vecN property") { REQUIRE(invokedCallbackCount == 1); } -TEST_CASE("Test callback for matN property") { +TEST_CASE("Test callback for matN PropertyTableProperty") { Model model; // clang-format off std::vector values = { @@ -2633,7 +2633,7 @@ TEST_CASE("Test callback for matN property") { REQUIRE(invokedCallbackCount == 1); } -TEST_CASE("Test callback for boolean property") { +TEST_CASE("Test callback for boolean PropertyTableProperty") { Model model; int64_t instanceCount = 21; @@ -2715,7 +2715,7 @@ TEST_CASE("Test callback for boolean property") { REQUIRE(invokedCallbackCount == 1); } -TEST_CASE("Test callback for string property") { +TEST_CASE("Test callback for string PropertyTableProperty") { Model model; std::vector expected{"What's up", "Test_0", "Test_1", "", "Hi"}; @@ -2805,7 +2805,7 @@ TEST_CASE("Test callback for string property") { REQUIRE(invokedCallbackCount == 1); } -TEST_CASE("Test callback for scalar array") { +TEST_CASE("Test callback for scalar array PropertyTableProperty") { Model model; std::vector values = {12, 34, 30, 11, 34, 34, 11, 33, 122, 33, 223, 11}; @@ -2876,7 +2876,7 @@ TEST_CASE("Test callback for scalar array") { REQUIRE(invokedCallbackCount == 1); } -TEST_CASE("Test callback for vecN array") { +TEST_CASE("Test callback for vecN array PropertyTableProperty") { Model model; std::vector values = { glm::ivec3(12, 34, -30), @@ -2953,7 +2953,7 @@ TEST_CASE("Test callback for vecN array") { REQUIRE(invokedCallbackCount == 1); } -TEST_CASE("Test callback for matN array") { +TEST_CASE("Test callback for matN array PropertyTableProperty") { Model model; // clang-format off std::vector values = { @@ -3044,7 +3044,7 @@ TEST_CASE("Test callback for matN array") { REQUIRE(invokedCallbackCount == 1); } -TEST_CASE("Test callback for boolean array") { +TEST_CASE("Test callback for boolean array PropertyTableProperty") { Model model; std::vector expected = { @@ -3135,7 +3135,7 @@ TEST_CASE("Test callback for boolean array") { REQUIRE(invokedCallbackCount == 1); } -TEST_CASE("Test callback for array of strings") { +TEST_CASE("Test callback for string array PropertyTableProperty") { Model model; std::vector expected{ diff --git a/CesiumGltf/test/TestPropertyTexturePropertyView.cpp b/CesiumGltf/test/TestPropertyTexturePropertyView.cpp index 4b54c49b8..947b3b4c8 100644 --- a/CesiumGltf/test/TestPropertyTexturePropertyView.cpp +++ b/CesiumGltf/test/TestPropertyTexturePropertyView.cpp @@ -624,7 +624,7 @@ TEST_CASE("Check array PropertyTexturePropertyView") { view.get(0, 0.5), view.get(0.5, 0.5)}; - for (size_t i = 0; i < 4; i++) { + for (size_t i = 0; i < values.size(); i++) { auto dataStart = data.begin() + i * 4; std::vector expected(dataStart, dataStart + 4); const PropertyArrayView& value = values[i]; diff --git a/CesiumGltf/test/TestPropertyTextureView.cpp b/CesiumGltf/test/TestPropertyTextureView.cpp index f14306282..8db375f17 100644 --- a/CesiumGltf/test/TestPropertyTextureView.cpp +++ b/CesiumGltf/test/TestPropertyTextureView.cpp @@ -128,6 +128,8 @@ TEST_CASE("Test scalar PropertyTextureProperty") { 2, 1, data); + size_t textureIndex = model.textures.size() - 1; + size_t imageIndex = model.images.size() - 1; ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); @@ -143,7 +145,7 @@ TEST_CASE("Test scalar PropertyTextureProperty") { PropertyTextureProperty& propertyTextureProperty = propertyTexture.properties["TestClassProperty"]; - propertyTextureProperty.index = 0; + propertyTextureProperty.index = static_cast(textureIndex); propertyTextureProperty.texCoord = 0; propertyTextureProperty.channels = {0}; @@ -175,6 +177,14 @@ TEST_CASE("Test scalar PropertyTextureProperty") { } } + SECTION("Access wrong type") { + PropertyTexturePropertyView u8vec2Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + u8vec2Invalid.status() == + PropertyTexturePropertyViewStatus::ErrorTypeMismatch); + } + SECTION("Access wrong component type") { PropertyTexturePropertyView uint16Invalid = view.getPropertyView("TestClassProperty"); @@ -202,351 +212,369 @@ TEST_CASE("Test scalar PropertyTextureProperty") { arrayInvalid.status() == PropertyTexturePropertyViewStatus::ErrorArrayTypeMismatch); } + + SECTION("Channel and type mismatch") { + model.images[imageIndex].cesium.channels = 2; + propertyTextureProperty.channels = {0, 1}; + PropertyTexturePropertyView uint8Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uint8Property.status() == + PropertyTexturePropertyViewStatus::ErrorChannelsAndTypeMismatch); + } + + SECTION("Invalid channel values") { + propertyTextureProperty.channels = {5}; + PropertyTexturePropertyView uint8Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uint8Property.status() == + PropertyTexturePropertyViewStatus::ErrorInvalidChannels); + } + + SECTION("Zero channel values") { + propertyTextureProperty.channels.clear(); + PropertyTexturePropertyView uint8Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uint8Property.status() == + PropertyTexturePropertyViewStatus::ErrorInvalidChannels); + } + + SECTION("Invalid bytes per channel") { + model.images[imageIndex].cesium.bytesPerChannel = 2; + PropertyTexturePropertyView uint8Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uint8Property.status() == + PropertyTexturePropertyViewStatus::ErrorInvalidBytesPerChannel); + } + + SECTION("Empty image") { + model.images[imageIndex].cesium.width = 0; + PropertyTexturePropertyView uint8Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uint8Property.status() == + PropertyTexturePropertyViewStatus::ErrorEmptyImage); + } + + SECTION("Wrong image index") { + model.textures[textureIndex].source = 1; + PropertyTexturePropertyView uint8Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uint8Property.status() == + PropertyTexturePropertyViewStatus::ErrorInvalidImage); + } + + SECTION("Wrong sampler index") { + model.textures[textureIndex].sampler = 1; + PropertyTexturePropertyView uint8Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uint8Property.status() == + PropertyTexturePropertyViewStatus::ErrorInvalidSampler); + } + + SECTION("Wrong texture index") { + propertyTextureProperty.index = 1; + PropertyTexturePropertyView uint8Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uint8Property.status() == + PropertyTexturePropertyViewStatus::ErrorInvalidTexture); + } } -// TEST_CASE( -// "Test PropertyTexturePropertyView on property with invalid texture -// index") { -// Model model; -// ExtensionModelExtStructuralMetadata& metadata = -// model.addExtension(); -// -// Schema& schema = metadata.schema.emplace(); -// Class& testClass = schema.classes["TestClass"]; -// ClassProperty& testClassProperty = -// testClass.properties["TestClassProperty"]; testClassProperty.type = -// ClassProperty::Type::SCALAR; testClassProperty.componentType = -// ClassProperty::ComponentType::UINT8; -// -// PropertyTexture propertyTexture; -// propertyTexture.classProperty = "TestClass"; -// -// PropertyTextureProperty& propertyTextureProperty = -// propertyTexture.properties["TestClassProperty"]; -// propertyTextureProperty.index = -1; -// propertyTextureProperty.texCoord = 0; -// propertyTextureProperty.channels = {0}; -// -// PropertyTexturePropertyView view( -// model, -// testClassProperty, -// propertyTextureProperty); -// REQUIRE( -// view.status() == -// PropertyTexturePropertyViewStatus::ErrorInvalidTexture); -//} -// -// TEST_CASE( -// "Test PropertyTexturePropertyView on property with invalid sampler index") -// { -// Model model; -// ExtensionModelExtStructuralMetadata& metadata = -// model.addExtension(); -// -// Schema& schema = metadata.schema.emplace(); -// Class& testClass = schema.classes["TestClass"]; -// ClassProperty& testClassProperty = -// testClass.properties["TestClassProperty"]; testClassProperty.type = -// ClassProperty::Type::SCALAR; testClassProperty.componentType = -// ClassProperty::ComponentType::UINT8; -// -// Image& image = model.images.emplace_back(); -// image.cesium.width = 1; -// image.cesium.height = 1; -// Texture& texture = model.textures.emplace_back(); -// texture.sampler = -1; -// texture.source = 0; -// -// PropertyTexture propertyTexture; -// propertyTexture.classProperty = "TestClass"; -// -// PropertyTextureProperty& propertyTextureProperty = -// propertyTexture.properties["TestClassProperty"]; -// propertyTextureProperty.index = 0; -// propertyTextureProperty.texCoord = 0; -// propertyTextureProperty.channels = {0}; -// -// PropertyTexturePropertyView view( -// model, -// testClassProperty, -// propertyTextureProperty); -// REQUIRE( -// view.status() == -// PropertyTexturePropertyViewStatus::ErrorInvalidTextureSampler); -//} -// -// TEST_CASE( -// "Test PropertyTexturePropertyView on property with invalid image index") { -// Model model; -// ExtensionModelExtStructuralMetadata& metadata = -// model.addExtension(); -// -// Schema& schema = metadata.schema.emplace(); -// Class& testClass = schema.classes["TestClass"]; -// ClassProperty& testClassProperty = -// testClass.properties["TestClassProperty"]; testClassProperty.type = -// ClassProperty::Type::SCALAR; testClassProperty.componentType = -// ClassProperty::ComponentType::UINT8; -// -// model.samplers.emplace_back(); -// -// Texture& texture = model.textures.emplace_back(); -// texture.sampler = 0; -// texture.source = -1; -// -// PropertyTexture propertyTexture; -// propertyTexture.classProperty = "TestClass"; -// -// PropertyTextureProperty& propertyTextureProperty = -// propertyTexture.properties["TestClassProperty"]; -// propertyTextureProperty.index = 0; -// propertyTextureProperty.texCoord = 0; -// propertyTextureProperty.channels = {0}; -// -// PropertyTexturePropertyView view( -// model, -// testClassProperty, -// propertyTextureProperty); -// REQUIRE( -// view.status() == PropertyTexturePropertyViewStatus::ErrorInvalidImage); -//} -// -// TEST_CASE("Test PropertyTexturePropertyView on property with empty image") { -// Model model; -// ExtensionModelExtStructuralMetadata& metadata = -// model.addExtension(); -// -// Schema& schema = metadata.schema.emplace(); -// Class& testClass = schema.classes["TestClass"]; -// ClassProperty& testClassProperty = -// testClass.properties["TestClassProperty"]; testClassProperty.type = -// ClassProperty::Type::SCALAR; testClassProperty.componentType = -// ClassProperty::ComponentType::UINT8; -// -// Image& image = model.images.emplace_back(); -// image.cesium.width = 0; -// image.cesium.height = 0; -// -// model.samplers.emplace_back(); -// -// Texture& texture = model.textures.emplace_back(); -// texture.sampler = 0; -// texture.source = 0; -// -// PropertyTexture propertyTexture; -// propertyTexture.classProperty = "TestClass"; -// -// PropertyTextureProperty& propertyTextureProperty = -// propertyTexture.properties["TestClassProperty"]; -// propertyTextureProperty.index = 0; -// propertyTextureProperty.texCoord = 0; -// propertyTextureProperty.channels = {0}; -// -// PropertyTexturePropertyView view( -// model, -// testClassProperty, -// propertyTextureProperty); -// REQUIRE(view.status() == -// PropertyTexturePropertyViewStatus::ErrorEmptyImage); -//} -// -// TEST_CASE("Test PropertyTextureView on property texture property with zero " -// "channels") { -// Model model; -// ExtensionModelExtStructuralMetadata& metadata = -// model.addExtension(); -// -// Schema& schema = metadata.schema.emplace(); -// Class& testClass = schema.classes["TestClass"]; -// ClassProperty& testClassProperty = -// testClass.properties["TestClassProperty"]; testClassProperty.type = -// ClassProperty::Type::SCALAR; testClassProperty.componentType = -// ClassProperty::ComponentType::UINT8; -// -// Image& image = model.images.emplace_back(); -// image.cesium.width = 1; -// image.cesium.height = 1; -// image.cesium.channels = 1; -// -// model.samplers.emplace_back(); -// -// Texture& texture = model.textures.emplace_back(); -// texture.sampler = 0; -// texture.source = 0; -// -// PropertyTexture propertyTexture; -// propertyTexture.classProperty = "TestClass"; -// -// PropertyTextureProperty& propertyTextureProperty = -// propertyTexture.properties["TestClassProperty"]; -// propertyTextureProperty.index = 0; -// propertyTextureProperty.texCoord = 0; -// propertyTextureProperty.channels = {}; -// -// PropertyTexturePropertyView view( -// model, -// testClassProperty, -// propertyTextureProperty); -// REQUIRE( -// view.status() == -// PropertyTexturePropertyViewStatus::ErrorInvalidChannels); -//} -// -// TEST_CASE("Test PropertyTextureView on property texture property with too -// many " -// "channels") { -// Model model; -// ExtensionModelExtStructuralMetadata& metadata = -// model.addExtension(); -// -// Schema& schema = metadata.schema.emplace(); -// Class& testClass = schema.classes["TestClass"]; -// ClassProperty& testClassProperty = -// testClass.properties["TestClassProperty"]; testClassProperty.type = -// ClassProperty::Type::SCALAR; testClassProperty.componentType = -// ClassProperty::ComponentType::UINT8; -// -// Image& image = model.images.emplace_back(); -// image.cesium.width = 1; -// image.cesium.height = 1; -// image.cesium.channels = 1; -// -// model.samplers.emplace_back(); -// -// Texture& texture = model.textures.emplace_back(); -// texture.sampler = 0; -// texture.source = 0; -// -// PropertyTexture propertyTexture; -// propertyTexture.classProperty = "TestClass"; -// -// PropertyTextureProperty& propertyTextureProperty = -// propertyTexture.properties["TestClassProperty"]; -// propertyTextureProperty.index = 0; -// propertyTextureProperty.texCoord = 0; -// propertyTextureProperty.channels = {0, 1}; -// -// PropertyTexturePropertyView view( -// model, -// testClassProperty, -// propertyTextureProperty); -// REQUIRE( -// view.status() == -// PropertyTexturePropertyViewStatus::ErrorInvalidChannels); -//} -// -// TEST_CASE("Test PropertyTextureView on property texture property with " -// "unsupported class property") { -// Model model; -// ExtensionModelExtStructuralMetadata& metadata = -// model.addExtension(); -// -// Schema& schema = metadata.schema.emplace(); -// Class& testClass = schema.classes["TestClass"]; -// ClassProperty& testClassProperty = -// testClass.properties["TestClassProperty"]; testClassProperty.type = -// ClassProperty::Type::SCALAR; testClassProperty.componentType = -// ClassProperty::ComponentType::UINT8; -// -// Image& image = model.images.emplace_back(); -// image.cesium.width = 1; -// image.cesium.height = 1; -// image.cesium.channels = 1; -// -// model.samplers.emplace_back(); -// -// Texture& texture = model.textures.emplace_back(); -// texture.sampler = 0; -// texture.source = 0; -// -// PropertyTexture propertyTexture; -// propertyTexture.classProperty = "TestClass"; -// -// PropertyTextureProperty& propertyTextureProperty = -// propertyTexture.properties["TestClassProperty"]; -// propertyTextureProperty.index = 0; -// propertyTextureProperty.texCoord = 0; -// propertyTextureProperty.channels = {0, 1}; -// -// PropertyTexturePropertyView view( -// model, -// testClassProperty, -// propertyTextureProperty); -// REQUIRE( -// view.status() == -// PropertyTexturePropertyViewStatus::ErrorInvalidChannels); -//} -/* -TEST_CASE("Test PropertyTexturePropertyView on valid property texture") { +TEST_CASE("Test vecN PropertyTextureProperty") { Model model; + std::vector data = {12, 34, 10, 3, 40, 0, 30, 11}; + + addTextureToModel( + model, + Sampler::WrapS::CLAMP_TO_EDGE, + Sampler::WrapS::CLAMP_TO_EDGE, + 2, + 2, + 2, + data); + size_t textureIndex = model.textures.size() - 1; + size_t imageIndex = model.images.size() - 1; + ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); Schema& schema = metadata.schema.emplace(); Class& testClass = schema.classes["TestClass"]; - ClassProperty& testClassProperty = -testClass.properties["TestClassProperty"]; testClassProperty.type = -ClassProperty::Type::SCALAR; testClassProperty.componentType = -ClassProperty::ComponentType::UINT8; - - Image& image = model.images.emplace_back(); - image.cesium.width = 1; - image.cesium.height = 1; - image.cesium.channels = 1; - - model.samplers.emplace_back(); - - Texture& texture = model.textures.emplace_back(); - texture.sampler = 0; - texture.source = 0; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::VEC2; + testClassProperty.componentType = ClassProperty::ComponentType::UINT8; - PropertyTexture propertyTexture; + PropertyTexture& propertyTexture = metadata.propertyTextures.emplace_back(); propertyTexture.classProperty = "TestClass"; PropertyTextureProperty& propertyTextureProperty = propertyTexture.properties["TestClassProperty"]; - propertyTextureProperty.index = 0; + propertyTextureProperty.index = static_cast(textureIndex); propertyTextureProperty.texCoord = 0; - propertyTextureProperty.channels = {0}; + propertyTextureProperty.channels = {0, 1}; - PropertyTexturePropertyView view( - model, - testClassProperty, - propertyTextureProperty); - REQUIRE(view.status() == PropertyTexturePropertyViewStatus::Valid); + PropertyTextureView view(model, propertyTexture); + REQUIRE(view.status() == PropertyTextureViewStatus::Valid); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::VEC2); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT8); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->array); + + SECTION("Access correct type") { + PropertyTexturePropertyView u8vec2Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + u8vec2Property.status() == PropertyTexturePropertyViewStatus::Valid); + + std::vector expected{ + glm::u8vec2(12, 34), + glm::u8vec2(10, 3), + glm::u8vec2(40, 0), + glm::u8vec2(30, 11)}; + std::vector values{ + u8vec2Property.get(0.0, 0.0), + u8vec2Property.get(0.5, 0.0), + u8vec2Property.get(0.0, 0.5), + u8vec2Property.get(0.5, 0.5), + }; + for (size_t i = 0; i < values.size(); ++i) { + REQUIRE(values[i] == expected[i]); + } + } + + SECTION("Access wrong type") { + PropertyTexturePropertyView uint8Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uint8Invalid.status() == + PropertyTexturePropertyViewStatus::ErrorTypeMismatch); + + PropertyTexturePropertyView u8vec3Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + u8vec3Invalid.status() == + PropertyTexturePropertyViewStatus::ErrorTypeMismatch); + } + + SECTION("Access wrong component type") { + PropertyTexturePropertyView u16vec2Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + u16vec2Invalid.status() == + PropertyTexturePropertyViewStatus::ErrorComponentTypeMismatch); + + PropertyTexturePropertyView i8vec2Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + i8vec2Invalid.status() == + PropertyTexturePropertyViewStatus::ErrorComponentTypeMismatch); + } + + SECTION("Access incorrectly as array") { + PropertyTexturePropertyView> arrayInvalid = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayInvalid.status() == + PropertyTexturePropertyViewStatus::ErrorArrayTypeMismatch); + } + + SECTION("Channel and type mismatch") { + model.images[imageIndex].cesium.channels = 4; + propertyTextureProperty.channels = {0, 1, 2, 3}; + PropertyTexturePropertyView u8vec2Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + u8vec2Property.status() == + PropertyTexturePropertyViewStatus::ErrorChannelsAndTypeMismatch); + } + + SECTION("Invalid channel values") { + propertyTextureProperty.channels = {0, 4}; + PropertyTexturePropertyView u8vec2Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + u8vec2Property.status() == + PropertyTexturePropertyViewStatus::ErrorInvalidChannels); + } + + SECTION("Invalid bytes per channel") { + model.images[imageIndex].cesium.bytesPerChannel = 2; + PropertyTexturePropertyView u8vec2Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + u8vec2Property.status() == + PropertyTexturePropertyViewStatus::ErrorInvalidBytesPerChannel); + } } -TEST_CASE("Test getting value from valid view") { +TEST_CASE("Test array PropertyTextureProperty") { Model model; + // clang-format off + std::vector data = { + 12, 34, 10, + 40, 0, 30, + 80, 4, 2, + 6, 3, 4, + }; + // clang-format on + + addTextureToModel( + model, + Sampler::WrapS::CLAMP_TO_EDGE, + Sampler::WrapS::CLAMP_TO_EDGE, + 2, + 2, + 3, + data); + size_t textureIndex = model.textures.size() - 1; + size_t imageIndex = model.images.size() - 1; + ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); Schema& schema = metadata.schema.emplace(); Class& testClass = schema.classes["TestClass"]; - ClassProperty& testClassProperty = -testClass.properties["TestClassProperty"]; testClassProperty.type = -ClassProperty::Type::SCALAR; testClassProperty.componentType = -ClassProperty::ComponentType::UINT8; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::UINT8; + testClassProperty.array = true; + testClassProperty.count = 3; - std::vector values{10, 8, 4, 22}; + PropertyTexture& propertyTexture = metadata.propertyTextures.emplace_back(); + propertyTexture.classProperty = "TestClass"; - Image& image = model.images.emplace_back(); - image.cesium.width = 2; - image.cesium.height = 2; - image.cesium.channels = 1; - image.cesium.bytesPerChannel = 1; - image.cesium.pixelData.resize(values.size()); - std::memcpy(image.cesium.pixelData.data(), values.data(), values.size()); + PropertyTextureProperty& propertyTextureProperty = + propertyTexture.properties["TestClassProperty"]; + propertyTextureProperty.index = static_cast(textureIndex); + propertyTextureProperty.texCoord = 0; + propertyTextureProperty.channels = {0, 1, 2}; - Sampler& sampler = model.samplers.emplace_back(); - sampler.wrapS = Sampler::WrapS::CLAMP_TO_EDGE; - sampler.wrapT = Sampler::WrapT::CLAMP_TO_EDGE; + PropertyTextureView view(model, propertyTexture); + REQUIRE(view.status() == PropertyTextureViewStatus::Valid); - Texture& texture = model.textures.emplace_back(); - texture.sampler = 0; - texture.source = 0; + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT8); + REQUIRE(classProperty->array); + REQUIRE(classProperty->count == 3); - PropertyTexture propertyTexture; + SECTION("Access correct type") { + PropertyTexturePropertyView> uint8ArrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + uint8ArrayProperty.status() == + PropertyTexturePropertyViewStatus::Valid); + + std::vector> values{ + uint8ArrayProperty.get(0.0, 0.0), + uint8ArrayProperty.get(0.5, 0.0), + uint8ArrayProperty.get(0.0, 0.5), + uint8ArrayProperty.get(0.5, 0.5), + }; + + for (size_t i = 0; i < values.size(); ++i) { + auto dataStart = data.begin() + i * 3; + std::vector expected(dataStart, dataStart + 3); + const PropertyArrayView& value = values[i]; + REQUIRE(static_cast(value.size()) == expected.size()); + for (size_t j = 0; j < expected.size(); j++) { + REQUIRE(value[j] == expected[j]); + } + } + } + + SECTION("Access wrong type") { + PropertyTexturePropertyView> + u8vec3ArrayInvalid = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + u8vec3ArrayInvalid.status() == + PropertyTexturePropertyViewStatus::ErrorTypeMismatch); + } + + SECTION("Access wrong component type") { + PropertyTexturePropertyView> int8ArrayInvalid = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + int8ArrayInvalid.status() == + PropertyTexturePropertyViewStatus::ErrorComponentTypeMismatch); + + PropertyTexturePropertyView> + uint16ArrayInvalid = view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + uint16ArrayInvalid.status() == + PropertyTexturePropertyViewStatus::ErrorComponentTypeMismatch); + } + + SECTION("Access incorrectly as non-array") { + PropertyTexturePropertyView uint8Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uint8Invalid.status() == + PropertyTexturePropertyViewStatus::ErrorArrayTypeMismatch); + + PropertyTexturePropertyView u8vec3Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + u8vec3Invalid.status() == + PropertyTexturePropertyViewStatus::ErrorArrayTypeMismatch); + } + + SECTION("Channel and type mismatch") { + model.images[imageIndex].cesium.channels = 4; + propertyTextureProperty.channels = {0, 1, 2, 3}; + PropertyTexturePropertyView> uint8ArrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + uint8ArrayProperty.status() == + PropertyTexturePropertyViewStatus::ErrorChannelsAndTypeMismatch); + } + + SECTION("Invalid channel values") { + propertyTextureProperty.channels = {0, 4, 1}; + PropertyTexturePropertyView> uint8ArrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + uint8ArrayProperty.status() == + PropertyTexturePropertyViewStatus::ErrorInvalidChannels); + } + + SECTION("Invalid bytes per channel") { + model.images[imageIndex].cesium.bytesPerChannel = 2; + PropertyTexturePropertyView> uint8ArrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + uint8ArrayProperty.status() == + PropertyTexturePropertyViewStatus::ErrorInvalidBytesPerChannel); + } +} + +TEST_CASE("Test unsupported PropertyTextureProperty classes") { + Model model; + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + + PropertyTexture& propertyTexture = metadata.propertyTextures.emplace_back(); propertyTexture.classProperty = "TestClass"; PropertyTextureProperty& propertyTextureProperty = @@ -555,10 +583,105 @@ ClassProperty::ComponentType::UINT8; propertyTextureProperty.texCoord = 0; propertyTextureProperty.channels = {0}; - PropertyTexturePropertyView view( - model, - testClassProperty, - propertyTextureProperty); + PropertyTextureView view(model, propertyTexture); + REQUIRE(view.status() == PropertyTextureViewStatus::Valid); + + SECTION("Unsupported types") { + testClassProperty.type = ClassProperty::Type::BOOLEAN; + PropertyTexturePropertyView boolInvalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + boolInvalid.status() == + PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty); + + testClassProperty.type = ClassProperty::Type::STRING; + PropertyTexturePropertyView stringInvalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + stringInvalid.status() == + PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty); + + testClassProperty.type = ClassProperty::Type::MAT2; + PropertyTexturePropertyView mat2Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + mat2Invalid.status() == + PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty); + } + SECTION("Unsupported component types") { + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::FLOAT64; + PropertyTexturePropertyView doubleInvalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + doubleInvalid.status() == + PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty); -}*/ + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::UINT64; + PropertyTexturePropertyView uint64Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uint64Invalid.status() == + PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty); + + testClassProperty.type = ClassProperty::Type::VEC2; + testClassProperty.componentType = ClassProperty::ComponentType::INT32; + PropertyTexturePropertyView ivec2Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + ivec2Invalid.status() == + PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty); + + testClassProperty.type = ClassProperty::Type::VEC3; + testClassProperty.componentType = ClassProperty::ComponentType::UINT16; + PropertyTexturePropertyView u16vec3Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + u16vec3Invalid.status() == + PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty); + } + + SECTION("Unsupported array types") { + testClassProperty.array = true; + + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::UINT8; + testClassProperty.count = 5; + PropertyTexturePropertyView> bigArrayInvalid = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + bigArrayInvalid.status() == + PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty); + + testClassProperty.count = std::nullopt; + PropertyTexturePropertyView> + variableArrayInvalid = view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + variableArrayInvalid.status() == + PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty); + + testClassProperty.type = ClassProperty::Type::VEC2; + testClassProperty.componentType = ClassProperty::ComponentType::UINT32; + testClassProperty.count = 2; + PropertyTexturePropertyView> + uvec2ArrayInvalid = view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + uvec2ArrayInvalid.status() == + PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty); + + testClassProperty.type = ClassProperty::Type::VEC3; + testClassProperty.componentType = ClassProperty::ComponentType::UINT8; + testClassProperty.count = 1; + PropertyTexturePropertyView> + u8vec3ArrayInvalid = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + u8vec3ArrayInvalid.status() == + PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty); + } +} From f3ecde1681a19a4c6598073809668377bb006a7c Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Thu, 29 Jun 2023 16:51:07 -0400 Subject: [PATCH 087/421] Add callbacks and tests --- CHANGES.md | 12 +- .../include/CesiumGltf/PropertyTextureView.h | 322 ++++++++++++++++- CesiumGltf/test/TestPropertyTextureView.cpp | 338 ++++++++++++++++++ 3 files changed, 652 insertions(+), 20 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 307298603..91672f6d1 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -4,22 +4,26 @@ ##### Breaking Changes :mega: +- Views of the data contained by `EXT_feature_metadata` will no longer supported by Cesium Native. The extension will still be parsed, but it will log a warning. +- Batch tables will be converted to `EXT_structural_metadata` instead of `EXT_feature_metadata`. - In `CesiumGltf`, all generated classes related to `EXT_feature_metadata` are now prefixed with `ExtensionExtFeatureMetadata`. For example, `ClassProperty` has become `ExtensionExtFeatureMetadataClassProperty`. This also extends to the glTF reader and writer. - In `CesiumGltf`, all generated classes related to `EXT_structural_metadata` have had their `ExtensionExtStructuralMetadata` prefix removed. For example, `ExtensionExtStructuralMetadataClassProperty` has become `ClassProperty`. This also extends to the glTF reader and writer. - In `CesiumGltf`, `ExtensionExtMeshFeaturesFeatureId` and `ExtensionExtMeshFeaturesFeatureIdTexture` have been renamed to `FeatureId` and `FeatureIdTexture` respectively. - Replaced `FeatureIDTextureView` with `FeatureIdTextureView`, which views a `FeatureIdTexture` in `EXT_mesh_features`. Feature ID textures from `EXT_feature_metadata` are no longer supported. - Renamed `FeatureIDTextureViewStatus` to `FeatureIdTextureViewStatus` for consistency. - Replaced `MetadataFeatureTableView` with `PropertyTableView`, which views a `PropertyTable` in `EXT_structural_metadata`. -- Added `PropertyTableViewStatus` to indicate whether a `PropertyTableView` is valid. - Renamed `MetadataArrayView` to `PropertyArrayView`. - Replaced `FeatureTextureView` with `PropertyTextureView`, which views a `PropertyTexture` in `EXT_structural_metadata`. - Renamed `FeatureTextureViewStatus` to `PropertyTextureViewStatus`. -- Replaced `FeatureTexturePropertyView` with `PropertyTexturePropertyView`, which views a `PropertyTextureProperty` in `EXT_structural_metadata`. +- Replaced `FeatureTexturePropertyView` with `PropertyTexturePropertyView`, which is a templated view of a `PropertyTextureProperty` in `EXT_structural_metadata`. +- Removed `FeatureTexturePropertyComponentType`, `FeatureTexturePropertyChannelOffsets`, and `FeatureTexturePropertyValue`. `PropertyTextureProperty` retrieves the values with the type indicated by its class property. - Renamed `FeatureTexturePropertyViewStatus` to `PropertyTexturePropertyViewStatus`. - Refactored `PropertyType` to reflect the values of `type` in a `ClassProperty` from `EXT_structural_metadata`. -- Added `PropertyComponentType` to reflect the values of `componentType` in a `ClassProperty` from `EXT_structural_metadata`. -Additionally, views of the data contained by `EXT_feature_metadata` will no longer supported by Cesium Native. The extension will still be parsed, but it will log a warning. Batch tables will also be converted to `EXT_structural_metadata` instead of `EXT_feature_metadata`. +##### Additions :tada: + +- Added `PropertyTableViewStatus` to indicate whether a `PropertyTableView` is valid. +- Added `PropertyComponentType` to reflect the values of `componentType` in a `ClassProperty` from `EXT_structural_metadata`. ### v0.25.0 - 2023-06-01 diff --git a/CesiumGltf/include/CesiumGltf/PropertyTextureView.h b/CesiumGltf/include/CesiumGltf/PropertyTextureView.h index f6a7c0ded..b622cb4c9 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTextureView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTextureView.h @@ -84,12 +84,11 @@ class PropertyTextureView { * This method will validate the EXT_structural_metadata format to ensure * {@link PropertyTexturePropertyView} retrieves the correct data. T must be * a scalar with a supported component type (uint8_t, uint16_t, uint32_t, - * float), or a glm vecN composed of one of the scalar types. - * PropertyArrayViews are unsupported; if the property describes a - * fixed-length array of scalars, T must be a glm vecN of the same length. + * float), a glm vecN composed of one of the scalar types, or a + * PropertyArrayView containing one of the scalar types * * @param propertyName The name of the property to retrieve data from - * @return A {@link PropertyTablePropertyView} of the property. If no valid property is + * @return A {@link PropertyTexturePropertyView} of the property. If no valid property is * found, the property view will be invalid. */ template @@ -109,6 +108,104 @@ class PropertyTextureView { return getPropertyViewImpl(propertyName, *pClassProperty); } + /** + * @brief Gets a {@link PropertyTexturePropertyView} through a callback that accepts a + * property name and a {@link PropertyTexturePropertyView} that views the data + * of the property with the specified name. + * + * This method will validate the EXT_structural_metadata format to ensure + * {@link PropertyTexturePropertyView} retrieves the correct data. T must be + * a scalar with a supported component type (uint8_t, uint16_t, uint32_t, + * float), a glm vecN composed of one of the scalar types, or a + * PropertyArrayView containing one of the scalar types. If the property is + * invalid, an empty {@link PropertyTexturePropertyView} with an error status + * will be passed to the callback. Otherwise, a valid property view will be + * passed to the callback. + * + * @param propertyName The name of the property to retrieve data from + * @tparam callback A callback function that accepts a property name and a + * {@link PropertyTexturePropertyView} + */ + template + void + getPropertyView(const std::string& propertyName, Callback&& callback) const { + if (this->_status != PropertyTextureViewStatus::Valid) { + callback( + propertyName, + PropertyTexturePropertyView( + PropertyTexturePropertyViewStatus::ErrorInvalidPropertyTexture)); + return; + } + + const ClassProperty* pClassProperty = getClassProperty(propertyName); + if (!pClassProperty) { + callback( + propertyName, + PropertyTexturePropertyView( + PropertyTexturePropertyViewStatus::ErrorNonexistentProperty)); + return; + } + + PropertyType type = convertStringToPropertyType(pClassProperty->type); + PropertyComponentType componentType = PropertyComponentType::None; + if (pClassProperty->componentType) { + componentType = + convertStringToPropertyComponentType(*pClassProperty->componentType); + } + + if (pClassProperty->array) { + getArrayPropertyViewImpl( + propertyName, + *pClassProperty, + type, + componentType, + std::forward(callback)); + } else if (type == PropertyType::Scalar) { + getScalarPropertyViewImpl( + propertyName, + *pClassProperty, + componentType, + std::forward(callback)); + } else if (isPropertyTypeVecN(type)) { + getVecNPropertyViewImpl( + propertyName, + *pClassProperty, + type, + componentType, + std::forward(callback)); + } else { + callback( + propertyName, + PropertyTexturePropertyView( + PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty)); + return; + } + } + + /** + * @brief Iterates over each property in the {@link PropertyTexture} with a callback + * that accepts a property name and a {@link PropertyTexturePropertyView} to view + * the data stored in the {@link PropertyTextureProperty}. + * + * This method will validate the EXT_structural_metadata format to ensure + * {@link PropertyTexturePropertyView} retrieves the correct data. T must be + * a scalar with a supported component type (uint8_t, uint16_t, uint32_t, + * float), a glm vecN composed of one of the scalar types, or a + * PropertyArrayView containing one of the scalar types. If the property is + * invalid, an empty {@link PropertyTexturePropertyView} with an error status + * will be passed to the callback. Otherwise, a valid property view will be + * passed to the callback. + * + * @param propertyName The name of the property to retrieve data from + * @tparam callback A callback function that accepts property name and + * {@link PropertyTexturePropertyView} + */ + template void forEachProperty(Callback&& callback) const { + for (const auto& property : this->_pClass->properties) { + getPropertyView(property.first, std::forward(callback)); + } + } + private: template PropertyTexturePropertyView getPropertyViewImpl( @@ -140,6 +237,200 @@ class PropertyTextureView { } } + template + void getArrayPropertyViewImpl( + const std::string& propertyName, + const ClassProperty& classProperty, + PropertyType type, + PropertyComponentType componentType, + Callback&& callback) const { + // Only scalar arrays are supported. + if (type != PropertyType::Scalar) { + callback( + propertyName, + PropertyTexturePropertyView( + PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty)); + } + + int64_t count = classProperty.count.value_or(0); + if (count <= 0 || count > 4) { + callback( + propertyName, + PropertyTexturePropertyView( + PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty)); + } + + switch (componentType) { + case PropertyComponentType::Int8: + callback( + propertyName, + getPropertyViewImpl>( + propertyName, + classProperty)); + break; + case PropertyComponentType::Uint8: + callback( + propertyName, + getPropertyViewImpl>( + propertyName, + classProperty)); + break; + case PropertyComponentType::Int16: + callback( + propertyName, + getPropertyViewImpl>( + propertyName, + classProperty)); + break; + case PropertyComponentType::Uint16: + callback( + propertyName, + getPropertyViewImpl>( + propertyName, + classProperty)); + break; + default: + callback( + propertyName, + PropertyTexturePropertyView( + PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty)); + } + } + + template + void getScalarPropertyViewImpl( + const std::string& propertyName, + const ClassProperty& classProperty, + PropertyComponentType componentType, + Callback&& callback) const { + switch (componentType) { + case PropertyComponentType::Int8: + callback( + propertyName, + getPropertyViewImpl(propertyName, classProperty)); + return; + case PropertyComponentType::Uint8: + callback( + propertyName, + getPropertyViewImpl(propertyName, classProperty)); + return; + case PropertyComponentType::Int16: + callback( + propertyName, + getPropertyViewImpl(propertyName, classProperty)); + return; + case PropertyComponentType::Uint16: + callback( + propertyName, + getPropertyViewImpl(propertyName, classProperty)); + break; + case PropertyComponentType::Int32: + callback( + propertyName, + getPropertyViewImpl(propertyName, classProperty)); + break; + case PropertyComponentType::Uint32: + callback( + propertyName, + getPropertyViewImpl(propertyName, classProperty)); + break; + case PropertyComponentType::Float32: + callback( + propertyName, + getPropertyViewImpl(propertyName, classProperty)); + break; + default: + callback( + propertyName, + PropertyTexturePropertyView( + PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty)); + break; + } + } + + template + void getVecNPropertyViewImpl( + const std::string& propertyName, + const ClassProperty& classProperty, + PropertyComponentType componentType, + Callback&& callback) const { + switch (componentType) { + case PropertyComponentType::Int8: + callback( + propertyName, + getPropertyViewImpl>( + propertyName, + classProperty)); + break; + case PropertyComponentType::Uint8: + callback( + propertyName, + getPropertyViewImpl>( + propertyName, + classProperty)); + break; + case PropertyComponentType::Int16: + callback( + propertyName, + getPropertyViewImpl>( + propertyName, + classProperty)); + break; + case PropertyComponentType::Uint16: + callback( + propertyName, + getPropertyViewImpl>( + propertyName, + classProperty)); + break; + default: + callback( + propertyName, + PropertyTexturePropertyView( + PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty)); + break; + } + } + + template + void getVecNPropertyViewImpl( + const std::string& propertyName, + const ClassProperty& classProperty, + PropertyType type, + PropertyComponentType componentType, + Callback&& callback) const { + const glm::length_t N = getDimensionsFromPropertyType(type); + switch (N) { + case 2: + getVecNPropertyViewImpl( + propertyName, + classProperty, + componentType, + std::forward(callback)); + break; + case 3: + getVecNPropertyViewImpl( + propertyName, + classProperty, + componentType, + std::forward(callback)); + break; + case 4: + getVecNPropertyViewImpl( + propertyName, + classProperty, + componentType, + std::forward(callback)); + break; + default: + callback( + propertyName, + PropertyTexturePropertyView( + PropertyTexturePropertyViewStatus::ErrorTypeMismatch)); + break; + } + } + template PropertyTexturePropertyView createScalarPropertyView( const ClassProperty& classProperty, @@ -164,17 +455,16 @@ class PropertyTextureView { } // Eight-byte scalar types are unsupported. - if constexpr ( - std::is_same_v || std::is_same_v || - std::is_same_v) { + size_t componentSize = getSizeOfComponentType(componentType); + if (componentSize == 0 || componentSize > 4) { return PropertyTexturePropertyView( PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty); - } else { - return createPropertyViewImpl( - classProperty, - propertyTextureProperty, - sizeof(T)); } + + return createPropertyViewImpl( + classProperty, + propertyTextureProperty, + sizeof(T)); } template @@ -251,14 +541,14 @@ class PropertyTextureView { PropertyTexturePropertyViewStatus::ErrorComponentTypeMismatch); } - // Only uint8 and uint16s are supported. - if (componentType != PropertyComponentType::Uint8 && - componentType != PropertyComponentType::Uint16) { + // Only up to two-byte components are supported. + size_t componentSize = getSizeOfComponentType(componentType); + if (componentSize == 0 || componentSize > 2) { return PropertyTexturePropertyView>( PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty); } - if (componentType == PropertyComponentType::Uint16 && count > 2) { + if (componentSize * count > 4) { return PropertyTexturePropertyView>( PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty); } diff --git a/CesiumGltf/test/TestPropertyTextureView.cpp b/CesiumGltf/test/TestPropertyTextureView.cpp index 8db375f17..095b17ddf 100644 --- a/CesiumGltf/test/TestPropertyTextureView.cpp +++ b/CesiumGltf/test/TestPropertyTextureView.cpp @@ -565,6 +565,344 @@ TEST_CASE("Test array PropertyTextureProperty") { } } +TEST_CASE("Test callback on invalid property texture view") { + Model model; + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + metadata.schema.emplace(); + + // Property texture has a nonexistent class. + PropertyTexture& propertyTexture = metadata.propertyTextures.emplace_back(); + propertyTexture.classProperty = "TestClass"; + + PropertyTextureProperty& propertyTextureProperty = + propertyTexture.properties["TestClassProperty"]; + propertyTextureProperty.index = -1; + + PropertyTextureView view(model, propertyTexture); + REQUIRE(view.status() == PropertyTextureViewStatus::ErrorClassNotFound); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(!classProperty); + + uint32_t invokedCallbackCount = 0; + view.getPropertyView( + "TestClassProperty", + [&invokedCallbackCount]( + const std::string& /*propertyName*/, + auto propertyValue) mutable { + invokedCallbackCount++; + REQUIRE( + propertyValue.status() == + PropertyTexturePropertyViewStatus::ErrorInvalidPropertyTexture); + }); + + REQUIRE(invokedCallbackCount == 1); +} + +TEST_CASE("Test callback on invalid PropertyTextureProperty") { + Model model; + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["InvalidProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::UINT8; + + PropertyTexture& propertyTexture = metadata.propertyTextures.emplace_back(); + propertyTexture.classProperty = "TestClass"; + + PropertyTextureProperty& propertyTextureProperty = + propertyTexture.properties["InvalidProperty"]; + propertyTextureProperty.index = -1; + + PropertyTextureView view(model, propertyTexture); + REQUIRE(view.status() == PropertyTextureViewStatus::Valid); + + const ClassProperty* classProperty = view.getClassProperty("InvalidProperty"); + REQUIRE(classProperty); + + classProperty = view.getClassProperty("NonexistentProperty"); + REQUIRE(!classProperty); + + uint32_t invokedCallbackCount = 0; + auto testCallback = [&invokedCallbackCount]( + const std::string& /*propertyName*/, + auto propertyValue) mutable { + invokedCallbackCount++; + REQUIRE(propertyValue.status() != PropertyTexturePropertyViewStatus::Valid); + }; + + view.getPropertyView("InvalidProperty", testCallback); + view.getPropertyView("NonexistentProperty", testCallback); + + REQUIRE(invokedCallbackCount == 2); +} + +TEST_CASE("Test callback for scalar PropertyTextureProperty") { + Model model; + std::vector data = {255, 255, 12, 1, 30, 2, 0, 255}; + + addTextureToModel( + model, + Sampler::WrapS::CLAMP_TO_EDGE, + Sampler::WrapS::CLAMP_TO_EDGE, + 2, + 2, + 2, + data); + size_t textureIndex = model.textures.size() - 1; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::INT16; + + PropertyTexture& propertyTexture = metadata.propertyTextures.emplace_back(); + propertyTexture.classProperty = "TestClass"; + + PropertyTextureProperty& propertyTextureProperty = + propertyTexture.properties["TestClassProperty"]; + propertyTextureProperty.index = static_cast(textureIndex); + propertyTextureProperty.texCoord = 0; + propertyTextureProperty.channels = {0, 1}; + + PropertyTextureView view(model, propertyTexture); + REQUIRE(view.status() == PropertyTextureViewStatus::Valid); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT16); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->array); + + std::vector expected{-1, 268, 542, -256}; + std::vector texCoords{ + glm::vec2(0, 0), + glm::vec2(0.5, 0), + glm::vec2(0, 0.5), + glm::vec2(0.5, 0.5)}; + + uint32_t invokedCallbackCount = 0; + view.getPropertyView( + "TestClassProperty", + [&expected, &texCoords, &invokedCallbackCount]( + const std::string& /*propertyName*/, + auto propertyValue) mutable { + invokedCallbackCount++; + if constexpr (std::is_same_v< + PropertyTexturePropertyView, + decltype(propertyValue)>) { + REQUIRE( + propertyValue.status() == + PropertyTexturePropertyViewStatus::Valid); + + for (size_t i = 0; i < expected.size(); ++i) { + glm::vec2& texCoord = texCoords[i]; + REQUIRE(propertyValue.get(texCoord[0], texCoord[1]) == expected[i]); + } + } else { + FAIL("getPropertyView returned PropertyTexturePropertyView of " + "incorrect type for TestClassProperty."); + } + }); + + REQUIRE(invokedCallbackCount == 1); +} + +TEST_CASE("Test callback for vecN PropertyTextureProperty") { + Model model; + // clang-format off + std::vector data = { + 255, 255, + 12, 1, + 30, 2, + 0, 255}; + // clang-format on + + addTextureToModel( + model, + Sampler::WrapS::CLAMP_TO_EDGE, + Sampler::WrapS::CLAMP_TO_EDGE, + 2, + 2, + 2, + data); + size_t textureIndex = model.textures.size() - 1; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::VEC2; + testClassProperty.componentType = ClassProperty::ComponentType::INT8; + + PropertyTexture& propertyTexture = metadata.propertyTextures.emplace_back(); + propertyTexture.classProperty = "TestClass"; + + PropertyTextureProperty& propertyTextureProperty = + propertyTexture.properties["TestClassProperty"]; + propertyTextureProperty.index = static_cast(textureIndex); + propertyTextureProperty.texCoord = 0; + propertyTextureProperty.channels = {0, 1}; + + PropertyTextureView view(model, propertyTexture); + REQUIRE(view.status() == PropertyTextureViewStatus::Valid); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::VEC2); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT8); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->array); + + std::vector expected{ + glm::i8vec2(-1, -1), + glm::i8vec2(12, 1), + glm::i8vec2(30, 2), + glm::i8vec2(0, -1)}; + std::vector texCoords{ + glm::vec2(0, 0), + glm::vec2(0.5, 0), + glm::vec2(0, 0.5), + glm::vec2(0.5, 0.5)}; + + uint32_t invokedCallbackCount = 0; + view.getPropertyView( + "TestClassProperty", + [&expected, &texCoords, &invokedCallbackCount]( + const std::string& /*propertyName*/, + auto propertyValue) mutable { + invokedCallbackCount++; + if constexpr (std::is_same_v< + PropertyTexturePropertyView, + decltype(propertyValue)>) { + REQUIRE( + propertyValue.status() == + PropertyTexturePropertyViewStatus::Valid); + + for (size_t i = 0; i < expected.size(); ++i) { + glm::vec2& texCoord = texCoords[i]; + REQUIRE(propertyValue.get(texCoord[0], texCoord[1]) == expected[i]); + } + } else { + FAIL("getPropertyView returned PropertyTexturePropertyView of " + "incorrect type for TestClassProperty."); + } + }); + + REQUIRE(invokedCallbackCount == 1); +} + +TEST_CASE("Test callback for array PropertyTextureProperty") { + Model model; + // clang-format off + std::vector data = { + 254, 0, 253, 1, + 10, 2, 40, 3, + 30, 0, 0, 2, + 10, 2, 255, 4}; + // clang-format on + + addTextureToModel( + model, + Sampler::WrapS::CLAMP_TO_EDGE, + Sampler::WrapS::CLAMP_TO_EDGE, + 2, + 2, + 4, + data); + size_t textureIndex = model.textures.size() - 1; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::UINT16; + testClassProperty.array = true; + testClassProperty.count = 2; + + PropertyTexture& propertyTexture = metadata.propertyTextures.emplace_back(); + propertyTexture.classProperty = "TestClass"; + + PropertyTextureProperty& propertyTextureProperty = + propertyTexture.properties["TestClassProperty"]; + propertyTextureProperty.index = static_cast(textureIndex); + propertyTextureProperty.texCoord = 0; + propertyTextureProperty.channels = {0, 1, 2, 3}; + + PropertyTextureView view(model, propertyTexture); + REQUIRE(view.status() == PropertyTextureViewStatus::Valid); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT16); + REQUIRE(classProperty->array); + REQUIRE(classProperty->count == 2); + + std::vector> expected{ + {254, 509}, + {522, 808}, + {30, 512}, + {522, 1279}}; + std::vector texCoords{ + glm::vec2(0, 0), + glm::vec2(0.5, 0), + glm::vec2(0, 0.5), + glm::vec2(0.5, 0.5)}; + + uint32_t invokedCallbackCount = 0; + view.getPropertyView( + "TestClassProperty", + [&expected, &texCoords, &invokedCallbackCount]( + const std::string& /*propertyName*/, + auto propertyValue) mutable { + invokedCallbackCount++; + if constexpr (std::is_same_v< + PropertyTexturePropertyView< + PropertyArrayView>, + decltype(propertyValue)>) { + REQUIRE( + propertyValue.status() == + PropertyTexturePropertyViewStatus::Valid); + + for (size_t i = 0; i < expected.size(); ++i) { + std::vector& expectedArray = expected[i]; + glm::vec2& texCoord = texCoords[i]; + PropertyArrayView array = + propertyValue.get(texCoord[0], texCoord[1]); + + REQUIRE(static_cast(array.size()) == expectedArray.size()); + for (int64_t j = 0; j < array.size(); j++) { + REQUIRE(array[j] == expectedArray[static_cast(j)]); + } + } + } else { + FAIL("getPropertyView returned PropertyTexturePropertyView of " + "incorrect type for TestClassProperty."); + } + }); + + REQUIRE(invokedCallbackCount == 1); +} + TEST_CASE("Test unsupported PropertyTextureProperty classes") { Model model; ExtensionModelExtStructuralMetadata& metadata = From beffebe5034db67194022340ecda8fe4aa7debdc Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Mon, 3 Jul 2023 10:00:24 +1000 Subject: [PATCH 088/421] Update CHANGES.md, bump to v0.25.1. --- CHANGES.md | 7 +++++++ package.json | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 13c2ed0d8..586b3c9de 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,12 @@ # Change Log +### v0.25.1 - 2023-07-03 + +##### Additions :tada: + +- Included generated glTF and 3D Tiles classes in the generated referenced documentation. +- Updated the 3D Tiles class generator to use the `main` branch instead of the `draft-1.1` branch. + ### v0.25.0 - 2023-06-01 ##### Additions :tada: diff --git a/package.json b/package.json index 338e37338..f52ea79d5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cesium-native", - "version": "0.25.0", + "version": "0.25.1", "description": "Cesium 3D Geospatial for C++", "main": "index.js", "directories": { From bb1dea37392e1572644223ebe131a306fb6f7170 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Mon, 3 Jul 2023 16:27:43 -0400 Subject: [PATCH 089/421] Consolidate CI fixes --- .../CesiumGltf/PropertyTablePropertyView.h | 2 +- .../CesiumGltf/PropertyTexturePropertyView.h | 98 +++++++++++-------- .../include/CesiumGltf/PropertyTextureView.h | 4 +- CesiumGltf/include/CesiumGltf/PropertyType.h | 4 +- .../test/TestPropertyTexturePropertyView.cpp | 27 ++--- CesiumGltf/test/TestPropertyTextureView.cpp | 9 +- 6 files changed, 81 insertions(+), 63 deletions(-) diff --git a/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h index 977ccc995..08a7659e4 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h @@ -174,7 +174,7 @@ template class PropertyTablePropertyView { * @brief Constructs an invalid instance for a non-existent property. */ PropertyTablePropertyView() - : _status{PropertyTablePropertyViewStatus::ErrorPropertyDoesNotExist}, + : _status{PropertyTablePropertyViewStatus::ErrorNonexistentProperty}, _values{}, _arrayCount{}, _size{}, diff --git a/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h index 7adb05fa8..efeade15b 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h @@ -5,7 +5,9 @@ #include "CesiumGltf/PropertyTypeTraits.h" #include "CesiumGltf/Sampler.h" +#include #include +#include #include namespace CesiumGltf { @@ -115,7 +117,7 @@ template class PropertyTexturePropertyView { * @brief Constructs an invalid instance for a non-existent property. */ PropertyTexturePropertyView() noexcept - : _status(PropertyTexturePropertyViewStatus::ErrorUninitialized), + : _status(PropertyTexturePropertyViewStatus::ErrorNonexistentProperty), _pSampler(nullptr), _pImage(nullptr), _texCoordSetIndex(0), @@ -212,11 +214,11 @@ template class PropertyTexturePropertyView { // Clamp to ensure no out-of-bounds data access int64_t x = std::clamp( static_cast(xCoord), - 0LL, + static_cast(0), static_cast(this->_pImage->width) - 1); int64_t y = std::clamp( static_cast(yCoord), - 0LL, + static_cast(0), static_cast(this->_pImage->height) - 1); int64_t pixelIndex = this->_pImage->bytesPerChannel * @@ -284,15 +286,13 @@ template class PropertyTexturePropertyView { if constexpr (IsMetadataScalar::value) { return assembleScalarValue(bytes); - } - - if constexpr (IsMetadataVecN::value) { + } else if constexpr (IsMetadataVecN::value) { return assembleVecNValue(bytes); - } - - if constexpr (IsMetadataArray::value) { + } else if constexpr (IsMetadataArray::value) { return assembleArrayValue::type>( bytes); + } else { + return ElementType(); } } @@ -319,6 +319,7 @@ template class PropertyTexturePropertyView { for (size_t i = 0; i < bytes.size(); i++) { resultAsUint |= static_cast(bytes[i]) << i * 8; } + // Reinterpret the bits as a signed integer. return *reinterpret_cast(&resultAsUint); } else if constexpr (IsMetadataInteger::value) { @@ -332,40 +333,54 @@ template class PropertyTexturePropertyView { ElementType assembleVecNValue(const std::vector& bytes) const noexcept { - ElementType result = ElementType(); + const glm::length_t N = + getDimensionsFromPropertyType(TypeToPropertyType::value); + switch (N) { + case 2: + return assembleVecNValueImpl<2, typename ElementType::value_type>(bytes); + case 3: + return assembleVecNValueImpl<3, typename ElementType::value_type>(bytes); + case 4: + return assembleVecNValueImpl<4, typename ElementType::value_type>(bytes); + default: + return ElementType(); + } + } - PropertyComponentType componentType = - TypeToPropertyType::component; - size_t componentSize = getSizeOfComponentType(componentType); + template + ElementType + assembleVecNValueImpl(const std::vector& bytes) const noexcept { + ElementType result = ElementType(); assert( - componentSize <= 2 && + sizeof(T) <= 2 && "Components cannot be larger than two bytes in size."); - if (componentSize == 2) { - assert( - TypeToPropertyType::value == PropertyType::Vec2 && - "Only vec2s can contain two-byte integer components."); + if constexpr (std::is_same_v) { + assert(N == 2 && "Only vec2s can contain two-byte integer components."); uint16_t x = static_cast(bytes[0]) | (static_cast(bytes[1]) << 8); uint16_t y = static_cast(bytes[2]) | (static_cast(bytes[3]) << 8); - if (componentType == PropertyComponentType::Int16) { - result[0] = *reinterpret_cast(&x); - result[1] = *reinterpret_cast(&y); - } else { - result[0] = x; - result[1] = y; - } + result[0] = *reinterpret_cast(&x); + result[1] = *reinterpret_cast(&y); + } - return result; + if constexpr (std::is_same_v) { + assert(N == 2 && "Only vec2s can contain two-byte integer components."); + result[0] = static_cast(bytes[0]) | + (static_cast(bytes[1]) << 8); + result[1] = static_cast(bytes[2]) | + (static_cast(bytes[3]) << 8); } - if (componentType == PropertyComponentType::Int8) { + if constexpr (std::is_same_v) { for (size_t i = 0; i < bytes.size(); i++) { result[i] = *reinterpret_cast(&bytes[i]); } - } else { + } + + if constexpr (std::is_same_v) { for (size_t i = 0; i < bytes.size(); i++) { result[i] = bytes[i]; } @@ -377,8 +392,9 @@ template class PropertyTexturePropertyView { template PropertyArrayView assembleArrayValue(const std::vector& bytes) const noexcept { - if (sizeof(T) == 2) { - std::vector result(bytes.size() / sizeof(T)); + std::vector result; + if constexpr (sizeof(T) == 2) { + result.resize(bytes.size() / sizeof(T)); for (int i = 0, b = 0; i < result.size(); i++, b += 2) { if constexpr (std::is_signed_v) { using UintType = std::make_unsigned_t; @@ -390,16 +406,14 @@ template class PropertyTexturePropertyView { static_cast(bytes[b]) | (static_cast(bytes[b + 1]) << 8); } } - - return PropertyArrayView(std::move(result)); - } - - std::vector result(bytes.size()); - for (size_t i = 0; i < bytes.size(); i++) { - if constexpr (std::is_signed_v) { - result[i] = *reinterpret_cast(&bytes[i]); - } else { - result[i] = bytes[i]; + } else { + result.resize(bytes.size()); + for (size_t i = 0; i < bytes.size(); i++) { + if constexpr (std::is_signed_v) { + result[i] = *reinterpret_cast(&bytes[i]); + } else { + result[i] = bytes[i]; + } } } return PropertyArrayView(std::move(result)); @@ -420,7 +434,7 @@ template class PropertyTexturePropertyView { return integer % 2 == 1 ? 1.0 - fraction : fraction; } - return glm::clamp(u, 0.0, 1.0); + return std::clamp(u, 0.0, 1.0); } double applySamplerWrapT(const double v, const int32_t wrapT) const noexcept { @@ -438,7 +452,7 @@ template class PropertyTexturePropertyView { return integer % 2 == 1 ? 1.0 - fraction : fraction; } - return glm::clamp(v, 0.0, 1.0); + return std::clamp(v, 0.0, 1.0); } PropertyTexturePropertyViewStatus _status; diff --git a/CesiumGltf/include/CesiumGltf/PropertyTextureView.h b/CesiumGltf/include/CesiumGltf/PropertyTextureView.h index b622cb4c9..0ef93e7f5 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTextureView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTextureView.h @@ -210,7 +210,7 @@ class PropertyTextureView { template PropertyTexturePropertyView getPropertyViewImpl( const std::string& propertyName, - const ClassProperty& classProperty) const { + [[maybe_unused]] const ClassProperty& classProperty) const { auto propertyTexturePropertyIter = _pPropertyTexture->properties.find(propertyName); if (propertyTexturePropertyIter == _pPropertyTexture->properties.end()) { @@ -218,7 +218,7 @@ class PropertyTextureView { PropertyTexturePropertyViewStatus::ErrorNonexistentProperty); } - const PropertyTextureProperty& propertyTextureProperty = + [[maybe_unused]] const PropertyTextureProperty& propertyTextureProperty = propertyTexturePropertyIter->second; if constexpr (IsMetadataScalar::value) { diff --git a/CesiumGltf/include/CesiumGltf/PropertyType.h b/CesiumGltf/include/CesiumGltf/PropertyType.h index e5c964ec8..8f1c8deff 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyType.h +++ b/CesiumGltf/include/CesiumGltf/PropertyType.h @@ -1,11 +1,11 @@ #pragma once +#include + #include #include #include -#include - namespace CesiumGltf { enum class PropertyType { Invalid, diff --git a/CesiumGltf/test/TestPropertyTexturePropertyView.cpp b/CesiumGltf/test/TestPropertyTexturePropertyView.cpp index 947b3b4c8..7a329d00f 100644 --- a/CesiumGltf/test/TestPropertyTexturePropertyView.cpp +++ b/CesiumGltf/test/TestPropertyTexturePropertyView.cpp @@ -624,13 +624,15 @@ TEST_CASE("Check array PropertyTexturePropertyView") { view.get(0, 0.5), view.get(0.5, 0.5)}; - for (size_t i = 0; i < values.size(); i++) { + int64_t size = static_cast(values.size()); + for (int64_t i = 0; i < size; i++) { auto dataStart = data.begin() + i * 4; std::vector expected(dataStart, dataStart + 4); - const PropertyArrayView& value = values[i]; + + const PropertyArrayView& value = values[static_cast(i)]; REQUIRE(static_cast(value.size()) == expected.size()); - for (size_t j = 0; j < expected.size(); j++) { - REQUIRE(value[j] == expected[j]); + for (int64_t j = 0; j < value.size(); j++) { + REQUIRE(value[j] == expected[static_cast(j)]); } } } @@ -671,12 +673,13 @@ TEST_CASE("Check array PropertyTexturePropertyView") { view.get(0.5, 0), view.get(0, 0.5), view.get(0.5, 0.5)}; + for (size_t i = 0; i < values.size(); i++) { std::vector& expectedValue = expected[i]; const PropertyArrayView& value = values[i]; REQUIRE(static_cast(value.size()) == expected.size()); - for (size_t j = 0; j < expected.size(); j++) { - REQUIRE(value[j] == expectedValue[j]); + for (int64_t j = 0; j < value.size(); j++) { + REQUIRE(value[j] == expectedValue[static_cast(j)]); } } } @@ -723,8 +726,8 @@ TEST_CASE("Check array PropertyTexturePropertyView") { std::vector& expectedValue = expected[i]; const PropertyArrayView& value = values[i]; REQUIRE(static_cast(value.size()) == expectedValue.size()); - for (size_t j = 0; j < expectedValue.size(); j++) { - REQUIRE(value[j] == expectedValue[j]); + for (int64_t j = 0; j < value.size(); j++) { + REQUIRE(value[j] == expectedValue[static_cast(j)]); } } } @@ -769,8 +772,8 @@ TEST_CASE("Check array PropertyTexturePropertyView") { std::vector& expectedValue = expected[i]; const PropertyArrayView& value = values[i]; REQUIRE(static_cast(value.size()) == expectedValue.size()); - for (size_t j = 0; j < expectedValue.size(); j++) { - REQUIRE(value[j] == expectedValue[j]); + for (int64_t j = 0; j < value.size(); j++) { + REQUIRE(value[j] == expectedValue[static_cast(j)]); } } } @@ -932,8 +935,8 @@ TEST_CASE("Check that non-adjacent channels resolve to expected output") { std::vector& expectedValue = expected[i]; const PropertyArrayView& value = values[i]; REQUIRE(static_cast(value.size()) == expectedValue.size()); - for (size_t j = 0; j < expectedValue.size(); j++) { - REQUIRE(value[j] == expectedValue[j]); + for (int64_t j = 0; j < value.size(); j++) { + REQUIRE(value[j] == expectedValue[static_cast(j)]); } } } diff --git a/CesiumGltf/test/TestPropertyTextureView.cpp b/CesiumGltf/test/TestPropertyTextureView.cpp index 095b17ddf..e62dcda17 100644 --- a/CesiumGltf/test/TestPropertyTextureView.cpp +++ b/CesiumGltf/test/TestPropertyTextureView.cpp @@ -486,13 +486,14 @@ TEST_CASE("Test array PropertyTextureProperty") { uint8ArrayProperty.get(0.5, 0.5), }; - for (size_t i = 0; i < values.size(); ++i) { + int64_t size = static_cast(values.size()); + for (int64_t i = 0; i < size; ++i) { auto dataStart = data.begin() + i * 3; std::vector expected(dataStart, dataStart + 3); - const PropertyArrayView& value = values[i]; + const PropertyArrayView& value = values[static_cast(i)]; REQUIRE(static_cast(value.size()) == expected.size()); - for (size_t j = 0; j < expected.size(); j++) { - REQUIRE(value[j] == expected[j]); + for (int64_t j = 0; j < value.size(); j++) { + REQUIRE(value[j] == expected[static_cast(j)]); } } } From b024fe5b8f068dc5585ff0d93bc50b09bf12ca9a Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Mon, 3 Jul 2023 17:26:24 -0400 Subject: [PATCH 090/421] Stricter reinforcement of unsupported properties --- .../CesiumGltf/PropertyTexturePropertyView.h | 47 ++--- .../include/CesiumGltf/PropertyTextureView.h | 123 +++++------ CesiumGltf/test/TestPropertyTextureView.cpp | 199 +++++++----------- 3 files changed, 150 insertions(+), 219 deletions(-) diff --git a/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h index efeade15b..2b14e1738 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h @@ -286,13 +286,15 @@ template class PropertyTexturePropertyView { if constexpr (IsMetadataScalar::value) { return assembleScalarValue(bytes); - } else if constexpr (IsMetadataVecN::value) { + } + + if constexpr (IsMetadataVecN::value) { return assembleVecNValue(bytes); - } else if constexpr (IsMetadataArray::value) { + } + + if constexpr (IsMetadataArray::value) { return assembleArrayValue::type>( bytes); - } else { - return ElementType(); } } @@ -311,23 +313,15 @@ template class PropertyTexturePropertyView { return *reinterpret_cast(&resultAsUint); } - if constexpr ( - IsMetadataInteger::value && - std::is_signed_v) { + if constexpr (IsMetadataInteger::value) { using UintType = std::make_unsigned_t; UintType resultAsUint = 0; for (size_t i = 0; i < bytes.size(); i++) { resultAsUint |= static_cast(bytes[i]) << i * 8; } - // Reinterpret the bits as a signed integer. + // Reinterpret the bits with the correct signedness. return *reinterpret_cast(&resultAsUint); - } else if constexpr (IsMetadataInteger::value) { - ElementType result = 0; - for (size_t i = 0; i < bytes.size(); i++) { - result |= static_cast(bytes[i]) << i * 8; - } - return result; } } @@ -392,30 +386,21 @@ template class PropertyTexturePropertyView { template PropertyArrayView assembleArrayValue(const std::vector& bytes) const noexcept { - std::vector result; + std::vector result(bytes.size() / sizeof(T)); + if constexpr (sizeof(T) == 2) { - result.resize(bytes.size() / sizeof(T)); for (int i = 0, b = 0; i < result.size(); i++, b += 2) { - if constexpr (std::is_signed_v) { - using UintType = std::make_unsigned_t; - UintType resultAsUint = static_cast(bytes[b]) | - (static_cast(bytes[b + 1]) << 8); - result[i] = *reinterpret_cast(&resultAsUint); - } else { - result[i] = - static_cast(bytes[b]) | (static_cast(bytes[b + 1]) << 8); - } + using UintType = std::make_unsigned_t; + UintType resultAsUint = static_cast(bytes[b]) | + (static_cast(bytes[b + 1]) << 8); + result[i] = *reinterpret_cast(&resultAsUint); } } else { - result.resize(bytes.size()); for (size_t i = 0; i < bytes.size(); i++) { - if constexpr (std::is_signed_v) { - result[i] = *reinterpret_cast(&bytes[i]); - } else { - result[i] = bytes[i]; - } + result[i] = *reinterpret_cast(&bytes[i]); } } + return PropertyArrayView(std::move(result)); } diff --git a/CesiumGltf/include/CesiumGltf/PropertyTextureView.h b/CesiumGltf/include/CesiumGltf/PropertyTextureView.h index 0ef93e7f5..eedde43e9 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTextureView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTextureView.h @@ -210,7 +210,7 @@ class PropertyTextureView { template PropertyTexturePropertyView getPropertyViewImpl( const std::string& propertyName, - [[maybe_unused]] const ClassProperty& classProperty) const { + const ClassProperty& classProperty) const { auto propertyTexturePropertyIter = _pPropertyTexture->properties.find(propertyName); if (propertyTexturePropertyIter == _pPropertyTexture->properties.end()) { @@ -218,22 +218,23 @@ class PropertyTextureView { PropertyTexturePropertyViewStatus::ErrorNonexistentProperty); } - [[maybe_unused]] const PropertyTextureProperty& propertyTextureProperty = + const PropertyTextureProperty& propertyTextureProperty = propertyTexturePropertyIter->second; if constexpr (IsMetadataScalar::value) { return createScalarPropertyView( classProperty, propertyTextureProperty); - } else if constexpr (IsMetadataVecN::value) { + } + + if constexpr (IsMetadataVecN::value) { return createVecNPropertyView(classProperty, propertyTextureProperty); - } else if constexpr (IsMetadataArray::value) { + } + + if constexpr (IsMetadataArray::value) { return createArrayPropertyView::type>( classProperty, propertyTextureProperty); - } else { - return PropertyTexturePropertyView( - PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty); } } @@ -250,6 +251,7 @@ class PropertyTextureView { propertyName, PropertyTexturePropertyView( PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty)); + return; } int64_t count = classProperty.count.value_or(0); @@ -258,6 +260,7 @@ class PropertyTextureView { propertyName, PropertyTexturePropertyView( PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty)); + return; } switch (componentType) { @@ -294,6 +297,7 @@ class PropertyTextureView { propertyName, PropertyTexturePropertyView( PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty)); + break; } } @@ -370,19 +374,23 @@ class PropertyTextureView { classProperty)); break; case PropertyComponentType::Int16: - callback( - propertyName, - getPropertyViewImpl>( - propertyName, - classProperty)); - break; + if constexpr (N == 2) { + callback( + propertyName, + getPropertyViewImpl>( + propertyName, + classProperty)); + break; + } case PropertyComponentType::Uint16: - callback( - propertyName, - getPropertyViewImpl>( - propertyName, - classProperty)); - break; + if constexpr (N == 2) { + callback( + propertyName, + getPropertyViewImpl>( + propertyName, + classProperty)); + break; + } default: callback( propertyName, @@ -454,17 +462,13 @@ class PropertyTextureView { PropertyTexturePropertyViewStatus::ErrorComponentTypeMismatch); } - // Eight-byte scalar types are unsupported. - size_t componentSize = getSizeOfComponentType(componentType); - if (componentSize == 0 || componentSize > 4) { - return PropertyTexturePropertyView( - PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty); + // Only up to four bytes of image data are supported. + if constexpr (sizeof(T) <= 4) { + return createPropertyViewImpl( + classProperty, + propertyTextureProperty, + sizeof(T)); } - - return createPropertyViewImpl( - classProperty, - propertyTextureProperty, - sizeof(T)); } template @@ -491,18 +495,12 @@ class PropertyTextureView { } // Only up to four bytes of image data are supported. - size_t dimensions = - static_cast(getDimensionsFromPropertyType(type)); - - if (dimensions * getSizeOfComponentType(componentType) > 4) { - return PropertyTexturePropertyView( - PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty); + if constexpr (sizeof(T) <= 4) { + return createPropertyViewImpl( + classProperty, + propertyTextureProperty, + sizeof(T)); } - - return createPropertyViewImpl( - classProperty, - propertyTextureProperty, - sizeof(T)); } template @@ -520,19 +518,6 @@ class PropertyTextureView { PropertyTexturePropertyViewStatus::ErrorTypeMismatch); } - // Only scalar arrays are supported. - if (type != PropertyType::Scalar) { - return PropertyTexturePropertyView>( - PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty); - } - - // Only up to four elements are supported. - int64_t count = classProperty.count.value_or(0); - if (count <= 0 || count > 4) { - return PropertyTexturePropertyView>( - PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty); - } - const PropertyComponentType componentType = convertStringToPropertyComponentType( classProperty.componentType.value_or("")); @@ -541,22 +526,26 @@ class PropertyTextureView { PropertyTexturePropertyViewStatus::ErrorComponentTypeMismatch); } - // Only up to two-byte components are supported. - size_t componentSize = getSizeOfComponentType(componentType); - if (componentSize == 0 || componentSize > 2) { - return PropertyTexturePropertyView>( - PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty); - } + // Only scalar arrays are supported. The scalar component type must not + // exceed two bytes. + if constexpr (IsMetadataScalar::value && sizeof(T) <= 4) { + // Only up to four elements are supported. + int64_t count = classProperty.count.value_or(0); + if (count <= 0 || count > 4) { + return PropertyTexturePropertyView>( + PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty); + } - if (componentSize * count > 4) { - return PropertyTexturePropertyView>( - PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty); - } + if (count * sizeof(T) > 4) { + return PropertyTexturePropertyView>( + PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty); + } - return createPropertyViewImpl>( - classProperty, - propertyTextureProperty, - count * sizeof(T)); + return createPropertyViewImpl>( + classProperty, + propertyTextureProperty, + count * sizeof(T)); + } } template diff --git a/CesiumGltf/test/TestPropertyTextureView.cpp b/CesiumGltf/test/TestPropertyTextureView.cpp index e62dcda17..d442ee364 100644 --- a/CesiumGltf/test/TestPropertyTextureView.cpp +++ b/CesiumGltf/test/TestPropertyTextureView.cpp @@ -381,15 +381,6 @@ TEST_CASE("Test vecN PropertyTextureProperty") { PropertyTexturePropertyViewStatus::ErrorComponentTypeMismatch); } - SECTION("Access incorrectly as array") { - PropertyTexturePropertyView> arrayInvalid = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - arrayInvalid.status() == - PropertyTexturePropertyViewStatus::ErrorArrayTypeMismatch); - } - SECTION("Channel and type mismatch") { model.images[imageIndex].cesium.channels = 4; propertyTextureProperty.channels = {0, 1, 2, 3}; @@ -498,16 +489,6 @@ TEST_CASE("Test array PropertyTextureProperty") { } } - SECTION("Access wrong type") { - PropertyTexturePropertyView> - u8vec3ArrayInvalid = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - u8vec3ArrayInvalid.status() == - PropertyTexturePropertyViewStatus::ErrorTypeMismatch); - } - SECTION("Access wrong component type") { PropertyTexturePropertyView> int8ArrayInvalid = view.getPropertyView>("TestClassProperty"); @@ -904,123 +885,99 @@ TEST_CASE("Test callback for array PropertyTextureProperty") { REQUIRE(invokedCallbackCount == 1); } -TEST_CASE("Test unsupported PropertyTextureProperty classes") { +TEST_CASE("Test callback on unsupported PropertyTextureProperty") { Model model; + // clang-format off + std::vector data = { + 254, 0, 253, 1, + 10, 2, 40, 3, + 30, 0, 0, 2, + 10, 2, 255, 4}; + // clang-format on + + addTextureToModel( + model, + Sampler::WrapS::CLAMP_TO_EDGE, + Sampler::WrapS::CLAMP_TO_EDGE, + 2, + 1, + 8, + data); + size_t textureIndex = model.textures.size() - 1; + ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); Schema& schema = metadata.schema.emplace(); Class& testClass = schema.classes["TestClass"]; - ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + + ClassProperty& doubleClassProperty = + testClass.properties["DoubleClassProperty"]; + doubleClassProperty.type = ClassProperty::Type::SCALAR; + doubleClassProperty.componentType = ClassProperty::ComponentType::FLOAT64; + + ClassProperty& arrayClassProperty = + testClass.properties["ArrayClassProperty"]; + arrayClassProperty.type = ClassProperty::Type::VEC4; + arrayClassProperty.componentType = ClassProperty::ComponentType::UINT8; + arrayClassProperty.array = true; + arrayClassProperty.count = 2; PropertyTexture& propertyTexture = metadata.propertyTextures.emplace_back(); propertyTexture.classProperty = "TestClass"; - PropertyTextureProperty& propertyTextureProperty = - propertyTexture.properties["TestClassProperty"]; - propertyTextureProperty.index = 0; - propertyTextureProperty.texCoord = 0; - propertyTextureProperty.channels = {0}; + PropertyTextureProperty& doubleProperty = + propertyTexture.properties["DoubleClassProperty"]; + doubleProperty.index = static_cast(textureIndex); + doubleProperty.texCoord = 0; + doubleProperty.channels = {0, 1, 2, 3, 4, 5, 6, 7}; + + PropertyTextureProperty& arrayProperty = + propertyTexture.properties["ArrayClassProperty"]; + arrayProperty.index = static_cast(textureIndex); + arrayProperty.texCoord = 0; + arrayProperty.channels = {0, 1, 2, 3, 4, 5, 6, 7}; PropertyTextureView view(model, propertyTexture); REQUIRE(view.status() == PropertyTextureViewStatus::Valid); - SECTION("Unsupported types") { - testClassProperty.type = ClassProperty::Type::BOOLEAN; - PropertyTexturePropertyView boolInvalid = - view.getPropertyView("TestClassProperty"); - REQUIRE( - boolInvalid.status() == - PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty); - - testClassProperty.type = ClassProperty::Type::STRING; - PropertyTexturePropertyView stringInvalid = - view.getPropertyView("TestClassProperty"); - REQUIRE( - stringInvalid.status() == - PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty); - - testClassProperty.type = ClassProperty::Type::MAT2; - PropertyTexturePropertyView mat2Invalid = - view.getPropertyView("TestClassProperty"); - REQUIRE( - mat2Invalid.status() == - PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty); - } - - SECTION("Unsupported component types") { - testClassProperty.type = ClassProperty::Type::SCALAR; - testClassProperty.componentType = ClassProperty::ComponentType::FLOAT64; - PropertyTexturePropertyView doubleInvalid = - view.getPropertyView("TestClassProperty"); - REQUIRE( - doubleInvalid.status() == - PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty); - - testClassProperty.type = ClassProperty::Type::SCALAR; - testClassProperty.componentType = ClassProperty::ComponentType::UINT64; - PropertyTexturePropertyView uint64Invalid = - view.getPropertyView("TestClassProperty"); - REQUIRE( - uint64Invalid.status() == - PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty); - - testClassProperty.type = ClassProperty::Type::VEC2; - testClassProperty.componentType = ClassProperty::ComponentType::INT32; - PropertyTexturePropertyView ivec2Invalid = - view.getPropertyView("TestClassProperty"); - REQUIRE( - ivec2Invalid.status() == - PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty); - - testClassProperty.type = ClassProperty::Type::VEC3; - testClassProperty.componentType = ClassProperty::ComponentType::UINT16; - PropertyTexturePropertyView u16vec3Invalid = - view.getPropertyView("TestClassProperty"); - REQUIRE( - u16vec3Invalid.status() == - PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty); - } + const ClassProperty* classProperty = + view.getClassProperty("DoubleClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); + REQUIRE( + classProperty->componentType == ClassProperty::ComponentType::FLOAT64); + REQUIRE(!classProperty->array); - SECTION("Unsupported array types") { - testClassProperty.array = true; + classProperty = view.getClassProperty("ArrayClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::VEC4); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT8); + REQUIRE(classProperty->array); + REQUIRE(classProperty->count == 2); - testClassProperty.type = ClassProperty::Type::SCALAR; - testClassProperty.componentType = ClassProperty::ComponentType::UINT8; - testClassProperty.count = 5; - PropertyTexturePropertyView> bigArrayInvalid = - view.getPropertyView>("TestClassProperty"); - REQUIRE( - bigArrayInvalid.status() == - PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty); + uint32_t invokedCallbackCount = 0; + view.getPropertyView( + "DoubleClassProperty", + [&invokedCallbackCount]( + const std::string& /*propertyName*/, + auto propertyValue) mutable { + invokedCallbackCount++; + REQUIRE( + propertyValue.status() == + PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty); + }); + REQUIRE(invokedCallbackCount == 1); - testClassProperty.count = std::nullopt; - PropertyTexturePropertyView> - variableArrayInvalid = view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - variableArrayInvalid.status() == - PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty); - - testClassProperty.type = ClassProperty::Type::VEC2; - testClassProperty.componentType = ClassProperty::ComponentType::UINT32; - testClassProperty.count = 2; - PropertyTexturePropertyView> - uvec2ArrayInvalid = view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - uvec2ArrayInvalid.status() == - PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty); - - testClassProperty.type = ClassProperty::Type::VEC3; - testClassProperty.componentType = ClassProperty::ComponentType::UINT8; - testClassProperty.count = 1; - PropertyTexturePropertyView> - u8vec3ArrayInvalid = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - u8vec3ArrayInvalid.status() == - PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty); - } + view.getPropertyView( + "ArrayClassProperty", + [&invokedCallbackCount]( + const std::string& /*propertyName*/, + auto propertyValue) mutable { + invokedCallbackCount++; + REQUIRE( + propertyValue.status() == + PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty); + }); + REQUIRE(invokedCallbackCount == 2); } From d1849146e8917eeedee4444cc7310c83c037193b Mon Sep 17 00:00:00 2001 From: Jacob Martin Date: Tue, 4 Jul 2023 00:41:07 -0400 Subject: [PATCH 091/421] fix in response to unused but set var --- .../test/TestQuadtreeRasterOverlayTileProvider.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Cesium3DTilesSelection/test/TestQuadtreeRasterOverlayTileProvider.cpp b/Cesium3DTilesSelection/test/TestQuadtreeRasterOverlayTileProvider.cpp index 3476a966d..cfa8c0935 100644 --- a/Cesium3DTilesSelection/test/TestQuadtreeRasterOverlayTileProvider.cpp +++ b/Cesium3DTilesSelection/test/TestQuadtreeRasterOverlayTileProvider.cpp @@ -159,9 +159,8 @@ TEST_CASE("QuadtreeRasterOverlayTileProvider getTile") { pProvider->getTile(rectangle, glm::dvec2(256)); pProvider->loadTile(*pTile); - for (int i = 0; pTile->getState() != RasterOverlayTile::LoadState::Loaded; - ++i) { - asyncSystem.dispatchMainThreadTasks(); + while (pTile->getState() != RasterOverlayTile::LoadState::Loaded) { + asyncSystem.dispatchMainThreadTasks(); } CHECK(pTile->getState() == RasterOverlayTile::LoadState::Loaded); @@ -214,9 +213,8 @@ TEST_CASE("QuadtreeRasterOverlayTileProvider getTile") { pProvider->getTile(tileRectangle, targetScreenPixels); pProvider->loadTile(*pTile); - for (int i = 0; pTile->getState() != RasterOverlayTile::LoadState::Loaded; - ++i) { - asyncSystem.dispatchMainThreadTasks(); + while (pTile->getState() != RasterOverlayTile::LoadState::Loaded) { + asyncSystem.dispatchMainThreadTasks(); } CHECK(pTile->getState() == RasterOverlayTile::LoadState::Loaded); From eadd220fcf53dd11e45e007da0c1afafd8668cb5 Mon Sep 17 00:00:00 2001 From: Jacob Martin Date: Tue, 4 Jul 2023 22:01:11 -0400 Subject: [PATCH 092/421] run clang-format --- .../test/TestQuadtreeRasterOverlayTileProvider.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cesium3DTilesSelection/test/TestQuadtreeRasterOverlayTileProvider.cpp b/Cesium3DTilesSelection/test/TestQuadtreeRasterOverlayTileProvider.cpp index cfa8c0935..c5366b91d 100644 --- a/Cesium3DTilesSelection/test/TestQuadtreeRasterOverlayTileProvider.cpp +++ b/Cesium3DTilesSelection/test/TestQuadtreeRasterOverlayTileProvider.cpp @@ -160,7 +160,7 @@ TEST_CASE("QuadtreeRasterOverlayTileProvider getTile") { pProvider->loadTile(*pTile); while (pTile->getState() != RasterOverlayTile::LoadState::Loaded) { - asyncSystem.dispatchMainThreadTasks(); + asyncSystem.dispatchMainThreadTasks(); } CHECK(pTile->getState() == RasterOverlayTile::LoadState::Loaded); @@ -214,7 +214,7 @@ TEST_CASE("QuadtreeRasterOverlayTileProvider getTile") { pProvider->loadTile(*pTile); while (pTile->getState() != RasterOverlayTile::LoadState::Loaded) { - asyncSystem.dispatchMainThreadTasks(); + asyncSystem.dispatchMainThreadTasks(); } CHECK(pTile->getState() == RasterOverlayTile::LoadState::Loaded); From 047422b53d0475ba1ad2544fee6511602f0d865d Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Wed, 5 Jul 2023 16:35:34 -0400 Subject: [PATCH 093/421] Fix nearest sampling for feature ID textures --- CesiumGltf/src/FeatureIdTextureView.cpp | 23 +++++++++++++++----- CesiumGltf/test/TestFeatureIdTextureView.cpp | 4 ++-- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/CesiumGltf/src/FeatureIdTextureView.cpp b/CesiumGltf/src/FeatureIdTextureView.cpp index 17a1e981a..22553e8d5 100644 --- a/CesiumGltf/src/FeatureIdTextureView.cpp +++ b/CesiumGltf/src/FeatureIdTextureView.cpp @@ -68,14 +68,25 @@ int64_t FeatureIdTextureView::getFeatureID(double u, double v) const noexcept { return -1; } + // Always use nearest filtering, and use std::floor instead of std::round. + // This is because filtering is supposed to consider the pixel centers. But + // memory access here acts as sampling the beginning of the pixel. Example: + // 0.4 * 2 = 0.8. In a 2x1 pixel image, that should be closer to the left + // pixel's center. But it will round to 1.0 which corresponds to the right + // pixel. So the right pixel has a bigger range than the left one, which is + // incorrect. + double xCoord = std::floor(u * this->_pImage->width); + double yCoord = std::floor(v * this->_pImage->height); + + // Clamp to ensure no out-of-bounds data access int64_t x = std::clamp( - std::llround(u * this->_pImage->width), - 0LL, - (long long)this->_pImage->width - 1); + static_cast(xCoord), + static_cast(0), + static_cast(this->_pImage->width - 1)); int64_t y = std::clamp( - std::llround(v * this->_pImage->height), - 0LL, - (long long)this->_pImage->height - 1); + static_cast(yCoord), + static_cast(0), + static_cast(this->_pImage->height - 1)); int64_t pixelOffset = this->_pImage->bytesPerChannel * this->_pImage->channels * diff --git a/CesiumGltf/test/TestFeatureIdTextureView.cpp b/CesiumGltf/test/TestFeatureIdTextureView.cpp index 5ee2080fc..8569433fe 100644 --- a/CesiumGltf/test/TestFeatureIdTextureView.cpp +++ b/CesiumGltf/test/TestFeatureIdTextureView.cpp @@ -338,9 +338,9 @@ TEST_CASE("Test getFeatureID rounds to nearest pixel") { FeatureIdTextureView view(model, featureIdTexture); REQUIRE(view.status() == FeatureIdTextureViewStatus::Valid); - REQUIRE(view.getFeatureID(0.1, 0.24) == 1); + REQUIRE(view.getFeatureID(0.1, 0.4) == 1); REQUIRE(view.getFeatureID(0.86, 0.2) == 2); - REQUIRE(view.getFeatureID(0.21, 0.555) == 0); + REQUIRE(view.getFeatureID(0.29, 0.555) == 0); REQUIRE(view.getFeatureID(0.99, 0.81) == 7); } From dff210594d591532859d0bdcd47044cf9b64c828 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Wed, 5 Jul 2023 17:27:28 -0400 Subject: [PATCH 094/421] Add assert in status constructor for PropertyTexturePropertyView --- CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h index 2b14e1738..709c25e10 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h @@ -137,7 +137,11 @@ template class PropertyTexturePropertyView { _texCoordSetIndex(0), _channels(), _swizzle(""), - _normalized(false) {} + _normalized(false) { + assert( + _status != PropertyTexturePropertyViewStatus::Valid && + "An empty property view should not be constructed with a valid status"); + } /** * @brief Construct a valid view of the data specified by a {@link PropertyTextureProperty}. From 930760ea95e0b2d3ecc67f427d28601960eb76a5 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Fri, 7 Jul 2023 15:37:01 +1000 Subject: [PATCH 095/421] Fix handling of KTX2 images without mipmaps. --- CHANGES.md | 6 ++ CesiumGltfReader/src/GltfReader.cpp | 59 +++++++++++++---- CesiumGltfReader/test/TestGltfReader.cpp | 61 ++++++++++++++++++ CesiumGltfReader/test/data/ktx2/README.md | 9 +++ .../test/data/ktx2/kota-automipmap.ktx2 | Bin 0 -> 49426 bytes .../test/data/ktx2/kota-mipmaps.ktx2 | Bin 0 -> 66125 bytes .../test/data/ktx2/kota-onelevel.ktx2 | Bin 0 -> 49426 bytes CesiumGltfReader/test/data/ktx2/kota.jpg | Bin 0 -> 39887 bytes 8 files changed, 121 insertions(+), 14 deletions(-) create mode 100644 CesiumGltfReader/test/data/ktx2/README.md create mode 100644 CesiumGltfReader/test/data/ktx2/kota-automipmap.ktx2 create mode 100644 CesiumGltfReader/test/data/ktx2/kota-mipmaps.ktx2 create mode 100644 CesiumGltfReader/test/data/ktx2/kota-onelevel.ktx2 create mode 100644 CesiumGltfReader/test/data/ktx2/kota.jpg diff --git a/CHANGES.md b/CHANGES.md index 586b3c9de..72500d100 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,11 @@ # Change Log +### ? - ? + +##### Fixes :wrench: + +- Fixed a bug where `GltfReader::readImage` would always populate `mipPositions` when reading KTX2 images, even when the KTX2 file indicated that it had no mip levels that and they should be created, if necessary, from the base image. As a result, `generateMipMaps` wouldn't generate any mipmaps for the image. + ### v0.25.1 - 2023-07-03 ##### Additions :tada: diff --git a/CesiumGltfReader/src/GltfReader.cpp b/CesiumGltfReader/src/GltfReader.cpp index 080896bc1..564c62aa5 100644 --- a/CesiumGltfReader/src/GltfReader.cpp +++ b/CesiumGltfReader/src/GltfReader.cpp @@ -596,20 +596,51 @@ ImageReaderResult GltfReader::readImage( image.channels = 4; } - // Copy over the positions of each mip within the buffer. - image.mipPositions.resize(pTexture->numLevels); - for (ktx_uint32_t level = 0; level < pTexture->numLevels; ++level) { - ktx_size_t imageOffset; - ktxTexture_GetImageOffset( - ktxTexture(pTexture), - level, - 0, - 0, - &imageOffset); - ktx_size_t imageSize = - ktxTexture_GetImageSize(ktxTexture(pTexture), level); - - image.mipPositions[level] = {imageOffset, imageSize}; + // In the KTX2 spec, there's a distinction between "this image has no + // mipmaps, so they should be generated at runtime" and and "this + // image has no mipmaps because it makes no sense to create a mipmap + // for this type of image." It is, confusingly, encoded in the + // `levelCount` property: + // https://registry.khronos.org/KTX/specs/2.0/ktxspec.v2.html#_levelcount + // + // With `levelCount=0`, mipmaps should be generated. With + // `levelCount=1`, mipmaps make no sense. So when `levelCount=0`, we + // want to leave the `mipPositions` array _empty_. With + // `levelCount=1`, we want to populate it with a single mip level. + // + // However, this `levelCount` property is not directly exposed by the + // KTX2 loader API we're using here. Instead, there is a `numLevels` + // property, but it will _never_ have the value 0, because it + // represents the number of levels of actual pixel data we have. When + // the API sees `levelCount=0`, it will assign the value 1 to + // `numLevels`, but it will _also_ set `generateMipmaps` to true. + // + // The API docs say that `numLevels` will always be 1 when + // `generateMipmaps` is true. + // + // So, in summary, when `generateMipmaps=false`, we populate + // `mipPositions` with whatever mip levels the KTX provides and we + // don't generate any more. When it's true, we treat all the image + // data as belonging to a single base-level image and generate mipmaps + // from that if necessary. + if (!pTexture->generateMipmaps) { + // Copy over the positions of each mip within the buffer. + image.mipPositions.resize(pTexture->numLevels); + for (ktx_uint32_t level = 0; level < pTexture->numLevels; ++level) { + ktx_size_t imageOffset; + ktxTexture_GetImageOffset( + ktxTexture(pTexture), + level, + 0, + 0, + &imageOffset); + ktx_size_t imageSize = + ktxTexture_GetImageSize(ktxTexture(pTexture), level); + + image.mipPositions[level] = {imageOffset, imageSize}; + } + } else { + assert(pTexture->numLevels == 1); } // Copy over the entire buffer, including all mips. diff --git a/CesiumGltfReader/test/TestGltfReader.cpp b/CesiumGltfReader/test/TestGltfReader.cpp index 9f04b5641..cc18874b3 100644 --- a/CesiumGltfReader/test/TestGltfReader.cpp +++ b/CesiumGltfReader/test/TestGltfReader.cpp @@ -438,3 +438,64 @@ TEST_CASE("Can apply RTC CENTER if model uses Cesium RTC extension") { std::vector rtcCenter = {6378137.0, 0.0, 0.0}; CHECK(cesiumRTC->center == rtcCenter); } + +TEST_CASE("Can correctly interpret mipmaps in KTX2 files") { + { + // This KTX2 file has a single mip level and no further mip levels should be + // generated. `mipPositions` should reflect this single mip level. + std::filesystem::path ktx2File = CesiumGltfReader_TEST_DATA_DIR; + ktx2File /= "ktx2/kota-onelevel.ktx2"; + std::vector data = readFile(ktx2File.string()); + ImageReaderResult imageResult = + GltfReader::readImage(data, Ktx2TranscodeTargets{}); + REQUIRE(imageResult.image.has_value()); + + const ImageCesium& image = *imageResult.image; + REQUIRE(image.mipPositions.size() == 1); + CHECK(image.mipPositions[0].byteOffset == 0); + CHECK(image.mipPositions[0].byteSize > 0); + CHECK( + image.mipPositions[0].byteSize == + image.width * image.height * image.channels); + CHECK(image.mipPositions[0].byteSize == image.pixelData.size()); + } + + { + // This KTX2 file has only a base image but further mip levels can be + // generated. This image effectively has no mip levels. + std::filesystem::path ktx2File = CesiumGltfReader_TEST_DATA_DIR; + ktx2File /= "ktx2/kota-automipmap.ktx2"; + std::vector data = readFile(ktx2File.string()); + ImageReaderResult imageResult = + GltfReader::readImage(data, Ktx2TranscodeTargets{}); + REQUIRE(imageResult.image.has_value()); + + const ImageCesium& image = *imageResult.image; + REQUIRE(image.mipPositions.size() == 0); + CHECK(image.pixelData.size() > 0); + } + + { + // This KTX2 file has a complete mip chain. + std::filesystem::path ktx2File = CesiumGltfReader_TEST_DATA_DIR; + ktx2File /= "ktx2/kota-mipmaps.ktx2"; + std::vector data = readFile(ktx2File.string()); + ImageReaderResult imageResult = + GltfReader::readImage(data, Ktx2TranscodeTargets{}); + REQUIRE(imageResult.image.has_value()); + + const ImageCesium& image = *imageResult.image; + REQUIRE(image.mipPositions.size() == 9); + CHECK(image.mipPositions[0].byteSize > 0); + CHECK( + image.mipPositions[0].byteSize == + image.width * image.height * image.channels); + CHECK(image.mipPositions[0].byteSize < image.pixelData.size()); + + size_t smallerThan = image.mipPositions[0].byteSize; + for (size_t i = 1; i < image.mipPositions.size(); ++i) { + CHECK(image.mipPositions[i].byteSize < smallerThan); + smallerThan = image.mipPositions[i].byteSize; + } + } +} diff --git a/CesiumGltfReader/test/data/ktx2/README.md b/CesiumGltfReader/test/data/ktx2/README.md new file mode 100644 index 000000000..d06b718a5 --- /dev/null +++ b/CesiumGltfReader/test/data/ktx2/README.md @@ -0,0 +1,9 @@ +The original image, kota.jpg, is a photo taken by Kevin Ring and licensed under the same Apache 2.0 terms as the rest of cesium-native. + +The other images were created using the `toktx` tool included in https://github.com/KhronosGroup/KTX-Software v4.2.0~11. They were created with the following commands: + +| *Filename* | *Command* | +|------------|-----------| +| `kota-automipmap.ktx2` | `toktx --t2 --zcmp --encode uastc --automipmap kota-automipmap kota.jpg` | +| `kota-onelevel.ktx2` | `toktx --t2 --zcmp --encode uastc kota-onelevel kota.jpg` | +| `kota-mipmaps.ktx2` | `toktx --t2 --zcmp --encode uastc --genmipmap kota-mipmaps kota.jpg` | diff --git a/CesiumGltfReader/test/data/ktx2/kota-automipmap.ktx2 b/CesiumGltfReader/test/data/ktx2/kota-automipmap.ktx2 new file mode 100644 index 0000000000000000000000000000000000000000..44a08a447836ad836e8b51b0b0589a02e619c7a7 GIT binary patch literal 49426 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 literal 0 HcmV?d00001 diff --git a/CesiumGltfReader/test/data/ktx2/kota-mipmaps.ktx2 b/CesiumGltfReader/test/data/ktx2/kota-mipmaps.ktx2 new file mode 100644 index 0000000000000000000000000000000000000000..7504b0fa8a5e1a628cb1cb8ac71b76ce5eeb3273 GIT binary patch literal 66125 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%0AsV=2mL9`zTYMN=&Y-E}_ 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 literal 0 HcmV?d00001 From 355111dcf591b34e5916cea69a412e6a1452cf05 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Fri, 7 Jul 2023 15:37:53 +1000 Subject: [PATCH 096/421] Revert "Fix handling of KTX2 images without mipmaps." This reverts commit 930760ea95e0b2d3ecc67f427d28601960eb76a5. --- CHANGES.md | 6 -- CesiumGltfReader/src/GltfReader.cpp | 59 ++++------------- CesiumGltfReader/test/TestGltfReader.cpp | 61 ------------------ CesiumGltfReader/test/data/ktx2/README.md | 9 --- .../test/data/ktx2/kota-automipmap.ktx2 | Bin 49426 -> 0 bytes .../test/data/ktx2/kota-mipmaps.ktx2 | Bin 66125 -> 0 bytes .../test/data/ktx2/kota-onelevel.ktx2 | Bin 49426 -> 0 bytes CesiumGltfReader/test/data/ktx2/kota.jpg | Bin 39887 -> 0 bytes 8 files changed, 14 insertions(+), 121 deletions(-) delete mode 100644 CesiumGltfReader/test/data/ktx2/README.md delete mode 100644 CesiumGltfReader/test/data/ktx2/kota-automipmap.ktx2 delete mode 100644 CesiumGltfReader/test/data/ktx2/kota-mipmaps.ktx2 delete mode 100644 CesiumGltfReader/test/data/ktx2/kota-onelevel.ktx2 delete mode 100644 CesiumGltfReader/test/data/ktx2/kota.jpg diff --git a/CHANGES.md b/CHANGES.md index 72500d100..586b3c9de 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,11 +1,5 @@ # Change Log -### ? - ? - -##### Fixes :wrench: - -- Fixed a bug where `GltfReader::readImage` would always populate `mipPositions` when reading KTX2 images, even when the KTX2 file indicated that it had no mip levels that and they should be created, if necessary, from the base image. As a result, `generateMipMaps` wouldn't generate any mipmaps for the image. - ### v0.25.1 - 2023-07-03 ##### Additions :tada: diff --git a/CesiumGltfReader/src/GltfReader.cpp b/CesiumGltfReader/src/GltfReader.cpp index 564c62aa5..080896bc1 100644 --- a/CesiumGltfReader/src/GltfReader.cpp +++ b/CesiumGltfReader/src/GltfReader.cpp @@ -596,51 +596,20 @@ ImageReaderResult GltfReader::readImage( image.channels = 4; } - // In the KTX2 spec, there's a distinction between "this image has no - // mipmaps, so they should be generated at runtime" and and "this - // image has no mipmaps because it makes no sense to create a mipmap - // for this type of image." It is, confusingly, encoded in the - // `levelCount` property: - // https://registry.khronos.org/KTX/specs/2.0/ktxspec.v2.html#_levelcount - // - // With `levelCount=0`, mipmaps should be generated. With - // `levelCount=1`, mipmaps make no sense. So when `levelCount=0`, we - // want to leave the `mipPositions` array _empty_. With - // `levelCount=1`, we want to populate it with a single mip level. - // - // However, this `levelCount` property is not directly exposed by the - // KTX2 loader API we're using here. Instead, there is a `numLevels` - // property, but it will _never_ have the value 0, because it - // represents the number of levels of actual pixel data we have. When - // the API sees `levelCount=0`, it will assign the value 1 to - // `numLevels`, but it will _also_ set `generateMipmaps` to true. - // - // The API docs say that `numLevels` will always be 1 when - // `generateMipmaps` is true. - // - // So, in summary, when `generateMipmaps=false`, we populate - // `mipPositions` with whatever mip levels the KTX provides and we - // don't generate any more. When it's true, we treat all the image - // data as belonging to a single base-level image and generate mipmaps - // from that if necessary. - if (!pTexture->generateMipmaps) { - // Copy over the positions of each mip within the buffer. - image.mipPositions.resize(pTexture->numLevels); - for (ktx_uint32_t level = 0; level < pTexture->numLevels; ++level) { - ktx_size_t imageOffset; - ktxTexture_GetImageOffset( - ktxTexture(pTexture), - level, - 0, - 0, - &imageOffset); - ktx_size_t imageSize = - ktxTexture_GetImageSize(ktxTexture(pTexture), level); - - image.mipPositions[level] = {imageOffset, imageSize}; - } - } else { - assert(pTexture->numLevels == 1); + // Copy over the positions of each mip within the buffer. + image.mipPositions.resize(pTexture->numLevels); + for (ktx_uint32_t level = 0; level < pTexture->numLevels; ++level) { + ktx_size_t imageOffset; + ktxTexture_GetImageOffset( + ktxTexture(pTexture), + level, + 0, + 0, + &imageOffset); + ktx_size_t imageSize = + ktxTexture_GetImageSize(ktxTexture(pTexture), level); + + image.mipPositions[level] = {imageOffset, imageSize}; } // Copy over the entire buffer, including all mips. diff --git a/CesiumGltfReader/test/TestGltfReader.cpp b/CesiumGltfReader/test/TestGltfReader.cpp index cc18874b3..9f04b5641 100644 --- a/CesiumGltfReader/test/TestGltfReader.cpp +++ b/CesiumGltfReader/test/TestGltfReader.cpp @@ -438,64 +438,3 @@ TEST_CASE("Can apply RTC CENTER if model uses Cesium RTC extension") { std::vector rtcCenter = {6378137.0, 0.0, 0.0}; CHECK(cesiumRTC->center == rtcCenter); } - -TEST_CASE("Can correctly interpret mipmaps in KTX2 files") { - { - // This KTX2 file has a single mip level and no further mip levels should be - // generated. `mipPositions` should reflect this single mip level. - std::filesystem::path ktx2File = CesiumGltfReader_TEST_DATA_DIR; - ktx2File /= "ktx2/kota-onelevel.ktx2"; - std::vector data = readFile(ktx2File.string()); - ImageReaderResult imageResult = - GltfReader::readImage(data, Ktx2TranscodeTargets{}); - REQUIRE(imageResult.image.has_value()); - - const ImageCesium& image = *imageResult.image; - REQUIRE(image.mipPositions.size() == 1); - CHECK(image.mipPositions[0].byteOffset == 0); - CHECK(image.mipPositions[0].byteSize > 0); - CHECK( - image.mipPositions[0].byteSize == - image.width * image.height * image.channels); - CHECK(image.mipPositions[0].byteSize == image.pixelData.size()); - } - - { - // This KTX2 file has only a base image but further mip levels can be - // generated. This image effectively has no mip levels. - std::filesystem::path ktx2File = CesiumGltfReader_TEST_DATA_DIR; - ktx2File /= "ktx2/kota-automipmap.ktx2"; - std::vector data = readFile(ktx2File.string()); - ImageReaderResult imageResult = - GltfReader::readImage(data, Ktx2TranscodeTargets{}); - REQUIRE(imageResult.image.has_value()); - - const ImageCesium& image = *imageResult.image; - REQUIRE(image.mipPositions.size() == 0); - CHECK(image.pixelData.size() > 0); - } - - { - // This KTX2 file has a complete mip chain. - std::filesystem::path ktx2File = CesiumGltfReader_TEST_DATA_DIR; - ktx2File /= "ktx2/kota-mipmaps.ktx2"; - std::vector data = readFile(ktx2File.string()); - ImageReaderResult imageResult = - GltfReader::readImage(data, Ktx2TranscodeTargets{}); - REQUIRE(imageResult.image.has_value()); - - const ImageCesium& image = *imageResult.image; - REQUIRE(image.mipPositions.size() == 9); - CHECK(image.mipPositions[0].byteSize > 0); - CHECK( - image.mipPositions[0].byteSize == - image.width * image.height * image.channels); - CHECK(image.mipPositions[0].byteSize < image.pixelData.size()); - - size_t smallerThan = image.mipPositions[0].byteSize; - for (size_t i = 1; i < image.mipPositions.size(); ++i) { - CHECK(image.mipPositions[i].byteSize < smallerThan); - smallerThan = image.mipPositions[i].byteSize; - } - } -} diff --git a/CesiumGltfReader/test/data/ktx2/README.md b/CesiumGltfReader/test/data/ktx2/README.md deleted file mode 100644 index d06b718a5..000000000 --- a/CesiumGltfReader/test/data/ktx2/README.md +++ /dev/null @@ -1,9 +0,0 @@ -The original image, kota.jpg, is a photo taken by Kevin Ring and licensed under the same Apache 2.0 terms as the rest of cesium-native. - -The other images were created using the `toktx` tool included in https://github.com/KhronosGroup/KTX-Software v4.2.0~11. They were created with the following commands: - -| *Filename* | *Command* | -|------------|-----------| -| `kota-automipmap.ktx2` | `toktx --t2 --zcmp --encode uastc --automipmap kota-automipmap kota.jpg` | -| `kota-onelevel.ktx2` | `toktx --t2 --zcmp --encode uastc kota-onelevel kota.jpg` | -| `kota-mipmaps.ktx2` | `toktx --t2 --zcmp --encode uastc --genmipmap kota-mipmaps kota.jpg` | diff --git a/CesiumGltfReader/test/data/ktx2/kota-automipmap.ktx2 b/CesiumGltfReader/test/data/ktx2/kota-automipmap.ktx2 deleted file mode 100644 index 44a08a447836ad836e8b51b0b0589a02e619c7a7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 49426 zcmZ6y2UJsA&^CM$I<}AiqIfld0Aiyz_1+swAOVqH14@8|F1-s9dQ(d12pAxA!6;G{ z1QqE>6Co6%q5=X&qz1^B`@Qe_*1!IpwUg{~CMWwOduE<_W_GQCxiwr-zWtcsX+eO~ zK>z#n>c7|jZU6Te=RKIyT{-;{r)P0`0H^=|8~^_b3%>f_GynVg-)H`7VBkDZ0RjVD zT>mrvpXYo3Z@}g>C}+f(mbaf9;Z6WPz|H#(;OERa_TM!)$AkUc0tkLUfVW3L2s}vn zisBXdNCgGB9Ng2*@&6rCh*aQw)Bi`_%*hzdR?-?+!4>A&k;Jr#ulU}d9cp9660Q>2Y(xSuTmYbJVBZC}LZR5H7R2JJZKLGURL zW$aU;TUc+Y2jEK;z5FpI;1QdzjK5maNmD!TkD|1;$r(2<{Ci)5 zGk+>(A$8y%7e44DM3p$DFzi`Gi+`^0J<8X~_o`+k;b6{*Rdru)Bu1LJpiOlx7)gi1 zO#zwa@h+a}$D8TPIGDEqcSfL~`JPG5lNN$C!nQps^m+DRHI?6jIuGjIzca-R|rAcAPVdgkCp2i9$v)4V?8t%f|fjG(PS+^P>l-+|l61@-aUEHgVtlJ5_DyX;?AH_)jE&7k|Gc&-J&l5JJH^w_~YW3fj1dAf^?pR%O5q_h={Z5L04@-} zc47VK>d^xV3~)${QB^-Fqykt#7QqxVs268CN7oj9t`Dvp-KH!(q5X)_*zxzq^Gkr0 z0RVeb}X4B%9*!*z0PS$mg)RdST`@dl z0W<-P0`ytUAgm}yU*tLFEF~|cB^-3D;mm(yE(AC!uV9uGRRI@^hM$izMBQ)@l>kxb ziT~-3*!#c#{2j#~CB_^Fq^+ZoAz+w-5bzsrLK8}q;E(Zg1wlbDrv^n=h4IAT-IN30 zVE9w+ZS`khGiotJJ*~~HfZ(>TM?C?sViu{eo9FTQ-?GvREE+oLSyJapx&;&*O4`{S zhxv*4*EiqU{Q_OOn^D-fcDS}ij-RYD9=nFkR2~>j54-@-v!%9Lv;;3VZtbzw+1Sm( zXCNtIjy`)j{yK-M&U#qbta${&8FTCCTJWOfa>%6TcEeE;|Jf`3wR8ReGf@;?6)n>O zmKTCcyc;}Z4$22w$2>^{!9ibLdiCO&4{s_(A8F-Md-b;W2mARt3ws&+>ld>~gvvUt zYs}hZ2;~{)gc&21H9_U)V@I4p{X$1FyUq!*MYgM5T$O}jWF`GAykR+_zx>Q@k0*fR zD*xw3eg%YLyD89A->icpmzl=EzouVTmAGo*Man1VN)(z~Bt7NN=L_x^4g_Fi8OwKa zy5*X$p@XPCKY(1SJ6Q(Iy^!sP*1QS}1~~SlwMKqlODn?!T~S-X@-00aPnpnJ0hHjv ziiO(yjM?Pl5ue`fi%!Vz2Jc7E5n1o#%dR{|!&mVgl=aVH@%h7o|rmy>>~jj`45aINmrf z(0?+kA?_swMKLe{!{Rshtm-j$VK&0dE>>J4_EfCo+K1w8z1Inp zu4lqI=K6ugYtcg_I%y@G>?l*tt;{X_jZ(~>_ePU*#`+azQ!Cy+`zPaEmKQ*Op0!N^{{$@)Waby$F7Jkb2nSWaY*1|+?fcGK}F7<)YouPp5C|K=N zOd9}_hbiQ_(AOC_xd28=y9DT+sLh=m#`A;@v+Kwq<`mnNDFP+x!u&5s6!+($1X^jPofNW!Ao zL!sq!gN#n&%p^)`X%68-V=6ch>lqk;J^$-Y;pH%+mG(~R8S}dUuKA9io!gtyB=&N{ ziaQ!Y*Ii%Lm$@|^K@WVF)n_72R+_uy>y7zF$Po1nyMGa&&D=a`+FAl=qob^qwRBnI zrBk+<=;R9Ye9ZcM1Kjy&!iltxe&)a+ykU?yS% zhMX^D#)FoUlG)?1qREqwErd0(+~*Y{6aj{bL2jnXTo*!ETRlFwbb*n1mh^ejW(jQ` zW$70hn8p|+rm{Zj<21fYHc1db1jgqTU#MhLS)zG-&7SBoFYFY2qVjlZ1>wGCdx(+& zz;GkKxY4Gp=iIa_|O^fMnq;(PJk797(Q7>M!RTv{MuTp<``Dj92*B0JH@x z=I-8u4<58o5~}^dADsu!DthpZfF=N<0l$wsYh+2hT$t$WS|H9)64x`|niR$&{%DU% zFi{_wU5M?<2jJ+0n^;BYWa^V-y+#b0j?bCKqH$_=87t}DH&1gI#!mm2gvm_=Zb zM0;gvjbm#z4T3eIdwVe0Wno$U`=!WCWU%`Z0-&7w1wtJYd+*$kvgzs1(%U6xp*Q2teW!TpgN2`hnd~d}Yq}LzO zKZ4JcdHO2m8Un$kqnnVNH^MOS4$eSIdoz3r)aHNR)LoCM3h4O&<|th>w>;q&kz0639PXa6itrjFQj+mzd?$S`f?fU^hs z4|$Z@RFq7CeTGefA3^)YJ5QSgjXHtKJ!Z|qa}%zJueJ~u0!I5bkuk$h`xFxj6Ok9m zPHtY$^EH+G)kVH;ftO08j_h~P=`Cf7mXR&*z**>}lBI?vvdmoirL%RnhyE@i-|LbD z$dU`i=VgDd?nJJ#vu@u4#w*8uVQ;)J8i}~f+UdFs_|8r#bj3tYXmOkA2j34W5wDV_ zTKFYmxqNg23PRT-VNdw9e(|iaSCofJa?YOR#9su8s4hQ#pZcq{ym5briKTtAYx^SJ zah=M}x|?wXk$+NtH)h)hEn@7As4$nL^-Vjt=lEC7WG3xBh;kWtp$4_74VyKs2=nBg26ZV{UxvQ%fg1teP+7L%C#B``B*pw{0u3NCl@pGxh>zh#bKKrVA-*(}US@ zFX_sccRu~|5|e@{;V!YLs4WyJzt-RQGues_dM+wqsz#>X?)*Z|D5q|y+Sp9l0E`*X zo1>#gBmSZyb5*BLZsSeO^zKf?umlyQoa+ZVdwuO-y;tA7>f`lC=hht#A2L?>xsLyc z^5hpIrmP*T9r{kXid=XVWvl+mwQ_!q%|`q|5z@haLb2??dseuOq3}csMsheI z)XGI^SHziD{-g;LwPdzvGB<$`QB=P-!Jw=?;*o#RcMkeHMDW zJTEI-4WPWaGHi^%$s=<8TDQaEb0~2Ys3U#+wgNiWv`cgdN(^JS^>3EjYPQ)4K@B38 zxM0mC>zgk$7k!FzBdV@j0gN$XdeN%Tc*FFcj=aAkPBTwG5h+?4VT9L&A}it_v6va> z7NzvswjpNDA=x(X(#9#pFpodAYYkz|=d1K109@mhc>h(_SrZRqQ8a|z9!|a@er9uN z&fbzo6lI%TFD|+Mk9K=K(TIoYL3J;8AAeqPHtX8`EzP2A8PbpyKqim;lUV;?3*C?W z{A$z-pb3nLEHbSnxXeosGlsh5u(Gu$c4N`Ogqf4i&9F8cc?o%Y-ybhz=S7U7j{FOV zXL)O+_X9pxM6(?_@+1brS%68*ZBQrr>QU%ItzCxQ| zG@A3EOtOjJ+zW6#UZAU%vbP-~nCjG0boE_l2~HoXN^bmrU3*xj(Eo2ca+6tM1t9 zivR@*3v`XSlXns>t7VvL)C-du)mH3NG}@?edysA!0?@`1mLi8g-?_1Qio$PUUBg6u zdDfTg>e}ScRGHJ?xDV?(Gl7gCVl%_muUYSe0Ej`Wal5Zbx$cffa~X#;HU%|uMZdyH z_Wi-ueBXTKfdPoS^RPbh#EFAV-uw)1j{k}u_0`q*Vy9|Dd^nvqR~U1Qcw6@ZL72zV zqBgr>jfoPMzgn{>%j+1NH<-eHj3a$Wv%h(jYS*LGqvYhK#CY{V%I$%h>n)IrWwOU@FB#{Ca31%#H!rC0DfHBD%UaHs$ij}CR{6>NLE^C#B9%96)=hYXt9C;aAawF zct+51bDegc&H_%p~cfK&7!yU@no|a7`U9rhcSL%ktU^rVKzlrlBrDQMr`)Z^+gnAn z+$b)le&?dpzeUYjpo7%e#J*O%87#2-cyoX>D9^REV1E@4OZLwIH##eVb6p2(h7ZVX zi`JioC5FD$HX`5e-2=!)w~n{{BAmIHoT)y*j?P>2Em~iG_Wa_)L5c#E zm|jWdgIduQ=e#NqxC1 zv{5JW*vna8zCg*OU~h9#dPcF1oKWrq^Ee1v)Y79J-`^P}|JPr@UocOey3~RN9FN@7 zV;O`f**=|K-E6B~7?tNN<4V#Nex*F8txW}B$-vdR@J(3|mN82wr>1}`;(Y@@mA{3^ z=li$jH|PGTS8j{FD+fC_VV2Gbzhc;5ar`rZNtqYwIbWv zAB5zg=CsVUN3pe$l~2|wYk4UHvaqTXsAcDv&537~^3kOcfVjw~)?b9h#M;C+c^O%X z+06do4>C>#Xa^bJeW!RQV@Cf!lYFHI`w@0_8sI6fL54!p=5s1XzS_*&sTIeMxnpc@pXeL z3D+;O%`?eMMwnbHH~R4DL`b~ptnm(Xk@lLaYUzQyYj@vh5Ci>*K(-N@s2=ynX0q7N zKK{Z0h#Rtf_FotOdB4tB|GKt(z_AxT8U;=QmIU1x=ywRKi{BP0oAX&r9@gUVGPz-f zfhw$WeNg3UCn>3A+G5ONYL+d!W>zOcg%YarR^|>Fug0m?58M*Dg_47Z+ZR?nNV;jm zYxj-nRROpL{XhHhP5oAnY+0(cq#`k>Vj2gM)3fZCb(wwHb0;N&c?yO>7&YFEuo2zp zFlD6K1LW?rvIcAU8|wYYbhaG`Aj9T-RJ3Eauo?017a1sk+1;S))OX7mk0}dl8?HKJ z_OZJBr_V@82FmxP?t8qWb9u$_JU-Ob7q#%Hb}z1`^c%s-`cC=!7aG3|=+sSN!I07J z)6LMw+*ARoK*iy>kg#*>=&Kj98>-XD1Pji3;|=>R`ReMil?r6)CC+*#FZ@zG@wQDi zgV%&{`w~D)y3J?qn#y&>8AViVt77BGWBXp`%fNz*_BG~sfJuj)Tk|?aeDH8Dy9N!h zK#s1ddJ?q82geb&ecPKJU8JGN z$Ees-0oohZOtA@Z{j|ZD!PKQ!zfAKAF_N*oWala6zjnqd1jL7tqB{kin3$K0lToVL@#snydqM`t>2f}n_8_FJPtz2v^C#qL}$k9E;+gbuu^H4ttYRq z3(PDAqh(G2wDdcuTdd=c`H-Sdgtm>vX|P}DzcvLtM4~(uN8<7VVNECAyq##7HChpl zxz0+~?0nM$rX+a8Ht_rY}<+O{( z|9Z(lQ$Zp`A`3qbt1=-UzPb{n|IB`e_zsqhl!hO3v&m@=XKaq~1HL9W$G(KX_nHzW zN@Rn*dWyuWifM9lqV^{Rk`^KiK}q`Vvs5g%YtB#M|9WA^fEM+<@l-Khz;Ym}^I6?H zIwk7m7qJHve)+2S_LnscRtBWUC+>B2u&+d53?OJOfEM-2$NIr?UPpUof7Hw~mI6xr z3AX-6*ld0g;#h52gKLAF@(!rfh+DWL#B6QBL>`X|94xYGgf4R8#y&)FI9-y$x}0%s&18i4argyZp1t1=g%VJSx1e{O*_z6fU9C(z4#c zz=+3D!^U-xrd-6Z>itw2zv-CYbcuohUQ5fFk+EKHlSe= zPJGoY9@p4bAy~N}^vK!X$6az$pYjhh{+RET0GI;(Ik5qzX8gU%5M+`h_jE8 zQgQ!6VzpEO_c-~rty%H?Kj;k6hJS2L0jALV2Y$~>e2hYPbsnFb?^JN~=PB$O{itH; zzs~hS$kex>gZJ*-NE?^1fvN@s#bTPA%gOxvb`&C9vO3o7Ul&BC-0#AE?svjP#b5~kd{wyNr z5*2Gz$ybJKyYVR!N%y*tzqzby}pBeOPV_t zQ7{s;SGXy3(ZSl}PPAtw!*BMq9q{t$eVHJoBi@J3d-L_Jzff<6%-fRma^8tKuP+zKB!6L za8pi*N;4J?l=f74*v7>KZ-K8N*0fPj28v0gp8Z)B#c z-ZG*v7u<@M@^j>Tj>aVHbcb_&jWgE0u%yFnA!1W%|9hOI`cWm8lMi*qMqQY?2hh5# zDo&N+1#JUh4Cy*m3yT_^nURl{xvI z?}pS!SPq6uPrD(wIn-A?Br~-7L@%F{zs}a&+jhKT(ipb&Xe(DhkSgo%02f&H6}o^_ z;nVXN?nC712u{A`QSO8DUv!D>LWr*-6ZH)Jg;Zw;1El<}e^v;@8cHS?l^N~p3!|~I zM^7>WfWpjgMBqMJ=VUb60!y$nGt0jH%SXs0OVgNJY21hlz{Pr*-#kO>A%qLH+ty?( zvtZsYhZD_HF_VwbPz%vc9PDE3+EKy{J1TLH)^O6C1#=MB5zEx^THah=T)qV`lE3kQ z5TeYmO=$=CS9lAvAC;}_%5M?!F=R(U9xBWjYnt#WDmD(S)@;#9=$vJdMWZid$3qV0 zjCC4-8WGAVXv(uud&eR;;x|D%g@=)T5^Ui!z%A|MfKHGaMzCOyyRLBL^Xt(ZVV)W$ z#!5>9eM$nxT`9JCeKwqi-N+GA5n~MZNp4!H9Pq%(6pjju$16(Z=Tw$^cyQwNG{d5$ zQMyGxu#JA2R|R{1=gi=_{_xrMf4%i*1pye+D6}%E?$(HU!?Un34xY$fwLiVtPX|A8 zqqjy5@0NJ^>|KQ*u4kUEN zMoUA=Iy>tzQYnzl0A|VsY9s`2f-qW-XAam*{?Al_8c9JoEFtM|fz4)+93Gu+a}xIy zu5q2OU3kGn(c$_{GB~XqLU3*2!aOA^#5J_^Vq3h@Uic4BxQqd5{*K)Im9?8e&atY_ z96W&Ekqk7x3aAu!QMb@?9%c}0k_BqCkUkv~Rm=PFa%Q4#_3ib|QBJ!aJ2nUT z&Bx@abejtuW<2D30+UpC`L$qap`()DfuC*9IsNwCdkfw{YgWOG2cvAhm;UihC_)k4 zQhi|B&wzz=c`hBJYbLm^zoge<8OZnj_B&C7suJy@Ndj+5U~eLqZ;4R`YW_Qdt0|QjF5ksmj z8xrco`SzzBkA^?4`*x2!-?`3ynVcS!Gf# zl`PyiL9^%BCvg+R-}axb`^gvNr?5Jhz1O16T$7b ziyXeJ47V}<_vBT94AGY&7cO)7l`>C>?Dz*yKm^M+79oe6do4xjx(F0u62Dbi;||aY zzJt_)`gux2;&(hM0e{A*eg@=}7~CFQW)qi`xXy}}KBp@d9h=dHCSWZL7nyw>=}N}B z+7Kwu1?yYEY4l8@nyy$TaHrD4*b;^QW=8d9z8yB>=({kV=?~6)U<*f&>J}*SV7M>T zlKcQhdIIzl1o}QXp*4JNm-`KjdO7gZZSE|Das1Mj+A)A>CihzEt{yKcA*b)KXc0JIU~zvjTA0mr2$5 zo*bK|8(aBpho>2sfvA}2x#*L|LC$E2g@+9NX3H;=VjCrTaeLZ$OAu{<{r2~$)rtVI z;p6jG%`j$Pwa!>yiD?9!sN7PwpT>ZO7+D4lpdY%$Ef8WuA>=Xwee4S68@9hZzS7GP zpi|ehYNsoOMJx%ne;Rge3$tAO1XR{;$gmt(<^}9soRasQPm7BM)C!H_A~RZ*hzUjI)F}Llz5b)mJQ;JBKmpoa&reR zMm%4!8-s9`wwCr%j8qCIrCfvbsN?T`M_3ARYjQJQ5u5}{odnK+!;ow4hxJ{rKG$iK zN?pAJ&qoiYEYyp~X#L~Uqp@VYhuXEsa?v40<(`)<_@aT?Rer7%h*G9S!I1))aDj?_ z-yKsb+?;~ia>YbTo(FcgpYq7dfDr?{_$O&s<;&K6*NoRuL*uXrbAO!=_d?ladcsdH z17>8;`+>h+ZKKy2B)02AsbeC5E9W!8zHO43$%VAQhJm%g?0hVuy-+S~5Hc(*7j z#PYB%sYWsCLPQvVFWd?mlkkYS4pjw@gsl(zp5}xcC)@p`96$)P^1=%S^wZ*%Wt8{) zdgyRSFbED4xomu9DtYT|XD8aQHkgA)q%&%##~A`r({#Po7i6 zy)1FR2Jg2eXZdYR(+94~iY3gCy@ms3F+I{FW@e328S!C~kN*N_$#TMkfSX8tw9s?- zHZA}^F2t8lHQ$kvZ_ztU2}@!~$fIR~c=_(2Z6Kk1gZC)bX4$Giu6oKmiq)EX7E_Ee z$e@%@{K@p9Kuo9DY6Nq<49uX~Eg3-`B-HL(^!S==yn0e(-zVg!YSLMiG%HU2()hS; z-%iSjl#@CAwp{@?0R1v5@*B`E8)h92%)*+Fja?Wv`Jidc+qHTS4&c}p=rB_2WX zP^&9;J!ty7aZg9ZVyvhx2?qilPB4}CjW>@)1z}ej)87l<=H!Ee!DycWoui15s@A=X zJbDb_@V-T5sz*WQpd*o}WJcy)yKR`)Cc9B_x<3%h;UkK^PlS)YFkCK=e%5XXgbOv) z-DkWL%j_sWZE;$_Tbx|KDM4E+`Nv-=$Thd;F-$UG{i4!ronb*P_Wde{&z}kJzBo6K zlD|+f!mB8(i6?I{KIk`XEBb;=2WNU&6hUb22S4E^b65G<{-O0kCO$XzyKTVKO>=jP z;iEs-7zMmZM_17XD$%MA4J|QA6tgM6Y{kYLhor{Gjfc7Y%>Ep3E)kL-uZxZwPOkMB ze?B{wH^2PwpdX{*5 zVm|)F8}@tl=e3n}@pSS0|MWZkLB;*teBMTJ67C43`xL%+u-7wI#fhlUJ9$|w`H~nw z*7+X1Wd4!Td*H?2;mD7C2mh$G=ft9u3NH!uoWkb~<>g|X#kg@K4}XZ(DZCg|DB8qP zAq+ma>2!Pqt5Ef;v4tyOOSLS6eY^XQ=s&#z;Pk0WTDyuj)D4u zmn}uwW{wbcg14I6a3tQLYx<`Mb}AMG5t7JT$}3Ap4j!<+1$GDDx9CWO$wwHN4yYQ9 zcRt4ZU>pE(^19C~^XGzqvBeglhKOaF)ZF1SnC{_)s42W1`m?|^U84xAwKwzkHMz5r z*>kY+D!$4TpB(J*6zQ9J!)a5AY`8nuEXMUtr~>@u4&G3uM*F4^6gvt9Lh=?m}`o z`TcZVe{O33q2V@Fb!Iw@HOzr zXrDBFZIbk^p|v*Q9V3M}>A^5s;nNCQPwq`hpdBfD6~1_XS)pK{u%X(`qHA>Iv#1e^ z*Q8N+-GK$%|2wanOc&If~AiHk5B!a zU!OhPHzTP)gunmadOqkl`yN2g&;r!71Q2gNeh~Uz+U`OpjYXf)J%t|>qY~Zx>+0!T zb9fQXICW-zPcleiE}XO=&++FJ%ydPL;~tbkCw=HU({J-7(cE~MSb=DQNzSQ%)qvBR zkwd~^wyvD|JKX)N$KW>4oUzNhrU<}{v~vAS6#Tz>!sQK=0Vm&8=qcZQN)oa{V28J> z>57vq=TC9$PhQY&*jm_4IE&<6ZQLVUX)S6_v~M~j7RFSjDd=k~0v|2rEI-;#gvV%| zk=K{4ycC?@2B|=d!{EXd>E@j46K$^A-wgv42BxAtSJP&0nyN!CD*P)%8H>za3?pX+ z{M)ORpmiKfv2ik{&x!h7c9wafw^>*~rxdRww>Lm>(ze7C<*n>-a)i08LiyTI;A1n( zx19Py5#>_-wzB%mZ0h2S*9i{3W`T=$v;U}J_U{&GaMClv3!edphlg(Si<-;($^aSZ zpp03}+d^-s3Eeyh)#NNK@!aMnu0vZVXqfwSU5oV5aGp^muou z$Rmt(3TE58i82nKp0>xa~sYda@R3`@GYf7n6aNOGx2bO8Zv1<|8Tw`|Dqc& zqrZgb>Dx0VY4VuTdwVZ!DW_&8oJ0DR>_&`-V_xb>Q?NGC=AOrd?Z6-1Saj1WrU^0U z!(G$L#3L0N>jgnguz2a~r0{_)!<848l<1uLc@eK`U4#z>0#9yBi<*dF zWM?Y3&G#(;nqZA(S)YA0n%7)6CE&0Cfs-`kL?O6#^&g!|`&v=8llJC=3xW$N z8Mj6T7!xrtCy1GZi|HQTCWc)5360ZG6oLl^|K!w%ti*ZD#^Kbo!EY)n4hqTDnUTf! zRSZ`en5l`+1p&&q^y59U`ZmT{2PThPE~L%EgC51ON-sgs(8}b!@bKov=7~RjMW!NO z&mErI6vdlKZl2fzJZ}lTRA2Adf5;H%l^w6$RYg>jr9LLM?J*08=*Z{%dLNcv@K+KC)gG1a=9fgh(U~i{wN}e`C0@$^;1T~#s0v^$v5-VV zX%p0wWvs(k&V4#TfFc8HMzv1%{qf+3ON&LKW;}-v(+=&nzx;@8-e%KO)P(OkCNK6t zUW^;=Qqi)?S{Kj2eS+w_Q4(N7@1D)7c(my3LtMpOrqx<9> z;)eESzg5alkkjMzD?R0J4p+%;5wza1T)=Z zwo=iCS(BKXqtfFF>0iF zY)}g?li#?qf@0%x|LNQOjPQ^kOPNPX+qC|)#4mFq-QiYRg!5b_0Za^gBKB^Yjv!P7 zc`tnWkO_4aRf#m~E_$)C>Y zV2GAmgbn3dlW`6|KXx{ww5#BE=08j$bz}4XN~pQnKKG^wiP4S2+&O@}cH2IRbkhsYQy7^4O-f$Ad)@)_Z}i8nST12dufe^s_ovLb7Z`P!Y%BU1I!)XaHcF}yj*N=IxC5-TPb zd=C%@tyU^_^CKEpIt%Zu2eI&{ezFk)Gm}n_$2!K|qbsb5jMX4b$@kk7ys7Od?YdxU!UT||wc1=WWLCclR zf+WPr^D8D=*K09~80iP6O7dZ);sr26#8PItL5{GkF9#2c+sJndN(SsaS=~EaKP*hg z-G^$aBNF$0S2z_y8936}<0t!NDc0Yz_F}`tp5jQ+Q5=82SYB+3YCy9ekWl`=F%K49 zU(D`~Q0GuHIMO->*Dw3UvpFyPq5DH=7!s7ZSgv5Gw9;|?hPwm6&>YuP^%rjzb>?|p zl*gW-^<6oB(z{?%8qRu&?-PwNBj@B_lm2*}6aV={bSeT&Q&P?|M!sWOp29--!x!AN zJ{!?zqaS^7dqDwu)jsXMCikqqTTaI(@)7uItXYp?xvC#Nr@mS2~ z73Uy=4-H`t54KaV;gGB)8-JJJp!j|LARqkDxinteXx^H29X(}kMJ3-%*WTZgHiO>jl89D@f*2VtM(D_+!m6%Xx)xju zN;DH#T=%G*N$ct9cI;kA1p~YF!T0R~ipPqxePG~jIO7ZeIAokdMeN8#T~H1RB4viW z`K~|H4DFTdxgFcUMVrw|wySbRW70}|~Ps$87{d83Hl>|$nRAJ)9Ha%OY*lRRb z7%;}}7A;QX7m9eCSW-Ut<`V8;L8)$PFq1m*qpyALs%(E_*2I(_hi{1-yu4oULUy^) zHug~)M)|oD=@wLP%T|+W z7@3-VGB`_7HW_*0+DA@4CrRwx^Dr}uIuUuIOhtV@ten@i)OhCl<@u9uKf7@F4oAm0 zqrIpLT){5rrRpE+zWq?@@tIe%){>~_9KhT{5q!0LsRzY2?wniiAljxP=7RksPPSJ% zSIJX-m)~*nAwAmB;S~cRZ1*h68ASb$w}7CX8$B-e~65^{`v)n51@MUr~!0BAp>k<_4M?QYy2aEq~@y3Q)zD#Cl;iNjp5<0$gj!6qUD{?8=IBguC z*F1jpx#kpn*L0s1Gu#U0`1eQmHt#hBD+Y(hP$@#3{YfaHD5~)m#E%jsg;$T4Jbxb0 z1=mOqCmVoLM(%ZvuQx7NFRcsgc7ga zkux&OpHhIFcLng+4dMOiERqOsc4+g9g;Yx4p~aiILO;qvd0kjS@&OIIJxq6WPSZ=Oh7jfgV4R!0Xxy1nqr@KO=*9^4lG&><+`Vd&BD7 z60|0H@~cadTx*SdsVUVOjEW%lb?AU{PoU@ey?O6CTQlSmWjQP zg1_|+{dsaF4f-TbQ{$Gxk)?=){fEuG3S<7}gvcAp`tMq;mkk=Y@YrxH>G59*GTge* zpDCSlO#O_br03^MNUcs^2l5skGYtG=iGD`9k-Uoyy~}~`n6?9eXc(WUk8-v_jlqZ- zvp+%G+4Y1oe7nlGQaj28CPeyhRX@9wKN=c9HRkZ6lragL;Cy-hWF$B55l(##*(*y@ zgm~lT!!u)p#6DH>bGjBfUGxLKUl<70^am(?!&)+C=lEn=Ai2ItJ)Nu=nQJmCBgWqA z?hU*IkTE^CVI17rk>1X?XGN@VWRst5BLCi;eqm^mV`>>A3LtReurGZ%r6La6F-p^^ zESOdh)XC{hs5(9S$nv@`KyKzWSZ;ag_F~(%f!jUr#odwZlpaVOh?aVhtc|sM!wz2p{gSX0{i!k>u|77Z@U)1 zeYD@JK}&D|X_~WVs zD_b9xKQJ<1%k{0SEw{09@cHk09J1I&cW85T06|cw0BswznsYro6c_r1a8aeM}U+~@EE`{!XF8}TuLqaO%T+>DJnwl ze6$2VYQOZb;vlYuPSP_|?GclFxW-;zTkEwm0C|+$PUAp+ov4%de)9nX%CQUHoek_0 z(MX9-j{ooKHFV}rcHlP-e!ljdy}}NM#WF>8)hYbHbkIO7cPuRTMC@I)w$O)O2Wqu6 z4S>cEess0N<$eu2VAio3z$De?M2V_Irbn_UJD~> z{?T;WzuiAroSZpxM*lP(P}Gr56iL;=fl5;CdSsL|*?kOkprHnPi z?<>E>Nh70*{pTqTd(JGI3hFZKiKyST5;=p+_I2jYdw!1oKg;=Ud$I>CkI~9G$iB}I zI6d`cH8&=$GoePlQN@hR^8smfdSc&jqbcXVJ*Wx;H`BuB#*;NA!W9?H4po?=!#}1> zvzo8tg;7a5BMrt&B*k93*3SdCAfar8hl zQ|U=w3Kabe(`R!wj_dO;SnUrTyl5G>jrp8xddx57d0V=L@R2f_v) z6=<;cz3;1Y9wqe~x3;v-9(47~zVDu#%KR5rllmfc!3{CWxNoBHm%?qRdTYu~MwxK~wc}w5oqS4AN0u3phQsLwxz5COZ(1V5=9t#nyC}Zq{|`&&9n{qJz5RqLc-25CDi%T&l`CDuMhg(IKte}>(2F89AiXDm z0wE+c0YeE@F-WhX^eP~|-J?>Z8j%{xd%nMUCx2y_VKQgV-h1t5eV%94t(2C%EIZWM zGK9!>n^y~3rP5ApH5&IXSM&mCuejlnNl&i5)R!v({$j(V<~IRGF%=;1SzkZ|@8`8N zcefh}<$632tiJ2M05E7Wn7K7P?D{f8kIQCmfuUO~LHfAT)v3e^0`ftP6k<4Up+5&vrevPyO0U+ zIyvq}%bP-ZAZoB&wOzmf4xA-zrz*1Im8LD4Q5IqE+s(lH{HkP)(yG#`C^PKwBhbH( zc;W*_NrW7SY_ZhMHG`ka$D7AzzeVB5zV)!h}bhZkK(fXtA@_j9OhafrN#s z=4ULR6K?OQRm&`@k6FMo6*Jmze>@0ydtculMx7#qU2rV|zy>jf)(bQVT7*qS2!Q>< zPy#WtAO_OPBlVi3_<}lxkxz+sMQ%5cprGPY+rq4@yd<`XbHH}eZ`i|kXN$+ zRt5_QyUh6C_h-k}y6#!_#c%}dLtG8$_r^{A9r|MkK7Z***-IHXd*+rT)m;_wP1^Z1YkBtI#6;8No2{&nP z1cE&Ljj7(Q2B}Gb{Og{<5Jud#M}Z3(=QqIq4K8A@NaZyp0k!5H2@SmD6JE}9%-neU zXU%2*T&BOr{yP2hw8wCJJKnq>2QySNdl%C168wLwv(6M1sWd^ym_`T$;Q0iz>B_{$ zt*vit5Z>@HxO$A|ap&7!okh}?V@|>tsF+Vl^Fm&-zDNzz3m1v5}X_ z7Y5%S&fZAB0Y$@TRt*cYu5YQji85Eq}(0!2A8|zauH;dC2a(p(27H1VGhO&}vWA zo!h6mECvmarotQvS2GI?-|g-H($`eg1bP;AX>PA^r++>9g2b|7Nr=Z0O5WoK4~O#9 z<$g(mehH2m&kLEs&YQGuT^Dr-N&NNa%Iyf;uaEai_QcI0vS?S6F&F-5Q?U;TEg)4o zMZz-v*l$gqLYmJD>~PHMV}dtt)yZ`l+XU)dD&GS)24gD9$~F2y3sYl56^rfLLSJsY9q z#}859(cy{bmDL#g_kZ<+Ih-KSWZG;>SM3o6^9fFXOCJI`h8zohm}9vJuKzbkq#Cj% zv^xd%4ArIp?Mr@{LTH1#Gu^MW{eXpvz0bV1sg6{P3#)NU` zBt@489ItJW8;n4Hlc)&mO7jmmtUNBbI^qlgS`d}z>VsNuLt~7lGY)4gC>O0RPu5X6 z*#tDpBAbyJqzwLxx-nP?xh`V^)u|&CE+j4U`MhY}T-9-Xy@M7!jN{LIjB;0!9m?-n znJmkl!ii8pi@P60+Wz2@xA#ll4 zE1XS9gU)Sj65f(!U=VTCTY4+CGlTThA5}XW&r+{noIk<#6Q2LCk#BMm4*7+*TW5eD ztWPXpRo=Xj3aT2f@r3RH3zD{g^SL!mOZ&E4Y5o)b)QlVrasM_krvg&8swwqm3wrqV zV9S)XV!pQQZsy`F$OAo~ww=S?GCS!T~TUw@W0=0Zw_c>$^18pgP9rYNjxhGycPX{Bl! z)*iz7g(`iY_Jj3)j(V7~A%y;Q%7CPJE?1mK%-u*|o(T-yHz~hpK^G-EIrN##=>Vu7 z2{|u97M3$tw%CC3la99L^{Wj99>B}nhJmiqXmyv6p5PFyEQ0~a3 zyxcjMr{7_J5j^))ch?Yk0XbRVMNQC#z@yH*^0Fmvzuc5u^xQRq1Q12#^(qJS;ZfLa z_I!w@Mbz_G#WU?mm&s4kj3yPA;U7GQ85EW3TxaUt|C~&W4u&$%&CUwI4>Q1x$w5OZFTWz z@7MzR?JlVAiCWj&zp$j0Rnj|8S`(tL)$kdJL<+Zx?aB^$EE!xY7m@A6u6Q$T_o&#D zB!6mp>?xAMs&%~P5%$f%KMxz&9uN*qtNc_=F@XDo{0C0ux2#~ki6qYp5yq#R!pa~| z$HF2k;uulTMAfMW^MS4v!c<{4ym=Z+3?1Jegq-&()wAud_iwLp0WAV+saY~AnMd|r z>UdR02El~k(!bgl%ubNZxLM{j47JNQsCec&_bw=^7!C4Nup84=-+K>Z#R<1kZ%qdY zg-RSN%+1YNrm2l%i)?*mb-uVg_!D*t=T;uZjojfBe1vD_D02 z2kYWA-6O<*^*Ek0HzCk+NCjjRwg(8gZ}?iqN!*dC+h2+!Xj0J*DPixd5O46{pX2#$ z8g|sw1Bul%LX{N>ksMNTv17bQLxUrlIn^9%P%ygfU^Z$f*cm9(chKk&qurM8VqkB_ zreWv1OsL0p#S>^Yd|C<+3HU3*uQ^5!2fhtQ$p?327{+I11L#`NrP#nJ!rHtTU*~~( z3;r9&kH{&8*ho5xiF~mP=l`wnXw_mtd{R))%Ec2hEYB01zuN-pZF!$6Xf6=I>{`DM zj-*+tlPqM)dQTW5dgiUMS1age%Hy~v_80>2Sr7WS87Q)B^9mcXx*1c4w!aZuGCUB; zS@Z#NqofCqONHS53DB~v=o9DozE+6zPWy`N(FMy+`32iGhjmf}3;oHd^zE0ElCDR3{Pc;D zk%ouec@>qYQxG9Itmj{TUzkBUZQn>!KM#m?d^p_qlxY<4gMGXyp9#->vAqEpB!l(G zU$zKW$Z-D9Xqk8KorfzqS|yt{5K^F4nwR3VUX^RZK?HuDUMd9dPo`LV5OqmVpkQxwbOVM~Z2m zj`i$1`lWLJ7Jdk?n6&$ z$|EA63)n+Ijobcw4ubpZ@#^u>%yfeVRH)@b_Z2LggHe^HQI-)Eb@^>g{QD)D!?0Pk zT2*x{{EItL$Tuut->yT%8zPIH)J9H!N}Lu87nN~Qm`exJaH>ty{qMQO<)AQ1!2zQ8 zi2R^I>xuONdAet2Z4J-~sF2DLIiETZ-W@uFUx!240#B`7ooHH8hg^MC9z%MlmR{vx>7 zFu~xKA%5->4A#6XZ(NTx^0-0omHt($E4Y)W=?xlEZ`3WKi{Bk*%|G=L&pL=CEI_Y@J z(p*zF4TvpZje5cr1!3P?_n(NJq;mU>wg;bShZ(Yx6QW)csHj?WrME%8*oP-8V!6!- zfcRpRD4h|O$w`nW%tiTAd+;e9{1=GSnUsW@=9FGQ*Y#nFU$0T>QFO6kv?&Z8cIHu% zyO9_&Hcud@66Cl3D@T^po-ert5rN9v8t%}Cwe{m#QcGmDdDII6j<13J(_|Z23-qm} z^uMK8_g;Xfvzz8XF1RCtg1PaVienN5>Ke{QfILTmpaur?pTe!KY zKr+XLKRx4jq&FpQen`F#fs1~<2mBcIbv`Z-3SziJ=xHeeZaWYEnn~E3)Saz-L^8em z;ig}!iZs$dqT>&k=W{O6vRp*2D{1j%e6DUF$h)(C@QanyQQfmo$xv$Mr$Q(Hlgi0C z7k*s5u{yUgvl8(z`$mY>z<)L zJTM+vKyAv+wMo)drlWVQsV@D1(X}9Qz8K35OGbA_u6Ba_KJWbh{CooPE=rAD$5voI zRAFlT`jY#*-07c3TLW~p(|}69WV|I)VP9dNeLoq~+{XGd(~PE^X;bG2S_BhyA6CF0_N5aUu#Og0N+oJ{>lr_SDL(M z*{^S5Af!Cr5z#x(15(5ua!B3RhcG%DuPTFlfQ&sXN+~*yX%T_36)^Y5a^@H-wr`ZrZyE-$ud3U#A6NwXVnm4%6RMpmp#J1*DJKJ=5(QI?U0@RNOec9m7L6_dW!o7(GKs|) z77H++EeDW3Za?y5zx)$1g#SCVaR<~NPK|fHOI2Yfa!Tnw@CAAPjW^fNfO!>@DeuA! z-NEyCPPJdEzu|v+#r}O>P>)yP{s^2EeeGy3(#+m}(<0&qu2*3emia2psOYuQYYUQF z#m_58geI6FOvirRoNiFfUyeBc`L>+#Tf5@)1iJ17M>rybu0iQu?OsJO8A*_iaM`RR z$4(taU-^3<2I}ABaOtd^i%|ajo};5|hJgnDOIy}QS>w8fxw_UPAkBfZ!~JwYZ~f%q z8XhV}9oEn-m$v!?%}^8nrFV1z=QD%T zq1O^q;2)Qg#if-RuyfFZ?YY09jbpzV>Q|jU&@j_T9TZ5)kv0zxnbTmyvV~7EPMQuE zc)F(Cp_R!r7xkur{ga`RHq##-#hqiOn_zzytx+&)KFrZL5L)VK?~Av9KMs<5Iri=1 zIDfvTy`~~PDV@0Eg0ABJrEPng_lyUFsD%t#!>qE(Az2lCBg?1;f;%07eKb`un?q78 zz-%p_TJJ*)ymIhW>ZDUPshCH&RsWr0_u+uwA-_^5KE@ditIRS5g;Uf zzg6z&8n08Ue2iM-Waw~Ff4%>k&Z!2^)7UQ9WtjTjjJ<^}h321MQFS^1Bv*JX6D&IK_C zi2p#rhg*4vm*3bZ-#^Xol?H^hCT$Y{uqBVQCfi3>sy^YJ@m ziQihM)m$-+slH2a@#9Yir<4Aw)i(=pSh)PJUl=y(FfsZ;N>eX-KeeBs?_~m36%H&I z1>Ur#~_BxA>0*$dyi3 z=GvqQWZt_5p1+XG3%gZk?{+y&7Com;0=GiY{A06J zc;XKZ49@7#!U<-tE=i`QTW>F*J+?}Zh4(~d{rTy!@n?1d-*+f<^5{lqd>BBO+ji8~ z8fQe!#l$HZK(Pq3lScSJRDnaG_7XqIP==wt_>a;P4&y++y*rv1qzr@GFShDgT)Rv( z)2~1FjbGSgRn%VE?5>Gq3A~sbeFzXOH9w0B4IwaaqpF0?IS&*m>a*07A$D(UOnyvr z2@op~)l>VVUT?|QM@&{0N!2|GdP(D|R2^i;mXCg>0!gFx+m?x(((XExxQ&^0c)0*y zWkV*-nKHsP><8|rQ5^RasDvHoB)-ZdvJbdl%4)Yg=}mq#UU=N&E6c*@u@TImk9P-^ zu}33g?fnrKBKUi2xNa*@)b>0!5(BX21F~T!Z!fW{tKm|*){m*N{ohI2ft&UcDI6ty zWukzYm*?GC+l%o`he?rYD2594#HL0|5w3{9d6jwZ!75osgPScwjxWEg{dE$Q==4~7 z5}HGf2W~ir;sawiKz*Ix<$wMDrlYQ-G+)r)4T)X+AD`zykTJse4)g+{&uvVj#gt8P ztitj>UpA0&}+So+!N2rwm z&t_v1touF=v^KYA!}FAA#T>RbR{D>XTc3GB;38re{s!BZr2KxTfd+pFbsT}v{p-Rd zyr(h3lLSwO=LQ$^ZP)&yJbPHdNV)`R`MUJCHe2PpIMhh9r+9XqdNOsbE2AEOL*#ig z#0E?msw8*UV!QKA>&#fEB;j(B8GkJPxm3T`N(JnMz8?hW+K(E=@sb1O)r55%GB&8( zg~?S8XdeN%2wxO?nLL$8XRykF;&5eAA1RBl#aobUZfV>d=*1-4GmY#utrs80OB(%L)ka+7%}&luF__=O`B@dN?;?>`gPuZw z^SkX5Hve!aiAV}3X#1R-BIi#>JsnMDUt$&)8>zzJ@@JL}3qYRVl#^Ht_LIQ;n@hTb z=0DXY1dU8=kf2}X@rrDTW6K5mOR8m#8O0_kvP0Uc zx$m-b?`k@v+YJb>(#^`$ODd;}Z*mw%i|#0T(Q_p<($Y+dZqamL&Gsd+)NhZ5>)x5w zOPR^=;nkS_vZ$WU+*{^(^yc|T@%~2|^cWhY6`9fc&QfD!=a64R7F|td*3M1c`pZ>* zn?=zP=(#VpZS#U%wpXDk?r7;X19b~sfy}J9;r_(F((!19I_NmiikfbH?9kdozLKQHTm$r4`)h;i)i)LJvb(u^>MA+ZDL+f z`Znp${&NWRE0HA#*gj)0iP+^CRq$kzYzutHp3C!88SR_x8}3t~He%eg|G2(wRL(|B zg-+|`wf=9t&)rZZ{wcc52GN6O%$03`#@fl%j{R+RNF3|bjg1?}*;c&5>RN-G5Jn3| zh7c~P1G6(6PnlY24hYkFZZM=wwR0%rF^A+A>p@#C-LsnAaKmZ-Q!WnuA8%52D`nH~ zWjDIkfT_P?!P6=X+v%;eI&UNZ`aO{((XST@-oGqcschyy41z!y={H46ZXYEVJv|V= z&BFs_be)rUH~IRQ8_Ss}+3K_h=E43a_KDVbyw1G$UfdOe=pOqS^pkPgeIn_g;&^_} zf^v_;yd;^gGvlw-eNPi@(X1NH#H%$dagh1 z&98In$l~Pi@Bwy2S6IiRE#ku%rrMJHqUJbNP5nw>o%o%aqvSQ4F6&zOC&9OWHf-a) zzVN!cgOnnMWEmM_68ct*D8R2!3i7J__freE4U!Q<0)?^f!8|zBFh4DjB>x~qEhBk_ z!y91HF{}(W6xU+c!qlZQ-_a+BDR}d*NWcB+nBKE-5Lxqt(>)`DpMo%|t><>Gk~dQX zB%I^Swvj}^h~X_zzec4HKh7IRv>Yc-+E&^*QqSwh-bBY}!7!pCb}^2>7)dk%rNDN+ zc$Y+#+{TPUAaUoDJ+6;k39cXO2J7B~#hui+ju;I}%cgSOwtdkJO!GKdn&53TYIyX%n2%z8d3I42$ zRYXl)#)lQuu)FKOA{wm_eT~)3h!P%lYFBg%zlQ+_Pp8bp)|B$csfhO0DC#`gN=Y$| zT#Bx>5S@9tA@lPzyu^k1VxP@GC>L(DA@ldN;D-H*%gT#HQpDJ4^`p!x=hz(cRVO(J z6_pXO;Phy;#_9b}4!mIq10^}M;NI8nPg%_@;jERUChZ!2k$pOxa3DZ4UFlz=qJd5A zfN$D8$GxRxg2Dy>Zw=+3FJl^&JUY;v`j8dqjvIUhqy&aUH=|D&sG_IBj8B!WjhULq z7cYD0dq_@^#v;0JjYbOdjD%nR+JKrYXqIL- z2JcHDUk%=@J%4(<#Y&C`{Ydc3`guF?6nJGBXxA(-WK3oMG4Vud>*6V|cJ6%P0{JBN z@i4Q_gj!eM1kqVx52B2&WFS>ZSl(E3Ahx(vmN)4G-c4#G80du{|5+y$+Y_Goa zTymeVt4;Q!ql5~sH6etW4zxN|)4iMk!hLJM=qsjLB5@es)6FkQEsa-uIKwye-|W^-6j6@X7l~OVAG_|KDc=o3-KcvhyPEeITR{G=u+c)LRXB-iNT> zzY8JNjyi4c7S5J=TpHZkw}A|+jFfxBC?UwqC}1QQ%mYSs{JgRG`vz;SLWNN~dWRYc z+XyJn*K@D4?!r!Z?;~`kis+=dXa~Q^4fE+r!#Oe90JB z8<%7ehs7z*)x*hWF1gB5wh`TYRacfTLa-B5LiZqSP@Gqt#Q=U>;t_i~`upIhwFy%O zAp%$8p-)y!b z48){B0N&71pWw$vsh1t0hw3_Q3|?ja=-^&9>$4Sde0It^9=yuhinwkz8D4FXbVBbO zLqFk@p+8Yv0b+|xd6M7^#08re`Xws7*@Zd?YtZ6=N&j#e@k^=++K0D%=QySqTr3(9 zx89I?j&4`t0wcJn@S5m_z=u0HwsD;plMpr_7Bbcz9N&?(hs4DwglbWflv0MIGrzwO z{^929Utq(C`4{%BKIZ&iWp%TXiuyH*Q;PH!vy3> zjmdiS^{-XMTFE6v$Rmc%)GGL+kJBvbhFouZYB zD+*)a`>W`FRVO&{FX(qXTjPd-zzsuo9(*JQP1v0ESApq3JZ!q6YKWs|71Nu1o&~e0J31U`Fp3mZmMQgjHR7 z_vrPUbm(=&PX;FhyO2@_6}-U%{eoCjKggY-E~}9`V}#Opw)owR;azgB3IHh;b3+Ig zb0fbdqN?s_h{S7 zpEjK8Z>DfbJ%KRbo#g#NG!?}*?bbbz8AsI}ND3c(%EQ_0WwxO_r%fG_{7iEjQgCmM zsV_>4L(@Ok@r5@|!Je`x@e0ZhD8h4DzTRv+nT#;ts150519_t{B-wgEA?xTMi+YF6 z5Idv}^LTgpt%3L}jkV^nGWgqjWJ&xbUc_K@>n_?90-zK6^vRchA8kaN8nS4~AtoPp zBfXJ|cOY4Ehe2Hzsl&$X4*tE0>2)j0S0KV()WrKS_JQ4E616hoGFYCU@Ct{7Q}_C7 zPK5EUek*5(v~-&qV_Tkr?vC!4{c8g=R9&;re8I2Sp4byi_c+m_7P*nsOv-HI;A0NW@G{$2s z=-e_e+A}?xX{mS|JM&)x5PN#4>e)*ce zZn>7Q=Nsh%I*(6n2MUb3tY-Dx?iPbFI;RXUMwxOU%XXb5_H)*VMBkyd;5ag$h_J2L zca8ih9xG!MwfY;@1LjaiZ^8nq0nn}B$-L4~@~%R-{5gi&V4v-L7W%j;%n2V+lm>J! z8rihW2w%jcM)&Q{j)T?zO!qUqZg2^1>SKR(Cbfswm(=w}E&pim$j^zzmoW$yYo7gv zybyW*T4iP|wVU%pwZpapn=FC7S7(4Q%x~>6r_-}zHM{t$Z6^>s&s;xF)z=qG^J=r8 z#0D}LJUT0r1x*Y=sGNp|UDq+z{zkc+D4hY5<88;SH5b!|wd~tHSE_T;vw91dJW-@! z{Q9_KR?hYK9~{Od476I!`8FCa+d?;_VD{v17@6r8^^eiuU533>dNVY=z6t%)=lH2Mv{W;7ea-4W0r z)^$Bb)&lfzp0IuPS>hRQC;R9>-_CplpvBe`@x^}Td-6093Y!;P=JEn}o!i4C~rz+czn8bYP=V2RTj3Z)Fm(hpf9So+A1nUqty_`!q=k8o*NH_G&{mXMtGWX#AN6 z$=j{APpFrjt}}Kt@AB;*u$_W@(W(N5WRo?WG?7gS4X$xclBy*ByufVbI5{abnBTmY zFj!y6j^}m|<)jQa26u-~K`e4F`(ArXXW>}3U^iwrIHqInhwA5FR&B(?B=VG*Pq1iK z4#nU$zJGY*%vO)~HmFlyDGGs6d78$M!s@Ox11`B6BBgUY1N0Ri} z+`bIF$$gm^JOibTZqtvetO1yq8pXGYR4)HEPwCNcY)sMalbWI!I>AwE)cWB)yNE+{&5H>FE83Qj^=zBznd<=jovZ~W!}_V=<4o@Td$&sU(srQ966 z?>Tm(`>fQjY50n!>+c>+k)#tLmHp$dQ=d7g*p9uS$J|ef_%&_g`m#Aba7HE{=o--Z zwl?{Nw4>q4s!qEJ=$?_2un1hxVx7@BZeGs>SmJy+Z2fjrwG3ac&M=#>X7xp68;_Jb zFh_N!U$-`+Tfk@3CAkA;oQF@{Z+ynUP(!o+J~$O;I(BmZ*}(%a-*jiwxqVz56)<0) zAK+jCM-0SgrVP~}YH*h4xnY2Y+1EES!_4N@9e#BdF$7f)v&CU`uG;<3_o!}d<_!XU zEuvwK_)#`OWJvWex(;i=X}c7$rOye)$QPy`9l`P9v`?&FGoyDd^2@yg^R-mrHYzze z+_P~X-;~dNm;={;h3KB^Lri9rhr2tdx2HT;j|-d#vCk3&+M>X`MxO3RS(|6jWG4C- zZ;k?#VSt$@Wj`snBco{;M{Z7Gv=~cQmZVqmLbN@nd!`0}E{cosm&;_By_i>2`oso2 zQu_Oiu3sznHYvY<|Mq~K3|nt}WNFp3u~ZuEmd61>tTI2yaNben6;|^J>m?pzySJ+} zE?;sKR@1b9Q@pVTpyYfcG-eIiWJNHkHI0XinCzh)bh?<5sv*A7ui3FhCxhL@HY@p# zB$+iu`Byi+%NO+bouv`{B5P7sIcRiLL4Soe+19#)S6dx-XCL%q=Ab#>$gvMnjDM?Z zLl8P}y)P4Q(9gwu2*_Q5R_tqqp5(vN4p42!O_Iy)pRD+eD2VO8% zepZAhS#48Ee7_jvPdXoIZo8bXQg`QrmITbs0!bpoFY_ijXC{O90)0Md|qn-i< zPEay#zk^@`q@N+O2ZnK^*A1klWFkO6Fs8?&=u&wE8y-%Ge7Awfa7eP|nX~pOMG6G+ z%yCeM+=dMOdNuhpp0nJC2<2kGaR_{PeeP2NOV{hpxx!TsxF4TB@u{#zh6K5Es(F^0 zc3)kX=b~^zP_dSmD+K8PLy1d;~9erq`iN zBc+yPyltlH@36M2S^j*DmSt#ct8>r|ZZkLoesgBmRV;b)xE3)C;&_H-FaMCn2qT** z#?FkvmlTS39PW|42GZLI)&wMK>n#KiZp(4Vi}PzXOVCjkqk=D#c0L5QrA4gZ_5swU zR-5-w@+iliRMeGMt`=bqj;(p~+-Ofga=v<5fXbzs4CWo$|7)L;@>kN31t49G=~|DyII&pJyFmRDn2I=K60@g9^3noO6D_R_v%EGI zzm!IunvgJ5j6mL+Ip%)-oAHhMMh|e_)9bg++Z1TBefozI`z?T=BPeNWS-~P4sk5S- zCWUBe{?xy4GA?pm{|L4UWzyB^MX!}3PS&iE4y>65zu-B1-oyXO_LW!XdU)!?Kwj&H z>cNM$x^Y*ayaCdEFh8lXtrYqic_YsQ| zUmU!Q4^H9u(@e_0^J3l z^ZogeHY=f8Vj|i`Eir;UITI5eJQ`84DMtnUB=XwdpEarFTptT+sQ=u6z~LM@HM}IS z|5$aM{MrhV4iU> z`U(Ui`QlV_%_KO#OiVbe5$rdJT9?F4v_G;M*6fNjMe~E_;n0Fj%~(7%8l6C>KcGIW z8d9<><`fgv6eF;fvD3kW&&2YbvFT1_Y+I~MbQo!pg1vRw!^eIuEs%EN3l)>rN9{WM zohv-mX|Lo~3YfoPde|Wmj_0$_~)==z$k7!(#-mZ&+#A&<0TQV{QJU@-Alb?p`$CJ%qXi=f8*g z7uwc7tbaHfI;N_9k$F~UcRjLOe=p*IGy{)ji5(t7JjM)4JbCco0m$dxApUn{I~r+S zd@Z0J!DPZV0#6nlMYHSk(Dnu1*H49w9Q^|MmQ(b6hxPsSbqmt#-<=naw6Y44y5uN| zQ7v4>pq@XbhChgEjaCY0qsPqkw|S1+5`MWCpi&5NjIIDd5@3doVtUuJdWn0$bCsTj zXJvxxm5)s_3iNv+f}#n(&_C^o6hm(fFy8{z%pKA9GzTH^%d=R;zmWNzc zxS!YW5BBdB|5`ps`fy@U5ZgI!2J%IevDwmikf)>ce^pTd`PEU)O`^`WJyDSPy9ES3 zr?UA4Ty!M;CS?`XIU<`GN%wmO8wKj*Ytm}xUbdMtx^b;W{wKBN4B8ay6Jj%fVK);s z*;x-BM8lmqp}bN$SNdrYwvD>)5&)&TOYIfD&g&!rHoH%{a$y^dzBCgL#G)aU9Qw zIc^2VB&BJyCw3;o;FD;QieY?C(BB6(Z&+X=y_a58uB9*z*9-Ze z(3hT}sO-*AWi?S+yxgXM&^K9J2?F!pS;_4-XF(dRuw>aa-INjIr&Y0cR)c@ZZZ{+Q zoi4R&9X0A{TB$|AEJoEb+35zuq*0e~@)gsbgkCQ>8G7eSp5(O)n1uMP=(Mnx6e{6E z?+7k6Ir`Pjd*uQOtkjlA|46vbCcF8#JHL$yTW7R@fa;6P*3%x*J2QpHg;XECVg~Q`&+Byxltr2I69- zoTyzi7-?o&?LJL#o3-jC86GJS{-gxalB`TnDFSD*#BwA9xJ^q9wj`TmA* zHai_cTIs|K3|#g}pe5xmf*4J(JiZ1Ai;?7@d7ALw!*k$}(S8S0tY!Mhk!sx&2pFQy zvALcaJO0gcW*X}CMzJJvmvIH`&zmhdSpG$J^SLa6`Ckk@PCUg0?bzTPTDH==USk0# zrrSTQrd-??VhE|8)W_uBU%4~zx?HL|?`MYqm?z#noTyftjgUPuEn4$ys09)w!Ygq} z#{Padon5YRJ$OWt<63_v&iM~by}Ml#0g!AH9xEyJ zeT{R|Q~ct`VuN_J5jgKS#xODs=n|mt+_jK>T@dTu&kowX#h)iICX9cAzt?z6@;ro8 zP?oj(*ci;es1OJVR|NI!7ZWAo8el$f3Tov!xF1ANstOU&=h61X+b$9gA|MapaKa~s z2J#N%AW?5{{u`|{TV}H2;gBTJzmXp@Oq_jXX&3PqIYe6|hk$-sBGt`leoO%8%CBF% zzo!f68t6V*<9kxS=3b=cs3Awfj~44Xes1XccxOJJ6YNK#WKFi+j5zcT2?}Duyxt_z zP@?D8SUE6H?Bt7ZB{0w5eQY84+i0p{x2P%9?;wCQdreGnJ;}s{W4~;k{EHOP7r2x( z&>%fo@RFJX=J|*5zj#qQ2ZQq%8Ev8Fbvy_!P78QtwW$j`3opw1;Vg*8H^eLF`X#pW z$;sTwR$xBAQ8L3EbMenW%no}%DyY}BCm*NU*57gOj{N&lo1sQbs5l%;q9^E#s}Qev z0EDldZJuLx-Y`WT4~su>bX33~Xjwqt;&@|Qp1e>75KN+Me_4uX-b+oS0QHaN*E@^hWChCzD37>l4VH&gXPw^GV zKeVeIeU6X-QWlQ2a<=KvN$lppNm&OP~+J zNnd_Z&^JkTzN!vkWtiMcqezyv(xT0(I`pm+gc{Si<(#{D>mk1>|6TrIO+XbcewBvh z_G|Fo!xX?U@KlKpnaWXeF=d=+tBM}6$`XtVmwJ*7pGt;*jEh?MBQcFOl3eKP+@)iRch_@brTw;CJRyEeYozMcr) zM?1<@Xd>M14V5z$%+_`I(1izv@=E_d@534rhU$7~3uN<1V%<@rF9M^eI=M!24`;tN zlmy5$Ie_zvhqOCzHar|n!QB?!iW>TZsNp_uZ#29vE&wHyx7Pp=^wG)Q$s!yCVYM}j z)Qtcr^-Z_fwidvXhBMll|CJE|)(s!l+qn$b_p|0FW=W)x_m94&$#G3eMqH9Hfn-E{ zhknl6(&5$O2YCKqZ5V@%i8&+Yp80HAV!rR;+o0b2nv6^KabOyzx-PM((!0;F+jKoO z2qJWw*zdX@Q&Gf+IbB)Ur%uGIvt6id3(*_q)a!@eQsp~Mt78j{z2HzcVV*=esji&; zKhmlo|FiHPJW&4|5*59`-~;os$?tldgR@k)!uS%&rxYP{;^L56%N*S&BdRH~to#J2 zd^XR7X0%}6&6l(V`tQQJkK?2K;go+V@$y~w^y|R-q!)MfF|7247eZob8N#Z4 zZIBwGEqIQ5g7X1(**Sct@?}ibV(;|DF##RoXeDmUPE}UX#x}ZekMT)<5VJPNyO-T5 z2u}}_V0_{Nzgvkk9@m@2S^I-|5vZs(|A|OH7B(C4=;{r+nse;Dr+KBYBE}ns%<840 z;s_%+^GXFBOpna+;VU3!<8+m$QDuDXZec8fUuKv+Xiw9~=qEBQMYPk%2f`%HX1)Rz zjJfpL_hH2XG-}KV^^xGzJX1T8rM0EACZp>fYQYVYe_CDLvFa9cg#oo0+j5zRbmGQo z_xWw~0xfuyfTLw43=2=KuT!u!YDNMka` zTRPP5LFAT@Mhkxi`6!0d*8*O)U<3Xhc0+?a_T~C)1}fld8_vwm?I8Mc3@=+4l_xI2 z2b_oV2C0Fyd2bEozgV82x$zr46V@9+jwR1okX;@1MhfC##AC_}HKTcYnDxik-BQnK z1w%DoCjZ+X4iyNTsdX`jTNcb$fh^e<^wojJ$lb68{Z;-sPT)LvUIv&ZEG!Bo**@jE;`d}r znMf(FjX{9>rN~9(M2CJ6iaU}!?I@Oks)sMQosWW*t2p7Zx849KHQq?ZqzF$%my=V9 z`WDc&(YfU|mFKeStH;CY`3B5~YB!mBRVOteXg}g{1Jr+SoU9C{*qsnj%V-^NofQgQ zxXnhc;^tGe*2Sg6_JNknsARuyaxhAC>3;KcCg@+k@GR|-96K@k|8;cj@l5akf1431 znYmQDm|MD>lUw(5IyToS+3ez0u5+#9I?N@74V&xC+{4OU(aJ59RAR0nB%#mwrqD$@ zCvx|D_xp1XkB9Nud++z_{k;06==#D0-DB}tZm+YBz&EsSF{oy$JZ*Om`%Z^j$VI2c zLVk`g$=$YxCC*>nF9RGHJ6b}<>V_$FhvS&tyy4U zD^=ETygiZmB%W`r@y*wp2TA2d`0Q$KUrrtegOjTWIugI8XY%2v1g z;1Q$nzWuEx$j>_ZQZnMDq~mD&uRfr9mF$yXe%{lW$pFH_K>d$8 zKh0QB`y)b00L48*?KA8Y`lP2I@h@}tnFH#igt!fboB$s&{y!=!nnY8u(c{S%&^|-$ zP6Ob17)zP!?g<-fYZc(*I85;RW`S`Sqi8NnXcQ+>u)hV_<=FQnG$6&iJAco|w5Ec7 zHI*6Wx#K`Dv|S10g@qu!GxDEoRu{AzNg!uJCwTEnlBiRivwx%GsIRLVvs<;g6%AQz zC0LBeX!s8FHmFDE3$E7vtOQSK@XNX_$W!JjlQu!VubBO5r}1PFd}pRkhxbCsmXIyY zsSi^2Xx(J6k##|A$GE?LmXs{YUGd}_Tr1NaZf7@A>Vs5hgh&Irxku%4e$1h?uO&-e zS_qieuC=@$P`gHd(0D#-PXeQIHMf~F9Fwt#TFY?C)aaSEb=#xo7szTbWqETuUL|zp_m|(w^H!vd3YB0oPuad%BZ6)O6~%(9{Q#Y(+o%W&p?o=w#un zreMcoj+>FpLqLCBm_i8Sm9gg&DUNSx5mgL$m%tNO)Bt!xwbfNN#3*`qCiQllyT9;Y z;o(#USU)SbuZ(`YT(}^ap5+{?_yewuIOXZMgwEL+`_(%tIhY&&hh~ri zm|ySgDbv~LO?E4&^lCo!d5x=8XgSi@Ur|%(M1fOkNlI-I$eyGh_`z+}s+3bO$BeRD z*7lw9w<<5K5S*$d!92B{2RQ__9U8r=T_Dv4ZWeBk!4qx#WPua9slPv|WZ6d?O8QLi{#!mkE zPZleudVF-ndF8K=3=^g1u<@cqypk?7G_Hg>aR4=5uy*Sn!$P-R!LHNw-Yui8=s9n) zUiL|GdvIzAogep4*_cfB^@&{I509&hQle`c>xLSS_trvqiJkR~5%36luSJ>h% z4LJp?%b+5Zn$c1dDG*v5mnT675t5l+XElG^(O%=#Ov>%)?6oMdwl`{aP!$?Il+tiL z_%AGBen4@CqbrRLW?cV^)aKBXgEDGLZp7}J&!ki8=)Mle|S)Z zCs88_?hF0$#^clCn{OEp9TrnIh3&P2xGT=;r%oYqSZ}tn#CU57T*pC_xtV;$o6Ux3 zNTh958bPHWQ@{CJ{pDH>vE^Y4egvN!K@M?#dNkfsi2u|M<{A3wa*sy=17)H}wAvP2 zsdL9pzbcO^Uw>I)m)?sP(R-K|S$|4QOec|npR{nM;#>TIYRM|uyt-AP%)iRPGx@0B zTuy|n74@GNa4sRUf<`E*>?-nSmUFsT`p9T~#2Eb|#muHge){AvYdAyr4^LlfUQ@XR z2lyb__ew84$LQa{HL|ySYC#YGPm6yfV0snLK{tR&5^!yx-6*=n1H733ZU;Wy@2Evz;uZ)pV z0@u~ec_U_^{b$p2lLSi?!bsCF_{)~-^KC||NZVZCbzz!nQUaBZLAai`c}0Mffs5K_`DNG#*fa4{ z*&(=TUUC208{hkpWm=GhaYklVbB(#;VrFx<{B;~?=_($q~G86n;PZ+vAM zlBJKVZREEecI`dc={P69TCqi|#F(h*yE?2uxI^oxqp)y=uaO(~0v$f<{ZIR+5Jog+ zc|OYCM3)yK(q(#EDj|Bd#2AJ# z`sJ7T!5;&D5%iy#myoz0ri02iqU#j_{--?p>3_Hqt0-J=->MXRu=j?-o?Pv zclBa9LzVmctr?xAOw%&;JNiR*Lt+j=sWf^>gYLO>wPYV?VI+->q~CLtw^BHDBFTQ? z8c)k}*v6t5d$poZiGDd=>5x4GWpKFtbL`SIL`)}g(#BPK6f+7L2 z3!l1;7h$i4VNA0;r^^Lw=P}&mH2mm0!5?o{s)w+99&+ebT@=Vi?moo2GneOvCjCqh z?dZb`u;(}<5xeVeJ_$%&XeNX~md>{I5mYjH7OCcOgyiFjy=rUVF)h{~8+*UdQ}Yt= zi6^RWy=y%@m{sy)%nJDD{^I2PdFcmszU*wV;dPLA=4SjdXb%f!dT93gi}jgML+;S|lLyw){A%xpkpOISYf zkxfNdPf?z$<5~yj72EfSZeCb*`K=`g-mIxwhLL?XRsU_Re|j9`73k{Yc4MxQTuC3P zbKQFLr_?ZV$8=%00NE-xOzU1=5| zR~_amA?)W2cW4R~z8)B194d){h8A!=GlMIqOk6DE{6|g-r5&iGIVZ%0*KP2L&%_7; z{wmPtE1O=@T$e6C9q%K=2fwZl8l%UTH~I#Sb?!o|r2H3o)XX!mI}DCO z)bcQxOhcERFVvUIJu5?q*E}@l1XGPW$lrnbk)E9E@m4=KqZjmNvMHg~9*J}_x}lfR zDw-eq_NsR5S;<_L#Y&x%FkWf7 zp9K9{-0am?Q&d>Hj4a=TzSEc~6&LyPYr$QCC~JsBvoiRiO>&8CGrt??YEGRPpTJ5W z^ytF_-&#nex1TYug8UwCw(%$)^vx54vtmaao|$Tp6q|iSD?(Dw`|?YRWl`N5y?A|? zo>=j|<fJa-&(^O#du2?_mE> zek$OLPDhQ#^;NPKbo<-*7V#4t&|wL(A72_^o36o_Kz!60^0i zv@_E@9s&B3Ms7oIh}*`;*B<$V#Rq1n+~oG{C0Y|M(x-dYe8h?7{lq~hy`sGQ@W%t- z^OdoS#fpm5c;8CzO%!!21>%kQtO$zfMVHVMZ1RFF=!aKkMU!dB^8YR{B@_t)(Lzk{ z*9lxbqci@9vjQK&t7g9R-BVUr@t`o2A?Yp-+fuv z2Kg6RC5$YR)M3w<2t(^v5dPFaUDSxXVqh}T&TUJC!9V0b7`)}GmtYrKy%uIG5c!Y9 z%}nXL&i!X~BkV9-DEihh6Orq#P<+7H*GeNzICLoW>Nxq)^{p=JczXmBvftBaNLr~= zS64xuS8JmRu*MHsd(JF6Z_~!=BW6P}Bocfo^|rZv6k}??MC;jvq>vu+%UyX-ue<7Fw9| zhQE#k9%9*0Pq-s~3cxz@^czAdDyYZnx)VN$Em2lwK_iAYQ>18*X`5*f`w*oxg0B&s zV?<)GN82Eyo(l#Zh3(I3TE>GnI~;`g&xs=uwzMduO}RI-a6l}IHDO*uX*{i5W!%@s zDu`p66lvb+2!+V<8zE}C`jBiutRD8jcV(~g-c9yKs*qh{`fw&UrqdY~uA4=mgMQNi zx5-JA>4}(t$DhN0@-PP%d*otuWyZ5T$n&62hc{{^XwNI&x#+`aVN*>o-RCkAsQ(>; zJv#iRdi*Y!7oJO-M#(AqdG$Ev$yEM@)<7N`*17V-G&ezCtq1h;J>ODzFqji_W(~NbCwiSHmM@lB9lIDa6#G1oKvfwDs3^7^&2tO9OhLFZoR2Y(@#tYgyg_{$G_7 z3R=xxehJYXP?E#H**3#U6)OlA1V#%50sgJM!=9i=cMA`Xh{i>tX$6ug@yW1iWR=`Z z<~WbPP*%QcyaC^>xl9bl06nv^WPIq5=U#*vrDY(oz>()QWIWQj8%~z2bXcj(Bk`~7 zI6Bh<^aN!$yDtEKy8SJZA2CAKZA;H@u*<~@MgKEZw?BxoEpKUEU_6!)w=XI4qm>QM zo~B2~osr@X^GkcLF5Uuq2t|vOvfn|!y0rocx}~m^_p|TTLN2L2aog*<=&@i=pDZQ2 z=eq)idAFzjE6sf%3HsrEs=Wc<@U5Uj=UaL*mEx}6rS(Q{=o>D0E+3b$t7SodL}6Aa zQntA>r$T~9G^Le2PAnI2yD!v-z9IiHE!hjBNR>`-|G`&kE+L(^np;|=TDA#@3zYhZ zQMzV4=ihq)VUj|N=L*I%PU_hU^cEc71f1vUu!f*9zK*Jno5THq`@*-UUQ>ePwOq;y z)^e{hOoTFSy*48U6`Tv|=ym~Zc(IIZwhg5agQkZkZTLPk5y*HaltyRLkgGdFbXduB zfh9ZMZKM!`Ni*|Vo=(3G4cf>Aj~AkfluU`YrthbS+ljJ4efv?{8ZZgft+f*ZnFq@5 zBUA8$7_5GXdGr^t>{F#OKc2~@fuoiu6%38gF(;RjzcQP(x-&gr_^QL8o&(*tNA_V4 z|MUu~MCK^+&@lYZCfhP>w<1W<)dqaj$I1q$Msp`bp7;8@>Iheb=0MF;k-YPWD#b~k z&3mA4@C2{?29~H3XIYW*wlHe1i7hfxu9Mf*PDK1uR*dd zLtN2bSWHXhT=LrFq!90u{(1!@0{2L-p6pB#{>r1wb|Y&=EGrseVllcxjr&GiZ$ z`02>|kSUAGzUZ>I*f+B(Z!0oJ`Tp%>7V@skmXfKjsSo=8`O!BbdikjaiKb@oF zXiXEmZ)c87j`iOyt&(-y)p#&p;rQC`d&(6&id{;bO=L{O*#D$nM(6OS&eY4^ZEJ0T zh}$G6(F7AiM1t+CQ~ViWVs=_4!bLi)c;4eLAZy=n-58^j;c>OL%<(jU{ny8CFm}ENsC5nx`lfl zO4zn4D{haMu8+>nJU{mIMb;{-3w$5tHC&4j#>sQ;%J)4sT`hS@tWf0>yH{+3Ps41q zAgsZ5(#(E!-u&sGcAqM3^SRoey(T|)IZ-{d!_SJ&2)Z>g>iu?8u4PJp(pmeX6ykbk z@--@$Z!8_ z>~dKy?gn}r8-6rT1EG*`VmVqn1bQ0&dM08#jzQDKe6F7C#o&jfpF_K24v$DJda-F? z0+Dr3CY@-&l;*XNHep)4IOVK!mCp?i4;#ijzYFrMTYU&aHXZ3nf8HZOJeO$WXI>FWo z_sLYrH;L_u&f5T9$~&)SY*cR7@MVqn@iM?y`-`vU5|A*bbRmA%rI&th=r?zDI@pLf zW3Jr?`PtMFv-_g1>b}QqfN%0PM-WxpGqmQ8Z53Q+tnsvay?4LBNqW>aJKVQi5fIT5 zTG3X>^V7(9N#-G2J>sb`J*%ScE)4pA39Db(Vv!>{4pOzb?mit_rR5F!u||QOV>j#O zTF2<+F7IVwyIE&l)R=~JC9EjeJDW2R_uCD%kWNM#X&5HlIlF9z3=|JSJ81zZ<4Z=icQhAw8@6NJ%@v5M|Gs*c7HE~#@nHg9 z^6}`cF?p+1DTfA6Sb*4Kn2H;m#)P|TN~qFm<4H<89cTqCU77C8mpxaFfS!i-VJ5wd zbFIjQdEXTNLmp8QGj2 z76>8UJ)X`OBE9rRYYrfrj|gOZJIPV63@))%l1!p?5fHnL=l=DIv0jMTPw~&I3Q%Ha ze0``bKUGIRjvj)T)x%GSI{fy4j(O8ryS2uzf!L!-9e(3XC!?a|{QTBtJ>ogZGT$+} zLzTo&DvEJfj5SEaO*)}=J+hs>`?*MjJ`tE4Wsvhr-$DJ;-vP{1L@T$x18&5;xwcdT zlPV#5Y@{Q|o<*HE@@NRZ%WlUWPO%xELdmQ-zUxozzYAHgw;4}Pp&X^$Lik<-j$-^Y zD`R9LrczSbEG(gXqn&*}%WY(zzE956qiMZ9$wF*?R~=)7etF_{lbeUyI`r&sTg;#P zUv=-#U9M@lH!c(fJu6}G-4D>7CQ?dH{VUF>;?_sl(zVmPVcQ&wII6svnZvn_(ta;b zRBC#YDFDCVfuGa7Ezapmudpj18J8C{23NxkZephy_K7Uu?0-!MuXXKAcpC;BIOu1N zzGmOqf4RtErXJn~kBoEv8^l{MewIsv>b z#c=2MH-L{;StBYN0em|F%3wqZ?kFowt7@}<}_1`IwBtuHE7Un zZX{W9F#inDqrr&v&hpkCQt`E+1+9R;f4~2sCAiPVn=N#mfX*5Sx)#$GT`$rl`EOmnGSA?tEEK2^tcOgxXD74?ct*m^Qj91did%t z($_+Sk~AwRvF_WJ5Vgz&e1W0Us(lPm5~GW;yz`3KH}ze*92aP-IBmyMdbN9f-KGxn zO!G&&vjfnpd`$S3US@%W!RM709$gT3mlJ99A$9qzMQRR(CJFd#4l_zo1F1)s7_pr_ zCy>o~77{dMLangPmZ_uvbVO@j__8D0GUwKnS$q3Uq!4>fR)JV}KRt<2d$oEgPuSCL z6xhDsNQQss-BKIN8x4o!zAhSp`-Squ>GOvGU!wls?c$l<%kHP2=T-EL@+A70CCKCl zheeqV_Z@6t0vU%vuzqNM6$R@#jgrA)EH<`;PA)l??oYaS$m$VB_PG(*53u>2;R#z8 z0$^<+Ex_Ay$)Y6(5@i_i0DhKI`BCiy>p>bcj^~&8X0vDw zHc~OG^P2)oNHS=!ASU~IDrbp9Xc&$={^FraO!MK-+5;AU+Jby1iQ&M$cWORC9L$Pc zVkKIMu>9S>wzve9J^kf+6+dR_0A9nw@GE0T*l6o}>>m_HZ^@I|i5q5W2L7)c|& zaY;J?+~=~f^Y07+K2fIdyyBag?1#NTUF=^amc2Rp(LpudQrE&H$uqxAh)>x0(buum zY25fL0tBdvvDq1=p?sQl);h=3^n|2d_wS1C&=m}6zV_oa&8SdSuD_}}zi~*uRte-Y z*~>t9$6fp=GC8#{iVobx5rXt`6f1hz_!twjfPM>S3h-lc>csu+e(N76rN`}mf_|(1 z`p_Jcs7N2_tRsv2AY!aF4m%BdwBGyWGUi$gBs<#?D4Np!Yb7Ny><|(M=374aeVRrr z^SA|0P9ETwtG{V3y%g8hmsI0R@j~`47T|x})wBvE`v^X>j;Pu(Nu@^SZeZ<#(w(!AEQ|d>>Vvh0(?(;#$bVNn`4K=a!9YjMBLxszv^r< zg^h|2zIoR8fc^xz6lhe|v4iYJ(YLV?4z-zifV2W5o%M@vX}Jmzcgr*++D0?=gec{S zheWK9{VG=P&s*OW7Lpt;Ry1~rMe1WraGoSfoC~*|t%Ssbg?t)a&QM!Ga<7$cV~7cB zO6vHj_nvOS1_hEmD*}-{=*L5A{t=$ST$6Oy5sPYmy*9LLnDe8bATXe=Pkd(7YYj6K z6N%fYlqQJ7K$S)VzFQyj25E}_g64nBc|9v?-N}uw8f`X&fq6nK}H3&aIgUd?t1 zRQa<%<9%4yNYy9X2dyvA0tje*R}Hn|Q+2iN>?RX+qB9iQ?`g=rvYaZw7m-RMvP@DR z&EJanbmS(9mzh$TV{{vn7(e^(`9EQ6Ty3A;9r0!aBZu72PFy9SXj%yY)>CRA`eDLG z%5DMs(?yS{K~t#&!$^x|`$P!-FFEttusb8$f<7h&c@Q6CqG_>{?rB0q!v>=r-(!OnIUC)$>B;L^&jxtumfXppK1up%_f^vnU1NxS zptB=rcj-+#zut-UgzG|l;PcX;Oqw0A(`4bjslcBPet*7OSeu(uSeT-wIB{1>N%W|D z753x_O$6l=#6CV61nmzBt1VB=4OctN<9Sit3aQ|JrF`v@<7JTFI6dkSL{!uaMrZ2H z2DFLs)mFSYRUB4}N4;ZrZw*cyHmi@wq6NuZW}+6|e+av(mFlBa=uy^e33K9}mjLfx zG@cxjt$H39>gHBfCX{(b2A?0<1oqbZ;{f@@1c(ywcLjO^p9`hcmBdX*be!{xzuX&u zQaANpEfhX&?r{HlK!G%tv*1Pyi)_bjBk{q0agtK(bixEhLi4!`>^&AAz%@3w`rQ0oyz5Q#Y8`v+L+t z?ua?9GoO&BSpx1s7H;I=;N6XHVTZs{80IORiwgnyIB&CGd+z}AQupoWfM26gC-S~* z=1FAmP}VOm{F&b@Gn8#7JQ)c3tr@Rbp^Az*<^f?g4vpW?=*;Ff0NHLNx8?r+vRgc1hpT&#H90#bzEg^*50nKLtR7?z!x->ZBEKR`7JV7 z4ERjk-~p5sRnemrXdnSz%L<;mg}d5i5$WT#3Gk`WV%MhZUwFlbOo|=w(t=;H=-WdmF&PRZgr1qI>g*9v)qCT`&EaN?x z$)M1`eO|q&s2h+h&<_t zIgO>d{ZDs=gm&)l|C(}Ern~HwoDT&ilqpWF4^`&-oy46u9&jpNj6Z11idLjpzPV8% zGApiw+DA=j0k24Ak}KNknkE`Tk%e(c6xyR=cYI+ugGB-T!Q0MvqZ62G0aW}>-fwUfwX;8%^8s#>E0{nJzV4u-P3 z_VR(xaxJo&(BMO>pph$>wD~vOhS17AF*2|E7lYo`JxP=}vHjh_5-9Qb^Gt>!*)7oq zVUBo05^#6jbQ_u0o77(a-LR{C8#vEM>iCJLoEWvh8v#-ALVS#h`}hH9$Glxr)r)R_ zblhmgCvrloleq<2%FSP1`9t?cevjh-8XkL1F!Z||RW$^W};Eh$6HZn#7X7(|2`Rb-Wm z9?p~E{LlT^xTr4~qZmM{^#=U*^JJ2XoCX8Fp`w+EO%V)HaVFj=644v_v`FMVZ%A|< zu5NWpd@Mb&=Z1ZPo+GdH=$GWIizU_+`)1kAh)7gz-v`BVj*)-5a=M4vF0rMc-`zXS zIQn{8=w&3~1eCWrXmsMBuB&$yi?f_KD;61d1_#*Ktj+~{5*|vv5aWlzytxmR#9sCL zfR9J>;@)-FHk*UofkU$v&$Yzz%P;KzvexjK7P6l6VK z-7UnDGH~`+)X{HN7b;)UNfT)2qn@al%&s&1GM`(Z3T-eD;6p{In3JcNf`Q>80o!Q= z@IOTNM4KF)us0xYK%f*1?F+OrzXfk)wIxEbYJB3zB#ir}ajUI$Du0+4{bNj>4e-D+ z*oQVr*dV)H5#Vbyn~0YMVJ7s?&>%XS$>=wxmJ`Z4rdKvV9?iLMV|&2AW@Y^S=Uy1; zPHk`eff#+@2O)%a0KUWGXTQ4r0fZOz;+kXu;{rrT+7=R}K(++@tA^&51eE9x_9rqO ziNJV_EZCNd*(%8ZpQQQiyq0{u$doR?yR$n&ck^>RpM92kbJdQ=v$_}f_LqK7F&+Dt z4ZtfslWYbLLYIFUj>|rb&xX33E{%lRll}PKVJyF!mO`SnNolkqE&U+zk~UrOE$|;< zOQZR;t_XtRy+pk=arXi6{+Tp-lo$B~p(a}(YNO)t2gLnJ{`(u2VXFO(6Q*ajEt*C7HjtKrgkUtrwYLIG_M|-m42H>;wtv~wNlMAnprZ%ny zm;t<-3SKh{w<(ovRI(34_#zXjVAXnhMx$|2VvU?Y!e7txSl``iF!AeY(ark(1pmH(fu_r|j|CaZ1lqL$4OlLpWK>Z$uZK5= znRs79_jhDJOtf8<5-0kZwEv-0tdJROzU44^JCKM_WRhAcTCp9^M6Fba-H-0Tx%NONIYs^2XFb#!yh zH>SjFCrkL5w@)k9;<6>|ukJG>V*CHvA216OnbX-XTh-R${P!W08s&25@b6a~7O80T zc3S!D%_Y;Gy5!$`gMgm4?5S36r#yWA{w02{Wo-<+`&CJ_E!`+NXR@$-cNUNT0GG4j zScm;blklkOJXr6eN%Sb5hO)aF)hM6$6KeOv0sW8rvAWr9oSgn1Au(T$5@lnUN(t6x zX6tT3gBNzOLWfM0F|LGW?XOXTUDX{WQOYzNL&nX;;9>LxZ))MU@lHAHrfE&J%wnA$ z@jp~Z#2{I=p2F5MWwxR3NR}YZ+m@ITq%a0sM?fCVPdrvHSIu1UX4Tgl;fJB_&|Rp* zdEblgbuGM`^KNwH-Fe6=`S1+Cn(MG7Y3~8ndlta=Xo>&c`rz|`BPrlCyb5f;LX6A{ z%#&EnuMiVul)-+4OVCg=w!mzGz9s*mOa{qBj_G;3GBarvK=j- zohyM~-Xdsl3MSqge8KgyH&@91WEwPbK(ENIIfp|m3eHE>7A)v2y|@yJ-;4L(a*h$n zh?aaO=u&!jSG~WaG=TdScO+sD*$7$b<$JPvQ}EQ0N?*LJuo3kzIlc$MnTYwP_?ymF z?=jcQrFpS$4OA6sIVqE)%7I0tx%Ro0bB<^i-3tJZ-|>S|S+aM}RiWs3lG|h-S_N9EN4e4u> zc+lAbc3Aa$@71IM#vHOl+||P(xm+^nYRu378JhJ<(9RvxzbLD~`Am$Gr2ZKXb$^6I zx_gUh%ekCnlifj%86(Bd<>NVuMnVVFf)rCAs^LRM6U8pm)nj+cLykfgyGFzoZ(k)l zlrl*J+zV)f>+|ZtcEw0;{3^S`7pxa^6_TB`x~ywzc5Z-#ix4Z(>3)r1Oyx4{+F%U^ zz#o8Cz)uc7X^LoVmKmS`ynqYlNt(L6?*+ebygh`C#Ahwv#lVoX`@~Cq@~Nu_q3~F?^EO>gmVLVj0~X%xi=0+A!6ZX9`Wv4Pzq7 zb-@-TKDk-O9$RWa-!=U}*7K(RZ`~z0Rgw>kmpS>m{UPu#1Ctsn`vMHg@XCB9nrb%) zBoBPMl`AR0Ur75kZ(i?F5_SA22@=DfDa|l)O41(-)<)mNJ#j=sq5tS?S{yJ%t(JXT z%xq^HnZNY2Exr6SpVW3L$_sklpK*I+BJgmY#j>Qaf(G!jTl~@|aI+clwQ&tLkjyNC z>@A1B>EXgzjxh67MIW!cqqAsMQ~KSPACcY$b#K3v6{KXERbZ3{;uy2nxMhQ)lEtpS zx0F8CS9@Z460X+X=gjN0=1Yc7*Z&0*up3{NM3W7!c}pZcS}T78`k3qFl**YR_6xfX zX)lB$=J<`fB^$9q6KOt!5cBa~0qO`$~@JMjpYYCe0;67GT#=$BB|4KYu z-^y8>TJAb`ciV;(;1B((&*tiDb!#UdsK&qq(*N)?qh1^9ddH@Ev>wtP=J7wPKL+_) z^I7jg9YDWk;!WaYha(j87TsXJK-}M_l-``kwG3$XTtTxmPTG@g>rA=Zgh5v?fIMZY zOYb@P+ZYSx<=SKlcwVwLq|5#4>i;IdwdHL4AiLww(B3j=s)vg~g66V?u>1Uw?w?M& zF-di0T|bi7NO9+Fu~dc6}iww|{UB3!%w`q6x7UHysz z6S!B2YwP8*JeI%xqn0l>S>W1p0o|JMcqK9hLvS-@ts34 zvRhO30sjS65*Y3KBh(H{} z@*m)rNO7NWR?ofuwD3`z>r5oNcyCX|hHS%va94AAvw|bfvqFfq;9UGZFkhcmB26AtFD+cV_E-wP4diVz^(hAj zmwf4DV_S>&6a3mBrrM&^Np7H2!&>#4(BTF`DS(aa?JN3TV;{cF zS|)<-$gSy`m(P}_UGlu-Po=^klzOH|=qm%yq(C~MS)4Dxx5SzR3`qF;u-UucN)1f# z%H$E;tQYsP?`GYh`T+c2nna`97+9#~Cw$^0II5x+{uC6?#8%dp)bQ;W6b1PIQJM5% z_0tH=c8OMDkpMpd_>~OgHAD(^(~xa09y*_(N#Bk%vt4S{^-#YP*j?Ti=Q4;xUR*z~ zsAS|X#&7S$VE3c0NVtbJt)65<-JSz#haF%e_qMVlbWiIEyPJSlM@HhJhMLzBi)k-- zJ0#gH8f>yRj8%--Xk>-<#g{Xfx1Os8$Y9`5Lr-s}tqe{cXB@E~Xbt}F0 z+{Vlsp3b(v4ijXI?mQ83QPQE@ttovzXba84b!d!{)FdsK%9Nonm>#~x?5P^{KJsVw zYYMym?--+&bQ{7Bh3HN~Kd$Tq&6T(S7atpgUGLStFKkn<5VsdGjqUnyUn=jYvV)(O^Cr&14MGpQLkQ_d7GK1)}GvO!}Z=YuLkamt5Q?x=w%JyWxH6m~oZu zFZ_w1zc^rrzEvTLO-aN_g)vv!WmaD^8JV=x_Mhxh|8OyZMASjRm+_yeYRLa=ova|m zNL1Z?pZY65p)11Q8pc!k&GCI1_BIBj^e`eo9|}4%*W+TUzD&E~%lB^;0lxf-YjoI& zuqAny_IId{7sL*uz)!q+AF=-Uk&NSmyO^Q#5*GLJmLtP#OWoIuzB=Bi@ftofZL=M@ zr@^n`$yLx-cs{6TKz=*FrnGh|y`J=NdzYm7*F~Q(kFe+MM`3!UHi95^eZ=awhphqk z&~Kr3DO28Lu7aSc$)~R+cMT$TGPjZ5m2FO5o$nuL0pEvssmB}gKXP-ip++k@Wnz)_ zwyB>(!#BtVP5x?CCc}3|kL4K;HG{mIAFQjO&k?U`TOUoK73^NT5O+qF1+lG%aFm0H z;}Ua6*Y|vWEELI7Z2G3LdLqU0%*~2$vFL!c4aFJzR~0vjx?<5Jz#ql&Al10Q(+Q!` zxsuCbd|D#NvpR9v)LgnKV(5{$>mHQ^9Z*-&wTs(WHCYsk%C>|1ede(j8a`Oh?Mniq zNAa)eyX($>U{Nh()Iy=$@BJR3gC;pAgS6Dph);nWTME!usu#IzwmnKQtOET@F9Dz8 z5#6eorkJ1<0Qk|>FFKoDG0pQHpFL(Ih1^tpY3OUZ4%Xn|wx1YA%wQ2YBx=B3bDz8&^p&si{pPe% z5-jg>m*hOFT(^WVlGggf1Moc>N`|+B#1cYL{4RC;W%y&d8*eY&!B@P-Mp9dX za}YK^lhFe0udPeS3`~r?<%a1AkiSxH$3c>jw-WjGIT~FW0%=ZyzQqNFLPYJ!NVku# l1r`q_4D7Z`_=Riho8I0wi{>E+y81@NFlpw=<{^LB{{e{&S4RK< diff --git a/CesiumGltfReader/test/data/ktx2/kota-mipmaps.ktx2 b/CesiumGltfReader/test/data/ktx2/kota-mipmaps.ktx2 deleted file mode 100644 index 7504b0fa8a5e1a628cb1cb8ac71b76ce5eeb3273..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 66125 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%0AsV=2mL9`zTYMN=&Y-E}_ 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 From 72075c3a578d273667141490405c3e4e5e938180 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Fri, 7 Jul 2023 15:39:42 +1000 Subject: [PATCH 097/421] Fix handling of KTX2 images without mipmaps. --- CHANGES.md | 6 ++ CesiumGltfReader/src/GltfReader.cpp | 59 +++++++++++++---- CesiumGltfReader/test/TestGltfReader.cpp | 61 ++++++++++++++++++ CesiumGltfReader/test/data/ktx2/README.md | 9 +++ .../test/data/ktx2/kota-automipmap.ktx2 | Bin 0 -> 49426 bytes .../test/data/ktx2/kota-mipmaps.ktx2 | Bin 0 -> 66125 bytes .../test/data/ktx2/kota-onelevel.ktx2 | Bin 0 -> 49426 bytes CesiumGltfReader/test/data/ktx2/kota.jpg | Bin 0 -> 39887 bytes 8 files changed, 121 insertions(+), 14 deletions(-) create mode 100644 CesiumGltfReader/test/data/ktx2/README.md create mode 100644 CesiumGltfReader/test/data/ktx2/kota-automipmap.ktx2 create mode 100644 CesiumGltfReader/test/data/ktx2/kota-mipmaps.ktx2 create mode 100644 CesiumGltfReader/test/data/ktx2/kota-onelevel.ktx2 create mode 100644 CesiumGltfReader/test/data/ktx2/kota.jpg diff --git a/CHANGES.md b/CHANGES.md index 586b3c9de..72500d100 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,11 @@ # Change Log +### ? - ? + +##### Fixes :wrench: + +- Fixed a bug where `GltfReader::readImage` would always populate `mipPositions` when reading KTX2 images, even when the KTX2 file indicated that it had no mip levels that and they should be created, if necessary, from the base image. As a result, `generateMipMaps` wouldn't generate any mipmaps for the image. + ### v0.25.1 - 2023-07-03 ##### Additions :tada: diff --git a/CesiumGltfReader/src/GltfReader.cpp b/CesiumGltfReader/src/GltfReader.cpp index 080896bc1..564c62aa5 100644 --- a/CesiumGltfReader/src/GltfReader.cpp +++ b/CesiumGltfReader/src/GltfReader.cpp @@ -596,20 +596,51 @@ ImageReaderResult GltfReader::readImage( image.channels = 4; } - // Copy over the positions of each mip within the buffer. - image.mipPositions.resize(pTexture->numLevels); - for (ktx_uint32_t level = 0; level < pTexture->numLevels; ++level) { - ktx_size_t imageOffset; - ktxTexture_GetImageOffset( - ktxTexture(pTexture), - level, - 0, - 0, - &imageOffset); - ktx_size_t imageSize = - ktxTexture_GetImageSize(ktxTexture(pTexture), level); - - image.mipPositions[level] = {imageOffset, imageSize}; + // In the KTX2 spec, there's a distinction between "this image has no + // mipmaps, so they should be generated at runtime" and and "this + // image has no mipmaps because it makes no sense to create a mipmap + // for this type of image." It is, confusingly, encoded in the + // `levelCount` property: + // https://registry.khronos.org/KTX/specs/2.0/ktxspec.v2.html#_levelcount + // + // With `levelCount=0`, mipmaps should be generated. With + // `levelCount=1`, mipmaps make no sense. So when `levelCount=0`, we + // want to leave the `mipPositions` array _empty_. With + // `levelCount=1`, we want to populate it with a single mip level. + // + // However, this `levelCount` property is not directly exposed by the + // KTX2 loader API we're using here. Instead, there is a `numLevels` + // property, but it will _never_ have the value 0, because it + // represents the number of levels of actual pixel data we have. When + // the API sees `levelCount=0`, it will assign the value 1 to + // `numLevels`, but it will _also_ set `generateMipmaps` to true. + // + // The API docs say that `numLevels` will always be 1 when + // `generateMipmaps` is true. + // + // So, in summary, when `generateMipmaps=false`, we populate + // `mipPositions` with whatever mip levels the KTX provides and we + // don't generate any more. When it's true, we treat all the image + // data as belonging to a single base-level image and generate mipmaps + // from that if necessary. + if (!pTexture->generateMipmaps) { + // Copy over the positions of each mip within the buffer. + image.mipPositions.resize(pTexture->numLevels); + for (ktx_uint32_t level = 0; level < pTexture->numLevels; ++level) { + ktx_size_t imageOffset; + ktxTexture_GetImageOffset( + ktxTexture(pTexture), + level, + 0, + 0, + &imageOffset); + ktx_size_t imageSize = + ktxTexture_GetImageSize(ktxTexture(pTexture), level); + + image.mipPositions[level] = {imageOffset, imageSize}; + } + } else { + assert(pTexture->numLevels == 1); } // Copy over the entire buffer, including all mips. diff --git a/CesiumGltfReader/test/TestGltfReader.cpp b/CesiumGltfReader/test/TestGltfReader.cpp index 9f04b5641..cc18874b3 100644 --- a/CesiumGltfReader/test/TestGltfReader.cpp +++ b/CesiumGltfReader/test/TestGltfReader.cpp @@ -438,3 +438,64 @@ TEST_CASE("Can apply RTC CENTER if model uses Cesium RTC extension") { std::vector rtcCenter = {6378137.0, 0.0, 0.0}; CHECK(cesiumRTC->center == rtcCenter); } + +TEST_CASE("Can correctly interpret mipmaps in KTX2 files") { + { + // This KTX2 file has a single mip level and no further mip levels should be + // generated. `mipPositions` should reflect this single mip level. + std::filesystem::path ktx2File = CesiumGltfReader_TEST_DATA_DIR; + ktx2File /= "ktx2/kota-onelevel.ktx2"; + std::vector data = readFile(ktx2File.string()); + ImageReaderResult imageResult = + GltfReader::readImage(data, Ktx2TranscodeTargets{}); + REQUIRE(imageResult.image.has_value()); + + const ImageCesium& image = *imageResult.image; + REQUIRE(image.mipPositions.size() == 1); + CHECK(image.mipPositions[0].byteOffset == 0); + CHECK(image.mipPositions[0].byteSize > 0); + CHECK( + image.mipPositions[0].byteSize == + image.width * image.height * image.channels); + CHECK(image.mipPositions[0].byteSize == image.pixelData.size()); + } + + { + // This KTX2 file has only a base image but further mip levels can be + // generated. This image effectively has no mip levels. + std::filesystem::path ktx2File = CesiumGltfReader_TEST_DATA_DIR; + ktx2File /= "ktx2/kota-automipmap.ktx2"; + std::vector data = readFile(ktx2File.string()); + ImageReaderResult imageResult = + GltfReader::readImage(data, Ktx2TranscodeTargets{}); + REQUIRE(imageResult.image.has_value()); + + const ImageCesium& image = *imageResult.image; + REQUIRE(image.mipPositions.size() == 0); + CHECK(image.pixelData.size() > 0); + } + + { + // This KTX2 file has a complete mip chain. + std::filesystem::path ktx2File = CesiumGltfReader_TEST_DATA_DIR; + ktx2File /= "ktx2/kota-mipmaps.ktx2"; + std::vector data = readFile(ktx2File.string()); + ImageReaderResult imageResult = + GltfReader::readImage(data, Ktx2TranscodeTargets{}); + REQUIRE(imageResult.image.has_value()); + + const ImageCesium& image = *imageResult.image; + REQUIRE(image.mipPositions.size() == 9); + CHECK(image.mipPositions[0].byteSize > 0); + CHECK( + image.mipPositions[0].byteSize == + image.width * image.height * image.channels); + CHECK(image.mipPositions[0].byteSize < image.pixelData.size()); + + size_t smallerThan = image.mipPositions[0].byteSize; + for (size_t i = 1; i < image.mipPositions.size(); ++i) { + CHECK(image.mipPositions[i].byteSize < smallerThan); + smallerThan = image.mipPositions[i].byteSize; + } + } +} diff --git a/CesiumGltfReader/test/data/ktx2/README.md b/CesiumGltfReader/test/data/ktx2/README.md new file mode 100644 index 000000000..d06b718a5 --- /dev/null +++ b/CesiumGltfReader/test/data/ktx2/README.md @@ -0,0 +1,9 @@ +The original image, kota.jpg, is a photo taken by Kevin Ring and licensed under the same Apache 2.0 terms as the rest of cesium-native. + +The other images were created using the `toktx` tool included in https://github.com/KhronosGroup/KTX-Software v4.2.0~11. They were created with the following commands: + +| *Filename* | *Command* | +|------------|-----------| +| `kota-automipmap.ktx2` | `toktx --t2 --zcmp --encode uastc --automipmap kota-automipmap kota.jpg` | +| `kota-onelevel.ktx2` | `toktx --t2 --zcmp --encode uastc kota-onelevel kota.jpg` | +| `kota-mipmaps.ktx2` | `toktx --t2 --zcmp --encode uastc --genmipmap kota-mipmaps kota.jpg` | diff --git a/CesiumGltfReader/test/data/ktx2/kota-automipmap.ktx2 b/CesiumGltfReader/test/data/ktx2/kota-automipmap.ktx2 new file mode 100644 index 0000000000000000000000000000000000000000..44a08a447836ad836e8b51b0b0589a02e619c7a7 GIT binary patch literal 49426 zcmZ6y2UJsA&^CM$I<}AiqIfld0Aiyz_1+swAOVqH14@8|F1-s9dQ(d12pAxA!6;G{ z1QqE>6Co6%q5=X&qz1^B`@Qe_*1!IpwUg{~CMWwOduE<_W_GQCxiwr-zWtcsX+eO~ zK>z#n>c7|jZU6Te=RKIyT{-;{r)P0`0H^=|8~^_b3%>f_GynVg-)H`7VBkDZ0RjVD zT>mrvpXYo3Z@}g>C}+f(mbaf9;Z6WPz|H#(;OERa_TM!)$AkUc0tkLUfVW3L2s}vn zisBXdNCgGB9Ng2*@&6rCh*aQw)Bi`_%*hzdR?-?+!4>A&k;Jr#ulU}d9cp9660Q>2Y(xSuTmYbJVBZC}LZR5H7R2JJZKLGURL zW$aU;TUc+Y2jEK;z5FpI;1QdzjK5maNmD!TkD|1;$r(2<{Ci)5 zGk+>(A$8y%7e44DM3p$DFzi`Gi+`^0J<8X~_o`+k;b6{*Rdru)Bu1LJpiOlx7)gi1 zO#zwa@h+a}$D8TPIGDEqcSfL~`JPG5lNN$C!nQps^m+DRHI?6jIuGjIzca-R|rAcAPVdgkCp2i9$v)4V?8t%f|fjG(PS+^P>l-+|l61@-aUEHgVtlJ5_DyX;?AH_)jE&7k|Gc&-J&l5JJH^w_~YW3fj1dAf^?pR%O5q_h={Z5L04@-} zc47VK>d^xV3~)${QB^-Fqykt#7QqxVs268CN7oj9t`Dvp-KH!(q5X)_*zxzq^Gkr0 z0RVeb}X4B%9*!*z0PS$mg)RdST`@dl z0W<-P0`ytUAgm}yU*tLFEF~|cB^-3D;mm(yE(AC!uV9uGRRI@^hM$izMBQ)@l>kxb ziT~-3*!#c#{2j#~CB_^Fq^+ZoAz+w-5bzsrLK8}q;E(Zg1wlbDrv^n=h4IAT-IN30 zVE9w+ZS`khGiotJJ*~~HfZ(>TM?C?sViu{eo9FTQ-?GvREE+oLSyJapx&;&*O4`{S zhxv*4*EiqU{Q_OOn^D-fcDS}ij-RYD9=nFkR2~>j54-@-v!%9Lv;;3VZtbzw+1Sm( zXCNtIjy`)j{yK-M&U#qbta${&8FTCCTJWOfa>%6TcEeE;|Jf`3wR8ReGf@;?6)n>O zmKTCcyc;}Z4$22w$2>^{!9ibLdiCO&4{s_(A8F-Md-b;W2mARt3ws&+>ld>~gvvUt zYs}hZ2;~{)gc&21H9_U)V@I4p{X$1FyUq!*MYgM5T$O}jWF`GAykR+_zx>Q@k0*fR zD*xw3eg%YLyD89A->icpmzl=EzouVTmAGo*Man1VN)(z~Bt7NN=L_x^4g_Fi8OwKa zy5*X$p@XPCKY(1SJ6Q(Iy^!sP*1QS}1~~SlwMKqlODn?!T~S-X@-00aPnpnJ0hHjv ziiO(yjM?Pl5ue`fi%!Vz2Jc7E5n1o#%dR{|!&mVgl=aVH@%h7o|rmy>>~jj`45aINmrf z(0?+kA?_swMKLe{!{Rshtm-j$VK&0dE>>J4_EfCo+K1w8z1Inp zu4lqI=K6ugYtcg_I%y@G>?l*tt;{X_jZ(~>_ePU*#`+azQ!Cy+`zPaEmKQ*Op0!N^{{$@)Waby$F7Jkb2nSWaY*1|+?fcGK}F7<)YouPp5C|K=N zOd9}_hbiQ_(AOC_xd28=y9DT+sLh=m#`A;@v+Kwq<`mnNDFP+x!u&5s6!+($1X^jPofNW!Ao zL!sq!gN#n&%p^)`X%68-V=6ch>lqk;J^$-Y;pH%+mG(~R8S}dUuKA9io!gtyB=&N{ ziaQ!Y*Ii%Lm$@|^K@WVF)n_72R+_uy>y7zF$Po1nyMGa&&D=a`+FAl=qob^qwRBnI zrBk+<=;R9Ye9ZcM1Kjy&!iltxe&)a+ykU?yS% zhMX^D#)FoUlG)?1qREqwErd0(+~*Y{6aj{bL2jnXTo*!ETRlFwbb*n1mh^ejW(jQ` zW$70hn8p|+rm{Zj<21fYHc1db1jgqTU#MhLS)zG-&7SBoFYFY2qVjlZ1>wGCdx(+& zz;GkKxY4Gp=iIa_|O^fMnq;(PJk797(Q7>M!RTv{MuTp<``Dj92*B0JH@x z=I-8u4<58o5~}^dADsu!DthpZfF=N<0l$wsYh+2hT$t$WS|H9)64x`|niR$&{%DU% zFi{_wU5M?<2jJ+0n^;BYWa^V-y+#b0j?bCKqH$_=87t}DH&1gI#!mm2gvm_=Zb zM0;gvjbm#z4T3eIdwVe0Wno$U`=!WCWU%`Z0-&7w1wtJYd+*$kvgzs1(%U6xp*Q2teW!TpgN2`hnd~d}Yq}LzO zKZ4JcdHO2m8Un$kqnnVNH^MOS4$eSIdoz3r)aHNR)LoCM3h4O&<|th>w>;q&kz0639PXa6itrjFQj+mzd?$S`f?fU^hs z4|$Z@RFq7CeTGefA3^)YJ5QSgjXHtKJ!Z|qa}%zJueJ~u0!I5bkuk$h`xFxj6Ok9m zPHtY$^EH+G)kVH;ftO08j_h~P=`Cf7mXR&*z**>}lBI?vvdmoirL%RnhyE@i-|LbD z$dU`i=VgDd?nJJ#vu@u4#w*8uVQ;)J8i}~f+UdFs_|8r#bj3tYXmOkA2j34W5wDV_ zTKFYmxqNg23PRT-VNdw9e(|iaSCofJa?YOR#9su8s4hQ#pZcq{ym5briKTtAYx^SJ zah=M}x|?wXk$+NtH)h)hEn@7As4$nL^-Vjt=lEC7WG3xBh;kWtp$4_74VyKs2=nBg26ZV{UxvQ%fg1teP+7L%C#B``B*pw{0u3NCl@pGxh>zh#bKKrVA-*(}US@ zFX_sccRu~|5|e@{;V!YLs4WyJzt-RQGues_dM+wqsz#>X?)*Z|D5q|y+Sp9l0E`*X zo1>#gBmSZyb5*BLZsSeO^zKf?umlyQoa+ZVdwuO-y;tA7>f`lC=hht#A2L?>xsLyc z^5hpIrmP*T9r{kXid=XVWvl+mwQ_!q%|`q|5z@haLb2??dseuOq3}csMsheI z)XGI^SHziD{-g;LwPdzvGB<$`QB=P-!Jw=?;*o#RcMkeHMDW zJTEI-4WPWaGHi^%$s=<8TDQaEb0~2Ys3U#+wgNiWv`cgdN(^JS^>3EjYPQ)4K@B38 zxM0mC>zgk$7k!FzBdV@j0gN$XdeN%Tc*FFcj=aAkPBTwG5h+?4VT9L&A}it_v6va> z7NzvswjpNDA=x(X(#9#pFpodAYYkz|=d1K109@mhc>h(_SrZRqQ8a|z9!|a@er9uN z&fbzo6lI%TFD|+Mk9K=K(TIoYL3J;8AAeqPHtX8`EzP2A8PbpyKqim;lUV;?3*C?W z{A$z-pb3nLEHbSnxXeosGlsh5u(Gu$c4N`Ogqf4i&9F8cc?o%Y-ybhz=S7U7j{FOV zXL)O+_X9pxM6(?_@+1brS%68*ZBQrr>QU%ItzCxQ| zG@A3EOtOjJ+zW6#UZAU%vbP-~nCjG0boE_l2~HoXN^bmrU3*xj(Eo2ca+6tM1t9 zivR@*3v`XSlXns>t7VvL)C-du)mH3NG}@?edysA!0?@`1mLi8g-?_1Qio$PUUBg6u zdDfTg>e}ScRGHJ?xDV?(Gl7gCVl%_muUYSe0Ej`Wal5Zbx$cffa~X#;HU%|uMZdyH z_Wi-ueBXTKfdPoS^RPbh#EFAV-uw)1j{k}u_0`q*Vy9|Dd^nvqR~U1Qcw6@ZL72zV zqBgr>jfoPMzgn{>%j+1NH<-eHj3a$Wv%h(jYS*LGqvYhK#CY{V%I$%h>n)IrWwOU@FB#{Ca31%#H!rC0DfHBD%UaHs$ij}CR{6>NLE^C#B9%96)=hYXt9C;aAawF zct+51bDegc&H_%p~cfK&7!yU@no|a7`U9rhcSL%ktU^rVKzlrlBrDQMr`)Z^+gnAn z+$b)le&?dpzeUYjpo7%e#J*O%87#2-cyoX>D9^REV1E@4OZLwIH##eVb6p2(h7ZVX zi`JioC5FD$HX`5e-2=!)w~n{{BAmIHoT)y*j?P>2Em~iG_Wa_)L5c#E zm|jWdgIduQ=e#NqxC1 zv{5JW*vna8zCg*OU~h9#dPcF1oKWrq^Ee1v)Y79J-`^P}|JPr@UocOey3~RN9FN@7 zV;O`f**=|K-E6B~7?tNN<4V#Nex*F8txW}B$-vdR@J(3|mN82wr>1}`;(Y@@mA{3^ z=li$jH|PGTS8j{FD+fC_VV2Gbzhc;5ar`rZNtqYwIbWv zAB5zg=CsVUN3pe$l~2|wYk4UHvaqTXsAcDv&537~^3kOcfVjw~)?b9h#M;C+c^O%X z+06do4>C>#Xa^bJeW!RQV@Cf!lYFHI`w@0_8sI6fL54!p=5s1XzS_*&sTIeMxnpc@pXeL z3D+;O%`?eMMwnbHH~R4DL`b~ptnm(Xk@lLaYUzQyYj@vh5Ci>*K(-N@s2=ynX0q7N zKK{Z0h#Rtf_FotOdB4tB|GKt(z_AxT8U;=QmIU1x=ywRKi{BP0oAX&r9@gUVGPz-f zfhw$WeNg3UCn>3A+G5ONYL+d!W>zOcg%YarR^|>Fug0m?58M*Dg_47Z+ZR?nNV;jm zYxj-nRROpL{XhHhP5oAnY+0(cq#`k>Vj2gM)3fZCb(wwHb0;N&c?yO>7&YFEuo2zp zFlD6K1LW?rvIcAU8|wYYbhaG`Aj9T-RJ3Eauo?017a1sk+1;S))OX7mk0}dl8?HKJ z_OZJBr_V@82FmxP?t8qWb9u$_JU-Ob7q#%Hb}z1`^c%s-`cC=!7aG3|=+sSN!I07J z)6LMw+*ARoK*iy>kg#*>=&Kj98>-XD1Pji3;|=>R`ReMil?r6)CC+*#FZ@zG@wQDi zgV%&{`w~D)y3J?qn#y&>8AViVt77BGWBXp`%fNz*_BG~sfJuj)Tk|?aeDH8Dy9N!h zK#s1ddJ?q82geb&ecPKJU8JGN z$Ees-0oohZOtA@Z{j|ZD!PKQ!zfAKAF_N*oWala6zjnqd1jL7tqB{kin3$K0lToVL@#snydqM`t>2f}n_8_FJPtz2v^C#qL}$k9E;+gbuu^H4ttYRq z3(PDAqh(G2wDdcuTdd=c`H-Sdgtm>vX|P}DzcvLtM4~(uN8<7VVNECAyq##7HChpl zxz0+~?0nM$rX+a8Ht_rY}<+O{( z|9Z(lQ$Zp`A`3qbt1=-UzPb{n|IB`e_zsqhl!hO3v&m@=XKaq~1HL9W$G(KX_nHzW zN@Rn*dWyuWifM9lqV^{Rk`^KiK}q`Vvs5g%YtB#M|9WA^fEM+<@l-Khz;Ym}^I6?H zIwk7m7qJHve)+2S_LnscRtBWUC+>B2u&+d53?OJOfEM-2$NIr?UPpUof7Hw~mI6xr z3AX-6*ld0g;#h52gKLAF@(!rfh+DWL#B6QBL>`X|94xYGgf4R8#y&)FI9-y$x}0%s&18i4argyZp1t1=g%VJSx1e{O*_z6fU9C(z4#c zz=+3D!^U-xrd-6Z>itw2zv-CYbcuohUQ5fFk+EKHlSe= zPJGoY9@p4bAy~N}^vK!X$6az$pYjhh{+RET0GI;(Ik5qzX8gU%5M+`h_jE8 zQgQ!6VzpEO_c-~rty%H?Kj;k6hJS2L0jALV2Y$~>e2hYPbsnFb?^JN~=PB$O{itH; zzs~hS$kex>gZJ*-NE?^1fvN@s#bTPA%gOxvb`&C9vO3o7Ul&BC-0#AE?svjP#b5~kd{wyNr z5*2Gz$ybJKyYVR!N%y*tzqzby}pBeOPV_t zQ7{s;SGXy3(ZSl}PPAtw!*BMq9q{t$eVHJoBi@J3d-L_Jzff<6%-fRma^8tKuP+zKB!6L za8pi*N;4J?l=f74*v7>KZ-K8N*0fPj28v0gp8Z)B#c z-ZG*v7u<@M@^j>Tj>aVHbcb_&jWgE0u%yFnA!1W%|9hOI`cWm8lMi*qMqQY?2hh5# zDo&N+1#JUh4Cy*m3yT_^nURl{xvI z?}pS!SPq6uPrD(wIn-A?Br~-7L@%F{zs}a&+jhKT(ipb&Xe(DhkSgo%02f&H6}o^_ z;nVXN?nC712u{A`QSO8DUv!D>LWr*-6ZH)Jg;Zw;1El<}e^v;@8cHS?l^N~p3!|~I zM^7>WfWpjgMBqMJ=VUb60!y$nGt0jH%SXs0OVgNJY21hlz{Pr*-#kO>A%qLH+ty?( zvtZsYhZD_HF_VwbPz%vc9PDE3+EKy{J1TLH)^O6C1#=MB5zEx^THah=T)qV`lE3kQ z5TeYmO=$=CS9lAvAC;}_%5M?!F=R(U9xBWjYnt#WDmD(S)@;#9=$vJdMWZid$3qV0 zjCC4-8WGAVXv(uud&eR;;x|D%g@=)T5^Ui!z%A|MfKHGaMzCOyyRLBL^Xt(ZVV)W$ z#!5>9eM$nxT`9JCeKwqi-N+GA5n~MZNp4!H9Pq%(6pjju$16(Z=Tw$^cyQwNG{d5$ zQMyGxu#JA2R|R{1=gi=_{_xrMf4%i*1pye+D6}%E?$(HU!?Un34xY$fwLiVtPX|A8 zqqjy5@0NJ^>|KQ*u4kUEN zMoUA=Iy>tzQYnzl0A|VsY9s`2f-qW-XAam*{?Al_8c9JoEFtM|fz4)+93Gu+a}xIy zu5q2OU3kGn(c$_{GB~XqLU3*2!aOA^#5J_^Vq3h@Uic4BxQqd5{*K)Im9?8e&atY_ z96W&Ekqk7x3aAu!QMb@?9%c}0k_BqCkUkv~Rm=PFa%Q4#_3ib|QBJ!aJ2nUT z&Bx@abejtuW<2D30+UpC`L$qap`()DfuC*9IsNwCdkfw{YgWOG2cvAhm;UihC_)k4 zQhi|B&wzz=c`hBJYbLm^zoge<8OZnj_B&C7suJy@Ndj+5U~eLqZ;4R`YW_Qdt0|QjF5ksmj z8xrco`SzzBkA^?4`*x2!-?`3ynVcS!Gf# zl`PyiL9^%BCvg+R-}axb`^gvNr?5Jhz1O16T$7b ziyXeJ47V}<_vBT94AGY&7cO)7l`>C>?Dz*yKm^M+79oe6do4xjx(F0u62Dbi;||aY zzJt_)`gux2;&(hM0e{A*eg@=}7~CFQW)qi`xXy}}KBp@d9h=dHCSWZL7nyw>=}N}B z+7Kwu1?yYEY4l8@nyy$TaHrD4*b;^QW=8d9z8yB>=({kV=?~6)U<*f&>J}*SV7M>T zlKcQhdIIzl1o}QXp*4JNm-`KjdO7gZZSE|Das1Mj+A)A>CihzEt{yKcA*b)KXc0JIU~zvjTA0mr2$5 zo*bK|8(aBpho>2sfvA}2x#*L|LC$E2g@+9NX3H;=VjCrTaeLZ$OAu{<{r2~$)rtVI z;p6jG%`j$Pwa!>yiD?9!sN7PwpT>ZO7+D4lpdY%$Ef8WuA>=Xwee4S68@9hZzS7GP zpi|ehYNsoOMJx%ne;Rge3$tAO1XR{;$gmt(<^}9soRasQPm7BM)C!H_A~RZ*hzUjI)F}Llz5b)mJQ;JBKmpoa&reR zMm%4!8-s9`wwCr%j8qCIrCfvbsN?T`M_3ARYjQJQ5u5}{odnK+!;ow4hxJ{rKG$iK zN?pAJ&qoiYEYyp~X#L~Uqp@VYhuXEsa?v40<(`)<_@aT?Rer7%h*G9S!I1))aDj?_ z-yKsb+?;~ia>YbTo(FcgpYq7dfDr?{_$O&s<;&K6*NoRuL*uXrbAO!=_d?ladcsdH z17>8;`+>h+ZKKy2B)02AsbeC5E9W!8zHO43$%VAQhJm%g?0hVuy-+S~5Hc(*7j z#PYB%sYWsCLPQvVFWd?mlkkYS4pjw@gsl(zp5}xcC)@p`96$)P^1=%S^wZ*%Wt8{) zdgyRSFbED4xomu9DtYT|XD8aQHkgA)q%&%##~A`r({#Po7i6 zy)1FR2Jg2eXZdYR(+94~iY3gCy@ms3F+I{FW@e328S!C~kN*N_$#TMkfSX8tw9s?- zHZA}^F2t8lHQ$kvZ_ztU2}@!~$fIR~c=_(2Z6Kk1gZC)bX4$Giu6oKmiq)EX7E_Ee z$e@%@{K@p9Kuo9DY6Nq<49uX~Eg3-`B-HL(^!S==yn0e(-zVg!YSLMiG%HU2()hS; z-%iSjl#@CAwp{@?0R1v5@*B`E8)h92%)*+Fja?Wv`Jidc+qHTS4&c}p=rB_2WX zP^&9;J!ty7aZg9ZVyvhx2?qilPB4}CjW>@)1z}ej)87l<=H!Ee!DycWoui15s@A=X zJbDb_@V-T5sz*WQpd*o}WJcy)yKR`)Cc9B_x<3%h;UkK^PlS)YFkCK=e%5XXgbOv) z-DkWL%j_sWZE;$_Tbx|KDM4E+`Nv-=$Thd;F-$UG{i4!ronb*P_Wde{&z}kJzBo6K zlD|+f!mB8(i6?I{KIk`XEBb;=2WNU&6hUb22S4E^b65G<{-O0kCO$XzyKTVKO>=jP z;iEs-7zMmZM_17XD$%MA4J|QA6tgM6Y{kYLhor{Gjfc7Y%>Ep3E)kL-uZxZwPOkMB ze?B{wH^2PwpdX{*5 zVm|)F8}@tl=e3n}@pSS0|MWZkLB;*teBMTJ67C43`xL%+u-7wI#fhlUJ9$|w`H~nw z*7+X1Wd4!Td*H?2;mD7C2mh$G=ft9u3NH!uoWkb~<>g|X#kg@K4}XZ(DZCg|DB8qP zAq+ma>2!Pqt5Ef;v4tyOOSLS6eY^XQ=s&#z;Pk0WTDyuj)D4u zmn}uwW{wbcg14I6a3tQLYx<`Mb}AMG5t7JT$}3Ap4j!<+1$GDDx9CWO$wwHN4yYQ9 zcRt4ZU>pE(^19C~^XGzqvBeglhKOaF)ZF1SnC{_)s42W1`m?|^U84xAwKwzkHMz5r z*>kY+D!$4TpB(J*6zQ9J!)a5AY`8nuEXMUtr~>@u4&G3uM*F4^6gvt9Lh=?m}`o z`TcZVe{O33q2V@Fb!Iw@HOzr zXrDBFZIbk^p|v*Q9V3M}>A^5s;nNCQPwq`hpdBfD6~1_XS)pK{u%X(`qHA>Iv#1e^ z*Q8N+-GK$%|2wanOc&If~AiHk5B!a zU!OhPHzTP)gunmadOqkl`yN2g&;r!71Q2gNeh~Uz+U`OpjYXf)J%t|>qY~Zx>+0!T zb9fQXICW-zPcleiE}XO=&++FJ%ydPL;~tbkCw=HU({J-7(cE~MSb=DQNzSQ%)qvBR zkwd~^wyvD|JKX)N$KW>4oUzNhrU<}{v~vAS6#Tz>!sQK=0Vm&8=qcZQN)oa{V28J> z>57vq=TC9$PhQY&*jm_4IE&<6ZQLVUX)S6_v~M~j7RFSjDd=k~0v|2rEI-;#gvV%| zk=K{4ycC?@2B|=d!{EXd>E@j46K$^A-wgv42BxAtSJP&0nyN!CD*P)%8H>za3?pX+ z{M)ORpmiKfv2ik{&x!h7c9wafw^>*~rxdRww>Lm>(ze7C<*n>-a)i08LiyTI;A1n( zx19Py5#>_-wzB%mZ0h2S*9i{3W`T=$v;U}J_U{&GaMClv3!edphlg(Si<-;($^aSZ zpp03}+d^-s3Eeyh)#NNK@!aMnu0vZVXqfwSU5oV5aGp^muou z$Rmt(3TE58i82nKp0>xa~sYda@R3`@GYf7n6aNOGx2bO8Zv1<|8Tw`|Dqc& zqrZgb>Dx0VY4VuTdwVZ!DW_&8oJ0DR>_&`-V_xb>Q?NGC=AOrd?Z6-1Saj1WrU^0U z!(G$L#3L0N>jgnguz2a~r0{_)!<848l<1uLc@eK`U4#z>0#9yBi<*dF zWM?Y3&G#(;nqZA(S)YA0n%7)6CE&0Cfs-`kL?O6#^&g!|`&v=8llJC=3xW$N z8Mj6T7!xrtCy1GZi|HQTCWc)5360ZG6oLl^|K!w%ti*ZD#^Kbo!EY)n4hqTDnUTf! zRSZ`en5l`+1p&&q^y59U`ZmT{2PThPE~L%EgC51ON-sgs(8}b!@bKov=7~RjMW!NO z&mErI6vdlKZl2fzJZ}lTRA2Adf5;H%l^w6$RYg>jr9LLM?J*08=*Z{%dLNcv@K+KC)gG1a=9fgh(U~i{wN}e`C0@$^;1T~#s0v^$v5-VV zX%p0wWvs(k&V4#TfFc8HMzv1%{qf+3ON&LKW;}-v(+=&nzx;@8-e%KO)P(OkCNK6t zUW^;=Qqi)?S{Kj2eS+w_Q4(N7@1D)7c(my3LtMpOrqx<9> z;)eESzg5alkkjMzD?R0J4p+%;5wza1T)=Z zwo=iCS(BKXqtfFF>0iF zY)}g?li#?qf@0%x|LNQOjPQ^kOPNPX+qC|)#4mFq-QiYRg!5b_0Za^gBKB^Yjv!P7 zc`tnWkO_4aRf#m~E_$)C>Y zV2GAmgbn3dlW`6|KXx{ww5#BE=08j$bz}4XN~pQnKKG^wiP4S2+&O@}cH2IRbkhsYQy7^4O-f$Ad)@)_Z}i8nST12dufe^s_ovLb7Z`P!Y%BU1I!)XaHcF}yj*N=IxC5-TPb zd=C%@tyU^_^CKEpIt%Zu2eI&{ezFk)Gm}n_$2!K|qbsb5jMX4b$@kk7ys7Od?YdxU!UT||wc1=WWLCclR zf+WPr^D8D=*K09~80iP6O7dZ);sr26#8PItL5{GkF9#2c+sJndN(SsaS=~EaKP*hg z-G^$aBNF$0S2z_y8936}<0t!NDc0Yz_F}`tp5jQ+Q5=82SYB+3YCy9ekWl`=F%K49 zU(D`~Q0GuHIMO->*Dw3UvpFyPq5DH=7!s7ZSgv5Gw9;|?hPwm6&>YuP^%rjzb>?|p zl*gW-^<6oB(z{?%8qRu&?-PwNBj@B_lm2*}6aV={bSeT&Q&P?|M!sWOp29--!x!AN zJ{!?zqaS^7dqDwu)jsXMCikqqTTaI(@)7uItXYp?xvC#Nr@mS2~ z73Uy=4-H`t54KaV;gGB)8-JJJp!j|LARqkDxinteXx^H29X(}kMJ3-%*WTZgHiO>jl89D@f*2VtM(D_+!m6%Xx)xju zN;DH#T=%G*N$ct9cI;kA1p~YF!T0R~ipPqxePG~jIO7ZeIAokdMeN8#T~H1RB4viW z`K~|H4DFTdxgFcUMVrw|wySbRW70}|~Ps$87{d83Hl>|$nRAJ)9Ha%OY*lRRb z7%;}}7A;QX7m9eCSW-Ut<`V8;L8)$PFq1m*qpyALs%(E_*2I(_hi{1-yu4oULUy^) zHug~)M)|oD=@wLP%T|+W z7@3-VGB`_7HW_*0+DA@4CrRwx^Dr}uIuUuIOhtV@ten@i)OhCl<@u9uKf7@F4oAm0 zqrIpLT){5rrRpE+zWq?@@tIe%){>~_9KhT{5q!0LsRzY2?wniiAljxP=7RksPPSJ% zSIJX-m)~*nAwAmB;S~cRZ1*h68ASb$w}7CX8$B-e~65^{`v)n51@MUr~!0BAp>k<_4M?QYy2aEq~@y3Q)zD#Cl;iNjp5<0$gj!6qUD{?8=IBguC z*F1jpx#kpn*L0s1Gu#U0`1eQmHt#hBD+Y(hP$@#3{YfaHD5~)m#E%jsg;$T4Jbxb0 z1=mOqCmVoLM(%ZvuQx7NFRcsgc7ga zkux&OpHhIFcLng+4dMOiERqOsc4+g9g;Yx4p~aiILO;qvd0kjS@&OIIJxq6WPSZ=Oh7jfgV4R!0Xxy1nqr@KO=*9^4lG&><+`Vd&BD7 z60|0H@~cadTx*SdsVUVOjEW%lb?AU{PoU@ey?O6CTQlSmWjQP zg1_|+{dsaF4f-TbQ{$Gxk)?=){fEuG3S<7}gvcAp`tMq;mkk=Y@YrxH>G59*GTge* zpDCSlO#O_br03^MNUcs^2l5skGYtG=iGD`9k-Uoyy~}~`n6?9eXc(WUk8-v_jlqZ- zvp+%G+4Y1oe7nlGQaj28CPeyhRX@9wKN=c9HRkZ6lragL;Cy-hWF$B55l(##*(*y@ zgm~lT!!u)p#6DH>bGjBfUGxLKUl<70^am(?!&)+C=lEn=Ai2ItJ)Nu=nQJmCBgWqA z?hU*IkTE^CVI17rk>1X?XGN@VWRst5BLCi;eqm^mV`>>A3LtReurGZ%r6La6F-p^^ zESOdh)XC{hs5(9S$nv@`KyKzWSZ;ag_F~(%f!jUr#odwZlpaVOh?aVhtc|sM!wz2p{gSX0{i!k>u|77Z@U)1 zeYD@JK}&D|X_~WVs zD_b9xKQJ<1%k{0SEw{09@cHk09J1I&cW85T06|cw0BswznsYro6c_r1a8aeM}U+~@EE`{!XF8}TuLqaO%T+>DJnwl ze6$2VYQOZb;vlYuPSP_|?GclFxW-;zTkEwm0C|+$PUAp+ov4%de)9nX%CQUHoek_0 z(MX9-j{ooKHFV}rcHlP-e!ljdy}}NM#WF>8)hYbHbkIO7cPuRTMC@I)w$O)O2Wqu6 z4S>cEess0N<$eu2VAio3z$De?M2V_Irbn_UJD~> z{?T;WzuiAroSZpxM*lP(P}Gr56iL;=fl5;CdSsL|*?kOkprHnPi z?<>E>Nh70*{pTqTd(JGI3hFZKiKyST5;=p+_I2jYdw!1oKg;=Ud$I>CkI~9G$iB}I zI6d`cH8&=$GoePlQN@hR^8smfdSc&jqbcXVJ*Wx;H`BuB#*;NA!W9?H4po?=!#}1> zvzo8tg;7a5BMrt&B*k93*3SdCAfar8hl zQ|U=w3Kabe(`R!wj_dO;SnUrTyl5G>jrp8xddx57d0V=L@R2f_v) z6=<;cz3;1Y9wqe~x3;v-9(47~zVDu#%KR5rllmfc!3{CWxNoBHm%?qRdTYu~MwxK~wc}w5oqS4AN0u3phQsLwxz5COZ(1V5=9t#nyC}Zq{|`&&9n{qJz5RqLc-25CDi%T&l`CDuMhg(IKte}>(2F89AiXDm z0wE+c0YeE@F-WhX^eP~|-J?>Z8j%{xd%nMUCx2y_VKQgV-h1t5eV%94t(2C%EIZWM zGK9!>n^y~3rP5ApH5&IXSM&mCuejlnNl&i5)R!v({$j(V<~IRGF%=;1SzkZ|@8`8N zcefh}<$632tiJ2M05E7Wn7K7P?D{f8kIQCmfuUO~LHfAT)v3e^0`ftP6k<4Up+5&vrevPyO0U+ zIyvq}%bP-ZAZoB&wOzmf4xA-zrz*1Im8LD4Q5IqE+s(lH{HkP)(yG#`C^PKwBhbH( zc;W*_NrW7SY_ZhMHG`ka$D7AzzeVB5zV)!h}bhZkK(fXtA@_j9OhafrN#s z=4ULR6K?OQRm&`@k6FMo6*Jmze>@0ydtculMx7#qU2rV|zy>jf)(bQVT7*qS2!Q>< zPy#WtAO_OPBlVi3_<}lxkxz+sMQ%5cprGPY+rq4@yd<`XbHH}eZ`i|kXN$+ zRt5_QyUh6C_h-k}y6#!_#c%}dLtG8$_r^{A9r|MkK7Z***-IHXd*+rT)m;_wP1^Z1YkBtI#6;8No2{&nP z1cE&Ljj7(Q2B}Gb{Og{<5Jud#M}Z3(=QqIq4K8A@NaZyp0k!5H2@SmD6JE}9%-neU zXU%2*T&BOr{yP2hw8wCJJKnq>2QySNdl%C168wLwv(6M1sWd^ym_`T$;Q0iz>B_{$ zt*vit5Z>@HxO$A|ap&7!okh}?V@|>tsF+Vl^Fm&-zDNzz3m1v5}X_ z7Y5%S&fZAB0Y$@TRt*cYu5YQji85Eq}(0!2A8|zauH;dC2a(p(27H1VGhO&}vWA zo!h6mECvmarotQvS2GI?-|g-H($`eg1bP;AX>PA^r++>9g2b|7Nr=Z0O5WoK4~O#9 z<$g(mehH2m&kLEs&YQGuT^Dr-N&NNa%Iyf;uaEai_QcI0vS?S6F&F-5Q?U;TEg)4o zMZz-v*l$gqLYmJD>~PHMV}dtt)yZ`l+XU)dD&GS)24gD9$~F2y3sYl56^rfLLSJsY9q z#}859(cy{bmDL#g_kZ<+Ih-KSWZG;>SM3o6^9fFXOCJI`h8zohm}9vJuKzbkq#Cj% zv^xd%4ArIp?Mr@{LTH1#Gu^MW{eXpvz0bV1sg6{P3#)NU` zBt@489ItJW8;n4Hlc)&mO7jmmtUNBbI^qlgS`d}z>VsNuLt~7lGY)4gC>O0RPu5X6 z*#tDpBAbyJqzwLxx-nP?xh`V^)u|&CE+j4U`MhY}T-9-Xy@M7!jN{LIjB;0!9m?-n znJmkl!ii8pi@P60+Wz2@xA#ll4 zE1XS9gU)Sj65f(!U=VTCTY4+CGlTThA5}XW&r+{noIk<#6Q2LCk#BMm4*7+*TW5eD ztWPXpRo=Xj3aT2f@r3RH3zD{g^SL!mOZ&E4Y5o)b)QlVrasM_krvg&8swwqm3wrqV zV9S)XV!pQQZsy`F$OAo~ww=S?GCS!T~TUw@W0=0Zw_c>$^18pgP9rYNjxhGycPX{Bl! z)*iz7g(`iY_Jj3)j(V7~A%y;Q%7CPJE?1mK%-u*|o(T-yHz~hpK^G-EIrN##=>Vu7 z2{|u97M3$tw%CC3la99L^{Wj99>B}nhJmiqXmyv6p5PFyEQ0~a3 zyxcjMr{7_J5j^))ch?Yk0XbRVMNQC#z@yH*^0Fmvzuc5u^xQRq1Q12#^(qJS;ZfLa z_I!w@Mbz_G#WU?mm&s4kj3yPA;U7GQ85EW3TxaUt|C~&W4u&$%&CUwI4>Q1x$w5OZFTWz z@7MzR?JlVAiCWj&zp$j0Rnj|8S`(tL)$kdJL<+Zx?aB^$EE!xY7m@A6u6Q$T_o&#D zB!6mp>?xAMs&%~P5%$f%KMxz&9uN*qtNc_=F@XDo{0C0ux2#~ki6qYp5yq#R!pa~| z$HF2k;uulTMAfMW^MS4v!c<{4ym=Z+3?1Jegq-&()wAud_iwLp0WAV+saY~AnMd|r z>UdR02El~k(!bgl%ubNZxLM{j47JNQsCec&_bw=^7!C4Nup84=-+K>Z#R<1kZ%qdY zg-RSN%+1YNrm2l%i)?*mb-uVg_!D*t=T;uZjojfBe1vD_D02 z2kYWA-6O<*^*Ek0HzCk+NCjjRwg(8gZ}?iqN!*dC+h2+!Xj0J*DPixd5O46{pX2#$ z8g|sw1Bul%LX{N>ksMNTv17bQLxUrlIn^9%P%ygfU^Z$f*cm9(chKk&qurM8VqkB_ zreWv1OsL0p#S>^Yd|C<+3HU3*uQ^5!2fhtQ$p?327{+I11L#`NrP#nJ!rHtTU*~~( z3;r9&kH{&8*ho5xiF~mP=l`wnXw_mtd{R))%Ec2hEYB01zuN-pZF!$6Xf6=I>{`DM zj-*+tlPqM)dQTW5dgiUMS1age%Hy~v_80>2Sr7WS87Q)B^9mcXx*1c4w!aZuGCUB; zS@Z#NqofCqONHS53DB~v=o9DozE+6zPWy`N(FMy+`32iGhjmf}3;oHd^zE0ElCDR3{Pc;D zk%ouec@>qYQxG9Itmj{TUzkBUZQn>!KM#m?d^p_qlxY<4gMGXyp9#->vAqEpB!l(G zU$zKW$Z-D9Xqk8KorfzqS|yt{5K^F4nwR3VUX^RZK?HuDUMd9dPo`LV5OqmVpkQxwbOVM~Z2m zj`i$1`lWLJ7Jdk?n6&$ z$|EA63)n+Ijobcw4ubpZ@#^u>%yfeVRH)@b_Z2LggHe^HQI-)Eb@^>g{QD)D!?0Pk zT2*x{{EItL$Tuut->yT%8zPIH)J9H!N}Lu87nN~Qm`exJaH>ty{qMQO<)AQ1!2zQ8 zi2R^I>xuONdAet2Z4J-~sF2DLIiETZ-W@uFUx!240#B`7ooHH8hg^MC9z%MlmR{vx>7 zFu~xKA%5->4A#6XZ(NTx^0-0omHt($E4Y)W=?xlEZ`3WKi{Bk*%|G=L&pL=CEI_Y@J z(p*zF4TvpZje5cr1!3P?_n(NJq;mU>wg;bShZ(Yx6QW)csHj?WrME%8*oP-8V!6!- zfcRpRD4h|O$w`nW%tiTAd+;e9{1=GSnUsW@=9FGQ*Y#nFU$0T>QFO6kv?&Z8cIHu% zyO9_&Hcud@66Cl3D@T^po-ert5rN9v8t%}Cwe{m#QcGmDdDII6j<13J(_|Z23-qm} z^uMK8_g;Xfvzz8XF1RCtg1PaVienN5>Ke{QfILTmpaur?pTe!KY zKr+XLKRx4jq&FpQen`F#fs1~<2mBcIbv`Z-3SziJ=xHeeZaWYEnn~E3)Saz-L^8em z;ig}!iZs$dqT>&k=W{O6vRp*2D{1j%e6DUF$h)(C@QanyQQfmo$xv$Mr$Q(Hlgi0C z7k*s5u{yUgvl8(z`$mY>z<)L zJTM+vKyAv+wMo)drlWVQsV@D1(X}9Qz8K35OGbA_u6Ba_KJWbh{CooPE=rAD$5voI zRAFlT`jY#*-07c3TLW~p(|}69WV|I)VP9dNeLoq~+{XGd(~PE^X;bG2S_BhyA6CF0_N5aUu#Og0N+oJ{>lr_SDL(M z*{^S5Af!Cr5z#x(15(5ua!B3RhcG%DuPTFlfQ&sXN+~*yX%T_36)^Y5a^@H-wr`ZrZyE-$ud3U#A6NwXVnm4%6RMpmp#J1*DJKJ=5(QI?U0@RNOec9m7L6_dW!o7(GKs|) z77H++EeDW3Za?y5zx)$1g#SCVaR<~NPK|fHOI2Yfa!Tnw@CAAPjW^fNfO!>@DeuA! z-NEyCPPJdEzu|v+#r}O>P>)yP{s^2EeeGy3(#+m}(<0&qu2*3emia2psOYuQYYUQF z#m_58geI6FOvirRoNiFfUyeBc`L>+#Tf5@)1iJ17M>rybu0iQu?OsJO8A*_iaM`RR z$4(taU-^3<2I}ABaOtd^i%|ajo};5|hJgnDOIy}QS>w8fxw_UPAkBfZ!~JwYZ~f%q z8XhV}9oEn-m$v!?%}^8nrFV1z=QD%T zq1O^q;2)Qg#if-RuyfFZ?YY09jbpzV>Q|jU&@j_T9TZ5)kv0zxnbTmyvV~7EPMQuE zc)F(Cp_R!r7xkur{ga`RHq##-#hqiOn_zzytx+&)KFrZL5L)VK?~Av9KMs<5Iri=1 zIDfvTy`~~PDV@0Eg0ABJrEPng_lyUFsD%t#!>qE(Az2lCBg?1;f;%07eKb`un?q78 zz-%p_TJJ*)ymIhW>ZDUPshCH&RsWr0_u+uwA-_^5KE@ditIRS5g;Uf zzg6z&8n08Ue2iM-Waw~Ff4%>k&Z!2^)7UQ9WtjTjjJ<^}h321MQFS^1Bv*JX6D&IK_C zi2p#rhg*4vm*3bZ-#^Xol?H^hCT$Y{uqBVQCfi3>sy^YJ@m ziQihM)m$-+slH2a@#9Yir<4Aw)i(=pSh)PJUl=y(FfsZ;N>eX-KeeBs?_~m36%H&I z1>Ur#~_BxA>0*$dyi3 z=GvqQWZt_5p1+XG3%gZk?{+y&7Com;0=GiY{A06J zc;XKZ49@7#!U<-tE=i`QTW>F*J+?}Zh4(~d{rTy!@n?1d-*+f<^5{lqd>BBO+ji8~ z8fQe!#l$HZK(Pq3lScSJRDnaG_7XqIP==wt_>a;P4&y++y*rv1qzr@GFShDgT)Rv( z)2~1FjbGSgRn%VE?5>Gq3A~sbeFzXOH9w0B4IwaaqpF0?IS&*m>a*07A$D(UOnyvr z2@op~)l>VVUT?|QM@&{0N!2|GdP(D|R2^i;mXCg>0!gFx+m?x(((XExxQ&^0c)0*y zWkV*-nKHsP><8|rQ5^RasDvHoB)-ZdvJbdl%4)Yg=}mq#UU=N&E6c*@u@TImk9P-^ zu}33g?fnrKBKUi2xNa*@)b>0!5(BX21F~T!Z!fW{tKm|*){m*N{ohI2ft&UcDI6ty zWukzYm*?GC+l%o`he?rYD2594#HL0|5w3{9d6jwZ!75osgPScwjxWEg{dE$Q==4~7 z5}HGf2W~ir;sawiKz*Ix<$wMDrlYQ-G+)r)4T)X+AD`zykTJse4)g+{&uvVj#gt8P ztitj>UpA0&}+So+!N2rwm z&t_v1touF=v^KYA!}FAA#T>RbR{D>XTc3GB;38re{s!BZr2KxTfd+pFbsT}v{p-Rd zyr(h3lLSwO=LQ$^ZP)&yJbPHdNV)`R`MUJCHe2PpIMhh9r+9XqdNOsbE2AEOL*#ig z#0E?msw8*UV!QKA>&#fEB;j(B8GkJPxm3T`N(JnMz8?hW+K(E=@sb1O)r55%GB&8( zg~?S8XdeN%2wxO?nLL$8XRykF;&5eAA1RBl#aobUZfV>d=*1-4GmY#utrs80OB(%L)ka+7%}&luF__=O`B@dN?;?>`gPuZw z^SkX5Hve!aiAV}3X#1R-BIi#>JsnMDUt$&)8>zzJ@@JL}3qYRVl#^Ht_LIQ;n@hTb z=0DXY1dU8=kf2}X@rrDTW6K5mOR8m#8O0_kvP0Uc zx$m-b?`k@v+YJb>(#^`$ODd;}Z*mw%i|#0T(Q_p<($Y+dZqamL&Gsd+)NhZ5>)x5w zOPR^=;nkS_vZ$WU+*{^(^yc|T@%~2|^cWhY6`9fc&QfD!=a64R7F|td*3M1c`pZ>* zn?=zP=(#VpZS#U%wpXDk?r7;X19b~sfy}J9;r_(F((!19I_NmiikfbH?9kdozLKQHTm$r4`)h;i)i)LJvb(u^>MA+ZDL+f z`Znp${&NWRE0HA#*gj)0iP+^CRq$kzYzutHp3C!88SR_x8}3t~He%eg|G2(wRL(|B zg-+|`wf=9t&)rZZ{wcc52GN6O%$03`#@fl%j{R+RNF3|bjg1?}*;c&5>RN-G5Jn3| zh7c~P1G6(6PnlY24hYkFZZM=wwR0%rF^A+A>p@#C-LsnAaKmZ-Q!WnuA8%52D`nH~ zWjDIkfT_P?!P6=X+v%;eI&UNZ`aO{((XST@-oGqcschyy41z!y={H46ZXYEVJv|V= z&BFs_be)rUH~IRQ8_Ss}+3K_h=E43a_KDVbyw1G$UfdOe=pOqS^pkPgeIn_g;&^_} zf^v_;yd;^gGvlw-eNPi@(X1NH#H%$dagh1 z&98In$l~Pi@Bwy2S6IiRE#ku%rrMJHqUJbNP5nw>o%o%aqvSQ4F6&zOC&9OWHf-a) zzVN!cgOnnMWEmM_68ct*D8R2!3i7J__freE4U!Q<0)?^f!8|zBFh4DjB>x~qEhBk_ z!y91HF{}(W6xU+c!qlZQ-_a+BDR}d*NWcB+nBKE-5Lxqt(>)`DpMo%|t><>Gk~dQX zB%I^Swvj}^h~X_zzec4HKh7IRv>Yc-+E&^*QqSwh-bBY}!7!pCb}^2>7)dk%rNDN+ zc$Y+#+{TPUAaUoDJ+6;k39cXO2J7B~#hui+ju;I}%cgSOwtdkJO!GKdn&53TYIyX%n2%z8d3I42$ zRYXl)#)lQuu)FKOA{wm_eT~)3h!P%lYFBg%zlQ+_Pp8bp)|B$csfhO0DC#`gN=Y$| zT#Bx>5S@9tA@lPzyu^k1VxP@GC>L(DA@ldN;D-H*%gT#HQpDJ4^`p!x=hz(cRVO(J z6_pXO;Phy;#_9b}4!mIq10^}M;NI8nPg%_@;jERUChZ!2k$pOxa3DZ4UFlz=qJd5A zfN$D8$GxRxg2Dy>Zw=+3FJl^&JUY;v`j8dqjvIUhqy&aUH=|D&sG_IBj8B!WjhULq z7cYD0dq_@^#v;0JjYbOdjD%nR+JKrYXqIL- z2JcHDUk%=@J%4(<#Y&C`{Ydc3`guF?6nJGBXxA(-WK3oMG4Vud>*6V|cJ6%P0{JBN z@i4Q_gj!eM1kqVx52B2&WFS>ZSl(E3Ahx(vmN)4G-c4#G80du{|5+y$+Y_Goa zTymeVt4;Q!ql5~sH6etW4zxN|)4iMk!hLJM=qsjLB5@es)6FkQEsa-uIKwye-|W^-6j6@X7l~OVAG_|KDc=o3-KcvhyPEeITR{G=u+c)LRXB-iNT> zzY8JNjyi4c7S5J=TpHZkw}A|+jFfxBC?UwqC}1QQ%mYSs{JgRG`vz;SLWNN~dWRYc z+XyJn*K@D4?!r!Z?;~`kis+=dXa~Q^4fE+r!#Oe90JB z8<%7ehs7z*)x*hWF1gB5wh`TYRacfTLa-B5LiZqSP@Gqt#Q=U>;t_i~`upIhwFy%O zAp%$8p-)y!b z48){B0N&71pWw$vsh1t0hw3_Q3|?ja=-^&9>$4Sde0It^9=yuhinwkz8D4FXbVBbO zLqFk@p+8Yv0b+|xd6M7^#08re`Xws7*@Zd?YtZ6=N&j#e@k^=++K0D%=QySqTr3(9 zx89I?j&4`t0wcJn@S5m_z=u0HwsD;plMpr_7Bbcz9N&?(hs4DwglbWflv0MIGrzwO z{^929Utq(C`4{%BKIZ&iWp%TXiuyH*Q;PH!vy3> zjmdiS^{-XMTFE6v$Rmc%)GGL+kJBvbhFouZYB zD+*)a`>W`FRVO&{FX(qXTjPd-zzsuo9(*JQP1v0ESApq3JZ!q6YKWs|71Nu1o&~e0J31U`Fp3mZmMQgjHR7 z_vrPUbm(=&PX;FhyO2@_6}-U%{eoCjKggY-E~}9`V}#Opw)owR;azgB3IHh;b3+Ig zb0fbdqN?s_h{S7 zpEjK8Z>DfbJ%KRbo#g#NG!?}*?bbbz8AsI}ND3c(%EQ_0WwxO_r%fG_{7iEjQgCmM zsV_>4L(@Ok@r5@|!Je`x@e0ZhD8h4DzTRv+nT#;ts150519_t{B-wgEA?xTMi+YF6 z5Idv}^LTgpt%3L}jkV^nGWgqjWJ&xbUc_K@>n_?90-zK6^vRchA8kaN8nS4~AtoPp zBfXJ|cOY4Ehe2Hzsl&$X4*tE0>2)j0S0KV()WrKS_JQ4E616hoGFYCU@Ct{7Q}_C7 zPK5EUek*5(v~-&qV_Tkr?vC!4{c8g=R9&;re8I2Sp4byi_c+m_7P*nsOv-HI;A0NW@G{$2s z=-e_e+A}?xX{mS|JM&)x5PN#4>e)*ce zZn>7Q=Nsh%I*(6n2MUb3tY-Dx?iPbFI;RXUMwxOU%XXb5_H)*VMBkyd;5ag$h_J2L zca8ih9xG!MwfY;@1LjaiZ^8nq0nn}B$-L4~@~%R-{5gi&V4v-L7W%j;%n2V+lm>J! z8rihW2w%jcM)&Q{j)T?zO!qUqZg2^1>SKR(Cbfswm(=w}E&pim$j^zzmoW$yYo7gv zybyW*T4iP|wVU%pwZpapn=FC7S7(4Q%x~>6r_-}zHM{t$Z6^>s&s;xF)z=qG^J=r8 z#0D}LJUT0r1x*Y=sGNp|UDq+z{zkc+D4hY5<88;SH5b!|wd~tHSE_T;vw91dJW-@! z{Q9_KR?hYK9~{Od476I!`8FCa+d?;_VD{v17@6r8^^eiuU533>dNVY=z6t%)=lH2Mv{W;7ea-4W0r z)^$Bb)&lfzp0IuPS>hRQC;R9>-_CplpvBe`@x^}Td-6093Y!;P=JEn}o!i4C~rz+czn8bYP=V2RTj3Z)Fm(hpf9So+A1nUqty_`!q=k8o*NH_G&{mXMtGWX#AN6 z$=j{APpFrjt}}Kt@AB;*u$_W@(W(N5WRo?WG?7gS4X$xclBy*ByufVbI5{abnBTmY zFj!y6j^}m|<)jQa26u-~K`e4F`(ArXXW>}3U^iwrIHqInhwA5FR&B(?B=VG*Pq1iK z4#nU$zJGY*%vO)~HmFlyDGGs6d78$M!s@Ox11`B6BBgUY1N0Ri} z+`bIF$$gm^JOibTZqtvetO1yq8pXGYR4)HEPwCNcY)sMalbWI!I>AwE)cWB)yNE+{&5H>FE83Qj^=zBznd<=jovZ~W!}_V=<4o@Td$&sU(srQ966 z?>Tm(`>fQjY50n!>+c>+k)#tLmHp$dQ=d7g*p9uS$J|ef_%&_g`m#Aba7HE{=o--Z zwl?{Nw4>q4s!qEJ=$?_2un1hxVx7@BZeGs>SmJy+Z2fjrwG3ac&M=#>X7xp68;_Jb zFh_N!U$-`+Tfk@3CAkA;oQF@{Z+ynUP(!o+J~$O;I(BmZ*}(%a-*jiwxqVz56)<0) zAK+jCM-0SgrVP~}YH*h4xnY2Y+1EES!_4N@9e#BdF$7f)v&CU`uG;<3_o!}d<_!XU zEuvwK_)#`OWJvWex(;i=X}c7$rOye)$QPy`9l`P9v`?&FGoyDd^2@yg^R-mrHYzze z+_P~X-;~dNm;={;h3KB^Lri9rhr2tdx2HT;j|-d#vCk3&+M>X`MxO3RS(|6jWG4C- zZ;k?#VSt$@Wj`snBco{;M{Z7Gv=~cQmZVqmLbN@nd!`0}E{cosm&;_By_i>2`oso2 zQu_Oiu3sznHYvY<|Mq~K3|nt}WNFp3u~ZuEmd61>tTI2yaNben6;|^J>m?pzySJ+} zE?;sKR@1b9Q@pVTpyYfcG-eIiWJNHkHI0XinCzh)bh?<5sv*A7ui3FhCxhL@HY@p# zB$+iu`Byi+%NO+bouv`{B5P7sIcRiLL4Soe+19#)S6dx-XCL%q=Ab#>$gvMnjDM?Z zLl8P}y)P4Q(9gwu2*_Q5R_tqqp5(vN4p42!O_Iy)pRD+eD2VO8% zepZAhS#48Ee7_jvPdXoIZo8bXQg`QrmITbs0!bpoFY_ijXC{O90)0Md|qn-i< zPEay#zk^@`q@N+O2ZnK^*A1klWFkO6Fs8?&=u&wE8y-%Ge7Awfa7eP|nX~pOMG6G+ z%yCeM+=dMOdNuhpp0nJC2<2kGaR_{PeeP2NOV{hpxx!TsxF4TB@u{#zh6K5Es(F^0 zc3)kX=b~^zP_dSmD+K8PLy1d;~9erq`iN zBc+yPyltlH@36M2S^j*DmSt#ct8>r|ZZkLoesgBmRV;b)xE3)C;&_H-FaMCn2qT** z#?FkvmlTS39PW|42GZLI)&wMK>n#KiZp(4Vi}PzXOVCjkqk=D#c0L5QrA4gZ_5swU zR-5-w@+iliRMeGMt`=bqj;(p~+-Ofga=v<5fXbzs4CWo$|7)L;@>kN31t49G=~|DyII&pJyFmRDn2I=K60@g9^3noO6D_R_v%EGI zzm!IunvgJ5j6mL+Ip%)-oAHhMMh|e_)9bg++Z1TBefozI`z?T=BPeNWS-~P4sk5S- zCWUBe{?xy4GA?pm{|L4UWzyB^MX!}3PS&iE4y>65zu-B1-oyXO_LW!XdU)!?Kwj&H z>cNM$x^Y*ayaCdEFh8lXtrYqic_YsQ| zUmU!Q4^H9u(@e_0^J3l z^ZogeHY=f8Vj|i`Eir;UITI5eJQ`84DMtnUB=XwdpEarFTptT+sQ=u6z~LM@HM}IS z|5$aM{MrhV4iU> z`U(Ui`QlV_%_KO#OiVbe5$rdJT9?F4v_G;M*6fNjMe~E_;n0Fj%~(7%8l6C>KcGIW z8d9<><`fgv6eF;fvD3kW&&2YbvFT1_Y+I~MbQo!pg1vRw!^eIuEs%EN3l)>rN9{WM zohv-mX|Lo~3YfoPde|Wmj_0$_~)==z$k7!(#-mZ&+#A&<0TQV{QJU@-Alb?p`$CJ%qXi=f8*g z7uwc7tbaHfI;N_9k$F~UcRjLOe=p*IGy{)ji5(t7JjM)4JbCco0m$dxApUn{I~r+S zd@Z0J!DPZV0#6nlMYHSk(Dnu1*H49w9Q^|MmQ(b6hxPsSbqmt#-<=naw6Y44y5uN| zQ7v4>pq@XbhChgEjaCY0qsPqkw|S1+5`MWCpi&5NjIIDd5@3doVtUuJdWn0$bCsTj zXJvxxm5)s_3iNv+f}#n(&_C^o6hm(fFy8{z%pKA9GzTH^%d=R;zmWNzc zxS!YW5BBdB|5`ps`fy@U5ZgI!2J%IevDwmikf)>ce^pTd`PEU)O`^`WJyDSPy9ES3 zr?UA4Ty!M;CS?`XIU<`GN%wmO8wKj*Ytm}xUbdMtx^b;W{wKBN4B8ay6Jj%fVK);s z*;x-BM8lmqp}bN$SNdrYwvD>)5&)&TOYIfD&g&!rHoH%{a$y^dzBCgL#G)aU9Qw zIc^2VB&BJyCw3;o;FD;QieY?C(BB6(Z&+X=y_a58uB9*z*9-Ze z(3hT}sO-*AWi?S+yxgXM&^K9J2?F!pS;_4-XF(dRuw>aa-INjIr&Y0cR)c@ZZZ{+Q zoi4R&9X0A{TB$|AEJoEb+35zuq*0e~@)gsbgkCQ>8G7eSp5(O)n1uMP=(Mnx6e{6E z?+7k6Ir`Pjd*uQOtkjlA|46vbCcF8#JHL$yTW7R@fa;6P*3%x*J2QpHg;XECVg~Q`&+Byxltr2I69- zoTyzi7-?o&?LJL#o3-jC86GJS{-gxalB`TnDFSD*#BwA9xJ^q9wj`TmA* zHai_cTIs|K3|#g}pe5xmf*4J(JiZ1Ai;?7@d7ALw!*k$}(S8S0tY!Mhk!sx&2pFQy zvALcaJO0gcW*X}CMzJJvmvIH`&zmhdSpG$J^SLa6`Ckk@PCUg0?bzTPTDH==USk0# zrrSTQrd-??VhE|8)W_uBU%4~zx?HL|?`MYqm?z#noTyftjgUPuEn4$ys09)w!Ygq} z#{Padon5YRJ$OWt<63_v&iM~by}Ml#0g!AH9xEyJ zeT{R|Q~ct`VuN_J5jgKS#xODs=n|mt+_jK>T@dTu&kowX#h)iICX9cAzt?z6@;ro8 zP?oj(*ci;es1OJVR|NI!7ZWAo8el$f3Tov!xF1ANstOU&=h61X+b$9gA|MapaKa~s z2J#N%AW?5{{u`|{TV}H2;gBTJzmXp@Oq_jXX&3PqIYe6|hk$-sBGt`leoO%8%CBF% zzo!f68t6V*<9kxS=3b=cs3Awfj~44Xes1XccxOJJ6YNK#WKFi+j5zcT2?}Duyxt_z zP@?D8SUE6H?Bt7ZB{0w5eQY84+i0p{x2P%9?;wCQdreGnJ;}s{W4~;k{EHOP7r2x( z&>%fo@RFJX=J|*5zj#qQ2ZQq%8Ev8Fbvy_!P78QtwW$j`3opw1;Vg*8H^eLF`X#pW z$;sTwR$xBAQ8L3EbMenW%no}%DyY}BCm*NU*57gOj{N&lo1sQbs5l%;q9^E#s}Qev z0EDldZJuLx-Y`WT4~su>bX33~Xjwqt;&@|Qp1e>75KN+Me_4uX-b+oS0QHaN*E@^hWChCzD37>l4VH&gXPw^GV zKeVeIeU6X-QWlQ2a<=KvN$lppNm&OP~+J zNnd_Z&^JkTzN!vkWtiMcqezyv(xT0(I`pm+gc{Si<(#{D>mk1>|6TrIO+XbcewBvh z_G|Fo!xX?U@KlKpnaWXeF=d=+tBM}6$`XtVmwJ*7pGt;*jEh?MBQcFOl3eKP+@)iRch_@brTw;CJRyEeYozMcr) zM?1<@Xd>M14V5z$%+_`I(1izv@=E_d@534rhU$7~3uN<1V%<@rF9M^eI=M!24`;tN zlmy5$Ie_zvhqOCzHar|n!QB?!iW>TZsNp_uZ#29vE&wHyx7Pp=^wG)Q$s!yCVYM}j z)Qtcr^-Z_fwidvXhBMll|CJE|)(s!l+qn$b_p|0FW=W)x_m94&$#G3eMqH9Hfn-E{ zhknl6(&5$O2YCKqZ5V@%i8&+Yp80HAV!rR;+o0b2nv6^KabOyzx-PM((!0;F+jKoO z2qJWw*zdX@Q&Gf+IbB)Ur%uGIvt6id3(*_q)a!@eQsp~Mt78j{z2HzcVV*=esji&; zKhmlo|FiHPJW&4|5*59`-~;os$?tldgR@k)!uS%&rxYP{;^L56%N*S&BdRH~to#J2 zd^XR7X0%}6&6l(V`tQQJkK?2K;go+V@$y~w^y|R-q!)MfF|7247eZob8N#Z4 zZIBwGEqIQ5g7X1(**Sct@?}ibV(;|DF##RoXeDmUPE}UX#x}ZekMT)<5VJPNyO-T5 z2u}}_V0_{Nzgvkk9@m@2S^I-|5vZs(|A|OH7B(C4=;{r+nse;Dr+KBYBE}ns%<840 z;s_%+^GXFBOpna+;VU3!<8+m$QDuDXZec8fUuKv+Xiw9~=qEBQMYPk%2f`%HX1)Rz zjJfpL_hH2XG-}KV^^xGzJX1T8rM0EACZp>fYQYVYe_CDLvFa9cg#oo0+j5zRbmGQo z_xWw~0xfuyfTLw43=2=KuT!u!YDNMka` zTRPP5LFAT@Mhkxi`6!0d*8*O)U<3Xhc0+?a_T~C)1}fld8_vwm?I8Mc3@=+4l_xI2 z2b_oV2C0Fyd2bEozgV82x$zr46V@9+jwR1okX;@1MhfC##AC_}HKTcYnDxik-BQnK z1w%DoCjZ+X4iyNTsdX`jTNcb$fh^e<^wojJ$lb68{Z;-sPT)LvUIv&ZEG!Bo**@jE;`d}r znMf(FjX{9>rN~9(M2CJ6iaU}!?I@Oks)sMQosWW*t2p7Zx849KHQq?ZqzF$%my=V9 z`WDc&(YfU|mFKeStH;CY`3B5~YB!mBRVOteXg}g{1Jr+SoU9C{*qsnj%V-^NofQgQ zxXnhc;^tGe*2Sg6_JNknsARuyaxhAC>3;KcCg@+k@GR|-96K@k|8;cj@l5akf1431 znYmQDm|MD>lUw(5IyToS+3ez0u5+#9I?N@74V&xC+{4OU(aJ59RAR0nB%#mwrqD$@ zCvx|D_xp1XkB9Nud++z_{k;06==#D0-DB}tZm+YBz&EsSF{oy$JZ*Om`%Z^j$VI2c zLVk`g$=$YxCC*>nF9RGHJ6b}<>V_$FhvS&tyy4U zD^=ETygiZmB%W`r@y*wp2TA2d`0Q$KUrrtegOjTWIugI8XY%2v1g z;1Q$nzWuEx$j>_ZQZnMDq~mD&uRfr9mF$yXe%{lW$pFH_K>d$8 zKh0QB`y)b00L48*?KA8Y`lP2I@h@}tnFH#igt!fboB$s&{y!=!nnY8u(c{S%&^|-$ zP6Ob17)zP!?g<-fYZc(*I85;RW`S`Sqi8NnXcQ+>u)hV_<=FQnG$6&iJAco|w5Ec7 zHI*6Wx#K`Dv|S10g@qu!GxDEoRu{AzNg!uJCwTEnlBiRivwx%GsIRLVvs<;g6%AQz zC0LBeX!s8FHmFDE3$E7vtOQSK@XNX_$W!JjlQu!VubBO5r}1PFd}pRkhxbCsmXIyY zsSi^2Xx(J6k##|A$GE?LmXs{YUGd}_Tr1NaZf7@A>Vs5hgh&Irxku%4e$1h?uO&-e zS_qieuC=@$P`gHd(0D#-PXeQIHMf~F9Fwt#TFY?C)aaSEb=#xo7szTbWqETuUL|zp_m|(w^H!vd3YB0oPuad%BZ6)O6~%(9{Q#Y(+o%W&p?o=w#un zreMcoj+>FpLqLCBm_i8Sm9gg&DUNSx5mgL$m%tNO)Bt!xwbfNN#3*`qCiQllyT9;Y z;o(#USU)SbuZ(`YT(}^ap5+{?_yewuIOXZMgwEL+`_(%tIhY&&hh~ri zm|ySgDbv~LO?E4&^lCo!d5x=8XgSi@Ur|%(M1fOkNlI-I$eyGh_`z+}s+3bO$BeRD z*7lw9w<<5K5S*$d!92B{2RQ__9U8r=T_Dv4ZWeBk!4qx#WPua9slPv|WZ6d?O8QLi{#!mkE zPZleudVF-ndF8K=3=^g1u<@cqypk?7G_Hg>aR4=5uy*Sn!$P-R!LHNw-Yui8=s9n) zUiL|GdvIzAogep4*_cfB^@&{I509&hQle`c>xLSS_trvqiJkR~5%36luSJ>h% z4LJp?%b+5Zn$c1dDG*v5mnT675t5l+XElG^(O%=#Ov>%)?6oMdwl`{aP!$?Il+tiL z_%AGBen4@CqbrRLW?cV^)aKBXgEDGLZp7}J&!ki8=)Mle|S)Z zCs88_?hF0$#^clCn{OEp9TrnIh3&P2xGT=;r%oYqSZ}tn#CU57T*pC_xtV;$o6Ux3 zNTh958bPHWQ@{CJ{pDH>vE^Y4egvN!K@M?#dNkfsi2u|M<{A3wa*sy=17)H}wAvP2 zsdL9pzbcO^Uw>I)m)?sP(R-K|S$|4QOec|npR{nM;#>TIYRM|uyt-AP%)iRPGx@0B zTuy|n74@GNa4sRUf<`E*>?-nSmUFsT`p9T~#2Eb|#muHge){AvYdAyr4^LlfUQ@XR z2lyb__ew84$LQa{HL|ySYC#YGPm6yfV0snLK{tR&5^!yx-6*=n1H733ZU;Wy@2Evz;uZ)pV z0@u~ec_U_^{b$p2lLSi?!bsCF_{)~-^KC||NZVZCbzz!nQUaBZLAai`c}0Mffs5K_`DNG#*fa4{ z*&(=TUUC208{hkpWm=GhaYklVbB(#;VrFx<{B;~?=_($q~G86n;PZ+vAM zlBJKVZREEecI`dc={P69TCqi|#F(h*yE?2uxI^oxqp)y=uaO(~0v$f<{ZIR+5Jog+ zc|OYCM3)yK(q(#EDj|Bd#2AJ# z`sJ7T!5;&D5%iy#myoz0ri02iqU#j_{--?p>3_Hqt0-J=->MXRu=j?-o?Pv zclBa9LzVmctr?xAOw%&;JNiR*Lt+j=sWf^>gYLO>wPYV?VI+->q~CLtw^BHDBFTQ? z8c)k}*v6t5d$poZiGDd=>5x4GWpKFtbL`SIL`)}g(#BPK6f+7L2 z3!l1;7h$i4VNA0;r^^Lw=P}&mH2mm0!5?o{s)w+99&+ebT@=Vi?moo2GneOvCjCqh z?dZb`u;(}<5xeVeJ_$%&XeNX~md>{I5mYjH7OCcOgyiFjy=rUVF)h{~8+*UdQ}Yt= zi6^RWy=y%@m{sy)%nJDD{^I2PdFcmszU*wV;dPLA=4SjdXb%f!dT93gi}jgML+;S|lLyw){A%xpkpOISYf zkxfNdPf?z$<5~yj72EfSZeCb*`K=`g-mIxwhLL?XRsU_Re|j9`73k{Yc4MxQTuC3P zbKQFLr_?ZV$8=%00NE-xOzU1=5| zR~_amA?)W2cW4R~z8)B194d){h8A!=GlMIqOk6DE{6|g-r5&iGIVZ%0*KP2L&%_7; z{wmPtE1O=@T$e6C9q%K=2fwZl8l%UTH~I#Sb?!o|r2H3o)XX!mI}DCO z)bcQxOhcERFVvUIJu5?q*E}@l1XGPW$lrnbk)E9E@m4=KqZjmNvMHg~9*J}_x}lfR zDw-eq_NsR5S;<_L#Y&x%FkWf7 zp9K9{-0am?Q&d>Hj4a=TzSEc~6&LyPYr$QCC~JsBvoiRiO>&8CGrt??YEGRPpTJ5W z^ytF_-&#nex1TYug8UwCw(%$)^vx54vtmaao|$Tp6q|iSD?(Dw`|?YRWl`N5y?A|? zo>=j|<fJa-&(^O#du2?_mE> zek$OLPDhQ#^;NPKbo<-*7V#4t&|wL(A72_^o36o_Kz!60^0i zv@_E@9s&B3Ms7oIh}*`;*B<$V#Rq1n+~oG{C0Y|M(x-dYe8h?7{lq~hy`sGQ@W%t- z^OdoS#fpm5c;8CzO%!!21>%kQtO$zfMVHVMZ1RFF=!aKkMU!dB^8YR{B@_t)(Lzk{ z*9lxbqci@9vjQK&t7g9R-BVUr@t`o2A?Yp-+fuv z2Kg6RC5$YR)M3w<2t(^v5dPFaUDSxXVqh}T&TUJC!9V0b7`)}GmtYrKy%uIG5c!Y9 z%}nXL&i!X~BkV9-DEihh6Orq#P<+7H*GeNzICLoW>Nxq)^{p=JczXmBvftBaNLr~= zS64xuS8JmRu*MHsd(JF6Z_~!=BW6P}Bocfo^|rZv6k}??MC;jvq>vu+%UyX-ue<7Fw9| zhQE#k9%9*0Pq-s~3cxz@^czAdDyYZnx)VN$Em2lwK_iAYQ>18*X`5*f`w*oxg0B&s zV?<)GN82Eyo(l#Zh3(I3TE>GnI~;`g&xs=uwzMduO}RI-a6l}IHDO*uX*{i5W!%@s zDu`p66lvb+2!+V<8zE}C`jBiutRD8jcV(~g-c9yKs*qh{`fw&UrqdY~uA4=mgMQNi zx5-JA>4}(t$DhN0@-PP%d*otuWyZ5T$n&62hc{{^XwNI&x#+`aVN*>o-RCkAsQ(>; zJv#iRdi*Y!7oJO-M#(AqdG$Ev$yEM@)<7N`*17V-G&ezCtq1h;J>ODzFqji_W(~NbCwiSHmM@lB9lIDa6#G1oKvfwDs3^7^&2tO9OhLFZoR2Y(@#tYgyg_{$G_7 z3R=xxehJYXP?E#H**3#U6)OlA1V#%50sgJM!=9i=cMA`Xh{i>tX$6ug@yW1iWR=`Z z<~WbPP*%QcyaC^>xl9bl06nv^WPIq5=U#*vrDY(oz>()QWIWQj8%~z2bXcj(Bk`~7 zI6Bh<^aN!$yDtEKy8SJZA2CAKZA;H@u*<~@MgKEZw?BxoEpKUEU_6!)w=XI4qm>QM zo~B2~osr@X^GkcLF5Uuq2t|vOvfn|!y0rocx}~m^_p|TTLN2L2aog*<=&@i=pDZQ2 z=eq)idAFzjE6sf%3HsrEs=Wc<@U5Uj=UaL*mEx}6rS(Q{=o>D0E+3b$t7SodL}6Aa zQntA>r$T~9G^Le2PAnI2yD!v-z9IiHE!hjBNR>`-|G`&kE+L(^np;|=TDA#@3zYhZ zQMzV4=ihq)VUj|N=L*I%PU_hU^cEc71f1vUu!f*9zK*Jno5THq`@*-UUQ>ePwOq;y z)^e{hOoTFSy*48U6`Tv|=ym~Zc(IIZwhg5agQkZkZTLPk5y*HaltyRLkgGdFbXduB zfh9ZMZKM!`Ni*|Vo=(3G4cf>Aj~AkfluU`YrthbS+ljJ4efv?{8ZZgft+f*ZnFq@5 zBUA8$7_5GXdGr^t>{F#OKc2~@fuoiu6%38gF(;RjzcQP(x-&gr_^QL8o&(*tNA_V4 z|MUu~MCK^+&@lYZCfhP>w<1W<)dqaj$I1q$Msp`bp7;8@>Iheb=0MF;k-YPWD#b~k z&3mA4@C2{?29~H3XIYW*wlHe1i7hfxu9Mf*PDK1uR*dd zLtN2bSWHXhT=LrFq!90u{(1!@0{2L-p6pB#{>r1wb|Y&=EGrseVllcxjr&GiZ$ z`02>|kSUAGzUZ>I*f+B(Z!0oJ`Tp%>7V@skmXfKjsSo=8`O!BbdikjaiKb@oF zXiXEmZ)c87j`iOyt&(-y)p#&p;rQC`d&(6&id{;bO=L{O*#D$nM(6OS&eY4^ZEJ0T zh}$G6(F7AiM1t+CQ~ViWVs=_4!bLi)c;4eLAZy=n-58^j;c>OL%<(jU{ny8CFm}ENsC5nx`lfl zO4zn4D{haMu8+>nJU{mIMb;{-3w$5tHC&4j#>sQ;%J)4sT`hS@tWf0>yH{+3Ps41q zAgsZ5(#(E!-u&sGcAqM3^SRoey(T|)IZ-{d!_SJ&2)Z>g>iu?8u4PJp(pmeX6ykbk z@--@$Z!8_ z>~dKy?gn}r8-6rT1EG*`VmVqn1bQ0&dM08#jzQDKe6F7C#o&jfpF_K24v$DJda-F? z0+Dr3CY@-&l;*XNHep)4IOVK!mCp?i4;#ijzYFrMTYU&aHXZ3nf8HZOJeO$WXI>FWo z_sLYrH;L_u&f5T9$~&)SY*cR7@MVqn@iM?y`-`vU5|A*bbRmA%rI&th=r?zDI@pLf zW3Jr?`PtMFv-_g1>b}QqfN%0PM-WxpGqmQ8Z53Q+tnsvay?4LBNqW>aJKVQi5fIT5 zTG3X>^V7(9N#-G2J>sb`J*%ScE)4pA39Db(Vv!>{4pOzb?mit_rR5F!u||QOV>j#O zTF2<+F7IVwyIE&l)R=~JC9EjeJDW2R_uCD%kWNM#X&5HlIlF9z3=|JSJ81zZ<4Z=icQhAw8@6NJ%@v5M|Gs*c7HE~#@nHg9 z^6}`cF?p+1DTfA6Sb*4Kn2H;m#)P|TN~qFm<4H<89cTqCU77C8mpxaFfS!i-VJ5wd zbFIjQdEXTNLmp8QGj2 z76>8UJ)X`OBE9rRYYrfrj|gOZJIPV63@))%l1!p?5fHnL=l=DIv0jMTPw~&I3Q%Ha ze0``bKUGIRjvj)T)x%GSI{fy4j(O8ryS2uzf!L!-9e(3XC!?a|{QTBtJ>ogZGT$+} zLzTo&DvEJfj5SEaO*)}=J+hs>`?*MjJ`tE4Wsvhr-$DJ;-vP{1L@T$x18&5;xwcdT zlPV#5Y@{Q|o<*HE@@NRZ%WlUWPO%xELdmQ-zUxozzYAHgw;4}Pp&X^$Lik<-j$-^Y zD`R9LrczSbEG(gXqn&*}%WY(zzE956qiMZ9$wF*?R~=)7etF_{lbeUyI`r&sTg;#P zUv=-#U9M@lH!c(fJu6}G-4D>7CQ?dH{VUF>;?_sl(zVmPVcQ&wII6svnZvn_(ta;b zRBC#YDFDCVfuGa7Ezapmudpj18J8C{23NxkZephy_K7Uu?0-!MuXXKAcpC;BIOu1N zzGmOqf4RtErXJn~kBoEv8^l{MewIsv>b z#c=2MH-L{;StBYN0em|F%3wqZ?kFowt7@}<}_1`IwBtuHE7Un zZX{W9F#inDqrr&v&hpkCQt`E+1+9R;f4~2sCAiPVn=N#mfX*5Sx)#$GT`$rl`EOmnGSA?tEEK2^tcOgxXD74?ct*m^Qj91did%t z($_+Sk~AwRvF_WJ5Vgz&e1W0Us(lPm5~GW;yz`3KH}ze*92aP-IBmyMdbN9f-KGxn zO!G&&vjfnpd`$S3US@%W!RM709$gT3mlJ99A$9qzMQRR(CJFd#4l_zo1F1)s7_pr_ zCy>o~77{dMLangPmZ_uvbVO@j__8D0GUwKnS$q3Uq!4>fR)JV}KRt<2d$oEgPuSCL z6xhDsNQQss-BKIN8x4o!zAhSp`-Squ>GOvGU!wls?c$l<%kHP2=T-EL@+A70CCKCl zheeqV_Z@6t0vU%vuzqNM6$R@#jgrA)EH<`;PA)l??oYaS$m$VB_PG(*53u>2;R#z8 z0$^<+Ex_Ay$)Y6(5@i_i0DhKI`BCiy>p>bcj^~&8X0vDw zHc~OG^P2)oNHS=!ASU~IDrbp9Xc&$={^FraO!MK-+5;AU+Jby1iQ&M$cWORC9L$Pc zVkKIMu>9S>wzve9J^kf+6+dR_0A9nw@GE0T*l6o}>>m_HZ^@I|i5q5W2L7)c|& zaY;J?+~=~f^Y07+K2fIdyyBag?1#NTUF=^amc2Rp(LpudQrE&H$uqxAh)>x0(buum zY25fL0tBdvvDq1=p?sQl);h=3^n|2d_wS1C&=m}6zV_oa&8SdSuD_}}zi~*uRte-Y z*~>t9$6fp=GC8#{iVobx5rXt`6f1hz_!twjfPM>S3h-lc>csu+e(N76rN`}mf_|(1 z`p_Jcs7N2_tRsv2AY!aF4m%BdwBGyWGUi$gBs<#?D4Np!Yb7Ny><|(M=374aeVRrr z^SA|0P9ETwtG{V3y%g8hmsI0R@j~`47T|x})wBvE`v^X>j;Pu(Nu@^SZeZ<#(w(!AEQ|d>>Vvh0(?(;#$bVNn`4K=a!9YjMBLxszv^r< zg^h|2zIoR8fc^xz6lhe|v4iYJ(YLV?4z-zifV2W5o%M@vX}Jmzcgr*++D0?=gec{S zheWK9{VG=P&s*OW7Lpt;Ry1~rMe1WraGoSfoC~*|t%Ssbg?t)a&QM!Ga<7$cV~7cB zO6vHj_nvOS1_hEmD*}-{=*L5A{t=$ST$6Oy5sPYmy*9LLnDe8bATXe=Pkd(7YYj6K z6N%fYlqQJ7K$S)VzFQyj25E}_g64nBc|9v?-N}uw8f`X&fq6nK}H3&aIgUd?t1 zRQa<%<9%4yNYy9X2dyvA0tje*R}Hn|Q+2iN>?RX+qB9iQ?`g=rvYaZw7m-RMvP@DR z&EJanbmS(9mzh$TV{{vn7(e^(`9EQ6Ty3A;9r0!aBZu72PFy9SXj%yY)>CRA`eDLG z%5DMs(?yS{K~t#&!$^x|`$P!-FFEttusb8$f<7h&c@Q6CqG_>{?rB0q!v>=r-(!OnIUC)$>B;L^&jxtumfXppK1up%_f^vnU1NxS zptB=rcj-+#zut-UgzG|l;PcX;Oqw0A(`4bjslcBPet*7OSeu(uSeT-wIB{1>N%W|D z753x_O$6l=#6CV61nmzBt1VB=4OctN<9Sit3aQ|JrF`v@<7JTFI6dkSL{!uaMrZ2H z2DFLs)mFSYRUB4}N4;ZrZw*cyHmi@wq6NuZW}+6|e+av(mFlBa=uy^e33K9}mjLfx zG@cxjt$H39>gHBfCX{(b2A?0<1oqbZ;{f@@1c(ywcLjO^p9`hcmBdX*be!{xzuX&u zQaANpEfhX&?r{HlK!G%tv*1Pyi)_bjBk{q0agtK(bixEhLi4!`>^&AAz%@3w`rQ0oyz5Q#Y8`v+L+t z?ua?9GoO&BSpx1s7H;I=;N6XHVTZs{80IORiwgnyIB&CGd+z}AQupoWfM26gC-S~* z=1FAmP}VOm{F&b@Gn8#7JQ)c3tr@Rbp^Az*<^f?g4vpW?=*;Ff0NHLNx8?r+vRgc1hpT&#H90#bzEg^*50nKLtR7?z!x->ZBEKR`7JV7 z4ERjk-~p5sRnemrXdnSz%L<;mg}d5i5$WT#3Gk`WV%MhZUwFlbOo|=w(t=;H=-WdmF&PRZgr1qI>g*9v)qCT`&EaN?x z$)M1`eO|q&s2h+h&<_t zIgO>d{ZDs=gm&)l|C(}Ern~HwoDT&ilqpWF4^`&-oy46u9&jpNj6Z11idLjpzPV8% zGApiw+DA=j0k24Ak}KNknkE`Tk%e(c6xyR=cYI+ugGB-T!Q0MvqZ62G0aW}>-fwUfwX;8%^8s#>E0{nJzV4u-P3 z_VR(xaxJo&(BMO>pph$>wD~vOhS17AF*2|E7lYo`JxP=}vHjh_5-9Qb^Gt>!*)7oq zVUBo05^#6jbQ_u0o77(a-LR{C8#vEM>iCJLoEWvh8v#-ALVS#h`}hH9$Glxr)r)R_ zblhmgCvrloleq<2%FSP1`9t?cevjh-8XkL1F!Z||RW$^W};Eh$6HZn#7X7(|2`Rb-Wm z9?p~E{LlT^xTr4~qZmM{^#=U*^JJ2XoCX8Fp`w+EO%V)HaVFj=644v_v`FMVZ%A|< zu5NWpd@Mb&=Z1ZPo+GdH=$GWIizU_+`)1kAh)7gz-v`BVj*)-5a=M4vF0rMc-`zXS zIQn{8=w&3~1eCWrXmsMBuB&$yi?f_KD;61d1_#*Ktj+~{5*|vv5aWlzytxmR#9sCL zfR9J>;@)-FHk*UofkU$v&$Yzz%P;KzvexjK7P6l6VK z-7UnDGH~`+)X{HN7b;)UNfT)2qn@al%&s&1GM`(Z3T-eD;6p{In3JcNf`Q>80o!Q= z@IOTNM4KF)us0xYK%f*1?F+OrzXfk)wIxEbYJB3zB#ir}ajUI$Du0+4{bNj>4e-D+ z*oQVr*dV)H5#Vbyn~0YMVJ7s?&>%XS$>=wxmJ`Z4rdKvV9?iLMV|&2AW@Y^S=Uy1; zPHk`eff#+@2O)%a0KUWGXTQ4r0fZOz;+kXu;{rrT+7=R}K(++@tA^&51eE9x_9rqO ziNJV_EZCNd*(%8ZpQQQiyq0{u$doR?yR$n&ck^>RpM92kbJdQ=v$_}f_LqK7F&+Dt z4ZtfslWYbLLYIFUj>|rb&xX33E{%lRll}PKVJyF!mO`SnNolkqE&U+zk~UrOE$|;< zOQZR;t_XtRy+pk=arXi6{+Tp-lo$B~p(a}(YNO)t2gLnJ{`(u2VXFO(6Q*ajEt*C7HjtKrgkUtrwYLIG_M|-m42H>;wtv~wNlMAnprZ%ny zm;t<-3SKh{w<(ovRI(34_#zXjVAXnhMx$|2VvU?Y!e7txSl``iF!AeY(ark(1pmH(fu_r|j|CaZ1lqL$4OlLpWK>Z$uZK5= znRs79_jhDJOtf8<5-0kZwEv-0tdJROzU44^JCKM_WRhAcTCp9^M6Fba-H-0Tx%NONIYs^2XFb#!yh zH>SjFCrkL5w@)k9;<6>|ukJG>V*CHvA216OnbX-XTh-R${P!W08s&25@b6a~7O80T zc3S!D%_Y;Gy5!$`gMgm4?5S36r#yWA{w02{Wo-<+`&CJ_E!`+NXR@$-cNUNT0GG4j zScm;blklkOJXr6eN%Sb5hO)aF)hM6$6KeOv0sW8rvAWr9oSgn1Au(T$5@lnUN(t6x zX6tT3gBNzOLWfM0F|LGW?XOXTUDX{WQOYzNL&nX;;9>LxZ))MU@lHAHrfE&J%wnA$ z@jp~Z#2{I=p2F5MWwxR3NR}YZ+m@ITq%a0sM?fCVPdrvHSIu1UX4Tgl;fJB_&|Rp* zdEblgbuGM`^KNwH-Fe6=`S1+Cn(MG7Y3~8ndlta=Xo>&c`rz|`BPrlCyb5f;LX6A{ z%#&EnuMiVul)-+4OVCg=w!mzGz9s*mOa{qBj_G;3GBarvK=j- zohyM~-Xdsl3MSqge8KgyH&@91WEwPbK(ENIIfp|m3eHE>7A)v2y|@yJ-;4L(a*h$n zh?aaO=u&!jSG~WaG=TdScO+sD*$7$b<$JPvQ}EQ0N?*LJuo3kzIlc$MnTYwP_?ymF z?=jcQrFpS$4OA6sIVqE)%7I0tx%Ro0bB<^i-3tJZ-|>S|S+aM}RiWs3lG|h-S_N9EN4e4u> zc+lAbc3Aa$@71IM#vHOl+||P(xm+^nYRu378JhJ<(9RvxzbLD~`Am$Gr2ZKXb$^6I zx_gUh%ekCnlifj%86(Bd<>NVuMnVVFf)rCAs^LRM6U8pm)nj+cLykfgyGFzoZ(k)l zlrl*J+zV)f>+|ZtcEw0;{3^S`7pxa^6_TB`x~ywzc5Z-#ix4Z(>3)r1Oyx4{+F%U^ zz#o8Cz)uc7X^LoVmKmS`ynqYlNt(L6?*+ebygh`C#Ahwv#lVoX`@~Cq@~Nu_q3~F?^EO>gmVLVj0~X%xi=0+A!6ZX9`Wv4Pzq7 zb-@-TKDk-O9$RWa-!=U}*7K(RZ`~z0Rgw>kmpS>m{UPu#1Ctsn`vMHg@XCB9nrb%) zBoBPMl`AR0Ur75kZ(i?F5_SA22@=DfDa|l)O41(-)<)mNJ#j=sq5tS?S{yJ%t(JXT z%xq^HnZNY2Exr6SpVW3L$_sklpK*I+BJgmY#j>Qaf(G!jTl~@|aI+clwQ&tLkjyNC z>@A1B>EXgzjxh67MIW!cqqAsMQ~KSPACcY$b#K3v6{KXERbZ3{;uy2nxMhQ)lEtpS zx0F8CS9@Z460X+X=gjN0=1Yc7*Z&0*up3{NM3W7!c}pZcS}T78`k3qFl**YR_6xfX zX)lB$=J<`fB^$9q6KOt!5cBa~0qO`$~@JMjpYYCe0;67GT#=$BB|4KYu z-^y8>TJAb`ciV;(;1B((&*tiDb!#UdsK&qq(*N)?qh1^9ddH@Ev>wtP=J7wPKL+_) z^I7jg9YDWk;!WaYha(j87TsXJK-}M_l-``kwG3$XTtTxmPTG@g>rA=Zgh5v?fIMZY zOYb@P+ZYSx<=SKlcwVwLq|5#4>i;IdwdHL4AiLww(B3j=s)vg~g66V?u>1Uw?w?M& zF-di0T|bi7NO9+Fu~dc6}iww|{UB3!%w`q6x7UHysz z6S!B2YwP8*JeI%xqn0l>S>W1p0o|JMcqK9hLvS-@ts34 zvRhO30sjS65*Y3KBh(H{} z@*m)rNO7NWR?ofuwD3`z>r5oNcyCX|hHS%va94AAvw|bfvqFfq;9UGZFkhcmB26AtFD+cV_E-wP4diVz^(hAj zmwf4DV_S>&6a3mBrrM&^Np7H2!&>#4(BTF`DS(aa?JN3TV;{cF zS|)<-$gSy`m(P}_UGlu-Po=^klzOH|=qm%yq(C~MS)4Dxx5SzR3`qF;u-UucN)1f# z%H$E;tQYsP?`GYh`T+c2nna`97+9#~Cw$^0II5x+{uC6?#8%dp)bQ;W6b1PIQJM5% z_0tH=c8OMDkpMpd_>~OgHAD(^(~xa09y*_(N#Bk%vt4S{^-#YP*j?Ti=Q4;xUR*z~ zsAS|X#&7S$VE3c0NVtbJt)65<-JSz#haF%e_qMVlbWiIEyPJSlM@HhJhMLzBi)k-- zJ0#gH8f>yRj8%--Xk>-<#g{Xfx1Os8$Y9`5Lr-s}tqe{cXB@E~Xbt}F0 z+{Vlsp3b(v4ijXI?mQ83QPQE@ttovzXba84b!d!{)FdsK%9Nonm>#~x?5P^{KJsVw zYYMym?--+&bQ{7Bh3HN~Kd$Tq&6T(S7atpgUGLStFKkn<5VsdGjqUnyUn=jYvV)(O^Cr&14MGpQLkQ_d7GK1)}GvO!}Z=YuLkamt5Q?x=w%JyWxH6m~oZu zFZ_w1zc^rrzEvTLO-aN_g)vv!WmaD^8JV=x_Mhxh|8OyZMASjRm+_yeYRLa=ova|m zNL1Z?pZY65p)11Q8pc!k&GCI1_BIBj^e`eo9|}4%*W+TUzD&E~%lB^;0lxf-YjoI& zuqAny_IId{7sL*uz)!q+AF=-Uk&NSmyO^Q#5*GLJmLtP#OWoIuzB=Bi@ftofZL=M@ zr@^n`$yLx-cs{6TKz=*FrnGh|y`J=NdzYm7*F~Q(kFe+MM`3!UHi95^eZ=awhphqk z&~Kr3DO28Lu7aSc$)~R+cMT$TGPjZ5m2FO5o$nuL0pEvssmB}gKXP-ip++k@Wnz)_ zwyB>(!#BtVP5x?CCc}3|kL4K;HG{mIAFQjO&k?U`TOUoK73^NT5O+qF1+lG%aFm0H z;}Ua6*Y|vWEELI7Z2G3LdLqU0%*~2$vFL!c4aFJzR~0vjx?<5Jz#ql&Al10Q(+Q!` zxsuCbd|D#NvpR9v)LgnKV(5{$>mHQ^9Z*-&wTs(WHCYsk%C>|1ede(j8a`Oh?Mniq zNAa)eyX($>U{Nh()Iy=$@BJR3gC;pAgS6Dph);nWTME!usu#IzwmnKQtOET@F9Dz8 z5#6eorkJ1<0Qk|>FFKoDG0pQHpFL(Ih1^tpY3OUZ4%Xn|wx1YA%wQ2YBx=B3bDz8&^p&si{pPe% z5-jg>m*hOFT(^WVlGggf1Moc>N`|+B#1cYL{4RC;W%y&d8*eY&!B@P-Mp9dX za}YK^lhFe0udPeS3`~r?<%a1AkiSxH$3c>jw-WjGIT~FW0%=ZyzQqNFLPYJ!NVku# l1r`q_4D7Z`_=Riho8I0wi{>E+y81@NFlpw=<{^LB{{e{&S4RK< literal 0 HcmV?d00001 diff --git a/CesiumGltfReader/test/data/ktx2/kota-mipmaps.ktx2 b/CesiumGltfReader/test/data/ktx2/kota-mipmaps.ktx2 new file mode 100644 index 0000000000000000000000000000000000000000..7504b0fa8a5e1a628cb1cb8ac71b76ce5eeb3273 GIT binary patch literal 66125 zcmZ_02UHVX)Gj=s2`HKnii(BM#Da9h9;y_DKu91&g#Zan=}k}s>Ag%sM~H+f5Q8*9 zKt*~}P+A0x3JM6k5dkHXoAbpN+U_|<<`{PyStEZ1E(Zi46=j!PJ_&9NW z|9%J8AL!%iNAv;wJl*|*qyse0s-0B{RaKQ%mcHZa@PA#Zp{m?p`u}_$?T8}y5bpW{ zN=m_wcfF*Ql!zXVo=!w*e}b={BcNEPye$n42Y}4fnw6`u|L1XV8|?pFV8lMG+?l!f zeRY@pKd=9Lfsh}n_FS#OcR%~em;RsqKmX?fl3PavGsTG^@!b2Sc-yI`eEs)S#Y}yk zN%$k{Cs6~AL(v0pIBR^*rI;F_Gzs;oPd5R048LFne*2|zuUPERkoo`q=~oZ|0DLk1 z=en91abEQp4~OalAc&AtUc6TU1Nh9fr3+P1#8SP}JJCR|`Zh!l(9Tl>)|FgO>-u)- z#<0ZF$z&xUq=-V{zdDr){uQUfC>9igUy9|Ke>#LKetsBwlqCuUwd})Q(op{Ii`|1> zzLWwuFwff_*$wGBYt%`0-r>@dngU7*W&>K{sXf1iz`7oeod>>pWs5U>Cr) zx{C0-62O~EqqVm6_V$m!=v5#-N=?3(46V5D`#%-22Y4U{0q+mcHR#?8VH%JSjh8QX zmO!l;jp?utmx?b9@HUa{0Pw;`!K|+SOXX)w!#DRH0pPM?ADyk~Qo|7QOI!K2aonJA~LSjGn*fzvN;+)GpC$i<1tIpbxIf9q`= zF`^4>nry~B9%sj0>#7S)>1bTZI47;{zd>ZliYX4 zw|Ll7JNA5k_EbK)`@v0_1jV6M@3R-nlOzGeRDkLa-plVWDr;)0mQWD=WHsyPi)Zcy z#UWotzgz$iqV3YEqJ9808&TjRd*2_5uLc0&cI>drWT>$;lL6xx{@w#4)p} z2Ei*&Nz*;~4i&lJ@zFlHwfNh75|}oUZE*x$*blEI$9iC15T~*45>E{ zHp9zSLgVvXvi2-$JbTt)1+b7$dus1zI8}IAI7A0we=tEuYG2vfei3Q;c1meZJ{w8s zD!K#u+DM;+&|T{o?TW)_jIBw20;o?_wFgs$TUHb zkfeZs$;!o*5pln?I`-6s^5+WhzO}2si280cHKgl8)$@9i#k*htf5QF~2N%_y5J~A# zNO#0CDR=JN9y#;i?_+Wn?iS`r+#Y}7d9De-TW^ohjcaid6OtCF2v-1%`sn+q{hL!OOIu8Gl_-Hy8XK*Up51vL>&l5(k|?2l=)~FWmeN_9IA+mV zfw?o=Sq}nz9bK`BSzwWQ&+X?qbHvFL4>cZL1Q5e}F`s_+^}!>2K1y#F(h-h$H6HlA z&SSD_jbUw#1Ky_0^jF|%*nIK(&-{m1d8 zeGoPGNo>oG(H#-l-Ge{lTs0B2V}pl(LeEH^Reui~;uhswWrczNb|4x~L@>v}q&sR}tD$SG^7aCXf_WMJNkeSe}*O z9|Tv0BgMk5v8BXUE>%o*+Bm^bfTsbD1k&=~v+J$r3^q&SPonn%V8m$s(oIV3$<-2t zx~TmC6YiPhIMtOdFKQ>Q`)yR8-4~J=;E`hB*63BBCt(o8PCF*>p(mG#gXrjQc#w>8 z;U6CSRQa$U6tu(8ElNm)zQF56mvi2v!~}W;#ZDGTNurK^o+v%MBPxV=jLF7<^`TaI zT7_rapKDwzP6_}J{StZywh#x#mztNsovI+w zZ-6?S+|xcW`eb6LCpjcbhroo-bQKc2M#VW2B_#j}#t_oW{21_oW@LZQI-1Y(IWv5& zA?G3T|rn68&OY6T2yap;P#EI|3uPF$6IgkIik1$#l({i=c zN-!A@q6g=E#)BEQH2I~&@mZ8f2j_HGcK8neCw9(i!G^(x46mpPSgid18;f13lvB!I zFlG%&aXM{^9BRew@9akFn?zPG_e}EYw8RJ~O@_~}q`)M+GN{bm$;_6Ho zBe(eQ0meoP6JX5u3gt+U=)YQTb`OIA;AuXd`etulo?JZ_PuUA%HZ?8W3dAJmy@*sCO<;*tID)xzg7mR1A~dh zr!9vromn0}qwZ>)r++6ZBtkjIWU3(@HbY`jt1)hPxLlhlt|1EtB@&z#T2`2{RY&_3 zx8uJ|lJ)uQaPw@4iAcwNNiv|`Kn$74uVxv=So+T5ee7ao97JK31$dak!>?sK=}O?j z-Tow-Ja|O^?=Ih%8u?$eXj4;bKvjo{oee%j_PU-KSl|?o#fP6&-&?+es+u2bb;(wz zmjM8qoy998$z1dH~L&2O;3TKZ}btJ2zK#H_Q9)TcMXwV}^a-n9LogT)Ft zQoq8~gphon7ixq6Lh-cub;aY6cyOiF4A+(F zv`cVDk;DnCzF~}R#|(1GV`pzm;Ore{(V4ub&wfIW8XSqj*kCS?)YjDV(M&|;(3XNiu_T-EiaPi$^k6M&vp1n4Bo@KmV>F;#QiroM6de6jk>jHOv z82OS_zn@_6tfUoLoy`fK8x=m(g%Y8g7-X9m%&=Y?>gus(M0XQL?K6T367b=oQryTG zRmD%^kK36#5R!H_<4f6&$?`e{1|`{=u#7Igp@qW9DwZ->`IxpCX1GG)XWEY0tvjkj zX-gOkAHJ-(Ah*6h2{)RbqxH!do<>?Uku~{-=*?Jnf29K8Atj<_Q)7ux1!%oKF$IQ) zt-Ljz>gw(YQJ9!4p1O+js$Q}Anpv{enx+*D)nPxw3!feQv;f}Bbc~Fsl z7P35+{xVjXLpe+cq48&GMq-u$%B;zjR2o9@yfmAd(Vz7=btM~H;L1!>crhoI(QP1R ziB^knY-bql<)?X(!7D1)y1Ux&`pj^wg7l#V6b$PgTvA@x20lx^l))RXR=jy-4^Qe9 z08cxu6MuH_$!!A+k|W%37)%|loN-E7H;BvlMvlJ%fH3;Qx1S@`PWF;NpSBj{B9pm1 z5&89fJOmU26L7qQ-BTT@`@R5{uAffJM2Xd72?y4N z>qDYet{s5IBahb*|b%5RTMlI-(LYA0}bRO=^0AK;#x=YF z1`Mt8gCT19>&%3 za4{~GE@h@j6#~owVL$>Z1yPzmWGG0uX{4Ws)EBH{y#FCT{z&l;R0WEZPLJduk{TV5 zyf4yJavudp$Ni0Cn7aFo*;)mDE(c#UPWI0*`khyzbm`YpA4f0Jo0?l$;5r!GlQ!}F z(jNysv?wn#O}VnK?Azc^QZ^<-{RkQ1FlQkAI?0Bg04tVpa$0B5l7ZgJy+*DYjeF;m zw$7`v?xAuR2cMneBU(At68|9X{H2PXt>6u6&3i8XS^gh%gn-Q6oOCt$^z_u;b7%k` zE_+s=>eo-1%AU*`>Ayg=;d~fIoh_Hf02v*mxTZ$k(UYY==todW~JeZ-G>0- zNvVKwBmY55Q32$D_D&mhGGnZ;SSl>8f1vH9?3gfGGwF7-0p=SQIarIxxdGtY>f}eK zvI_+t%Uch`YXNWrsPO*m$o1LgR*J{88$f;X&oca6z?w^+(uRAvsWkg}b;s$qVcC2R zNVIR(&s}!i0-%@&1mzrgv9AQby8}GkMZ0lS@7+t=4PsDBA(d;<r4s!E@qogEi6JCs@sDRyUODHmq3SMfO_J9hQBr#SSJ`T;*i zvHRoi^IPwpsc_r-i~nY301IK_6gYOs?>Hm1i*P28o0&L$hc{-$2L796X42 z94;T-}*q`$=@!pNj(9FU%mYz{S_Da(^L{`{Q<(TnSi)gAFtd7Mx*Rr79VT! zsnxHR>{q2P#q2lS+j~ZeX8c5aEK3MEG3!VTG{6-1L_N7x+AYi{hLEp#;Kq@qr^e$l z(*>t+hjI>cc9`i3cJi;>;7qy@9+8Y`8_k>m#8<#m>tgX)gpty2i z%QFwVMUv}h4;eLJL<331aqgP0UZ(4aG+za%$YdF*dX_MDQu-5QNo9y_gm<=yRtyqw zl$OzR44h*jLl{9B?l1hZ!uq0no3>f6Y&7F??oRB331DoUlC56%TyZ({MCPzdI|9L@ zr^|tFDxr^GkiUUmthZfIbl@^!pvA-s@icdTF{_Gf<`6aS%DHpcA&JoFpHJ0e<#<_# zUh)0yz@_41qHQUEghs(q`W&LM{ldwd%<>?s`v5g}82PIBc$kh~Ru7lSxJ9UF5GqT9 zKcmMtR>twCZ?nJd&h8<-;m5k5aL(~;Y%aXuoZjW_bU3Nrea&q_raJDDah zGHWIeu#NYo`FW+G{k8)yMF`b$`y)Km2v_>y=8C|Fr}{aM*?GZ_y7R@~-7VW{bu@(D z;}m%?fh3IR594BsO}s!oT%7$VTf;V$FJ9({%&a891l>>53)^|BUpjv0R+AhO%au^jX0WS<*M6?pb^4r4D*OIU#8weq7aA+ozfLZ_WzaRxOoUoNeJ9 z5Sv*L(^N@#lODw#KhF~G1&9Bf&e>+Ce&^2!dQWR*HpG_vkrrUZlPn*#%n&U6#Xn>u z{0U~{1jb%k60VGxAZ`)()KMV0-p*L!`_BRIHW9Kqm&1R4lM1>`?GVO_B*lIG0eI8# z$h)G6zT+jhcl#Q+%tq8q?eo)$J?TW;%j!$}0A_k_bI=>36L)$7)SCl+ISiwVvqIHQ z!gu(0^OvT!cUj2itV7jbg;fwFnFQ;ZYzFe;okLF$he;g0E@YIb6T^t367=YV3u=SM zUqQx(mo+Jpn6w`4W+Q)rS4rO-%g8zR;m$0&M)g%A=h|#=0z*dHYfVec*{99`>5s*3 zfHohcQ`WtYTpJys+znR(RP(eq!UR?!c?tUWdH)d02_qiVI;~$PRQPlgs? z)F$q#x}zl^1ulD>JyiM*aHDbZi?i0KlaImzefCPmpFnz^o0IpW=ES6n`?U*56T|F+ zf*g6k;%UFr6*uRIy)~OU(iZ_d12ML!U1ejiL~hP=!w}J>;H>Y=%{hbYOo;EU8i1En zj(zj8%FOzh3F$Fu{V?_Lc+$iR@(WxhFEQ|1&>|??V$w19aXV=8ge#Kb4S?;tM^5B< z*m+Ktu9BL30cQ4HT{HT_+5>;n02oAqz_64G7_-ETsdP8aj9+sx08bF3Kbej_bX}|+ z-tIjDsJb=Yuz24ILbI{i?5xb>^shNn*K`%o}mIAnFl$6@{6*+=Wd#X>=?OY#7xsQucVshD^7CEVN}i+|pM%bN35o+_x}OOKB16~_Z` zxq^|qk&$x-?*;rft>pkl`^Iu92^N!6No|Sg-EJb7;5#VyEv)p@Gv?z16s4K6(3T`c zLp}vfan(2CmQi>(%~t-!z&kx0A7bH@TQCSd2fpK?*Iie&sG_A305y#s-Todo$-nKg zMsW!&VWjKsoeMyB-N&ko%Vn&_<3VMq-qx1Gk8e#qp1N}K9;j6%6Lg)A$hvpc`rZLa z06d=>5Rj*7ZaO+kNlN50w*OS?O}ec`*F+!viL=kn`fjR3GWV)r2zP868yrhWRczK1)1d1y1(C&WM;FBj|{+`9RB#GL;Q70uogdE zEjQlV^7H``DOxl1aSBI0T91b?GMf4>UpzHih~yfdJ4FXGZk^355hd$n@4>X4Ljoc2 zR1*VADYV=oRRW;%TdAT?N1d?j5(ss+^-Vx+iX*bWL-<2q^GTqI=g|*UVnJD2Qei*@ zjoAA{jNN@3$ff+u)j1R+%QZsDzNV?1JU4XC4}az1tu7>Qu*bw_gl| zL}a`b(46kK&9+@AUl8C>g@Zn1_`pem0m)fWnxg97M(z?H*#~Ji{2MY!JpRgT(D2da zzt{UQ1!4_Iz0?r6#??nv2>SHOrIeAp1H1s9*QxM`;Xwqd4E0;z9}~a`)AUk|mOZ%7 zA;qC8smv%r!)e!iCS}(%^<>(3{T@Iy{eXRUwALSAF6@Fbfxm1doEDV)^Wah7g2vtW zp=#E&p0bG^G9I#|xxD%P%UrdrxTW`g3TzR=ZMZ%-lnUHUyv%`I54Qq zw@{th&We`uGI!NH)#2}tJxiTZHWuCB&`^Ja0J!*^#1B;dy3T6J4@T@QfIa+IxOoEJ zbPmfO5Oc1~lhNCT9htJOd7z^mwS$gw4gs;5MTD^rEzNG5_0?R9un(_@p4(bGC{586g##XmeiynkIkZ{n(SOgvVf z-Mb1}8WbQHV=clB138=Q++QLB!G`=;f6oAYLzJv{k4&nADn+*iqH?p8z8EtifBZ7u zG`GYcO=3f?LJ45^&UJM7q}Lfq9PsPw2_~{d;WzeJkIo6|6Z0kw14H38SDvTO_gCH+ zo!xjWb{POO2EN6*zpEUmc!@MtHV0HK9p}C~jF#BgzG%qQekkCbQ;)j)=gkpu6$p_y zqmfG}lzeq`3z?`PsxF;U)W5{~(uVx`t-G0Eb3)FHXl}@^D+pi+s!8`$tUbNU7wG4=meCCdmCbr4146d_W<2NJ zZvk)`i9hYzMc4E@1#8kpl7RQSH6`tMwSyY^NP!ss0xPL->;siSBUv0*RO14vhQMXT zOZGn3Hcr>pWjIXSWk*XAHfrN`o1?(OfVH#^P zf;aEGWz+A)dMZNkoq*3Zel?8BLtQ6`9=^!#%v^0tY^yu5xn7{e6JN9^hpIgl|74f4 zl1)Q8QTIWGVo1 zwKw9epWmRE3ehEDX6T*~m_GV}b$RrqQv8#OGZZT>^O(43ul%l7$wG{%*Y5#@h^O+m zSFQ@v9}5(dHtXqlxV?sw>dw1`&r6HdyZKX0MAe+zRHm)55z>gV`Ltq09-2|w+}NbbaxOJ zZ>Q>M>YL8#EYfa&=yB)blbOz`(sXVnnRb{yE(pM*?nby|4sR(f(#od&OaKHS0vh1c zC#NQ=hX!JS-z*a}x!#TJZ_D$iCGeh*0SgvP_)$N*FdC-^~&8;|)T5V;8 zOs`SQ$)LQCFy-Rd1%+|+NLm#x{;zwPbOu8T)!thtwHl*W zfX?pn>ci9aL<`>=J(BDSPFyLu8$j)0S6e$W6)yVKr}WO;-vfS3?zW@-I#TuB8*6aE z6F^uM0B1h%?o-+Ru(JHAlF5ers<(-k*Z#TRT=yiJhi?(`mfq^Qv$FHYm?CGy&7zHvrl|8hZpuq)M#SkupCmhESFEt<)SvaI#~F^}xwuP?3{O zV)WwDey_dbxCU{w<~6;u6e87(Z%21sjMJ@ zyFLuo-x1E79@3W=kkp7~*{Vx28K0VTN`mz5NX~K8;fBtxP?3#K(5*)2ECV%ov2xlK z``Eep#hUA#y_Nu#JWgJGaq}zxs4OY#-sVLe%0s<7ZzDv~hiKO72ulsPhGEJ#^v>U7 zqubw1uiFCDux)}3H(oQ6JpvPfDD$vmTcoug$thwtEvKIGAt7kyl4q~Oq(h*L0>ZTYnQPJgbR8yTr>Y6(iZ0XA zXh|*Sv<^Pl-{sE5C&ip(aMky3%^kA~b>dzC-0DEDX2Izj&s~qipV7DisJ2gjV`pAr zhyx~~1-x`Igw-6IDyjlSRfV;t&4dqtb?@giA!ssUwLaSVHa9bG(LCBo!UkNi*RqNm zA2=QY8vf*EO!wDM+8KP_KVQwZ<0zP=#zMj9Iwu^(W= zwBL64kSPL|*AY6Iu|tfWX0pS;ahanZrq*jON|5oCDJw~T7Kxp)$L!n9@k!=Cj8JCiI_ zH#2<1V!`$M?a76O3Qw63cP9L(-RT$0sVAP=Qf|!0&Ewx-p2(kWJzJ0+wXE-Hbs$8; zB4KkXDhIL&(1|-wJ<)wh?Thv66$id*W5;Z`fa<3C>pxj3eRnusOI=gr+w7jR4yWs% z9PZxMrQthpdE&9_J{JIL|Ad4=a3Y_W4BaL0p7ga4 za5P95mieJ_mSD?SU<-Ncn3BQslSQ6iI8i}*0xIk{dJ?zwXWwCqQf|Tnwb`$uv3@$% zk%Wz?xXsw&>#D=v{J&q@{Z4wxud5}tqKb!Qc=0fPn>ia>m%@IWm>-?=Uj6# z$_csWfRHrHQ|9?yqADf>hC{{GE@Pq7(ezY?n66 zFf3h2N+#*!xlo=KyTAZq5W!Lv`tv6$A#ppiPsd+Y#rw#Q1zlxr9$u7F_eA3Ikav-S zZu83lyri0;mrb+!5WHdF@r|3~XOuD=U5$OYO0eF}xpVFlbBWGkc|5;2XW$wK9_iUq z0%tEJfqaRf03+_e=j)wCB3V^|twvKQ4!mBV_Ggs4`Ra28VXaGNfbg`@RI|nFPpYcn zk>D~cgF3A2rKvt89{-n{*K`bOM6^p7nlHDD&~~WH)TP30_yxeI*&@!{ zRR&7dvR#wep7fHmsfi50hQAjWUiZ7PCrO~|?jkP8mt}HbaNR3qPgMi|-)!I-@HEOfWGJBR#wlb>H7WQISKD`VJYOEHZDH0ls@#) zGJhd`$_Go%X2g$)F3vvwa-v4RIqKo(#9Vm3X`K9wm{w`##Dh;QpO_I#^3k(9gRSZl z7Y2yYqU`Wk@i~u)Z&syZe^lTO8qDy~Ed*ow9gjCgtf`#737(u8bY21<-b;3@t8J3u zDt}PF-LrpNCiUZbE|pgf;34lxN#!e}xY7P9!eZVS8azOJWInpXRSJC;OW+0I8E3;z z-Ff1g^ZeZJUu+8iUIw|ZRiQXA{9Iw5fW&QpDJi$VlQ<`EDmu@tD$s2~3Z(HwOm#uC zX6x5Uv#2#ylOhlExv$65w<0&~e|XKqvk;-0MGzX5zS!Exd;oCLoWr=9Of^lGu)A#T=~IHLkG2AVk4Bdg{KYWZL}dl|$>9cECsxJ=JUb@tkY=3I4Q^hd?gx zl<@(^Z|ELF45s>TTsad?rvZ#D=mBwD`SNaSNN&W~=|uTuu`3xaopBSN7OAhsIzR1x z;%v)1^XSQ{%QBE(JIK4)jD za-cos5i3?%P{8vO*$_DA9~c?8!342hF;kww^^DkeI zg>k|-c=^6@BJFU`X|PU*AS`|EPA=#(_r>Zr&kPFvgre`U)6}z-Otdof;xKt zDQ)_PNAqK zeeWy>j_Vdx1Q$7HtaZaR#mPY^u<0aJpyleZRxDkoE+RHlnDXuzzDNZU{s9cVXkKA0aCGlYx zkYTYoA6yi&+&iwqC*`>m{AlCdsRmI9Y}x&BbG-!HIIioXM>Y+L&5Zw(Qk;u;&B4qD z*g9!g@qF`p8h4Ka&TMj(AN#9Nbc`(kC8-+ky`eXIj3?^*(BBoGUJKT1S&ZX+K071D zMd(rar*Sq4+&Ai$aTS>iBN79te!wcLoRDdQ<_dsk3|W<5#b zRHLur#rLuLF1+C*R+{ilc!@}LhD@qTREg`b(N7Wp|8TUIQc;DIFya$FVD8D~3#)~S z71yqR(xQ|{QPMGJ_2YTOY_3u%9Qg1=z!#u`)qGcX-W+(g^Je=)Pf;UcVgDCESOt)N z34DjF!k*`XB{#oRE?ZVPQhK@vrt_GPr_Xp#uAuH(YN^L{8e$mT>78{x68^T)j(;&v z`8U{tHK~>#3y{Xwv;eWXpudc`m@%J^wp7Fq^@iA?3Orm^q_QJ1;AU5+kBPCLm_h|mP%K_7Ns+*_vX$HOIM$7{oz=GCbtjICCOI|cC2SmC zWe7hT(CM&)#Plz87rc%B3mv`(1HZCcH4P{R|hcRQ&i{UB0=(Z4ZR`R6}=uN3cRw{#15I>YS(f)Yzas z{cxc>ihz{@?M|4ox-2c`1Wc^$o84KqxL2py!zQY{>Uw%xYU6~xU-+4yQsx1lvQIlh zFF(PX4os#S6rV~c!7VTQaQQj{^rq&9nz85&v;)#eo6FyLWb)F!hRv3IuK0((&IySi z`F518bslwv(*Kg^BtqU!(&CT)xJr(yFH95y1to@F;;JYY;(`k_!8`5%E6!}J&I7A& zPjU*H#`P``j5*|WQf_h6jlsRPeUJi1oJSJ4Wa|*JS2>d=Y46DHS?}x!7>{x@&&(f8 zco#$+#w3tPX_9Wl*nmdoO+NVZ0nE=)vv2ZmM8CBlMSyVI^SY{6}5DK=TLxV6GB5T2$w`0T*A2-ND9imBz)>O;!(X7xH}(rTr6T2#knp{JD7C zko)=QC{N#00IL^{Zl4`n;MEM;cFq-3V;QfsKVnHe8j3bo6hvEUGkHB7OsBYf0w3LN z`RVcsK7xLftDHk^r@B%2C&<}soe>k?*<2a^Khe4Jez1ewJue>}rf$j{cPWVKoA20V zg3CKJ+V;CkY8hwJU42W#jT-dTXA4A?w|X}p{C*}*MRi5j1&{g#`9alS8(I(9udf)* z24~muWT>@Pt^C+v^mq){28sLWmc1PEI(1Es(UWah9Qx7G!yxERMv8zsqi6pC-;dLX zBxdg&-k`WGc+rWky^FaQ_@^`36*;;9p*K0;x7lW-1kZW3orLKbLXJ`0`@`DEV^9Tk zBd5n%tf^L2D%kRhpAAn{1!H^QWTnF60g>wlZ}>{|?pgs<6LoSuNy5M-sy)T0TDpwg zBa}c3`kr||?cUsQ+3OO|K2<9j56P5AZ&5CgYx zr3TbP7k`&xKN^Ha=JL#IH^s8!4q$XG?A|{+>1ktA(cI01DIZlj)L@AbGMQeOXv2gH zx<%IB7pGnK8q(j&`&bc@R%$ImX&Bt1tj{kla|!!2=Wbp~>HhE=lv{wu+HofQ8*M+O zA^8sKIDuG)w_@w4U(=hPRy*lx@n^O&HvRO*$dTI7 zdERz>L@xfylBJY>01B1+ZEU=;=#6Y6S{OHV<7~V2pQrMXc^X#rR;FDu$26)$m~@ zkq5FHpjAjo6jp!Hm}+d7DSG4RuVbT6@8)i|3^8;XEYP!4GLV<}Et^K2ZkF+kF(p{c zUq!F@+EP`_IVLC(qMO|yD!-1Q&SFp{%(RlGdBvUU$86m={v2s6JXK+});PE3l627W3eSDjsZglG3qmw_d1zQYmHfiPf%YAnbX)f9&ru& zK}AX+mi>w;qIc^!D{-*g^QFxpG*^jybK%S~pTi=q!nVS|ijCxb?=UfiFhzz1mgjtL zV@46pH$JcdewL~#~y^WdXVbmwL-d~xW9w*bTKawl$)Ws6Z>@>D~ zd#GQbM~wr1J$C?BY66vLLU{nYB!-ZEZ)7N1?(vXe98XVRBP(tc)#_)iwRUM5GhVj| zs96-i9_%#7L$cj+@bFQO0mU7`{hI~F^t~aaEaSdcx)XLX=DCrF7BU_Vaw4X(%?@$# ze;QrT_J?f=h~uxuN8s~#6Xa&YaW zLgKG|493xRvh9D#F{v4ucHJWMo^q9+cCSV-YHv=JsQD4!e4&~ zI!Md%x)K}hTdNJ{dwVK0@wW&#)MO_o#ICQ;43RJL8%zG<%GeEY}#tLdBVWYuN47h!(1N8ItlS& z;gZsvbGiYHc;!^ISEBB96jlus`zy#W3X%M!k7v0Ac5T}DI)|}(lmS0KQFLf zc7r=U-9K}c(ne({+g)4w-tbsu%UZutur;(tB}ZbdiVaaqr`;Ee6eI`6mc%SBp~dBk zzn_Uz6Y4ENSq`}WJ3rI+!%7FU>@k!z1KVm4oE~jjxW$bp#l%`parslwAS1;+riMRF zg2R(!3!u~1t8KRvYlId`CM%A4bMb7!_J01r{YVN*A2o|dGJ3*{Wi)fxZauHjlahe3~UIllj960kQ>9amJpH24KHMRp-Iw69x zm?_AVPoZ+h!>Idk@R4V%dUEZ*`10(&;{f%wg(R9AuL?P8fa|eV7r-9;&+F%4sQRxjSzM_P>i?Wt|xG(TU~>IczFeEz2V<@Ie^?^pr*5sVNVD~0J#xL zV_dv3jH2l`Z4ygJ)Sd&KmQ&NJRrP_K@N0^W?D!OssL)|_Y_h$3Bp9ehHP$gA`=#mq ziXj_ejig~zVZ}%%uR(SDrQe|{8BU3CN&1pQeCmahanW;S^dR81Ma-Da`QsKRRqkQ7 zO#QD>)355QGLEr(+oGEM1H z(*-_D&Dj+4rR_oR3~=>*Y11dd^ZcVRgmB9Fs7f_BJmt6{3Hbz(CK2J|#nmIe4^8p+ zfnr)?8hc)ZjtajvHn-xjkQ!D&i2u2;+53s1<36m0dn}`>?(4H_9Dru`p4&qVLg}kG zb_RGf*u=7gvIZ=4J|Q07{M&Mu?^;Dq3 zGp7muLSAma+rDo)qmEsxe;xuX3-k7`^BGSN-fBUT6Xd@vSQ(bX`si@nF&5hwO#gRh z1+34Vho+MzfiBj#1)w8tPo9r7mJxa{u}8Lp1cXG)VY%y#QHAg~~ z3?h6Y-Z}}A-1!QE(?|8b z7)p2u&v@`Ty&Of;K}9!SsJrs7opf*9?sW+b+Y@Q%`#S}srTzp`hurKmXs(NpXeGGb?7Q--Z7PfLp zQcu+5UyrY-4`N@(y?e!wEWDM?w~1}=FQ+E#vHUD2`N}I{XK=bAeuCAWf`pwvD!qF* zL*zwkeAx&nqLQ4-C!=E&`S#t#TYwZ?=~79omB|l*YysQbbroEG<`i9!D0s&nN`Ta% zJpcW_0DGVf0tk#iOkpXu3jZqihkfq*aFtJ$7FbBv_u7Gxp$VhS2=K-8Y=EI%G_yM zncG~M;)qI1O^wWn%J=m9UHo+}&&36J-tYHoJfDvjYBz>XTc}WWbY*D%71D5RVZ+Fm zuKZ(qo`FFFC~n{!;6t}X=0wT_BjKEa911tZ=~)Xa|CnMJ7!&|-KRNM>a$`+gG|H<7 zCJ2agYn~7%qfr_6A{^$vU+T@O|KJWV#hy9sM1}u=n4|6bja2N$I2p$^0fJ2y0sAGC zeINYg375^f_++@G>JB?<+v|L$(iiz6pYV@bvknfI*sbJ@0Oj1*lKsz-*!1co96&Fn zQcmiMO6gb+&JGUD4~%6j2w8An{`-9`i;tfHe0p2(e31_PNMLoH&7Nob^tDjuoZ6 zIK+b^iF}-#TCg1H2cr26=+5g}d|srIg-yf*`EOvq(qS= zy$G-TFJu4X^9w0;ewhNi*8%*;NBUPglM=jub&gyq7c1s;%C9)0v8s1plbi5)w>`=~ z#&06?i^oH^X*3beG?b7sjdYh;ZUf-Kixdm#WVGIBYhbM_cGv03lK2h zGpTykM6gEKwnl`!%-m5jSE$Gsj<4~tWye;(=Ck3{RwcMn^S0sY_Ajmk?}S+Q!^%oc zY`{$TSQo%yn1RU6DwZ$`02r8!aYvQh2oV5Hz*QLjR+v4JT-A0=^c8knz*3Rh2{90v znb4$TEe%zIB`OcY!A|T=s{9`=TgyYsr93o>l_ni7P+21I<@TaQHF%g&P;USo4f5}~ zz@X*2>N7K-(llDw#Ay89iLBa{;ln^MGvL|89IJnVsGjYvTTr86tc)@-JUW4G&MSmK zZ3qSancK?sEjJHh{;3|C69HDzEgG!(3Chkt4;}TOk_@a^MuS}f;Zfz>pq?eC1fw@G zI#SlcM25rJ{qa!v0WGmGmh{g@2X4HYRw24W*(_?*a&X<`30EXXed(w#SowNr{++5e z_&l@_V*KYmfEW9)CC~XkV?l(1b$0tw5raNhn+|WIH0y^Qa0iJX@uf1gjN>W{#~|)E z*5>_p70BB$XZL^;1MCR4%1S=yfqh93$P?#}UAwS;boJ-~1r9jGN2#ix6jT8$pbH!n zGuR#Q;~ZU^|G7S}a&()r^o;f+N@K^*3(qIPp$sV4$l%XM$?qthQ@9j+rs5cWy2w$W zN?c%c=x6&p`(U1)=4GbZ#fQJA8tf$PC%H&2A)WoseF2+($$*(kasD6Ph1P= zM53Gw0+>^uFJPjSthqr9j6Yckg3{f{6L?j>-di>w@Bl_WgfBqN27a__V1Ej4Zk-;2r z1wr7qxCu=#UV<;`jw=KPfjiYHx+;vt2kj;u_ymbR=h{|(!C^)%gsP{sxaASt_VuVI z09Moj6@K$PK6epJb}Z^T=owPyin{m}9Ew`m?T5MX_%}D-+x>!Ex|fz;zjnB`Mvk4R zFdn^zO;_$8Ne#FF(jla_nzRHi*Kh5y*4fz2{1*@@A#go=I{rF`s?2zt->7*6bXwuL zbaX9v&~jO1(o4Ic2#LS@mHyf}P>B;SB3=cl#pa^1DI%;dDgC zd-;+pPtoG5`1aD)@isF_NbQfUTS5?<*wKE&I{-pdjPu5X*%YS}Y*!*nXGLMI6MdsC zrjjqa_n@WOvJ8F(0(=if8kvzej`LXF2S1i-KCoGDpKVW4h}-by+fN~H_2;?b zAhg~Gao;o_mvH+DHbo564w!tV99uSa@7&Zyq>8xKZ5gi*x?fsMW%x;Xf6v^E*`;Y} zb8i|JzI|X-i@6845n^_-V(PJ{q9xZp7H;dkiKBGB5Xv&w4=`Sf93;_6D`8|unNlug zE}=zAA$!gXP0kwaQmQp_Wr#&N`Ab!+!w@gjsarN!|n9|+9;>@kI0Mhhul4U+Q%ht zGV=Qzo1)a?H-#buj17HknPiwGxE}eoIgB9=5ELi8{+K=ww}#;u5_^{KygPW_Y)K=I zYy#Rs&w0P_ZEL`rn8*!|z3_uey&UKckWn(iq4qhd1%SxI6>?nY>kOQnKO?DC0`gw+ z{i!z{y9}q{bh+OV_NryH{>?kyGCp5Wm!EL{CyoOYpuyVnkIBe$2#v&Aw{~PN0F|=? zm9Kc7v-+Y42o&^i>#XfC`p-*QM@sI%ppq~%$sJEH6O+Y6B6>i46cl2ryb0dlD+tCz z5P-`;$GBjZG4c$t6X|ZvEiNIBNGJY)W7zVFZ|=_zU&2C!YmuObe_9^g&zNsGJGn+h zcGSo{u(PCl(%9#X;Xa}*pfdrJqIUyTz0M}s)EuyUK zFaFr+q{9?9qL1L0*IgrzmpLJ3%Dw}d9SnZVc(nhZ^QH7y$WlTgdkkJMaq_8!kS3Pv zyh6Alm>C#kr>o3%B80TnV}pw48R=(9UnXpp(B=`Az99iAi~(XY>ytiCZsgY&XsJ2Z%tRDn{?fk~$n-Q> zD(rL0Psc!xg>#emf_d}2QXWz6w>e|0->bI}10fO-9P)twocCPXdgP%Q=e4(4c!MWE zqELp&u@j(EQ?5YjALkpiQxtx|V=V=Y*Z63Ev;{os-rl2+9<&e=s`b$yokuUqy7BdZ zCIF!UzmGd>WJtW4AMfa#2P?_(>*?=I@}m)dv_~YEs87sJ#CCbOb&$cHR!>||7za)B z+T?=8Kkp2)k>+~Jbs~(;D?O?Nmpq%;zK^+tQ;M}9F!_8F6GNV5F{Um7Ss_crQ?wuMTfHCti%YNt0|Dx|Q zyu5R@wGhY(uA0v}ExtB;m)DNu+#Bn69!wysyhiUKuOysZHsGruk@}qe9@q^1_*kjq5o+rgFbJ$=5CLQt{N`{dPLN zsYKE8e$#u74D?dbQr!|+W;XTG*_zvf|0^Ir=#&J=lJkYAVd1%uFhDM%^FR;xf|@dKg$FRv}Hb@QufEdh7V-g{xxhy>C@ zejan!L8nUVMp~3(ZmSS8-Fw3aDOxLqAOqC`TXxIOcJJutH`3PI$yZ- zT3`LoL@PGrrHF*78ku^#<10C>l)9m6V>1bMGN&PLkB%G-|A&gqR-HPzjW;#ZyEh)i z5>S+KuI=yW@v(#VTz&hxm&XsCU2`~e$XMayJpLoXlTVbGw05v|=riFeeBpJ3t@>-% z^0_rO8}SE4NagSqjAjSqLOH}a;K@eE8gHS>G-F4ejE$jzFo@lvXjA>#2AjO0*Uh?R@duCOzY{7DlgYRPQDWOf`O ztf-vxbNY~+dB0mgH}1J?Ub&TQIp|EL7Uqcopi+0NG$$id4WPWeGGvUv$s@9To3}$_ zvnVlTs3X06w){HRw2O2IN(^JS^+n5VHQUU%z&hbeobbk?_02ZT1@FS_@QUkJ0ArMx zTCgfORyXyhJ?DSnrJB<7*#nq5F_uUXR=XX#AtX3ruSXPV*wfw4rV( zykzZ}-DqSGVfy4tGpr4Gzl6NK&yP0QIboxSBmV(n8D1JmeqOb#R&y5%nbU$}nf~!h zg^62ec4lLNvM6sRCm)UZIUM>dm=PCYi)Vu=34)=akd6l!3ay zP3h0lC}~{jVk3QZKRM;GX{BTdv3o4;xJ#VmFx#1xy(C_88KA%gzBq~PZ=d(%&2`@& zIH0RNh5N4lzK}AWHBmm{k`8|%_lNcNAf%1G>W;0v2vA_~0N1FyIVZ(swG6Y3df-wc z+KRo3MjK^rk5Vl`=fY^*(*2O+gJ`(Jyn7{cx}~*E?5!U;yFjIIO*Y;>5uw zPi`6)cz(r>_~>eUwNterKAy^%&5t@pysdkIAjEBHQJq=0#zcw9U#(h@<#7zk8AxJ3 z#gRUy*x$TLwd+>uR&sJvV!Zw+<@U(U^%lg%GSO;11a2*7_!#PIlU4P2d{yp006!*e zmF<{nl{a5i6{eL6((vZ%G264C(lPpz#R>*w@JQQ>rv)B2*J_>~S*^tVk!FvbXAQZxzt8BRHA*9Sc(b6*Ou=4w7f$dz(SB zK|t5>Mt^BYj%#z?{wf}x=$FP(@2tp??K)63bUK+>F}PS=kNmI)I-d$|9dG$X zICC*EUA>xw=jQ@q(CL6)|4KO^rVd(*xX)#YoiJJJznCB z)X6}X2lZ-(u~stD(4=ij!oP_!0U6s@9Pp*2-s~0Hh!c7A)r=2sfMi0Dm$?W%tx!i! zF#C~t3=}P5>CuYs>xhv5=O5r7xF>f_a^5_STkiR>G(v=IuTGC{rd1D|%6*n`C1DG{ zQkvD$q5`mF#ML_S4H;0DF-s?_DvvDebpt<{yM@T*{jcixU+34A+oJCa0zsz}=8HV& zm~2!ci~Nu^lNr?%d8eZmRJf4!+WP^Hm+sS+pDEz|vfVOfAqj zmEYYa+gd)ew_^;LW%7O?P0iF?-YDZp8Vm>Hb1&R`!bj0Gk*$kmx60X?`u&01mM0qo znpF$l-OcTzlh>RZ@{-RtyvN#+N6aL)%i?9%19sf2bEkiMr**El2#wic&Amg*|6zgQn==*nq z_x4m74`&#%)unC;E_?z;XRfu$O;b+>Zy196QDL~)y*CdI9;n!id=3QjM{ZHK)P~i` zm+QChB5we+{PYV&a|=dWS2wu7p}Q~&eI1T(>QqU%KH+Wd2_7=S)Ks#eVVW7Y0DwknOeqw(#$Vb;kO)wdDhF zzwp+`bK>a16qz{${w1$VR&`K!KgMI$fvUTgG@yNodPZ#UZno)#*2NMnW<`zBhT_<2{}8P7L?c zLtT9l3y(_o!b(b?5xk`Ll#gG&@hkrh-6R$q8RamqFWb!5Oc_z>QQatgl1$5GzFm7K0mBP1q&0Ui@uQ;QKiY*mv z9C>vA&iN7!fd%_2b3DML!_TeVIYoT*crUXG4YfdkoiFoEw`4_blj!cM4G74b`e*TL z%{RWYzQVp-zPAE#BoRFcTK%Krh}%A`4Nor8P~>A&>?wck4Qp}&-S0gUd(B@hjL9nu zNvP~Uv`Ank==%P>R)`};ug~n;Ffj^U(gafxg>)3!KlwR}4$pUY5e^*Zs{}0E5Dz41 zBg2n;GzwAn@z^3=4P8za$s&xB9K#1)tXwY4#bTLh_WEfm=j6HL%y>#YOV%lA=SPO?i}mg=ikWEUET+k6(uX}F#N!M!YFTm{hle1 zPbOpo3Ul3-G7uAsO!S^vrc7IhbNp5AzEb`E%l84Cq(dzvGxlxFCsJ8wU}h*K!Rup& zw}~^yOFhL}j`kcg$<*e!4Un+}LKN*n|1!PqNXeyTVJvMdk|laA0_i1=o>zp+x%7ME zdy*^Fg2o_7nU=;|_2~3i-6cnN&y(Y&BCkW4)rz1cK;QqM>My>hIuKDQdNMZET>&K_RlK@nhFsn5?S~$c!df1@b#4l{TKE- z#P{$_q_p@kH=C@+FvjL6A4s0SIrhc{e9)9IQ6d}c)lwv0mraoyZyxyWA^Iz zjR%`L-{YH6&a-9>;89_{rT0d4VB!)gO-<|V42)O|HFQh|Y060qt=vxr$vC6FQ$-5= zcr7hwlGCn2U*R#oiFpONChI3`pTjKLNKjIlFBGl53ynqSn#3~s6XP7w_6n~&BKTte z)A>m=dz0oMdq!LXq8xA8yxz(*;WhfwoS!D>C+Q^Zq`{))>H;ghC(|{|%qL@aWS%k8 zU+dF*bJFCDUj+|O8#qx$KW4PNn;@m=m8;L_@9~21(ropmdQ5FtYGC}~%o?NDH{<8K zvwsB5oZ~8Uvk#b{QgC(T+(H76u1AOl+kl3NJMm4ka7<%cg<$1^&?9Gh9e2r2e$Lll z|6{HPbdK})Wkvg&n(_50j|F{RrZajuhMm2QlnVRi<13}|xW>qDY|RQE{sEQ2b^qF$ zf=;3jk9=Pic^d`u=sZ0+*P-C($DQ9f@=3+gZ=JJE(9|cdo#)={a0{o9fvN@+#bTPA z%S!*>?FdAeWM#D5e=dl0xuG``XP^7PhLOptnn`r2C-)Q5;$jURnmK4N&-|6Z{+owdA{>;BrV>)4B7ROO767a33V*Rh z+@~6>MW!vNlwp{yt8QtjetfjfRc>AWvw)mURIFAdZxIS6Alz!gAfwJ%kvq3+KnHA| zphBNWgPqhWNx|3wW5GQUcbK~+oD_SfumD#ol^~R!J_E+@`d>wgRSJw_QoP;A@0Zlj zKH$i21|MtbslsBFsq%ZwzW>NIOX+FH-l?O5vqI+)V;y&PQ`8lYL5EMZYCM)j6dNh_ zB?XZ~?&OOe2%AtM6dnl;Vy>@wJXapFaWTPL;H!vLEfkc2 zVnVTJUq+d6w0Tw@xF5WczS=2O&H+Z5aX9wVuKQi%rpq0ZQd3zH83TBlXnsbai<`ILD48FF1FzV=J?FP;!+)V}y0 zESuoR&c!ey{}?d4;bB7R*b8QRUxjE_T#1zjl-cTwRx`sggGS50#VS zW0EwMI0bvosYOuTF>2aZNVU625b9``7?P4_!KL5%eSW@b2;Xge$TNG+?%xu;26Dka{1Uh2hlGt_x}m@evD752-xS!|UXy zvo-s!74MiZifumH%;_Jf%KD$b3q11*onI>d`FRZ2A@VfnY>|4B{pkExU1F;s;+ybz zEkl1k+1bGWDZlHN5e&74k;w%mM*I3gXl(S+lMH_#KYfu1JVfi9j6_>t33g^?nYVv= z3z}qT8gnU)8G$5+=sV^&&(OLFVS=r;RcXsCxYw(pc=Ke;#1k~kLZkx+zZkuClyJii z^r6%0PMWje4q`f@={k3oH`f=IZvl+NMQ#W}gc-Ui?I8Y5youR|%2amcvk3kayrUow z6Jm@ujC&Up8i!PBHtEE5%&^EJkry&!p$D_ZI(0ymFy$01>BWe>V}Us0H$gjzn~{2w z!@|3tOWMf+9Vaz}V8NeuUIFjVtwn=3b5}7jR$3D1Qxb5la?#D}GhsCBMwXz8C}XHs za??uX0A$iC92F9aRg}ujDlhf$0Q2<}!-Ay|xRxtn(F_hjP9CXV8{SZDojiILRQ3l4 zG!lgxC5W!sob`blc>LFi>KU#}*A$e@AkN)~@T?QcXh!VzPJlfe{^Ars5s0W0YeyUr z+I2Xf&3|tvp~h%`$!7rBxu@^o;4uy&c*RCbL&`cc<0(=pfXx7=OZlrL1a3kwT2H4B z*i61JRQ@VS0daU-!r?rd%^*2EIo;wU<|$O=I#)g41}bsI^&4bxT3Ljk>U_|79TDsr zQhc!`R%tKnho`uV0cq~8+}xG5n}N>Js?K0Mff`#A&2!+d-S_J=&XJF0@{J-ekw|s< z9|(CxsInv?RnL(xN^a*WQ`=D!2~~#YN?88;^X&t-Ax4C4hYUb5-B{badMZdhOJ}$0 zXIvt_j}n_dWkr%g1?Tkc_0CdGyB<3>3;oT@1_x?5#3aIVA{uk2X}fd9iwZ;xvsyWS7RB-4}JDK z5d*3cts)8hZ;Rk>?=RmHr3}ce)GsC+c>pMk7p1rTioA+8TMMqvhbl45=NT|~bL<6? z!^bOO1BD7dKQ+j6UM0UR3ebTAD%yxaRhJD3b>dv>^Y$l0pVoa$j--{$<|B;a+G`r? zI0L}_VfYOrFS4V~R|A4V`jQ1lp8c#aDV0hTsvoC;BoVBb3F3eDpRfDM=jA4`+L=Aq zBF$VAm2Q~m8fWdU^uphY)S|u_h;!xgMxhqKdRYl>W9)wuSNYRKUI|~g4AxglJS8$? zA3cK*EL&KFEOPd>B&F-ZFoa3$R&kX(K+F3MQ3Kg?#lf*V9_4@^V?;jo};{Q{={QU8pAcf{gSy*k>s0Lt)-v1kI@_*aG94$mH6%~n}!=(xh;pM8JPZvsHxe=lg5G0Xo>m9 z4E;vSuM?shMS3xN+IUL{t)Knw_lVUBKe6u9%Vy0`W^bj=Xm62exHwU{sb)Wg0Sh*= z4D3fgc8i%OM2A4hB?S8D70e>GuQay&jw47yT+^zaDi;#AB-s9`+qKQlaPj3=S-T;_ za$uR~v3GGwUUxq)EaXuyFfXt=iUr_O$9}q+B_GVZ_*Y7Q2XyLF@7~zOFVqi_*FvD`J=HHh)}melW*ZLVX2d!n;^c@=Qg z6J;Bmou+uOvKicb;mHcH+{9HYXEm^Oo59yKghtO^e zzJ{HU!>t48WCJ-WAV94)oxfWW9&lwa9SM zAw^`Lm(BaCf!S4lsT6=xrbUP&`P0Su%l3VCOsV4L6x5b0CQ|Y|u*3D7TV93((a(c_ zmU30TWZh@YcpWu31`jv))A{%ygk7R1^!zelM)rId@ay$9noSiJIB^Fg3;GIOIWT0I zbs(scv=weRwPf{`PxYMPTwI#=wmUG^C4vgJJgiBmQjE9|9tz;|w*p5cJfg0{R5^x2 z*N1#ggDJ+Yf#_=LcZX9Z|y=OOb|wJX{oM_A(Jcvq#I%y(7&@xMQ9-aLjPiyzxNPF z{9UGDYEsr?3O4)wThB4AH6HmmEv;M$obYuDCc%Lmf!*^qf-hWkAG;VJ6jX0=` z?3NxjGpmS|*T( z_b%E78p1p9fMRWysT%02r_8Nbskvt{$tZyiNO{MeOf3k&bcn8oGsj9e7*x9@Bj}^J z>V1oDACryO&kF2&1$|XbIw}%o#K>RkpVsW#NjZ_S(x=|FD&YEIUq^(01NtRHti%2p zc;m6r3qvL!HH~>XR}aDfoZ20`s`Hy=2O&`DYZAnP_U_j?iU_V~-b>4&M-dJmT9hYyQL7dl|V6@^vzVP z&vHnpe_DT--N)?9;>adK|DZ%Riff`j@V`isgGreobLA z`DgpQLGtX@6~AFQ!^zP9CQIx6!RJ}x-HEx_6K~lc*k9IG*2PlAa{sRH)JJ6xvvYas z#Yng#(5_SXo`D|EXcZ@-LeIoy(Zoxl09ogI(31HlO3%R^zII1G zVD~9}&R|Y9)>)JbNAmE4YMsK1!UQ8t92G)wB(cLYTBi{41F&AD*gzLtR2S;3zAV0e z55ELW6+}@Yygm7gzwsXz#7gF?&EpuTuXx#Fq;2{zVJB#-u?0us8N8-{ieRT=K@cVh zzoWdibOhr8>suV|91ktp6;= z$UEqt4vytQ;Pb6SstR&QQZ5dOQ9tKbnfX>08`M*dvIwac{BP}c5ISFFzv~~#vRw6E zEnWRFPi{OHt|}eB@aaW;`pURD_<8IP?-me;fM}~_=pG@HB8b!J_8)-X06Z+_=7N!8 z!nZw=ayL>vm;IK@T4!d*B%j8=4U7ZpX%a+UHGeQZ^X4UZ8*tu3q}upu4I?OBlYB3x z=F=+o0{2L(PhL^dCH%8*-Hc1lH7^t2MKqfUi(ls;VM@_m;#>EUECHGcJ|Xj7nzA@I zJjdpTUl1j+3|#3q(eiQrtNjPTO*pb-^_8^j8+On-LN(r28b=l**Qv*G-JTjyjdv7I zGou#aW$QAw-EvRL-LpFn&~GRelx)$v!T~&`+;DIw)>Fz-%aJo;^#E@YraAB{ zwhb#UjSrcgnK={G%w^Ld? zlDfnOHaT;4CwDh@3nWv1b6z?R_!xMkwN9A6F-dq|*IXUbH@L(9N@M;CFC-x-7 z(TsI4`fR?&n;S0^%McB4$yxPp8shXu#w0&e-K$Qv_f}TDg8E z0wi-GvCA7M1F+wf?4K!Rxl`c&lM}cbx)yp%C9;HjDAJ5XOJtX!<$Qc`=FOy9%T;7NV23BscA#f-FsvQ^qb9l0l#Fy7!>N@Jj zaZBkCZtSbej6WQw22YsJJ)Wz}z39fn=quuW{_c!Piae(H!QLxd%Bksb=iojiyJ6#@ zs8@Q@6s%38x#uw~#6I`Mw1}6R5H*>9vnU^O)-<`5)#XaFT{# z7J_S4|Iwke&lyKn`5;~VuD8uAH!D1E|2Fu0UBEnlW%q?$UDapd>tw&n&kdv}?9Bz`1?5xHZVmS{#-rd)P%{Y^(>=UR6uJ5{8mFNsC>{{> z6V!*S#CXia;MBA^-jpEe^N z_#}!|d`g;5RV+MbZ>{#`# zDx#7s^(nq(kC{g#A7}OVUsijC-6L9|;GF*~mt)^PSYpY*SAW=n`PHKP%AKvFUfr2_ zF}X?_h&)#{g3`Y{;Xke3SN^;$&M3Zuez>D;AcGW_G>bVHK+xR=FYk4pk#96z$vL2} zQ&6|1_|rf0)ow{`8xOviXFZ#L^H-mYlD6dL1}yeyz1j%TNQ5XTSGEA;A1EaW9@K%L z%MLjJ;9TD21w0^sQs%wb9VY@4WNcbE!T7(-@PAtT_{Xz7kBcw(DTzU2W{L-pM#@wj>0hg|X8Vi9z2 zC2&8e8KTbTw;FllI6=#`mWIq3O^cYU-tvNclBv_RKG_A=EW$@b7LqJRlH^d*m!;;k zWXxJjSxDm`C-#-}Psz+EeNy}6J~4~9q5W(GruwWNB=1DbdJi(w8{@lr#b}kKmYl(% z^h1Wp{92&V?1Z{jsKpfTuErSkDZdPJ`8qhmO}M@x5G1cmAbE|ZVxZbC1XkhTAYAxP zp+H+!n=Jd}I%eP)xZt}U>5a~{$WEWUHXZ=(2cx=tS-z$!P#{=+xpmpp ztOF%0FV4>U=zEx=gAKU2qFMymLn#F2SS?vz*!;85v|6rB${9V)(^_c}mH(hJUn##e zB`ygd#@+*+)RU=(5-A)tN-=R}y2os#A`LS@R+J>xuHyZuqp;iLd26rTlU*H(^zIk+D6AzQmxV(&F>M*ECyKf%1&(912lNkqb&EdxO)LDJI47I zu4LX5U;h1*rzrcoAzI26oIyvP)xi)gw+I``)h1(LeSY+8T5)II?~s|FkE(gEaVU(v zKh_;+)<7%`Z9e@hrJ0d1I8ib+U3^iqqGD$)_$NpT?Yy!1a3#dtY@cgWn8fJ9VeTG4 z-?(ibMY`z)&ecl%RRXTS@Vaz{+sAPeXl|+CW|=UplP*-d zIridObMRiok~c{HgN@% z5y^TfYUbSVD4r~2xg!>wfhQ^#^Z*cptd=Ww@geG0I`SW^2eR;|exJF+1!6B%m9OuFs@aeRY$3#k5Ljkl>wd-&qEkX@}iA7=n zMMXkfOY;Gx=YpGSqH8>A3s$OZ7APS`o?9`|x?YV@#7I9nRg?=a7R!ShB9_ui4YGu6 zeZY8F%tpRTK+=Eb+3Fq$Xv|N=J%nkgBjWdcRzQVN8jf`K_{lz5iuGc~UUaDFa~vr$ z0zB^*N()UTNHBY7pp}9RgJvw*__+iH#_sC}dgBMrrSRBB z@>H$s=qYn4D*2?l_WYh8e^G*CBG$XJTPWBUA2t>;BEzM_EY`6@TnznldV7PmY4i@4 zc(gJM!bo#5Lf=0sr0S}rYr#3M1d?N8x<>3wnom!)V)uf}7}%|kKH$tNZY$6}(0?zC zaRvY!($1m6cVr?iC3oE5|60eTu9vVZ*uS>r3aaQ zJ}UBBf~8QbFy6LJ&lD`YGmZD%21R|L|(Y|3GC-2h`xUrYGzR*EKigvtIdU% z^0*coPhY=0ck=cn42g9ua_@% zqu9nBv+M0d+hoLSkgvqa)^g_xd8*Iyd$1ou6u7mYy@g)M`c_W{`~CV(Mq^W!jz|0( z+!lv3?HKy(j}5-rfmcx}IuElB^H`E8d|sJx>DG`ZIP;=hVwI{@vn<2B$V9GPH0~ z9iwsWUpq#ng`5>R6>OX~j?ZZxzxq;hQhe8RpA|LK3(}cKQZY#Fq9-1|BF#zTIUg$d}Mv@c)#)i>w`+7kurSJD7dk5 z24NQ$1^Y)x--dxAL=$@42HdWZzU248C#Yjo11eYL*xCBT*rAp@(!MM2RO zH1Z@*JFs5vOn#yd!cl_p*Y3(0ndMF@K+n4Zc4?D;#iLTY;dx z5A|cD5l=4OwaRQaTzfF2&Lu(X6-xlgV-pTdJv00CiZ7&*E8jxG9T_}6lI#6GBqj%Av%`5S!A zW5nO=wDsLdQ zfcH$>en2FYSHxR6)1b;=SdG~ir|s-|!dZN~!lzt2!UZl!`gm18vzRXu=1(;S>!XxW z37eo?dA>v>7taw;Uqil=At_9}ar5z+(E(zwD)}W{3!N(R5#J{SglK{!tll9l8MAY| zGELBIpM>rXR+P*&8I@sUuXXo29si~jXp&`W z86^TBaANSUy;;S=4%$&lQ^_p2Rv^sD>1~KQJ@d%&I!Iz|(ymHvNQe2FgOg+;C%=i6k+w(2$7u~_lk$wb0p^Rfo zd5s^}y=Gu3WU$;WH#^|iGgI|GPE(d`s4 zx%eCI%|ng&jm;^TO7wcX`m72AKtu@dD%1313K7kZx7<7_-n*BG{9 zVk9q56$iH);?o$#&hcQkW^= zEo{PNnWg%)*hKe~llfBG8shhr-(sZU5yigq6o)-$mQ5LT8U9Sf_gayhL3-;tbLRse zxc*;ceYZW?%^{D`$~wq=$lyOc`E@lrDy1W?O1@sjjLiKJX?1#h-*BTL>#qk@q5oz| z*z8!MrbL+HyxE}&b7bhpq-jRub-WNNL1(y**$IdE&Dxu~MGKYW?^aEMWE0^rhOKEG zkDp%VaOQBQsf*Rv#2H5qG&7l=a7TfnpJw`E*2Zytu8r0D*x?Q>?Y1$mlTEkzr5rCy zw_sjUT4Tn0+l*L_Zul1^6I}%w{6o)&%B&{|ea6jA%`*p`eX<|ACMMJWgI6WDCC|Gd zMi>uG6#h}T4O4GU+G&qxBt%SV{_nPePogWw{g|LbMxP#zr#hMm7DnMk~*Zq$OU>2r^;am zbyeIJ!dMZ;==UTGS6vIw(ngNS3)}squJt4nM3#bJOi*Bl%KIRY3`|%~B^-aMZ;s|S zKHfmGy{((&J496F6pFB#E^~Z_CBQCme23mF;2YNIUNiOop>B!uw`uWy-L|$g^ zcVRVyQ|~WuSej}08+`Yf7Ocp&91Vg&W8(JiTdKen&lO(Womn%iR)${NumgPh`{2pn z1g_oL(`k}ND5;R^Ct}Z($7VfXKiK4G znePB!=hz!;aQXp2S6%Tt1^&HW@T<^s4i!##`LRdEpDq||EchAf-tY(g$BXas^gV;} z)^wJ!dsr$HLsqP;dhUUPSSeYRTe09#$Ga7hRdCy@9oU~gtpHL4ikiP@nsRMhvr+!L z)+7#IppxHx?ZtlBlmCoEp%i*1*ag>(XK(?QkXDiQlQ@zO(mCeeYfrX= zzIP#^3XXt2OKJlB-huQ#qrbt)1yYxz70%`d5?Gru6mK2SKlz_&gS${e)?og;u?$3o zlCH&5Jb3zfU4Q@2g8dmy=KryD)=^FN{~O;(!ABj8P%#)O`uIqTSTqa_U^Eh=Ly;Uv zNpAzmvC-g&(c+L!QEH@g!*i>Iq*KWezx(-~-|?^gA}75M+H&pVS8 zCDQmE@y!qjNUnz2(6Z1m8=G5OxJ({8N3YQ$=3>X2^XimU{An0S*8G^XDBvaKtFCI2 zJlIH1H0Fs6hN)|C*~my12!g*K_Pz+eK{s^_Rta4A)|cBsiHaOy+-0MqV4g=i zAjwmdWOGib^BB|Gh@)$}4t3gTQ?9v(Y%XMA{ZR!%jn5TLNa}iQZNDZA!TbIEKVvCo zAlWB>xERL|l1v&&`pVB#oI7VZ%!dq)XF?ru*Ru)@%J&a`=&C8H0lmxG6t`H+*&ok7 ztD~9G1jN&DdGE=?$HVz5(m%w(yaZdF=cTM*=iihbZ5I`=IU)8_^-ehE$A<@{`=VxG zlZdPFgbQz!iOBo-Hqcc%LqOAi*>6vuQ8!!UJK!&%kN7VaS|(tWi1jIl$U-#$l?Tn7 z8k%PH#MTw<8>Etlg*##yXXkvP>Nl?{@o0LiE&+nfcf5E|P+nHUN89MxkMF*OA0pr# z5IQ9Bjtso#e#yB@(da6N{Un1{D~J@g$~J`AH{WQ=wn2YTvUDcA<4W{NYf{F0L;IIP zVqz!)Z?cKS?)qcK?Qf8qpnp7R{3oGj_Vtg(ity5(*CD9tv(MQIqJmxx`lOZhfuvRK zQrk=8=reY!?uYp&%O?TQ({BHr|L0b&=V}GK*vaXN?nUt@Kc!M6eWg+{Ws&tz^}J>*vGe{dgg=Tv}Xl+)}C&|NJ*Un89&;EheodRHa^FkWX+5Jo*sG z3FJiJ{Q}cv@cd`0t1GLwh4jLiZ>_wMbe@_P>Fw72Pu~q)ANDYcp+{$_rKN4@o+u;N z-1lp0o8WblYx>5wAc?vP1ek?+}ORaQ$&xupAu7nJ(*h!m&d_mC#!z(RK>&DDCmOR*={5 zkxMS4K?*ua{tJl^K|gRlb(l~;YCZpeXhWN_FOebS4c8LdL&ZFocGQ z<4OgP#K$3Z^Xk8RAW2fqp#BKXD^TrgF#yi{xhkOw1`z6(89jpBg*;I%5qCpf83r)? z(75ulIaQeG+Nw=hbiV zWzRiBkQ^^8qfC0^Fo6!k+(O`!o?bE94OY?1ZsN>NSiV$oU*#k-Y zD|$C7g`~RCYu*goeKPtq!JnKSeTE>rZXKs~jLsbV>v0pyBixZmjh~VU3UHs2dC#u! zmKo%mh;zLZqJO;oUo(aR6O*8jV|ZZ;S*sD`1KlWsDZy;G^Ho>qTE5?Kx$hKf=R0E$ z?rv}ZZG0Q4*^-J`$M!T8tdb)gXH0h)Snm&J#ffL!u5cQG+7;+kJ$GFw4~i_&2mMsA zTeCG^`;MYTad%Sh%mxXBh@HqTEG$^2DNmw{ZGEM*KD#~o6?0a4UvL*TuDSyx)(UY9 z=bqy96!j|Zo!&B!7{=Wmze`5i^LRe;d{j+Vl8|vJS#k+kO=N`a6AnQ?vB#Zsn+tv? z-aV>VB`i4qV;>A$L%TaTSeK+}A0z&4#BiOv4S`ldsvzUAeL%o{%hxhS?4D%9!AcBH zjjZpG5?XGBxX%!r@NJx*CUbuctF;;OVNtMK#+NiS(-UrP?wXB7%EMFzU-=HU5QJ~) zlUe!{Z!*L~OU?evbG*1qL64hwsH4>kkrl-P1c(2cxwzF0^o}WJWHYp0;rNb&>9_%Z zcc5hdVY3Hbv!lR8&)$wj)y{Vn*NCRY;V3pd8nO^E_#51h1$r+VwgW>d0H4S(l+W50 zKwHu#*}zGHn%pQ~=fOsE-diV6h$#l>2r81H{&E%0`&0JGy7`jmG{26Oizj46hAX&W zuMO1O@;_ElTp)nyjR7AFL9I+HS-^z(fgnow+*>2Bb}-MB&-Os%DFooL9`bS1lVjT9 z7BpaXGa?V~j3%~acp#Fqse{C3aStw+D*lI4U}Ra&C&uwzy#S$H^P1H0CChG^CEE>$ zO+q*m_1T&9ombQ1u2wgdA#m9W?n0)Y4gX9>{m_d&2N@vT3P3e@lB zX?(A^_$!WDjEgmKEHm(b;}-wkhUIO&WW1&X7|n#=3S^-kn`GZd|IKTNiyUFLn>Ety zZ+iS&eyr<-D)_uuoIN!**7Ue1zp5H}1|*lkdjH||h3TbJ4h+?F^MPo`$0Pj~48!nm ztdlJT40ztloh?8w8Js`+y}lMpoXL9m4CQi4AbvLz}fqpwI2Ki%6>3)i%5?2TuQ5 zs?0gm(9^;)HrA&43HZ+0XiwVl55UKTFw14F ztLg1-I&MMO4ALGSK+mYjAi|+b=p#Ux)Ba*Eg7eGC`pNO!Y?C=upzTu6H8hKZVU3z$ zwjmjL^=)0;yA{c!(0S#0B^5OMvwNa|Z)m`QU6+tIL<&8vsXqHLVOAteSkgsyAstA= zD7DNEyyFy=hJu}2hlsvo;-e;wXV!UZKL)Cd|vYIDQE~yHP}swm>noWph~KMN&0F%Wl+ABB@adc^>=H0NySBH=Cjog3>;@1thuh)K+WMVr_bEk999u9``h+(@g>Oyt!#~YrvHDQ4_~5fPCF0 zW|cK|mSGwiE$i7$je)UNRtxBHVElyRq0!9LwAW{U(IygeSW=537=|Ekkh{v3zC7kzt2tS%^?_Ooo`ZJk-3W!BmH7Tu6v~pMBG&}LBQ+S5~-Gg zWdG%p`NLF4VEAkj$>kK*jn$8*)iA4(49^`OcZe&LOqT8N&R*Sui^8N|iMCgsN@Rmy ztpxcGy!nfH*G8g}PPQ%0)U?xp=tAbmXB?3b*8NTYspx62*>Sux_*^HlKK+f3_x)*NOlVUydi<%m~e5$I0LpBK^s|*c1=mONr#Ul=!;Vls-V)^>K<{ zpJD28REa^92@D>3?n#onp@@2PK3{G%=->LcQoXePV(A@-5LCw2V3#_gsT)%;P|5Ju`?*q8HdngXX!h1qUm>aK&C@O)kq3L`$=;tWpS4DyO zQ@C|ZcEx(rAlU^t&^^_lp+S zQrfpq$&hd5B}1qGmB`J#5Oz|#wZ5=5w-)|5=hkbh!G|!Y*>@*)2mpb*bY)xDsq|a4%ymIkE*O^-pgiN|+9K{M+10nv(vW^gr!5IxEJ1U^lJ&bI*1JLf zKKJ5(c|JZF7x`w66DyDpRg@aHx#C`)H~al~8zeWM1r!Iw<187n2eJpO2LYiZ6R93k zN3I&(hRT!O6Y9q#_j-!wbt!P3wqwZFQE3;gFSg$8Q*JjXu$yroBoN&0H}Sj=SlB#@ zRlE5T{C%pLdfSH6TbH%AVuUco5Vw12)(=cx&r7AkhF(I%16a8 z%)?Q(d}jV=jsg~Y9|h2ALX<79@uOR&2ZjZ_dVFZ|N{6?N zz+EaxU$TaNg8n2(vkk9ZXCKM#GdgxCc*R2!mJdiiE zNx?imN(3pT2z{+xv?*N4*d~Vex{5vPp?RP$N;olmN~v2B)SrAUr6s{;UH%j!7noQa z!%4<~NtFg5ZM(z9rqL*a5J5yM*>5 zU(nCLmHGEMkXJFCQXXdD4!(~Ulm;XQn*P%(4jyuYdb}d%2jIMLtfRe9E9<~*^YB}k zKG}I#)|)iL;#m1ubAnsd_iM+v7MKA{%YM_0s#nWfiMaUbuC&oxyOQ*Hs`eCH7$SqJ zO6pnfSw}MHNsz8Esq7@jZY_F$=K9oi==-e@sd?hem3^?qM&w_u-Z%hV^!cUv2te(_AwdY(dSv zX%_Z+L6rr~5;j9WZ8B2m>6&tnQX$z|+?NLKPlii7Oul)PbWfQ42KTf2s)gfbBW%rs zA!VNSzF2ek(;$gg6Qh?Wc?;C+)#Rv2>5028`Zb(CG;Qy4pYxz6YN!Wopw^kC)tO~| zBPz&x{JUL&{S+k;n~MxC#DS3)6i0@oTpnkTZb>4V#b&tfYNW^LN0LO5tFk}OWM{P0=b5`P7%5;IN`H4 zyScS#4%PAaPd_MPByShg=sL88`uyjgRe*WG3ZyzQAZeSAGsDr{V#0gj!runZK>abC za(ABc+LGb3CN&9L25<>q?^HUv#%a|noFF&Y=~^7*AMgI8vn#{%Rd)*a=qA3mqwk)C zzk1~x)lkY{Ltj+PFTUX!9?#;WVb?=Anh`h1GF}?7FHEPTSES{P!eK0xnib+7KIH3Y zJ8#%R;9M;0r~755lFA;7g$uia^M~-McbN`rHD&!f+G|0OfKWE!)M-;uivu@SAM(xx1<)@xe$Md_!ks>IOV>uR6_Hh+mLoRGeW2Rb{OctlP|allHTDePqDXC zKGyy9`}XojQGN(wF>aSA_EY1mvMZ`N)prFhdh+q`Y|`2g`wjPQ{(R? z)O4Z_QU~a|UdG^5;lPBFRXf(H6Jx+ft0I9Ng917i*)SpDiHCOHSZ;@Ds0_PvWbw?& z8ZhlP)K)(2{;Fp#ElYHP8J!-&&gQAkDxv}t0@>f1guI-zCEG}8jy1~lOF?{~K4!&3 zavK(9F>)x_+9>G&4>=cEA!6p{FrL#v`U=km_V3CZ~9?(bchyQ^yP-a_4(^( zRz?0P`DG01n@_v)ZYC>m{HE|_J-7kBe<4?w_G-@Gr#Vd*zo1M5(NUu{E<}v1ZeJ~9 zzRA>_aPH?jpp*N|0c$ZbAHPB%*!(<@1oOXZl()Gbz-DHx6yxMzqW5({rYX5~y-nbP z7I#YU8>|Vdn9w@!#5@_E@Qn?HF+4JNg4wHx6Uph;J4^Z=+hr$$`@&NGywvEpbGw1> zx@5b#w4*ZK4-oPG{W?>AvO?M=uo7&!b>oaq^m6dCI5`gD3E9W zo*GJBlFsQDUGqGqQ!x) z2a*u^N#fZst2Y{7fNw1YqWL0w>z_61toZtfNJ*)awNHat(wG`02dRnGjlDU0Gv8six81D~gnY!76~xLxNYp6Vo`5BR*4((HKFm;7Y1 z=%m+IiizH9!=FK&>g&8N|Lyx*j%mkfzF@u^621JNKhFUt zX^8S2>;pnx*qB6#D41ZF1!a6bZ;?NK-t+^TQRN|$s4p(gkW-CJ785j;?ymM&HGAt$yVJ@)Kdz!DU4gWHS@~0+qxe-6YN*y* zGQUYaow`BGXawL88SV^`K@++X!QHjQ?qbU(BiboRu##ZP8;yM-G2pdU1v{nd2R6MP zM2=&)iGec8f?5t4TV&26u$e{QhYv2q6Uka3L*~*Ns&ODWTwB&vmqOTL%?UPlRPPV= zp_1(xhW2XK^d#&=t z`8{5oSLFCA6mdPs0s>sz>lCy3i%nigTrghK=fVuJU^dcXJe75YQBq>41cS?*TQw*I z{ro2Ei6!7Z3FO~g(H=7Ut~|wWXl$bn=2f1~Il6Q!a-{LGe;Bca&`p;kGy>0pWqzJA zesTxVBvtJ$-(38OsFV+oX&wJ=Ih{(i7sF9G_yiF4e%L_LcA^l~W=lPB23`LNQ+ za>@RRQiWqiiE)b5u%=R8c~0JaHHUP&LBVyZX@yE@^=!#)HlrxvT{$mmo|tM{nsMe%iP zud0-OEs;^bFmvY*N9A26IY*%Pp~#NSOIE2q*_N2&l~_9Rj((MTR@E){XZF>O$8+Q% z$H8{wZ2MD(_7>vhtRn#bZv4+s_MeAbY%t&ALkwN-rE$AD*hJ9U)N?j;V16`Y{?QK$z_}`)&@cOsknKI-(~Ar>xcIjF_6on^dqx3zL;)!u~v-D;p`M zG}iWF7;xsN^=@|)^MlfN2)_}<9{a558rTJu&@!*}PD+JLq z@jd7}{jB>`(qYxf;(|HhjCiy527v2`uusKE%fLw|em^X1U2DH1lNO6Xz5`0YKUU1o zG^z4ZMf`0wh?I`&cYCwH*|nrF(pdN)D}ol<^<+EoF`l8kBD1VENmf?57T6$qukJW` z!-i&E5C6#j_ScqeoY!Y=cX!aGh$2`<;EhAxiX`&!%9eqCRo;iGMLT-Qh+)2>=yxCw z4mrY02_(ooN>R>8USsnHn6wP4gAGJAST#@$sf=>!^au%S_C?+ApcdbEJ_aIX7Js&P zZ0KVUN~!(A?sekt6h1NM7}Fi~ME>xRZBV~PrX+q?GzxD!NuIW?wsRz3)Q!HakJo^q zgoW(zjz8#06h8UDPM$cI1jW4Oj3Xdn_oF?gpH&_%D;PkEmj-&A9#O#km7o}Vd(cj; zKQKc1OvG?w-2JAuHnpny;nnhI-oG->S5D`@y;g7?Le(E4s{6;X*r_FyBE8+DMhddC zYBiGoYj*WRjqrf^XXXxc$DA%)6<5?TUw%l(w}#pN$gttkFmuRflx`9bC&7SIM%}3% zkp?$4EeN37`w{+}#w?_)BI&~nYS=xEUl7e!i2mkUMtCU~E14G6#_OTS#?>u3wLPQo zVJ5t@J(9erZzV66Ml92>Hy56>*pmEy7GCPYczM90Cy)m>+>-ormVe8B&1LOn0wH|j ztjcj#jdOIa*}9W7gpAAxUvhdfUgz}gI~&&EH61BFyyV{B=}%hEDrK)1CnxP0e3r5p zi9h6{n5+$KkoAGzngOGly(fKT75svF0Cyefus>rKnLIw&n);X-=!qG616IYqj%w9E zrKhA1HjADq+n6vhiz`|6(De|XAxwn#NF19zh)O5Txy%PMm<7-$_RCi}7t??OvKxgE z>KOt5`%4pYy0G;os}Xo#di_N&bK`}@$u=`F4$LFLuj=OS#*yIF6<}Pm(11RZ^UK&% zT~ixNdb4})GY9BTVx0^%?T)W^^^F&v7xYM!)D{mU%L~dFsSQS#lu2bllTxQ_(kY7lmY)P)z%+)E?4rg%}wpscEId@czP>kubLRz$eOD z!@IS7m2L2T6~a%=XmASD710QoSlc`ykaNH1KeL@_@Uc)`QU>+rQ2X3jL6^DOcQ0hF z8-RKg@vPEA9k23d=Z~Mgi?9UqKr;V+(zDqZsjRpt^v(xD zcu&#$_g16Tpyxvf^TYcPLjAbY&R)@cg~yel?E@Rgh~ikKH;nXJJu4Cz3kG?>$gc0V ze*e71oF`jl*r~ru4uNe2R2JyCH(1lqQ{D#%t(jsf;qH6xX|5k!2ZEXC;=(hJCPSP# z;L@_Fg7ST|jo9amN##j#CQ(?7+(ILqc!*lfAWPssd ztf9+e7Vve8p`;zn!V4DqoJ>Lf&NQjuiy1io6a=N4X!qXZNW&U8h&1L0(KxDhjTq_-!bAj?{&PMTrW z9S8aY9mhPurcdAIx%feR3IyN|8TSc(YM6S}5qhMe)j{W0;Ef9IW3fJ8CCy`}ue0%K!J|g8X z4Pn;X&mQ!X9q)K_)VguU9f3R@SMyw_<_Yt>UxNe|Pj1_d;}0?r*PG7hPIY`;N(dig z8thD{0naOcJ>3}elNu3qsGDDEN;Hy7i`9?mS~G9NTraWNv#5*XWEsw6nr{S~tH8MR zUec?`MZXR*zEC-TW10|OvU*59&b?3PP>dS)Of3jbiEMfQ2rB{4Wn<}jwI8(5D|@N7 z8q`nVcIphlPxp$~s;2034g%6Q0RJ$I&q12OdRC^q?4mK^V^nZO`?qtg!3122|QfXKI4fy1! z!^Vg@C@M=^T??(bQvM`%;bzF+i0^cE2zn`{0?L1j3;G$cta6w)M_yGXbmIlmxVCxS z4W)gG0O?Z&pk2RG9xBEaCvvPvPRsEc&QSoE1*Hu&4DA_cnEs!yg?B6En7APzP-+0J z74yEQlrap2a!IE(tS_MT(xk$!KGcUpWo7)ZvnPnY9Xsi(bx>x6v}}=F4g`)kxB2W_ zki`VWl=HL%=y&XSou9c`LBAS2vEx{h!B-MS9r z6Nj8#Aq4rC**HS#iQR9p^G}6?AarvS`^I|Lik|XxG-F}20WOi#bUiWf>CtUNkoABI!epL~^t+g;}m61Q3A=HndKg~+MU6ibqsx|1wGT@cW zxcZy7dWQ=E2}Lsl2pV;(pf0?oorlGYd(Ubz7M}Mct%*6He^a*f$XO6P@3uGern{Mn z9_ng_XOR{%2*d}J9fgluPK~!yI3%7y=8?xYHZ}l?W zQdrO=4~u`IxDCs?x8fU%(_-|gpBi|=nrC1ZO!D0PGJ|sPJf<(Vn@=Yr^w{cO_pyL} zqX~7Q^`LC_@nJUk9*Y5bSOw-$e)X-M=o{6I)`<%E+XqB(>=karP*nS#z6k`-kMGwd zUj2Ez6=h<;q#=!%e%hn%tuA*Dk}Z7{M7vBLF=BP_?~_Y!SW~zL5%eM_JjB}v_K1kp zONvUOxxT}z9OBPB7^piH%Dp~X$qH%fF)>27S%B$|o>v1KgL7nU(@#9XZ&;q$<4pG1 z^@Yvzva}s8IkLpr8xe&m!sB3`{oM@%{e1gR+&fH?puZvE9Pjszg+I1DAYmE%=_!!( z&!rhO}L}OSP|KNb=v%@vdPuKnxdQ{pU?Vmz!dzZXu z(!cm!oS->=jh~f{zpeM*JddpL+2(jFCkvc5=~$~@SbMR|CTt*P35n~8I>BmbXiOH8f1P*JS0|wj>8QyvYMl`vH{e7*&jsuGnjl^p^j+S zcY3bX=B8)&6*9OY2_x9eNyqHmzvI5K8I?YQz!hAVmL(f!Z&)k~m-T}FiaTTfX#N^e zESAbCDbWJ^l1%UVid*rq-?zuLbNs%9AP~O_{T=@6_{B8;;=RfH5USSer+?@szg;!e z)QyJ>f5LAV&f__|0-8i8YKQRJFreiU-m*k5?v@wPih}Nmx}<87^{5j2O7h zcra7=$CK^>>Vs^XYH?sc)8ELt8fj^WK;=8@%a$ex<5kEuR2Qm5M`rx{rZTJ?p&!*h z*lL9%>%*^oiU}Yondq-Y9@mxvBSzka6<4u5?m0DceZ47keH3xYWl}~rBcWpQwzMFX z!=+o^cCI}nEYb|KQ?^C_^x$K4aI%4Sjnb}Tl#gZrp@3W3kt6{P zV5)O_vn7?glvvqp^odKIyGQu|*C;jHVB}~<^Bowpoq>GTr~!thlGU8l)LY`4Tw|QX zl?c4~fjJ5>(h|xrzeO)WaK4b5%&me6Ax+8a3oG0(f|d*dyYiEY)K)reK^go=6? zqFZoPsTm6s%U57LL+dlM$pv@t{KXw(x_-RBMV|RWk`0XHk!@G7i5DJVrt05dlH|Cg zgg)|UGI!Q7qz`F!CEYxq*Pnqkekh5@(vh0_9l9~qbpQiZC-+v4%;DeRd2>7rjW6DN zR#%Lt;v6-`tsgICn?}N&CHr0ixY)D#%4Y4&1(*idp12D6 zWc~_cbG4U}*;s(a-Jk!%s%j2$;Pj&i~8N-}QL+W{zCD`oBnk){af;>>Z zVml1zU(yQaTnwDZ_lKZXL;8v;=smZRZRfJ3gQ7zAQfNw%U=yKb=22{oGuT|%JPI~{ z3+L!rMNYCyLUK4xS0T{9+-?xeeR%WJCRAnKTHb$ zF>B-csx>ZfPO<=?4Qh>UOn)Zqs(Lc3P;LX-=cL8V1D7;~NcNvcXJSkyP9Ho! zd<61McYix~PKqJ}78?r!9Kfc!!MLoH;W|Ve#_}R33{W-wk~ufRXkFjsRbdiAkabYo zY*rU)-H&{a8#d-L5&Ad6n>G?ZNQDaxD;-5Op!L{oSHib-*`X+zqV(fqI98PMk=bi* z{N80=>2i>-r3AN8%+2MTkNJ>Ux$u4gJpWa~`>u~sS&<&@?x5bD@Oo(m4(v5URXlTv%-6lY6daH))ac1(aq}8^lEO1rsr($%pgD` zxfp%EN`%>qctxg9ZNVdMe!WHevG(9M>F3X%9+1k!H4r-~*7p#|6ZJe8^cLc|BBzQ@;&UTuw=u7?lHs5v7BPpw)4VOKhn znioP}{1zEkW#;9A7fw{37vf4*{w*(hPy+f-x}T`+xLmAJap!@S2F%X`NkWOA7mahz zO$YA>3Z^(yX&QRto_vK)P$K4_3ug?ZpG#y73}s7i7)(paLV$T-RIf+zmCA4yEF2e6 zzJ>RKFjLYeP+pD5 z-gzVm=J^tzzzd)2bSY2>sihfjTgke+%^TipE8bkL z#dN(Gt`VuL-)^D=)muqM&h(*IB$9Uw=824|y0;;$1xV1;SqdK7k!F(-}o zW1o`pN8Er3AY2dc>A&&|y6e$D7Bm?+6Mp#>7O^>gQ#z95Kr1f?kQ}^(IaANUw6rf> zeba;H@SDmJi%T#ohig+#pF7e(9;mi(`Bt31l1}4Ot`jIBTqCXDI(!HDTU@|kBmjK= zj1W8gC)yt|d2ZNO=-vhUnBr^!OVn$Owf}@^>k9c46VK?6OZgTI$vZMaGLcl;u)zq@ z>H}iWD?qT;aGQ+(Np1MhIC{Drx^sTfaRuN?U?q7!jpSp!Cwt3@$(%|9^-o|X{G2g< zUzy;g0U)Q^+UchG9ZY^H&02M@Ly>sC{0%eI!^X_Xt;S{#uG&FQ5;M+mytZHo>KJI;c)PA(L zF*ZjIIcGzQbKQHwrC-%xrd|=? zU-DZ^!RFo`7TtpsOUR6{^@ws>VOT8A&_>3#yfYMeg>`g;Cs78EZiw&?9i;YzCEb2% z)pg27!)9=xwFu}b{8Zr2t8TLvq9G!rY1oG6@6DZ>^59a9j7~W&93T)k{(P@Xt>pMn zSV#Wr7T6TdmRrY70QZkIe-mR(Ac!c_&yw7 zvZgf+lf7ejfjUC1Otn9VN4rO-dsJXx}iVI<<9kJ_bMU zOb=uhV{pDupiL$vNn$eTsh)4zOw&mpfE*+x-JHyQ&F)X>>)XOox%YHZ0%KAjgLo9L zw6Q-q@ri!k{mk%Jdx*LaHp&G+`*-4a;7|=GCP|%r@!BZ`U#!wHmeh(Yh5% zGal4OK9kPzllDsyn0XDCc{*CTJi8n8i;`4g`~uXoJ@JrxzOfNU`mL(kaRQ3 z*37{rDu_-yiehuj>d?3baYbAF!I9gr+I+5Ai?fQ(Ov1=r0V;Bdd-24$oq5|wGg6tP z#vp7A{3SP-kYs7T?&6AFFiLVCe4sUhc+aIYnJ}NJhcZlQsKoTy!_l*+xc21|B~9 zoue}z^q*4x-3Iw&fbtD)?;o>V9F7XE#GtiZfcE5(7cj?V2ybj!YueHTkTMe;{^K$( zrZVnc6yJS>iaGl~N4l3fHr{W(KOR0ID}R=J-eC84M33%%_#t5q9>o+rGK_eN8WMZ< z=+Pt4pL;9u-)lQj>eeMU0vZtv25c+vbn$T%t1cJifbSjH>^^o3Hvi(zPzxM34>mW= z39&!BFCS}U7bel9Npg{G93`NhKcS30jBJmR4`ZR?7X~^!Cv9;*+zXK@xEMMufS&-E z>PIrX8=1XCJ>Yrr&%?5_!1Kz(A|46my%0fBxF7o8?Gs4`-l`zq0@=zL-fxkM5cE@M zGK-gk6WHDh-BQk1UzL5BKj07U@8$kjK1zCjYKR}*J!uO1i%1jmWpSXNjx_K^Q4#d7 zj;sAn)Y`F6TH=1wQgSM65v}y3hrL71d<`;nY4r=QI?U)jn07<| z)0)zH9deEF(HX#qo3XOgyayMe>E41sei@Y`{j89>h0Wa`At@^hwS4gEzX~O_1z?a* zLS|Yk{NVRD@Ze_bx2Olca*jz^Q+?#Nv+;yG=ktTs5=$oPSN7?gZ$n3sm$wDny>@Zw zNJ)B6eRrpK+_Ee|>2O9iO2q?tR}&>=>_yJNss-(BH3G;FLxlS?c^=UeEVALyAIwMT zm(ffz%?(&&;PQFLbp5WF-M}yGFMS$IPExHK09yUomUhk%vGDS31{`rJG~|&(cL3-QYF?OZSnSLco6-R5GrCn zL*+8gSTnfdLH!J2IoWM-=`Rv)$dH`2c!&D2G$L;!O`1Q&zRT{&tA$E4ip<|PMZKJ! zThB3$g3&ovLfKp|6@WrtdWM{WJ6(y{Sbq6xhb%(ZczG=d_cW>2z@e5S>lv(6y%EB=%OvrdNpF0gm$W3c`xRI6 z#wApI+;&u2=qnN#_r7lolbRg$=Jtb1K3QgR+mpY`}N?}LKnEZ%t|TWa&pIVdv=z&nkCes_aQOzIfSy3Z=m z_AsR*n9tk2t8g#|KjTEEQDB6*S>=Zm{vGC;S44P(SeQlq>YCy~p&tFWJDB&G`D#`v zd?REvZ!Y|ha)cO(tMWK2Eot4aG%t7rJ0L*qQe))9rCC>bjAD&JK7{g}GX?(R($}Dc zn6%W`Ln{6$e}TUNoW)KHmsU3Q5(Srf7HCQOgGh|RS)Sa0gyO~7D4xc=53p<)^(eo? z8RiOI^|4y*QwSKM!LhZG96dSeIX4US%9JaO*rQ(q_w%MJ4wiqwCVQ6^kpD&3VaJkO z^c|a=Ln_w#HtWpciRtzhwWP}j0(1eT)4HgcGR395)8CFwVayDm|TQ5O}@oi_0V)lKpBGQ~ogc z`z9Q4DaBrK_gG75>~CI}o#7Qd5gEej8-jhuc!P*EfW}ASx^FHOTNv#=zzW8_MPJ0z zr;NUXf3MND_(cezup)c!sS(J(sN#DaCI{--FQ-aHRY5**3UciQ_&f+F)f6G3F6!Ht z?6`{9Qk;M5omUiKP)Q2g{#MfY+ zHj(V+v^c?sapl!5IoQ_*wDq)~ZSXv6+;A^ec2t!nV8=_e9X~bEKHOU@UGqLgz%9`gUOx0DX(!#+WBx5BYslMN8ub6^VE{?viH5ZLAVe z75}jp9;!MYXMwF!|3$gp)$a%iAY@~htLJ;(v63Mb->rTC+RSG@0Q{cJ2bFV;^(KvE z{K9aJ&6qt5BRj6bFir`jJs8-=Oefc|hHJ}UDy_&`yKSt#mkZKad@Frcl~;fv?sFJ7 zJvChnp9;z^Kb|@SCw%@v(!Wix^Hp*Ptw7~n8CPfOC@cP5)1bfS1R>+Qx1IBT-+9by z!h4@LSPf8ui(aRoIsKZv_fdr~6g*YzeU?I`G``|@T>XqGgvu3`)ahx+^Tx%2IKXz2 zEoFMnh=NJx@sBR@4?Tf~Q;{F~Jvyv@a?WTFq-qNG10uwFZk+M`OP9zyR_H9gVbZ#bq_tCCOMT!uodsFpX6{CFc#R2Ru9@gx_*l@A61oxQt$f@cMAxHYXz4hS@F#$-){QV{X zr>mdroh-zL6I9+XPu&WDk~4cmb~FI4G>qZ);*X4QaBg_N*~y{DdXT+1HBTUny?gQ{ zO`2m`Jp78JF(f1WEA&(TwidSrFTnK+Z9^YwPRJcI^DJP|5b=En-vRaBSRy9b$AMvx z>bk=CvlPfaMC}dI2qal-3D+z=>y*2r6qog zjmT$mLkLW5!)WC%O%lVDCC^DuupeNLmCbjiK+;4x`d)tw1JFtwuf|N+DM`uM*hUrY z(?7}#p*9w{_j9`W;pw4b^p70iXDgxRlSb1RYk!azfsE|%pNjBfVzCj8s@XlqQdIR9M&enJuR>#%v6-6U>B}Z6;_SFmxzpJOE z2zML$Kp2GitT(`t5r;180jz|NLdKs`84FI$H?bpFT3b4+(PC6d$golq|(vu%Bg`eI0RoW zUwC7tWS7uDvIqMX62=xiGYtF;K4Qt`c?)FPIpY7`U*cgeSL)~fo(ZQYx&afgGw;o6 zc#T}?^ltsfUW=Di;EwLss;~?7XrwU;1z0fIRv5hNI)2qfK9zJ%2FL-le!lq}*zmV8 z8Tnbc(`8ak$)1cO>{V~jl>&I!T(gqh!_Rx%cc!&cY4f2=HW!IcbmhE$_(vLp{l53V zy`RUud%B};L}YtQh59{;*cMQ2ZmhT7zax{A-z;K zT%?9te~9goctI%~uKPUw@4-k2U*KH5iz!JIX(w&?8Un|iY4SLAbHEG75~!y390Cu2 zF<9WGlaIi&eChzHg!DFlmb|%3Qa%T&QZn9BAYTQtVqe(b0Q%SDq!!OX&>-K+-u(%S zr4N2BF&?6dQ2THuy@~x!!sc7>-}@74+tYuqQ^*{Y^_s!FEo(4e<)7;W_Q7-0L7K3j zFqB|x!Ew#+*|tI=siYo{0H2pam({1bbc>Ok5u9nq(R5@Ze97%%B&<@=36ry(2_TiZ zBk0pYTvaqDrxcZKfVS1W?KYF|vgfPA#q600@}WA7XWleOO!3=~dE5f^-&?1vgGqL$ zgp@Pd2VLg{LYD5b5NkMjl&rNe>97N!Eh{qFZ&VsaiYhy3oy`LC>zAIVJ&|TjjB<;W zbb*k2CZb3-qonK5Be5($BMkw9n615Omsu9ew&iv#eJcf3R2C|khTY(=>wf_lfUYkYh2mRCmkDc|? z2!B;BRdwqYH(>Z3MCi^l{r=lneNo*UVM5`%IJ}U_cl%K8f7_--Kz`PZPFDX;R#?84 z)@vpIWH_&2*k>->e|*PDvRp z$EBHVO3W;am>EJ8FDuvWu9X+?aHG-E+LR{I9v<09JTD}arZkcVOndfTTUV*DRb(dc z4y_*=gFNL#KGZSD_od^PDjK+hy!>u}o2KcV7JMOSO*9EL! z@j+Tc^hjvZHWwqKL3CB@AIKvx4;!BQ#ol5W1}&)4wR~G_m;ry0Au`p~Uf%DO;hg3$ zPN%)p3}3v6FgFuSjw(6~1UNim@OhZYwsF|@2@@6`B zY|?j~Jql6pmQ_Frl)itb@ zX0Tq_=Ji78xh)9Rc#$IK~1e!vC+MYmaAo z|Nq;JP|3`t(#71mo^P(*PsipuC3bZy*SY4n4s%Ii!`x@)9#+h)Xyq0Pm6&S?Nz&(> zF6g42O74E|et+)a@i0Dn@BMzgpVu%XTcM?RA(6`&*Na`@fc2;R;+;hV$j3Q;-FlIA z?DeN7pF7q&HlpB9w~|0#ko4dDhbVu+hAwH>O(WT3FqR4`BbkSS{cjL;oidiH(6l)EZ@95{^3&bT6k+{zs-Z;q;BzS^3lq(aBaj1Psc@c-uAdJ z-qEQcoP@KQ!46=4y|qhlaJ@g(t*FYY<;15|j#jbdSkqu-ZIu%RPN^d)wMQcRQhwlv zx74UoPQVDLx5Jz_qPBn#QygKm?T3Hl)}tAaOGeTAG4MF4N;5A%!?Tv?P`5oZO* zQr&FKwXw)}lSSKx*x?6gfoF~fH7DsG*|-DUXpTxz(?guPq=i(giiZ}!10-95i*|uN zs~o*o&cI)ke@x-#fa7Bg8qEaP%-X;_Sx60yPNzDo2{!7dGyBloD;%z}X{jb#3SQSt zhKaMZ(`bzCymKEdmQVEg=!gp{o+BA1N-g1&B}sTC9cW}y31heqHCeQJ;|{|@r&GbM z+x5;3qs^E(Z?bOgadBrzdKsM;|BqloCim)8KJbUf*GDVSwT*Scj3@i+AiTu(2F95C zrOM3P+7-)eagTjqXcnxbFWCmN-A8_&(V~m+&cGZ@NVMXu;Nvn&^cVTbvnS#wgKnx@3e%-R~`z zJ987E9H)0^ScNN5D+%rkgYw3cGve#782251rfmp2>xOZcoz+jAK;*GrZsv&b))Kg` z{U~!Y`O23YjWLi&+p0X0NfCW8!BdF;*a_wt`pF89 z2Z2KZQ50Hj6Ry;~ZM%Q9N40N&tguJ->C>29%nPhPC8lOl$iPop{J!#Q!oC{GYT1JN z6`{<3$|3IyQGd7?hOU+jp5rH9MCJsKQPSDf(n?Fx)enm6m z$IhJIto~o0<_qF;GK^0mj{UXxWJkxwC`c%q19=50|DR8UTrc;(`8Pj8*W0`@P)grL z?UVcx>>cc}xI=aXZdy<}xcbufZj?X^vM|od4ph{LJYn%>nT2$7g!2uy@&w69e2z4A zgD4}!>*I|t&p@*DvDNj$wnMJ{$GaWp zC*6Owe+gj(W0vQG+zoU^2_jRbzpd)t|Hk53$A0VWte<)28JMwQ;mC%7N};F<4W1`$ zP@3N3o49f{sPhVC(bJJdJAT+B8VsNd2Xu^SO5+bEUCjA+n37q67<0WFNAiLuN( zj`CItCk#{U7yP(do=f&B=4GZ9%+j*Wqp(;)INBpx&hRkYeUkB6a0g=R#F==fC}wHp zNi`?4+M&F5WwWqtREKb{#P);}(vIZ;hdJ0ugBmdzLokw)COrG2Jd8WJ6*g73eH z(<8fe=2MCU#4he|n=HXz3CEb`c+OPt+0GNV=^6Nu*Zi|Dmus|yosL`_m>*EBxs#h- zUN3=PA+0NTlDW?rYi1M4KIJM(H0I9jcC*efNOEgBWJE$|X4k(Z0X*cujrwSikKB8J zb!)D`4Ndx$DB3oF=VQ+#k45fmxc(?GeWArB9I|w_ZHT0j$+Ji`kHaJ%SL_vAeUBNj z-o(V)g}&NnfKN26x$(N~&~Q%Kj|nT_pZhC0@6U@ru=9e`r3P0)-kF>6v*2AUnCXGp zt4~``M@jsBJOlb5ucV|3Q`|% z{MgyRsMhd8!UJMucwb3@tK(`{@^iNDVV#2Tnu;5X5WGcGRe+Iw{HEdS>fp>I$Scs% z!|lZQkz7e1sB^u#^C#3Waz}MwH|5Lad*oNdolhuwrX?NQ=pmcUJyQ?B`nvw_8HXR3 zdChM>t-*dbVlBEk`ASzJJMP*f)m|6si5(Hs{O?bbUEPC7HG#vtL){YKht4ks7i8P5cC`%^&umA>?{G*P&INd!l-XP-7UUOc zdsmc>;V!rEkt+^!RS@=5mOJzY6|oi+X&ferfkqZ^eeZ{tPnft^#s`cY7fL%&Npnt! z^Do-rQ=bTteEb!l&sR3RsJSLxaWcV2h!1(u5IjLosA%#Hn&{qvR!RFFb;Qgws5cui z-L@SDNz10{`;;iykH$W{SNVApmf zP697(Kox6VfzMi-{X~DepWx|&ty}= ztUZ$GW^@BDqZKqS?9~y(k@nb5ZzGo|9Ao_#60%@^g2kZ4x=pS4LYaqZ@} zgIvw2QBn8RaJ4U%Guk7!v)>U&#p z@uwiVcfB942h$ZR-nASVo9GNG;U66Es@39)q|AAdM z7{4ol7pBB)D=zQOHcvo+{-m*+&`ZLW$;s6RKH&*LSt{2#1G@>-;m02-l8nWX1 zd8UM-4PUen8}ele*TCpbIP9#zgYc@^&wO_YiYxCGhcP7G`JyY^8vRu~d}mLBrcD=Z z2p%ko|DsA7&Uly;8eR}37HyFN|8G3PlXsu2yBYzB634Qm$uxPL0~ISfMFl`FWSJdE zc0cQKy5aTbHEoc8kyFOVAxRzbjEyv~eh%SJ4An=Exhn>xBJJEZMHu{j-o4=)uDXeK zVKuAawtP{*Sp54pdaiTdt*(V1f(u1oyWU6UyDOCLGxoL8$PkVkNWU^kesFcO$2!3t z!G!F0H5rhW>($j&Q0LUzseG*Qy|%tnKb^N|WAzZTVHgq#!`XVKv~|lAB1Z3mCzQG6 zU_Q6W8>OIY;6X|B=fgYR_N4puSfZ|?GbHlN(U13q{im-p7?Pjx|M|O+4GG5!BTHCn zn3yyr)=3L3%(=9WZC0dtXCf4$Dz1g9>F7bSfpNOneczP5D*89r>*+#vt?B*u`LW&3 zun3(T8#?GW9detVMwuGM20r{0@r#Su|FcgnPDf@k*MmF{`gFMCR{YL_((ONe7_Dro z38wcFKZ@mgL&bJv}v@QqQ6(4V}VT7UuX^F(b0pKf0*Vc>Z$dCe!eH0 z3ipQdVo$9iIZ6aY5|mlOdFC|9(nP5l+v&F_I!a)qEhd}fFh@#V^K0MKqS4Io(*md8 z{akI7Pm#diIQX1NR|t(;M(AjGgie#xk12(kT9jd4sgO4R+6p6;>-T6tPxT}pE1b?M z19~mXTfqOTVyK|i;^m(h(*>nC{Fh4{RjOP@xF9fEC4aFl!V&G+*Bdy=3Z-lxVJ@C{#!x(W+NHh0GP= z5_hyN=#44N3PsAqTXQNTcw}>i;9*h)pVNE3A?zjj?2KeTj3QM&#rX$crMZZ7+H7fU zm1^C>CoE7JBFE{P3CaK62@IDMT0BuOe($87yFhQn@l3#Zt`2Jqp5Pr+J?Q3ecj&J0 z)rl9BV0kSULD6dd6^4mW#;uY*p3&=$mS9$#TYa_B4yq8z6oE(JFz?_ zn}%H37J87CO6Oa$6Wqp%F_;W9pQV}1tI#k}CS+Aqb{~5K-;crSg__5F7Rz=h3H-Sx7l)2m9#=3hLdPCoO#Q-a(dy0ieCn$X zgZlRM-W=P5J@m^fxC)u4$VJ2OznX0Y*j`1DqN@$~s1H?*PEF=chyw4m*ENx@3N1mJ zC!)CL5Y>v)J{xyH-=HD4;u@B4Fy68J%a%puUU1o6>YH6%uo)GreD`K53whgRQ_0lV)CYa{-1tioz6{f; zK<*cu{GFer$ct7qI2TxyocwS-jgLRE4S_El-uF18s6SvK3(O}^^=W;GuTk?QiN!jg zKi6|y1!q@=%}faUOqGM7v!pKcHrV|Dey62X?sni0bnc#?B*%(|N+XF8p;Ug>wI{qt zMa^&1AJ5QnwB{-9*Hee5CkAhqSIfHXXxd+>aBTH|yDAhsN?ppGO=L{O*zZ!$V)A%z zPBqBhZf|Ra2wNm5F*YU!h(z02r-W0&q}+^bhAmCaNk7c}lvW5oX~u*`&(t@tbsw_v zHTJ|Lxe%FjvWA|E0ol~-;BujSUD8n5;^5g_{Rj_{HxH6t8CRcnhJZi5$fzhNhu>TL zj245|bc^sjkho<Nm>WxJxm#J#pPeuKB)a4I0b z@Pic417j*$T}l#;6oB*SBaanErljh$hg=dA33Le|$}9$vtN?FVIOFZ7&*kiUtS1wD z58xNK?!VOp_rpp8&Z-37o^vE?DDQfUko~d?%)gjr$>c}#Qwb7$5j8t_@-oJ~zhyG& z6{8O7U|_-g3XfF_rx#tD|3%^`+09xxt%zxuPO$7QLs7wBz>ycn(qLLt#`DMmXK>WFypK5{aiLDRu}s+sP`;76sOKs#d( zjY<9VV$;I;BI~|vIzgW)&21%Zz_hsW$~kAMo){b&HHdw38|1;WvgY<7p0?ci-dIMS z0sT##RA5}l(?sZd#Q20IS3&vX31OZDWSG7OveInHQp^sJ{1Cj0#t^9GhUTp2^YL7j zdhtu$jI+7zp~W4Vx`cnNUi?(X;nl_;man@`ak-{*fB&2RLu*w_^6nK&;Kw@F?DWkV z`3rn36dfp8&n>tMBDtYT-fJzj3E#H7djoEq>Cvi`!R9xw` zS0}{m+)NZ!_3pN&jKPh9es8hpNEV&Jm5`OEo7n^YX$t-B+r`Q5%sq%uTVKyQ4?Z^D zq9HObTan{^JYDi-QfHF$7J!#>(3RoFFtUgdtc1n||t(kuBkNSITm5WnN%Gyj+L z>pQv~h@#{P*WUfSTQ2&$b~T60G?i>@+Ox!V2SJD=htJ?dH< z?piMM31|te7%Swt8DxSa^MI``;lzZlRmnFO2K{^D$``g+#mQU(2%Zzm4tZbCQrrxaZPQkJ3Vhlwu}w3Tvp7^u%JYe z6s3|pOj%78bz)HUM5mK93=(ghUNS=liHD)>v_O>cMI+l=nm>0MbYk7j6$q;TJ%61M zWR=tPZVF!Z;mC~%d8-vEhel6Wpx9!RiW{E6M7V28sM6{ZNJ`rsXhkd?ncnPYeOHWt zo`&}BeWoDUuf&CU*A)JPFT#^?lTN4<9W2XjvU^oLd{HtqMuB$8R4-R`a!*TdMIL67e6KH^4|qI=1phqR-3*AVUMJA`A;&Pj7m}q3)@tDl6Mc0%oVU^{#JQ;7&|7?c{VpZ8neLH#qp0nAebE4P7t zZiIrl_H=!dY9V`KtSi`_MV&YDXpFec?!+ESBTl|S$*elQ9!wv+4Oy_a7*D@JIZC;O z^1Ox|#rPRk*4R{Rm87y+cw)tRC;M)W+t?mGpS+GE8T~$~LTq7AJ!6c1Y3gRPn}^yO z^!RUE%%6Lo_wLPKs%^b9DHH`iE@SZA_t83%C}k)96K7R(8X|4!+8N%kEsiD0s@(VQ z59K#W`#&?R()1=%0Di#*Kc{(HoY9e9W>-QoE>CF;j)oiD#7;B(BblGP_XQoi*0D3; zuInemK|gbhpMCe>r4ont4e)k&RJ`-wnEVR4@<(Rvf;yWAQ0F!4u^4+AvhnZPSy6B8 zYWyw1L2y4W+qXcG@!0MH{0OB32RBk*ivaDPaO)5R56Q(*N}g{Myxd3>v^JyTw?E(i z8`$erEDlG=9l4Z?&BqBUMuZywg`^uJI>D@q{UXEwk?B;S8Q|q6k|Iqp_ zXt~aD0(e`R!S-)20UxchN>DZe_;w|8KxL@ zSUxm*SijfYNV05y;VGa;gAp2><*hxW66(T=+5mt5cJF;laGy;uTj)6fgY32rm@oz< z{ESW171T-&h-G}qp5ZI%{S=^T#&8ej2&0$ z`OdX9Vm;=u=8sHg2cTE^koYxIV1b0e=am*7oEP_25NPxfb@`m1)I17J67bo{%xFao zq%K`z%y#w+UpDV?Xz+*$wbC|Mrk?)G5v_Ut^R`^eyc?Hi?d>;^LhKn?1w!%N%oIl5 zm72u@VPB_FQ0HDF8Q!hei|s6LG#rlm^3w?1FO=`koI3#c67}ylOW*fjazFW`pmJcG zD>1+p+C@WgLdV`l0z(7OmwqNrs5A*tjw}x$J0W0O`U3s|Og_Cq`gD zz!r8#ByOG$%;n>C@i>aJ-qg-k7LmoX7WO!9DUVcU`MXkB@;=mkd{MFz!n6oVmDq@g zsh;`7$F?dXtX_-b){|G)(vri4#8tH0z$L;Tf!>yj7Oi=ZD9eZo@Uyh44{GmN_cEYK zJWt@8%c3zP)j9IozszNK-EIq!Oeo|&$Fuvpg^0YW_UNrdUhBjBqisa%C1fjY6=J!Vde37Ga zV6PP&M$*V^TGUPi_qkl`{A&Y%PY4vARDLy+eYYE^ivy~~ve(ByIH)FA>R6bhcowz` z@rm0%_&Sz5O&WhefB;o7HaDw0j7QVXSxYuGHI&rt{hy*cbQwdMulwMq86Bp|2~bt% zHI2yEDS><@dl?AtxPuo>CZ`ui(}BAo`x-=I}zRZ2y%x`@c%;mlDUo7lylYFJ(=+p!Jw4$!;C-0T?G)NH! z2{{`nQD|2=zsnQpG@j~Dn{v}eq0RRfMEjYyXUB6E*xOoD`1roetl=V^cE>J(x7o!$1buPMUO}njm4Dw}%5BJ~L-I8Ra~&P3SGRzc$7VjhhyXQ0g| zxz|azGsHHlO6vGGZ#~^Y^ou0>miZ!k(2s}KJR6b5T$OY`C>GWJer;gcIOk6{v|&I! zA9>8^7aC?JCK5N(Da}wa163Xa_-;MSOQb3OGn)4y@5QXBZ96BSdc4H|2IdKc_mTGX zw3pJ&eWW+(s_||o%1Yl$A%a>DREIlzuEO74>Mwn7@_r)+IbZh9eidGpR1Q85SCosZ z=Xt_{Dz|p41ghfkp9wxJKT^%{&SC5Gv_KoQo~wph>52NfPIj}2I>8wV8}u~bTwY4& z<4Z{8kvS&m59V(~ems1g#LZ5t$}_r&NlKXg@7$lTRgSh#|F#4(8zYDOPfi>qp=d@4 z0oGG`5c+=NdfHAt`{MJlD&rJk7!8$wslN*b9RS#qIH=R#g zJ1^v@NU>tLv^M2?YsP_HDz35s(u^FVtFr^t?DOGOe0biIr!gR~ikywEUH9bnt>pr|bW`ql@PH)! zr2C3#sE#qj-q+m~ytDkeoqzw-TH;k9KIln#a5l{j*lBX`-c;bv2fx42Exg^$DLhr;5XBajCcL?ySNIqh<|}IkaGzOH9;H_aDNZ8l{F96?(Ka zTf&^M>lwhie;Q9u$W}iI3UhN42!t|^$>8%No4{Uqe;6VgPJt+a09T+V@Ht;zQ$^T- zM8}eU^Ow5=QR=4NE5*W&mM-@%`xHnMc?)iY@TgAQ77`Ea7sn~3PKG8Z5}LFN5 z(!+0qiaLR*LXMXtz_$oP87Tu+cm2S8xOf2PDG3+j zZ^_0(Qv{EUj*F5-GG@T$!zqhaqFrPur~J4;M+kezv)#s~br<&j+G1&&3hl;=dP)YNmt%TvIP17+wvGHE&1wC zGxJG__fQXaNN>^^mF6W{A$^T45fbi`al@D4S2NbqrlrcH(Hc8qtiN0SFr8QY(R&p1 zQLu*|clqbh^G;%^ZvW~mlhDqCgJ054%k&DK%lS}XLYdO^hA?HG|8bn*vA`1vV*Gw% zR*WLW^5wNMky&Xy)H!ZK3w%y8lU&wT*EG=(iY$ynqtPCfI}?f{7%U3t51#yb`>TA{ zovu^aXDO$Igob1>Bm0i>yE>DKQ!F1wbumC-KK|YbKgwRyU1I1%A~8sp?fK z&_8v^cQKURwU_pNl53UKgof{11&>|EWX!+hG=^2}ij{dj_%rxb{i7ra!>zCPmqAI# zo@6r=$!p(5aMH1+$Z-z z+ve?>tDp7;pyS6YKavyMB)e+t zY_tRU5Va%3>6y2_66NuW>t&u$-p659WR}TU9kd4K)yBMVchVr##mm%Xd)DUG&PuX@;Noz zhuD%o==M$(5Gq(1HZt6=~-ppLoM1Hgs4|ik|y7uXMp;X8_=@~gf*nG+hMn1Rpd()BbJvNUV zW5+LBx=v*6Ew?G7F7T4jT(b#$J=jn4RXC8A9_Y()Mu$un;yB3-)vKB<#?bIRUaV-3 ztFuRCQO?7aokA=r3uk{>9sP1;q3Ri(G=+9P;)!~n-E)d3@VNo1(1wEmK2(B=J${18 z9~v#;vz^8O|3h$3BIX?oe+lvi_(~zro**mp8}K$(dlDq8#v_bP!#FRSHrw0Y6pnIZ zeoUyd0UjuTy=#|*4YMm00lr2fMm{SFH=%!mhS9l9#-K5^!bZ?Fv%C)SXwF0!+XMbJ zC+qJ&cf&}x>iQG*#p(e+h)qNn;5#gS4XWGkLwHdytV$L!&O9Im z+H}QNz<-1-kKxgJB5e%rBiKstxq4CgYN%S~F>Mie4|Q-DVziUECvLmeI4_VG3}g2kUk{;@-ckgNLVW zwm&tzcJur*dlJXc>}7ND@8@}R;IUAgy@Y*(bjN&Y6CXO&77vO~-_9j31aSvYowxD4x^3omeZP*$yJRYi1YlF(*J&zX{~^%>pX>W}O)V8B!3fZZWirU;f|=SFg+wN84Dhq0IWR5N zZx%rZb@I*E--y{xmhe-rIx1J=b0zGr>_*?(qGLU0d>80>)YM2JFd;UcC_ef6?$^Gj zuRqoqtvQz0HVsO9UdWR)`8i7|vf*0gc@84 zo?y@~?5>L{MQRn2X&%7fOW?O^Jge9IR{J1!!0-J5voMuCll!bqZ8bh%4??L)E`JXH zcG=-46^-6XtC+pMXxdkw`oI2Qpr;jdXytdy!{_f_wP?h9?jKIc2}bs6>@(;o&GqW|8YNBKf5J4Z?I2DD3qf_ z6AjWSA==DboegOC{0>&wh>0@B)uu)JOZ0G0O;=g8G7ZO&adXkXA2Y>$v+&1cw;XoE zw6;d(XT2`rJ1R7Cm@L~sVe6VQ+tIfq%Mj;m%S>&gF#4N^K_1R8JXSYf&0O(%^_OcA zhoIiD9jHSE-%9WFEWDocZgS+_y3Zl#)G8ueO8^mu%-lrII$nv=**DB)NN5Ru@q=3|fXC%j{+ad% zxDQBkx?;eVm9y025hN2R78MVJ`IqDnzY^&-Zw}T+NDt0_ze4E$5c}P{vx=hPkG=Z+ z_h0sOkhdl~R{_7gMey(&n0R-{dDlzc93lIoY4F%S-4eT&yktU2NFl1OXyM@E)5~G_ z-S~h_=U9=9Xwi3yE~R^W#rtzwBe-vI#v*r-jgVDdzQ=1e_#KW^`p-)W>(Td96Z#Oz zQ?dV)em%I^f7JC-c|qJOeN~0JV@0e+AhHzLTN=&Arr@{3(1rLlVTI0r_uKl<& za6i5n842`S_>3JM3^`3$KX-;+sc#4TDAa5}lyUlEzMH2SWWpzOcLhCsW3+kdf!+lP zdvyr)+ywrI!}AylLDIPC_%GL9G0OG9-b`#5Q|U|U#*7&INEtWa>}nop>M++nR3IiC zQK2vfG>cMNKDAE5ZN!~9Oyf%_615M8Y)_)q=zPvTitB?nYQ~kYWv@gm6W~E0yk?Aa z`k8zfk@TfmJnU=%JEZ!p|4PabV-8s+?&)KZTrTSOG!+*93d?!U@8nGAU655s{zQnD zr2d%z^?rauI=f40OZmyECOd;2v&KrF$R{K#8VMax3sOwJsFnvAO_jRL)J)u}2t5K> z>=+YUyn3GMP|hR`an7Ulugawosx%q(-E<&tCx4WN> zF_puxYlk)J1AhQo0Y5$bs5!E&MP`Ts@B$8)Cu!>PzUTcT@b(Zk3ZKnD)0+VbovNMQN?Db(TC%GLXo~D znT|5K-kzIm87vzD@;(&x2+rcNYg{|-1BTHQ>e!js(ihKA<<;*?=1q#lq6CBogXr;U zwu&51IP3!T$`S$oMK+sgl==#CoP_wzl>fiaF+twXav6z>-n6lZG%LgQ3Le370%2mp z=r)f?InCoPrZx_0Uqhz2p2?C*6@$_?KXS@sYkm2IzA<{Vjmhl|)+DNwVZa~enPU#w zeoLQz88*$&sDKKjw!0p3R@+V*Qci3Q9uTAS$ymeva14*+wQ}-di&#cy8`E#tT^pwQ z>{PMonNds>xjw|A%qKs`*ke--=(}d_$$DPb`=hrEr%LjHakHmiblwO4WnfZc<(`K@ zSzg(XMQ=LwgUCZ)Z{$nz@#iys&zo2Kltf)W%7Vr4$4c)Rd1aXo`K#kE;~zPqp|F1r zZdmLyMXd<)V8<{`zw=KWaQAlb(5$y#%8O*vlHWhTJz+y?#SV05$*)4wS6F9ky zgu3`fA|x}5AbZQ9uY0&8FGZSps-llo-8%TwthW4{FE28)9qQeBF(+8bG^fa@0K_ro zu5tv!qOzYoe{U*%XsGeTawS}?z0a7}Yt5I9oNV|D#%DJ@D~loP`*};GJXo!G3Hq4p z<&-LzBKA|eE@>}>Bj(t(+hyxmQryw~D4IK9Gk?)H{-DZn54*PPDR)9Teu-B*Kw@umOaWkty zJ<8>MQhx~YwdQkPhdF?L&7>`3dc!lQ%NPuKWj-cE+*voKPH~2$JXnjxlgqc!+~f0m zKgXUv#8D1ktA_)AB0ztl8~Ak|EBL>9u><(=Zh9%sT>$z8<%OB&>cAfuuxdQrt;}~2 zkgM}oHan#qNFFg(9>>w9@->wHI=(1tf=nMUx!11~=J7zZ%*TfG>U&#i_=V)Ee3g25 zJW68!0oS6ZO#A9hVO`^9i#RI-FON}$opD+4?HlTgggCZcXzw0`0#l`oYO}5ZiL6nb zI=oB=tLzKQjE53p8TTy`7g&f3^6IlIQx?5ozChgFqm+SoZR<@r zTZF+^&VxK`m z#+QcF(>18Ul9@X~pv+^VI)=sH{8@>NyU7znkj<8h>h{H`fIvyNpox-v;GZ9*2Xe?o ztGV*;BO2|X;kb!R;a->ah>aRwvMezOe=Ex)I@7=}S&eQ>UM}S7Kkv1)?r4$bD4Qq8 zTLHad$~cR$gR-rRw-z)dp*B^1YubQ<0TdmZAgX!Dp_JiDOy9ERXJu; z!M3Pt zsPyo%o++g-2E968sh5xPPUZXFx+<2{=rCYxGkLb=#lRDS;r!l%$*@0m!CZ%4eSYMf z6VBy*9aMbnP>Sr;l)cA00TaRtV0!pBaMz5KVxdn+vJg*-j~m|O7DA6})mo9wgDkj% z+4L9gI!TBi9K-S-;Fm~qfA6fGf3>6dLA&ewD0J!WzRGpk#s%SymWmbyN3Lh3Ca+@f zcEiusD&;a&M=n2FQ1&y^uZVP8{T3+%t!3(k6M}hfKIh(yz2K* z3cm&9Z8P;K`-c~O>11PDi?>s}x?rZ-PpRXaAgRXHnpL60HH1V-d6=`O>*rwy5ek-WA zVj$jS7>B&Dc1}^rC_s$g+Ks{PMO~I~4{u&M&W3tDhtv)^z{c)u=0@tA)D`wN1Fw#Z z#03pCuSFKqp8sk@vR5?RY;O>!8k-SR1Z5GCT6B@-qsWEmf0AwVg*_|TB$w?Pa{W1S}>IwxjSb&RNt5=FUOPI!X z1Gvvscjw5t7!UCLlh7^+5>nyj8>A$hH^ep?gy~W{QeVe!jW$V)b0_ZOe+@@z$jAB!ga8lvSu=M4=O^?;23W(mDt|b>6<}{-KuQlI67->}$X$*8jgPK?zQS|CB}4LCd9~$ro0$!y`&)Y?&A0ey~mRojLb3ax18&-3x8 zWLXf~dIU$=k2oeVcVunXr-wq3EQL5QgVhx&R%CBfMu|fQK6zZ}?Gar8h z$AwhmgH9%f#pFvaiSZdpAkWJ1lBv0LN#w`_anBtp3EHQwq+=JqzGCuIED~%5_xtRl zPc?k7o?93BMh_BR(0A6Kd&i<$$f$)u`QHXT!iG)qjt6V0p%EX0l5HtKU#VW=veEt^ z&7d0eFFgZ%ibqVFVuoU(QXt?*S3V!y=!tEa_xR-TUQ)&bLhejXPpEnOx)s!~{c`@JRdC{ZBd8h7@ zw}QU%Ri6KxR$8LvZO)>cXSM65Fjmr9kFXEEOGC-vMzB~yC`#C&j=uzdNO!{~d)h9E zhXohOf$0(kSYylVIis*nY{-N$Elt7TY3XbrNl%4ONbGrg)UM;+=O*(SOvWS$9h=l% zjsx?t6hYvx1Nexj96c-G*HUmWF<+4Yc?Vo;;_x0_*G=ZaRC@TARo(}Mg(Qz{FryWU z6QAtK$5+Ma)3%{4Tq`3pB(<;*!5xM=b?IB%o8E{0?YT15imgLf>An1 zQ<07|5kfI4Dj;A)YJhyX-}|m_{p+8z=A1R>OeS-Z*?T|x*?X$>&8%Pwa&0FB&I$nR z0{Y)m*MHalE&tv7uX3=f3%g!n*9>;`W7q$G;rqV>0DwT(|33NO>wlm5uW$etfie&W zz{&YP?f*H4|Gx%{T_Efhds?18uK4?YI6qg<`+$!V``&+Jua6Y(uzQEP1A&&RFU{|l=?>l-r;bHzb zUq46SVuS3VJ>fgHo46)~q zyHR03KxQc0K|^KR2~PS+4FGSQg~xmjVLb$BB>Yv=9zJO<$-1M?+KxK!ne=abkx%)@ zt(b>L%d9q1hQCs7m&D);1SY*Y2@B9lDdf{SqLSKH!}G&Ki$fC`OG4({SO0roi{g{# z0Pp@*9ABh8KLS|WV6hfi-UF>|7aj-FTGr#=Cwd0trA%#?ChYFw6^twMeLs4FGK-xL534#$=Te3xaD%U$HyjO%b{JG0N{Li?@WYQo>O~EE8TKXre~fNJS#isq4Q;jSFUdF65dz z0%PS<%)bEnGe|BdN&&|SK*8$ix?A)<4p>kmfsd0@6OtnpE}Gwn>b|MT2Uq)%b+Gnr z{fJ4zN2sv>Pa*S5RqP{xKJi|T{eEhl1(kl_)T!zCkPh%{ch*;!;YTZ>9k6O#t`dvEKFWqy$f3 zgCkec*^)7v@+*#Dq~bNu>?(ZGb)Wo~k&GjxR@q~ab0uiGE|)2}(MP%RhwBa{bwBe? zp&9(k$@f7u96v67(2k2Ja!jJzF$w1XT;qM3tDSqVl9_;oI>lGie7zO_(AXJeqGL`^ zI1*~~OE-&k_DDV1L|ef^J@vWL`~}SRjjNtFlLIgnJ zu@wft<>yZ&SGAuIeTCT(uu$N3gbzh#CNyhXNr4q1iAtkTZ05#oirgPA8;c{0UhSAA}dioOvoY-~9B;8a%a>gZ7b9a+M& zg+5XL6ka{wTeqZ2MOzwXqIt9f+MQPM0a_q3^b@zG%NuSU_~J8N6epaTIBni&#gA8V z`g!D_3znc`#4;Lf6Y!5K7lw5$IK}DxiP4cV=El+-PHs;|!Vam4g|WoHKG<{PR5c4x zol2%r;}*jk#!tB-IqFNteE{g>BeU-m)!`Q*g&?Cp4*{Im`)xVSzl{Xp@>ba$%SCkB zaBVuYo!p`qddM9pg20tZ*V0ca(;Wi2Ut5_C-j^rs#GKy;P7SdlSjwyUM~s99yg(j| zKX(1n#__e|hh!*VA0MTnc3MywFb6MjkWC@?*wZ<_zW8%vX!ZCGdHFf@N0j=muP2UA zoI?qax0c3TjFQ_`xFCNe_FTmY+-#A9Kov}2eB@`xBI|IGmgZ@y(!+;)pc3RLta99E+?o}qVBnXARwq?oq~(}M10_0 z(xG=C>;>13+Di^oN+DP+rPVbL?|Pt1IR!AImMGBM7je1YGE(!*>pE!}k{614_~q@3 z+E^V&x$(Hyx8K?R0$q8KmS4Yqw7yP?ovJVzzkx|t8XQaYzXZ@SCAXV31+La_?=v@8 zn63PmAW0#%JbO6&x`3?Ac#_|waSY6Eb7||C^Ppt2NW=!)kqGhs^p)QF1z&&>F9NHG zly2sb6NHJs8#-bP$@yDFJ&y;$Kwq8vbYmG$ZYxF}YvEMwdfWGd)v&?DT*dtQ#V8P_ zFwg23G56?#IYwC_MhGPhQ0c|!Q76!V;IZ`X3xX`+ol0jHMIk6jQEwY(PzoO?J-65E z0bn^x|GAZ01}58V@i*2sX=6zxCQ-1jsW(-`ubbZ^=903-^Ucf?Uhw7e1`Y`M15lE* z#XDJ@QjOP;A!NS~Kq}UmDgox(Wd_((#YYoKliI6* zB1}jjU+aKAmv}Pl)7t})Nx8kigD@IAVMFga>}@cg$?%o*`kuKJvq#m?;@&bSd=qY2i+%vL7GiWWW9l(yq9xWp6z=H0 zjw5%!6v{Hw^EX9C#YOwjdWGfht}byIz?s$pSB8M&QZQYGj=}u}a(+Ba_A~q> z#}J|K(awfHb)3`dN95)BBkn#vt&G=Z=%~5LcTS5^6Mh4zC3=%|wEsuOV97Z5} z5RwyGe?pIkTisv+fjQ52(T#0ywx!_5wg4@m7rdYOcGRKG48$hKe%RraehyS;FyKAL zq53JR6#&UW<#U{A8+5F!A3do}9Q01&-I>>&dvwRqblKk#b}D7m!L55<(%zquSD$kJ z3&R5P;2^EVCnUrLxO!r(YX_nq0L$6}`I$JL^LnCi5SR$Zls$PH%uG`Pbd6d{&OF&ls;cySc_ic2!Azkn_X`QkWM_Vcw#x4*(K6MfVo6dV__x zt~q3S1B`y1ixrUB)>q5*8mpL<>@$Jvy626R#KvX-X;H9=Og zFH$B4iIU3>Fg%Mj6)&o$+J_F0D`P>-mmC1J;jjDoS3?X}+d3)d%pL&Pru#m&u5ZQ? zSSxj_ZYVHKXJbuI`p!%k&Hr6Szp)faasG<8C;A&cO~gCo(Pe--d;7FWOA(-jim+1B z)L~8(PurxU5(DsQJSAf&Re({d3I0JA`NbbQn{<@oO7Ir^+|@JoWQ7xCs^l}I(Mjh= zPe%IJfQ>UMq3u$1uF3N`~0CZ#h>~!V%Zn%(^T5MqPB0c>)@$;1R zGRiE%!Y9~2g+4?`W`5Mes(+Vg6vu<`^v|o_5Q)Z;c(d55eUTL&=o#2#`N`xm{3DIF zAVqzE?n>&qL`}`PVJe~k@ss*RN2I68P#~XDemVqjEM1t!6)c+NmGX#kzsZ?c`(C{b z9|{(a;E)6S7Q7bHHX@HqId8nl!s$N+5`{8EPMiX;MA-t#znpGS&ye{APqY@$yKvEd zD067kgZ;-J+^NAtWZUCE+K*qB_2TLQ4FE(1exGzw&k%pLIN8~~NSGzZZ=}C9&X0!w z(HawHAU`s?;XCDFR)PBantgGBp&V3=8`DeXf4w!xMwsa;)rru%ul1?mAtJawO2GF( zCZ0(Y>64+>kFQ(T2~>&f??a(ig=F*|6(iCSfo{uifPCf`2zf&Ey;Gl-Ha9~t%aro? z_tU_+>oThfI`?XX0Q%gAEW6#;e@EY^dwS(+X~Gc|Ts5DxTYaqeudW}{~}a5$&7PF!NnDBajIFgHp5g`!trMn!0ACrLGR%@Fg+-S)%MPN+H` zz(D+}jVcO%+gsJOuy3%E0L{D$T3kD-xylmeb@##pSm96S|13=KR1O=N#{lWv;hB;zTg2OTt8bBTh*!{&D!8K~u=<+^2(^nB`-^EG#e|1Kbf zcS`^yiN(T;GQZb$AFi=7?%n|=%Ey0UZnYbZhFxXuc3%a&=ceVmqaIFba+&G{J_;xj ztB|6Y`@~~7y|n%Ef;S#QpYv+|;$CO1Dh(H9oj=d^zwl%c9X{Lvp^rRaVdi`U0-B!jE5y<(g|e zTo@w7yq%SKtj5L{`m*gD0sx6vD5ZDhx^?=Me!cxfdw>Q?Q@1j?E)mAGQkSxD)q$GI z!jX*o)>&$!TRcU9fcMWhkp6qEW8w&RxkdC*CygSd6KP(KzN<{gbn6crCTp&07|YzT z4;M->mw~bMQGcg^=HJ%x&i8X)NbbtLWsDbzY`!&f!f*swnaw+Gc+bhcO{g476;#lo z1+rpZ(Uh+4e){JXItg9GRb*aPoiAK^W1#+Lq9qH|AR=y}N}}BD{6b1ArEIELTTfd9 z^jXlG<73Ce{-PkVRc21_;7m+)A52Cu1r#KmY6m;}yltU<*WYyY^Z25&YmP>a=&O93 zCx1kE@QD(V)(_W@yr*1*FLg!OsCBuNFRZgz@IOd=Du<6?G|N91%mL$oCL5k;x`Qm! zh#h-6F@XX?K^k{BD5L?q!qrMKGaIf|BEgLN&!5kmG>c^p4NjmN88yL$8F?uL3Q}VJ zTxw;YpuTxeiAOMpp^4-_K1}Euvb7Q?uATVxus0G8$hBI=7|Az z8K{ZUoQzCWfc)m#h!Gqs2ha9t*$IiwBFB^=kM;A}@N3`DD$>R)(v4gq43?R_KKR*!pX+zH7|$#_mQUC@`xnlypt( z+}85EodvfD(k8Q3Ok(37t+rZ%AveXH;#TT5(NK0i$+j zvYeRN%dnEG02wOq*-`9Z=b{I1uG_)TAx-re)MxGYrIg96sq!i3bm&XjKg>6W!R@Ry zH%#SafDD29yF}g3ISrH1G{`pWgG!ERDfBBCZkD+|PPGUEsN-?V4@W-VzqNIS%x7*@ z#Xx>}*`Mg*(rDjUo;6T^0PQ|Ei3lTL(nB_GSnUP@@FB|y+ph@Oo{p#UX-8BR8991Q zugp>A{o(dP|3dkpK8UOHsP^HhQ-@nTxoKQ%`xQIpt)u?MR>hj|WF}`mKk5YGuFfUA z5VwVSb!Oc<11TnVy=qB@$00ChD2eq9OZ<>xcl$cUwpX!N(a}|r-t|G!^|7nV9gwp{ zqUA;~)JoRi3E0OvtLn+*n(V&-ZbHg3+ac95Z?UQ>R5O)8Qe3P@@62E4H;(>jzKRC0 zBq=*sTEIy&?KbYtJhs;<{prjnwPB9+&dj=0n^bdV6hYyUUDLQN7v)Ut{rw{>F4A#m;!Bgng0&!{{4+Tm*h+UJ0P;4VvuS7w(O)3^IuU?Q_!;EQzNbv$yN- zZWmCqBRCm)olBDc7Bp#s4wL8N`&)3PQ2(BjO@30K9G8~7gEbs9(Kn5w-bsNY+hwR~ zva-I6x}6bF%dp{@mrnbhSYS+n-Jofjw$^T#PWum^5AU1jLAC z@3jj2Mx1!?A{6dDZYuHyhqgKt8ZL7nT+e zljJFc)SA+xvA(piL+iUsP%RW*ug?=kPn`~M4zE`=h_#fK0w?W|6aGn*_RrY4W{)c+ z_GhnB#~exHujagY{Us6tJnMKxc&9Uw;9AK|Q!@lJgd^ z+_EoDq~RlE`nCIXGA;X{6z=o%YYE%9)zYliR%L)G4O8vLHD-XBMojIjsyvdg=PlfH z?lwG^_us1D?DwZ!yDR#xAV6cY4E=RC?4bZB8FLZTF?pULlSmuV}X+ut<;Ofz}k6KCdXu5OlbBn^kL z@wq4V9sYx8n#lI$vODE04ZXpD9gEYA0xc>9Z|~<0&`29L&IRS@OEKco))uyw4816C zUc;Q2``{Zl^TLNlu_d*!rS!(w!g-fjtLfa5e9{K@-o3-SEAtH--b{Bv^2fJY23yCf zg?F?*2+BdssOjrZqpKg5Ki?p)=OhivKr2omSDd1@CSR7zMHYtvV#1$Ve&LtmtK;9~ zq-7{%G6qIINIT}C?4`XA9Ah1g=mY;u@!p*&{9^Sf8-W*O>JD8ZrHecA8`wy=BHmOT39mNzP`!*71e{5ALw*= zU8h3C4hZjXPw|l8#y3h0KYThB6l*ePvpmGKb4xQd+B`6GJQ1jXvHYdn`=V&F*I;+i{A zCuL;)kzuV008^*^r$4@_-RYGnNw$(uAOw`nU_r9F76UTQbFX^uCxtQ2K+$l+`rBdF zBAXp1^b|XQ)N@`&e?50oZ2*zVvIPMo=)9M*R`fO|E%yBq9SJac>U13Y?-=3GB_XXN z6-SJIX1DLmIdKVpx&Gt>_jfeTdokS4j&$@y%-t*93Ma0BXWrUNe_u&TCFcfK%nuJCYi>e%RGZ}mc?T|A~pM_q@LNi+{7C&%!Czg4aj z=3+36G&{XCt6D7gpTLrsuQ%HKQD zf55+)rL%JIfX#QWc0h1L5Aox?{&xE&Kt2hd4ahI_T1bP8%`;K^rkT?1olf!Bxd%%1 z2CqH@uoCvQpv>4eF&~L#-2s^)NuL6!srQk0m?xj{B1E1G?ih(tp}$anZSlJcM|dcV#^m@z8&AD?JJ~#E zxGEIQYv{?=cju$GnrE+bS1VbGUkzi&BWm{vXR_Y|5l>N!!p41a|HPw`;X82mN9bmR zA1;M~(MOSn3yo9ugP5RFPE%-oSbl=_`y!q#3RREfOzvK2}s%v56q9jMD3cys7 z)he9$>lGbE0SOZbOxy&t!kBc_buB{grQI&!9W)am1v}wtoz)ad-x}uwyp6FA{c-;9 zHN=e-N&5S>Wbv-D8B$Zc)+c$QCOiaAPWbM%TqwI|#z*G+dTCdm8qrXHrVz((F&NSL zvgRF)9P#Rl=wmXUTt#f#tExIned4oI;hi0dsuLRfgJqok|&dkRLJ$~*d06)BT7XZ z4`3uQ;9TKndCyiEU1Rn#g%Xz@>}gL6`T-ia%zrmN2?W3#4}ouhy&wS1x%=I%IrfX_ zK&gd}iabCaJ2__NH=?Kpc53dci*aT2>kUi>nK(V*n^VfOVh-Vuq5Y)~# zq%t2cN^K7u3)eA@W$-7)IiT$1U%5x{#r~`PlWO`p&0gjlOdX^YZ_%>R#xUkJ{M?eC zCg>~SDCMZmq~_`XtNo|b)lJQ&V|S&WGt#^CX#F{9vPQ3hMrZXM$>Sd~THj6)Q*_JK z=JfV?*?4Kb`bs^zHZ(OL{%CHU-tUv~^X>URf~HP!mATo63=A`4ZS2BQ0*{V+usTbh zijF(=Riki1eMcE@=?vE;WqO`;&Q5;8H(38;p-&uO@DF4~`73m^SKrHR!2V`Bm^1m8dQX(;kC4C2v#c6y;x-ockUzkBSX zvW4#kXS<+@cU}k2gZa@`P9c32bug02FustL{`cJoc&J2WwClgl@O0Uc*Hhh^^L6*VwC&UscG+YK0F+Yd6>W#CHW_Ggj7QK_|Y}u1=o04>otYD zY<}?}Wl}rg1=mcre{v$5UyPO&+Bis3;2tdXs;uJ%$b7R2zV^eKa=lK>=m5 zfn=w6;4612x4n%$R8W3OLIVqdr)BIv_iA}%>D;_H}V4lsMJ@fVxNeXPNlXW9TP0^M{&Wm{9_!{ZIEa;x&6 z1*B|(LbVEM8(%O5;#L&~uplP|?%c8=ZFcYk8S+$$2Cb`-5R4r%65JPYgSc5hiLv(z z3$Ue<2}0@Vb8P&s_eG>wxxgqU#mjB-VMz`3J(l!(_=%>j3M5vEBDc>N_?J|(oSt^# ztr{vQD`XKq(Rpt#MNQ!Zc=Sx0`V$#=v7y31QXnb#UcTs|!0w2=wc)2bBODvGdiHkB zDQ*~e-e|yn{+8fndn@DnksjrAn|%*0u~6%uB1fYg|9c z@5QM4J*Afg(v>5$(-50XkdvXYCcnf}Px}3Db}J{yNMC2QSV^>IKumCJ(s~CSDdFYv zxGJ8_O*z8LO_^9w$_u3tYiDDeIj)LO)k;R{DivGu`gsq2jd_mD$W znGgwhOOHTbaxGlW&64#x9u>FS6UzBD#z^PVvNo5wuywKB?+K#HN9AaCJk$vjacMdn zpmtl9ohil%n9acA&yngfakZbTfAIu^qYhyEpg$loDeA*H$tQb92D8$rqxUudJ+H89 zXvwTZ7+LeABZ6#%3wu>pG=+CEzAJ*T&vv0}9EuubK^ak|kX-QUg8$S*cy_JH2sl$l{~tmP!{ zExE!!TS)B=c$eV;iFT_{ntt9<&t&6|>w6c@mcrArzdrRc0iap0^mvtR+5Bwm_NN9V zcD(1i0p%ey3(cvkRTtP4>@60Q9$a~pGE>Qg&pha3(5}23M2J4#IO$bTXcSzj(X1WUImaZ4L|)2_ z1s~2EY1aW&!sIiMq?coM4h1myZ@g9#H$C+1vmz1MDDo%0)&V)YezQ*pKTZ>|M z=B{F(Ej7haXT+gg<)T|R=R&EN%`8D>QTj-~#FnMtb$~2R(YwrJKJAR zF(_CbqnQr?JE#{q70`zJ=Y}o}gwD17>!~*<06-Cj!Q}}xcShCfUWSCQ@kHjD-Px@H z8pl&tid)&`Z>CTXV)|J1`sk)g+w}4C0C_CPzlk8!Bu;S2=By9c#Nob7RnKupPz5ST}S8-w2Mxh(Cys-JO*xB+r({){D25*d`!zu!MkKI?Mg z#60*nFN3?>bv}56{)G2AR6@=9*P?~FwsLL+Q&D+ za0OIz<)O&{9U9c_v3!E25$Ce;idK!GBi;|#?M4i#h_{I(@V_a7zInKEN0dAyyITJ( z;aE5zKUtLC{wwl2%5*)bIv=b^H(R7bpe?bNM2?=Uh7J|V|NPh}$9bLfrpR9#3MgyA zhgF<6#nlK4Z7({Wj(ptkHa?bCGM^7OjO(ars^j!$>xa?T^t{NoxnbxcD11~XFXf{tipbXqG4k1;n`V)S>U zDjMl%fg#+NtnLJ+(9#L2I-=>o{c?9B3nc2BDaDiVcEpq|??SvLKiKnuE*?Lwlc&Iq z=DJi(^a1Fpaga}7$os^&meBb}$yfx;)6Ztp1~p z2fB}%mHK-59j+uviB ztNetzj}0vvA&mY??eYF1lQ0-Tsk!DLg$@Zavu z`8@}KMp@Uao+%d+w!qu`soS&3&v5qPS6;s*&9rBl<+1j#ik|mBEiL6yE-@}KJBtON zk|%z;m?j_2z5GW~Z`TgMQtsc{!!6a1icXzL$0&&41~iGu4Ab9XTdF+>uecyAMK*K8 z`Lx^! zf7yZet_cNZMn-PCpd%$N0=rxBLn;M6^U0r3&Kc4)vf?SxY#sH@yiO3B(Q@9G7Eg}5}W9XDX2 zM+6yUaa5B~r4Vr`ECj&iZwHKvyGPxGsBnyiY>arHWxE`w+k7PLL2#7PVmlr3Y3bSu z(sN-WcqAwg1cM4+H99w)xc#=X6J<~x$i^d5Y1K0mbbeD!m%xBy^pVtUS{C$Q&YEsq{GA8@o8;=Ribso7mP0d_! ztnfy)W?D8FgyN^1Y~W|Fuc(eP?wRW{1=Esvk~u(6bI@yeXdKN0Gl6s}S>;gs6*zpS zE+}AM6}jDj4cL$}d^TrjgV$w5;}*tW!vNE$Ua3)2(|XCY*bs?le*x4)Ss}dNZG;|5 zumQG%^}|gF^5#;^b|vMSb&rxl66oS`DCqzm-uozPa4_#sIN8cHQzgJfSBYDpQe)qI znqC4PlJtr_om$|J?i5`MV@#BA&?&adhTz9>)d%Li-o~3<&kOAO1$|VEJ1Y|A#7JN2 zpVb`LN;(p=(r4bb$zum0U&e%g19~MR%%j0MXw!-DOC!b~G>mw<*A7Dgtm-}6s`i@r zV@PgFW!auPRc|lm#h7T6CB-@JkiWwbs{FqG_KAo9%xZn=d!f7RcyJ&G%4<;jI6SDL zWj`&47KJ~0WL}=^o|iu4Kp-fZl6cnd8sxOfY?hrJ@W-(Eh=T8vp=0d^E2WVy+YEqE z!Md79^mn4^9i?Z@&+>bUk!rWZsq01m_$mgtWcNOUO89MDR-CIb$jipOUt{z6v!Okg z=LeH=7t2O@6ofQzq;2{Ky~Z5{Z;;8*Y#);>0LlK~Bh+Z-A~!cMyphkqWk-Lv@teMF z=4L)}{O3A7k0;^y8cJU|QpLWmIVyo{I_;CGP@iR=Q2(s{D0_f0kj0Tr0LRJcpkhW6 ztKBCW=Eifz6P^7RGRr@iu=8Jf7AjUAB>6UnO6Q*+@B&D0wy*h)${I|E{5xG*@5f%x z;%`qa#GZP?de8d2zPcfnDwg}7e5XDxdz78aQ!hru9s~EB!SxOGc|(Ckhda zG3#}7gJ6=MUOu~9w9^LjG4`hDCoI8_iyj_~r}FaFAZQV=7Nueyk( zBfsEeiV-&Hqxjvx?WR^Nk!SdZ-Wj~DvN>LuDEyY(W#Pca16Fr9+&CVYcf>>G!t_lB zRSYLOpW(dF_J9~^!)uQ5bCKW3d>dayz%Y!f?(^!;^zwjJ<=+nfS!9^3lZ8~tCF>0gy`re!o_^>YsNwI2#np1;^!Eic}Tk5G9@M6Qh1E zt}*g0%{M7$9Aw~_%h-~B8zmR#w>Eef)Qlxb zR9{QWzGVxp!&l>Mq_8A0Qk_~H*WH;Rm3Rl?G*e0uPNptn$2Iq~>;v12098Lc`*VXG z{g`HoaQtW35FvhAek*_YKJ9|ZG zvqC~uy^SwVEH{|{=2leFnseR_naHeDQ~qiA4V9ZkpE|Ar&`O?Z*V*+ZpO3ca|1kXh zK4S)zS$=0C+ZNE~{C2;(1zWn7I6D7!kvkg5HQ&$MCsD%aQrQbN2DWco&|eGyn&jHK zp82V~M^>}ePixESu%xy6d-Qj=FZ-Bhs#fJbfhvXdNF+b<4<0Myy-;ThkTQq6CLyVT znAMx#sxE~AIC8nc@NTS!q=lvfXT;he&Nx(K=vQnzMotPBJiD++QknYDXtTfeV6*W7 zIGY{cPu20|q6{1v>`;Vns#yZw2{{5`hofK5X!1zt5E@ye%=z8iz1(fr)%vea%NGG} zefPAsDU;X63GeD!s^i|#lL%Apbi-9%&47)>zJxgHv68OPrAI6Bd4u_Nm9FO9W22u% z44FLl`ql;x6R&dMJj#H}@4Js7kXn@TBPNSH_0P8pE1yn!&91RnM=TaYQ({S=_^H6j z>7NT5b4LfJL}jqh_y3#E2bo}n1GF?vKvk0;{^sKc!SBUw&NSk9 zmc}`c6K1!QXBYM*0>tM-iHmYWL&!AZhyHT|)?ebyj8+I`@J6V_yxLcF z7_A90EEHnn!p^_LKDvGaX8qC$v$AIb2TX~pH_t`D{>vv^*+lBI<6Ze4ay@4xz^iyx zXq&2z7|~+k3|s%?1nh;ZhunrSiJp~4y)xxiB4z};#v?+0RC$WLp868-(R|+Gqs?S! zl;$}(J(=<=fw`^VGWY}(CRCPc#vY$weckRs2p~T=9qF-_GJD%Z4SZStUqSNt!|bIH zQik8ZeX4PqCppO0jz+Y35udA0($96b^7Clq!qvpKItW(EhH$F1g*8D6Gn0`oT_5&; zW@_=4onI)dRIJxpQhSv}S(?3fijA+C9EE$Ce^k*25AxL6;TfUD&w%}tBiDr`jg zfP}DDLNDcPqc&9qZy$!R*Z+cWRR65;-%+|=PmD*U$TUSmf=@gbnlswUH||Fu!{ke9 ztQ$o5DcULtz2n(P9uJ8_`3-Vf@o4h+=PfM4^)VLN4)3MR;*tI>L5nryvB(cTGZ!>A zHgw)`dBGd$7}?>*fDwPNR$Tng(dj}WU*2G;%eXJc9mON4k&g}|{%DdCG-bB%WT7ti zvMUdLposg$+jGV#a_Hjl{Z}^RGqaOUK?91mqedf9uXLrz80$zgj}t<+93NeoG?NO3 zF(K>21C#RjV`b_ac>#^kSgD)D(7|nk)%GijGH@Vy(|_z!vf&+kf!7>gK_gf}XL zUA4t!X3KZX4$J|nK$S&Fzg;AX$4n>5?NY?y`TcOXwG`Y(KNAs|L&+-8mTv?_#26iqiW%XKUK?5D{2Joy>Q+g`xC>{e{51 zzG469QSXC{KH|1rA`J~G9 zhlP)n4OZ(I$?**W0C_^{**-~a2koQ{l|!uLQ|Dj-PotQ{SHLJpdE$O(Xwy>DWKN#@|-Rn=FR zeS#$t%K6tyIp$6HGE*A5_QRI#Uww01y|;bbvo|v@CRb4%p68;9SNw-3?5E|s%Aa>( z^x|u%N4r}3(g>J@Y0TjeoaQ=wb-(+ZT$9Oa&LM4sjJzw!pZ8Y@QWiZ~ou>j4 zq^(;y+4#TA;BQTS+>`mfC&ib16~#c+$K`tXB#>{k=L_zvSJS)1iuoQt<+}}02CT#v z6R1cnyjr5PRS46mUpoL$pkquaR*Bv}9{+G|HjmecW%FTL!98|YpR&wats4s(aos26 zL?6qEa>1O-n%9^cVrkeWk*yDEMktHYw z4fB|+{_=u+qKV^;0huM2Ec^#}7J?*3l;BX*lcD6aX3U$2wrS*IaqVl}{h`b&#?={RwZ;J2f7o%2|T5tx1(2nTFi|YY~^HXY?A?7o@d+HOE zXZ+HPm7A3>rG)Dn0{}+C6oS`qCI+nKjAs@O4?~6T6$-Rxwac(hZ=i=xum!&7vF`Xn ztIX`_8q85E2gHj9*U{&&+99&uPZB@f-$`F|Otf55}XPKGIC!+T@O;@(Y}x zJT!|AXy#$?8I_ljt(_mec$=FR8Wdn5{Zw&>I*=0oWnQ=^)KU|Fk+aB;fo4rcKSA_k?v*+J`IK+|R=hG(n|1&d+Wa`nEiLYWf9tMMzPSsD_tfWq{^Tjh{BDevbV0M@ zr}Nrqg2gs|Q>of`g3Zs5pHD09&ifra_w#Wz?+p(5iFYS@156tUr6Db6pQp6Y6NaZs zW@d{oYgAP1t_S_}&TH(xwe@H<*v#~RYfG3&@4=$)AA(=I?i@$B>IUY>4=(&BCb44N z@V45X3RpR6A`jtF$D2p*Uax88gLZnb9)f^LAmri&6Flh|VEfw4<6Q*|4gle}YUfyU4+K*WF>LBMqCT}g> z{_H9GY-o66PKaJQD^xT2#@cvrHu&JL@>X)j!)hbmHph$bWZe{1Gj3=UPZpxw0h5Km zh{^_r17e`Ha)ll~c>QW;e)vWJ6L&5MVG^YVFxJ~M?MG<_@DebwKcZY?cZ5i zjap3U^xP;Ky@dYQ$l?X`iM^h6hKu2&7oR(YGkF235vO=gSWc;q&u zRLL|zT#U4^YOHy)8m)kqdVHoR7g{Wq2Q`2%rcogtKlC7wTddO(J3^(|vRIz;pZmKYhqw)ab=g(k>46zee&>H8b~ zc*&)GZf}$_kDSF4H_+GtnJ*qqIiXM7o=8Cvp!B6uc>~4Oj+?jK>;byQgocW*Sd)ko z_v?Zj)-1LE+R4+Nc~ep_<||ykNR%llEBA)f$D3^b&nv7`0brOAvtH739a3`S7ek-4 zb5Z-PMP815^v3Q7_~}-9wRszdTYk5giH+yQ^Hy0gpFy)#K73B@S~r+2m%P4%0dRU^ zBq9ZCFN_NgVvP*7kujm*jAd(I=fHs21HAw*-0+1I9-By>sts*jB`yU;?{t^G-&3T| zicoaKMsId28T0b}=2Avvm{h3w24;kdu6IFqf7m9C*6AFNQi6c!Y0ieIhsT9fTr_pe zITsbF#?Y9aF5IRLOvyMPSam5#Wi6c9j6 z4|?-mZ?*~2C((O1x{i}NtC?y+IHr1gDRJAc+1{6!9%%CExJZ{cQ@&V!vVDh^DOh-K zEL#XL!t50+P3GncyPsNCI{fAwYHv=iY^*btJoTf$ZT`BmJDboDVnxvbWlR4ImZCC{ib5amTlczg5?q=?7W=Ry&uqio?8#Jfil;@3 zaV3;Q$vhpHp&*lpxOC$qJD!su`mQ0w)VxMmjv!rDn+q-FaVa*My?J%v^xMzQY`(+6 zA;xe&;u2?|Gitf=$Al zA7vfoF(p#?JTv3ctw2w)W<|M142)uY>9Uxw@o9FjW-jw5Df7d_sGG0b*?a)SlUo&_ z4~X=w9g0i}9!h0Po7@y9i#_%75j$M^XPYNF*y2?pT@x$WIUd*XrE^?L$Vq`y-r8~V zkS&yusxFlX4FUvgl*rShHr&81}X%GMp4Lu?EOhd!3c`cHrR(8A&FCqmAH5j z(BY_(8cEa#C5?u6j6Bz#g_AqhZt20aq?=(zD^fzv? zYCFGhxFUW8xqmhryl^~#&g4=RdAO7lq}p+-s>_&sbCp&7GD9UvvR*m`$OBnGB*ZPiAma`(iS=|H%B!e7+BPv9u;6F7c3x*%_fcOmpf8 zE>nkm^kQP6qtQId8;`|#8VD>xeFKqSjlb7N*#D-MJYMSn>`PB0oc?y-GPA>AJ$yus zOPty-mcZjVH)Y@4H+Mj*_)Hw@;q#p@aL^ThrrTeKfzkDtI*kNq=Hqb5Tq!5Ckts>! z3D&)^U1k*lSkX=ZRLs98zWHSM)pEJVhF3(#GE7+fjXq{Eu-Cio1NRcS8KBwQn#Oj5 z^1fEPw1&iTD&%>LhWZ`(W6NQ22Tz)KAh>QTG6lL#9=})#AkoWOLOT! zekOI!GxXAq6B{lV6I&d=4(2RAqwD)d6MPJH9`Y>J^{x27W7rG=A|bpYUP_tzRr;f< zjDa{UCzn%Bu$>C;a;*qws37sfb-m1DzDS54#fZ(1lE=lZ19Rp05)oWH$JqHbq z!h~D5pPU;XBJ`_}8fcoRRFMz30U;n*!xtd;k7!DpUf`8(24{OG^ma0%q;E(oj~aPy zxYhCCK}NLfx(SY!j?^~ZeM>@_1B>)>3-R~XOuK<`mWf4_2mr^5LBI586${&IMJdiC zGohLR5J$&1!D_V3V=Eip0I7*bf2H}AYx{;{XXXj^{*u9>x9sfSg9n=Xf(S=0fB1Nh zSHo-8>3_Y}7xz8*O2kMbXFFv^Z>2oJ&qU5;hEV$rN59?AqNpJT{Ra+=YOv5xZ@ZVi zeYD%JLP>$dP*BOG&si7Ju1ju8h5DFX0Z5V4U&YEu+pe?H4Jymt0DY!Paps#*Vnmqi z#Yk~JlIIGLqbdjb$YRN!LT%9QnYK24O%}mRqnchZ< zC4K4aOnR^-v>m;XOpE*=YpH21^F)QBLJ4;HDV8SYEXc+$H#;BJwH3TPVC#cmIZd>L z*+-KZ-!|VsF;e>MIlZ$uKtWq7UN~7B3o1&s?Uj*4&j4>AQ_d?)m1o7qI%gcsmebbZ zzpwokBaV(K3|u7J?>jNA%P1?*=OR8gie&ZE+cp@x@A=sB|1#^l&FNkaIkaZhVdf(` z|Jms;YuQmLopDui^~$Cs?hgpdvy%q~n~hoj?Ln0vyqyv{Kar>*9;&ctdZf%48~HJ9 zlF@V%CxlGU9<5_^LqWdtb|$XTLM8coRnv6Lpzs9U#-xtNS2uGsbF|yU*>Ym)oP#@x zkxWatCr{Q(GkH00?Xa=X&TM;Pe~+4W*NE5Ay4UPVj;Dod5HB&UDdU|@Ml44!^s}O| zjyx6mzVCfy*3*Omqn75Dxx?-OnfEf63p4sI?^RcEr;Y zBBnL|zANvY=)&!v_6}P!CMua~8J;~ff$K0c| ziDUZG&LFX8Bgq(^B`+8g7|^NwE)Yvvz^|m@Pd?K#L-8A(Y$V#;)yeW1A*gT)MOe<3 zA2MiqlLW#sXPr2GOhl#CUfTOz1M!$=XsVuQQb;u*~_v+ zoh?I%Y`1x}pj9gE#8#tm|8hkyfcA@P zh{TyKs=$@7l<3#Ete!Hj`s+hR5Cp2H=-LqCV#86ys%MY>#8epPi~rU0VmIsLina@x z0I!qdZnV59ln0^)%T?P24B)_7(srsMD_&{Zq8Vio_P*T=yw9&n)+ntit%@?k9zO#8 z`-mq#V3b72amW@+-CQ&HxqQ5Nda)Zd{$hT!v;j+DKWfwO0BNmAW4LI31^ zbd?KV6{Rm@PG1tLNKI1b$mu&@Ue?pInt3p-#u$>QTtq7su;4N2l={LWfOMLDsM4gx zD%7>0unaeiA+w*Z78+9l`Sb7U@4O-K{A_B}>Ffo2Chgx$DORxGl=9dpP*>rEyOwa1 z_C_Gc!{3ZF>~BpmBZ!?BC!b_KH+qQxZ^X?vc>IOFrS{Jjcw9 zw|~}L_RnSdd+e{%KTmrMx3}ZX`*AQsHM4gi4KKm}w>s-gQISd$bc|_)KmeXkFq^JS zY~0%V#s=XHAA_sMcpi7Y?bTT%Z8_#7jDd>zlr%5oCF_gSFijq)rxH#0qJm*aEglS&;C1-VxEWW&KoKs2toi(+Hqhmgcyf3Dn)!2SAouVhc$93qQ$H5qf^k2V$ikkA5B zrBfs<tmfa8Xju(#u zpeNn_xBj15xt^&O^5UeXtGE{?9RHS0k@1yHK`&#?;yj3=it171L`%z}2%6 zN`Cwh1s)xqcwSkJv48(pKbXS_0!^mPrgYUFQ81t21i17ekYmWP(1$sei{ScygG8z! zTS9x_tT&ck$v96;iuH8r|4-iyTOIN+j$_1TYNn-a=pU=0R^0chsvF=nGArm#A8$+; zhfY#-dBE}77P-L)hfeA zm6J_Cvn;Y1nL*0nzo;97g^=qqHc*{9Lg7NvGM~?j=FL?d*Vj8}!NWNI%*QBqCE20; zp0!Cr^mzWd%~=lgXHS;)IT1J^VXD{-(^xy@_q5goeJe-^eE5RPw;*M`B>y=gMA#3k zPaVdPhs|g2k!`vr?2F||xkFV+`@kLP?O(=hcpUyhG@fuI)9R^ zUk|oSSu5si%kE|_&VoG918Unj>|NfljKX&t2>|!-$mbCOYEiH6ku!ybEK9_zkJ(JN zU;Spft0Wu%qjI}M(j4HFC%e8&V-2cfjIL%1LpkF=9T-B()N!c{ND|WfQqMyUNqiZ(<_V#9xen!y zT*}LxgL(QL_7}l(Pjz<%gKp!53 z-Db~+Xj())e^orwo^+Y~B+Y12aT)&cROpEpgofwKIV>gL!2V4brTS{#bOUNga`0)R z5$G2hc!9dPei|-OU*$lv-Xh_%y{9dLo(9a4OP5BTGqu|hC8Iaa@@+1UM;Sg5)KHQfZczKwjt zADw*E(HO^Lffu!@pZ}3AZ3D~Mqa779P^7TN9pWm2H9W>kWyhRqc#iYZ!IukfYui>A zkM@o&px^F-`kttDz5NSIT3IE%^Q1K)`dSU2fk>oqtJtpWkjIk2wQ>>JPV9;|({_)F zJxTJXrpKNlDXdz@YaU_W4E*!3f$ag|(6q`=)f5A`Pso4ZRDR0}=9@_Jybxi0x+$y- z@^maL!Xl0l1x-|)dN3d8S|LmoX2YAOvBc2v{XxiiuTnkR{(ArR8W+$au$G!7qmp@K z-=&UMbz~4s7%u&*eZlMm$&8z2PQy^Ue1nQ-u5<5#qKeTVPX)U%UG=^9Fjky!EA`fN zkWi?^vBKQkoMoEYIJU^vS61hX+k-!Gr{(s9cVMF$+W@gzglj101h=QSS5eR8hDGEM z;pXUVD$1VE^MU7sN~)@qyi4(dOXxC@8NNq40C{4MTj@6E{Eof5HSnrQy2>%W3^ zcW|&SPSZU?{8x|TIdc;NEr(P?T`}o&I)mtDLm#| zKRZd~{T^OzGw8#nY_}LwT-V4*lwRL4ABj?eX$Zgc9cUtnN~5W4y(-d7$*>ah|M@wd z-=<+lO+AoUO(RrUkr2rtB^Nu!i!?MiqM1|8u?7XB+YV-W&`7Q?b zc5E7UzRQGqY*##iX2YkY0Fi*dBK(?T^l;$YaFl#-M}}d1RyKgH1zm~_oFc5vi}7_H zsJGz1ar}s!Vu+2TqnOAS%W(eR3XfJT7Q`n7^{iYxA;a=K!TGx_px&1Esez%rmXjbF`{SQ8hf>Zex^K*dt#3v0H5`skDGxa%QmmDA*-7)b!ht=u_eO; zk(@;zAU8^S@VHb6-k$(1%Zfg6j_+%QNbj_-$R1s=?37=yU2|9`MX=DHoJ!w*IVtIC zC9MX5E0pmTu>7w3cQX2y!Rs@SVUkF@Wt$}*(jfBl@Tr%eemC1C@RCQc?6AqCNSnYm z1^+L8{`wXycjE;!gce{t6>%ewjecaBbr*Y`--r-3%xX7ntl!u0a8qfdOaD@!#4~>?2_uhH9lA~3!X#*iuvE(-4+jXt;$|) zoSF9c!DXYM)}51 zl%_l)0=j@b1k||g&*vbxzaFn1AI(fRSU`nZE_7ePvN;%4X&Pl2QBjxQ*2KSGk~s{U zRjXB1$HKq36NP-k0`~1XM7$xg*hy{V^rysWv2alt7lpZWAPuM5G~NH6TU-tbqZAw< zdXLBt8nm8RACRYeX4cjKoq!6d9Fg;>1L57FGx&8llr8YIx-zQ#S&y}i<|A;wxvE30{&w1)U9=av|h4{oP(!1sE<)dGv_0y=FFZ1W>izya6A9UF0&kQ5$!L6 zdkqr|ZW-d|F2Qg;&iXe;-Sz1VGXR-8Gl^{un9x0J;MxIDAsrG{nIor}W?``ko}KhK z7<+l8kO3FYPc#7<%SulRIsK<=jF`=qS{TVRnqs802n_Y58OS=;*FE@aHpv)be0y6t zY0ate=GDewUjU-pZP26b2^L$hb~M{Nr9DhOwPs_Hq$2f)w*0o#2<*3IUkOL!{~}HC z1}IlBKknnDQPqNPrQm%)RR25pykdf*nG@q!o@80lO4m_0z$?S~bLNd&h@@X2w|_HU z7DeLyIWu7}gJ7Dsze;&Y6XG&|Hb3kgH%Ug=yVdr z@0@kP}Tu~7Ay>)(RC4q{nMOS(orug+5r5;5W8%CSL;9+MT zCAk}kA!G9daw%VejN$vTPTM!YbyshC5eOOyRt|hfZR+~q?AmI2K*gs9Sk+neI zT1x+0igoV=csjdj4&;J6A}E*}zo|GTQJ}8jYy`-26bNcyK>sP+Ixeehwc+7hL0DNU z=no`wT=>&7en)y!;^v3s`w+P3*L%Q^QD5id0-+#=JA|H=BH*_3@UNMKy-D5K%10#A zyB}`)wW>%X4J11LfO$UW5-rO`j%GBNgdTa`;-i&W_~Jk@;|AZ zoO9vF)f=mG8#5~r53_HCSPk5VLCt?UaY6tH+?^2X>=d|fSS=R?vty{)x=y6u=z6m% ztic20kp-+^}SHXXI)p$nW#c|Ig1SAn&5o$aQQ5 z=0g>x#;-5AzssHed9*b^S33=;^h?HDG8Og}_SyFX!YHP)-DZwFRr+=1$2-ULPf6~z zG|#KDU_EWeRH&hMowq*Ue7jez)iB>~%6))Da=Y8W_aR_z{qVJ>^b7F)>-ELeSHX{v+=4j$Op*S!=jX;{>zT+Qf2L6hD=hlEx*Zw8)o}P`TPa~Sn5)nw~f$kI+(s>4gC!LO_IuP zH?<~zApB6dQ(fo;0nX_Y!rL;{nllKieuofQQr=Xrzryf_Uz(u7cc>slO;^K+Isf&K zyhvfs%JI}1V-Wwi+E_5nm+@vSoCxh**-hR?r3qd$2>|`7B)JFF@BIx@`{L5A z>)`!Ak2T(tn}|bx=2fqi)d2GuzQ%>!x@_Qq9zUvCCE0nPuIW;O`FvDKvZ|42ogJ(h zT-C%Tj{mBErezPxi|{5kvUDLmPKM{o&Mj*Sk~|b|R;g?gL+t=ihj9{S25_F`4o% z+|V67kLOhTrTQEGr&sLX=LPk674DC~S<%;y_9D&f{WmQlZs2+qW?`AH(u|5;E4{WL zxmEnUazto?8Nzhz*Ujk$)%@j%^Pg|a8NanFPEVlgPH==HGUytV?$z#96qAtz=?It2 zN^tGeUvq>YnZERJp$4kI6K@=7xdOo z9rWjjnqMbq#SAU@Q^tTHY{8C6yv1n zaDk_5${kvnOmk6h8rVM>DrqzQ;ZfWjp7y?Y3;5$8sh4Bl zE{^l(Yualn(v#AOJ1*!d?qAxrw|UQaFo;^npf$`Ys~nP5!8fvuY9P4N5!gpl6|*@c z#Zvykt}(jqjDHalHiVR`9gJ@v&>s;Ghiw1ZU7vfMX?jJN-h#K=OMgG^qSSPyS+J3v z4&i+#mHzz%dcSjRd2VT7h4psXjqHSt5EwM$=X0<>7b*_?pa0e;#pKwGX?7A?VwL@P zBYWfExl`N-3j|Qz1kU9E=N_;)i!`Ne%pg!Jh^sV_tHNVGE7R*6>lRQwkN@chh0Nrw z{3?BimN1|H@w3V>4_LlzJ8lo2B*2~F=>Bxfd+ywI!zZBr7(u%|OAE1N`mD%~!xjNT z;`dwSj;`@KwaUk+HBN>O7xmZszv-N6@H~y}f?bBG@6FiT#}O}I`bO84FgdX2Rq~3i zc}66#IceE-lMbgO4Kqy^hV2W|Y3XHY+27zWwsP$<$&VlN^mLuqY$0$Sw$+n;@)Jp= z4<#Z*UBUW8^u+r&4l7lqecQS#L6Cqj4$;&}Gx4YU9AsW_PiH{XZJw3?xO!bi2kTrA zbAb2{6nwZ9zp|A>bD>+1RyZp{uk~g)$lodDOK<2y;OdX@H&Z^<{Ihv;@sqe91Ti1K zLzeihbz038)0pbJ1Q$R4bZ|Q9uUdVx0EdOk|N4btqYe|JAEY$(qW4q#8Twu(U{&G3 zf>F>s(yNhR!oO8S0o#W83?8acV&WqY-Q1C!HnT8!PUonispA!3(rvKi-K6`=?wPbq z@i|s(dMGD{Cz4%E9VTL)e5_5%%}!gejgsS9p=3*(>-^r2rJB)IPVgKN3V+&@e$W{kssk5)v8H@=_Ufr+v44yIn1K4`t!B6lyQ=lMH1T>Wlvf78V8w zG?xIe0#QA+PwMrSe0{`ZWsy|flc1M0u1eKGc5M0RXDX01YQJrn$SLivLy6m%S%;Sk z@KrWs(wr$HT*H3gej3GbPk~CxP)}@Xv=rfr2%J}$_a3a0Wi+_iGUWL3%i3QjL5WU} zwI`uDc!ipaSBdwb%ZyjCTcYx>M?rOt>;Fr#Fqc^ z)nXTVqOZuEg{5raeYHus5)B_Ju~`n4eRqh*TP{*NAI}70v8D z3Gi$-Cc(Pz<3MY3dp0~ziB`;Edt;^lSh@9?7X&UMhT(6peM!picN%E$hfv262;ILf zT*7-ABRom)WO#0HG2eFWFUqrr6^x`yke07Ye`~Wx^E-GN?AvOUwtUelV9gdeLm z9xb?6!LqPx?rlPXm%S}Fe7)@q_6HMD-e8_YtCHAO$G5az#YXWu6pkeNd}}-=I>dQX zNpi#p`@i}S_q${g%%`a`J|pN4q4TDy4z;Aw&sA;2HQwyx+!TZPJ)ECa;rcETc{S)M z1USFjE@ATzhmwe-aDukaxhZn~bkx()RQ4riaj}sq3@(3W*{}fQ`As>A#b7@P%)hy$ zJ81q>Z9>q<#0ClaRUXecx^${=r3r9;9JYlp%oZcH0#A#%b9l=8DelKo(-%MSxofe$ zcW66K@sWT)^;kRDZM4``Fb{=8G@|XN!Z>0j_>bf7sdu71?QQHCsLzqf!K1A#m}o(Q z-o7@-cRTTA#wnmS?`_pefqXZ4hL@jgK(n7*Ea7AEgdm>WFE_Sau)m~Q=9p1zk|H~# zt(yBTJNK@pL%Q97@G9M`OueLXy7(rCakS`;q8B|^LL)8Br05n+2i9y~5=;H|Xt?g3 zS-q5*3?E*N=`V}w>CC-lo=0z?<9QW~hUX1Ffj(*2fO5P2`K2 zhd|8x(Z7RPfA8~fzYSmKISkFY2q z(sZBsPYY;yTGe#nA%hs4vQnEpY-W*QTE-D8N>!5&|NC&JbhwCCU)_Ua!dV~Jy4@z` z1*LD3{_H=8P`?scf`IKa29t2u)CdszIckH=5PnFTW*}maE6>1~KP5Y1Q+eYPV z#8l|CUS8|}*8AKIRpOtb%WM!mc*b1W2579ET*?q%2YdtG9GhCez6|3<g%6~t#aN8gmF(gnJ`yR}LLk;uO0!i`@Qq(e% zS2(-@79GRNU_)^&b}dX@D)SwEa+rcQ|BCe6ua47IiTC=0S)Y52}^Lc25ndS1H-g0WE?Nf-EU%J zU8kZCUa55I!%K@iwRFLoEBRL;bo3w@>HnI|PBXCt!Zj)Mb2FK@GdR{wt!<3eneC&5S7FVW)OQxA1!yaPV}>Ol(alf1HYFZ;hhPqpg$_ z)5xXhS_{#cryDXqPs2-Gm@oF(41{vwMjJAJPYZ6?uehwdNF+s!omM}}ta6UcF<*6( zgHTZ!5erU_Mr)kj|Kz|MhA>c)LksSG?f#V2%o5I8Novxr;TPGb!wClhG}D#-H7Xj| z)DHNj-E-VqS|%uL0PxmO4*D{tQOTnN&8ZJrf$q4$S3pW&NOUv$gn=r0D$Mv)>Drj7 zd3^D*hrWm86lpA?Tk6RCUUWKT#$`5`$tr|BwqLx=J)Z{TQ{8B!FwaQ%^{)-6$%1BS zc4P3q6!O*J&D!&)$6KuAc+igozpS6P6HkFxmVtK70z<}B_8${Zq_!@e@@nVK7cP)b zVjmAP>rAM1^-U0+74{&?=t>4sm4xMuH3wpgOJ#YJKH%M?MuNfqW3TSqSt?QTQ^NM@ zJI^Ke3A@^4KRQaN;93(xsOdnfQ#IYo2_W3J_KUt^swEPK@jc!AlGGB3R#do$O1kq{ z%{tEkf-SlETbP>)gK2pB);NItP15?i7e{$F?zzMAq7S2b0>a)M=!5J3fs3lk{`sYM`g#k2y(R@RS6)SRDrPi&{^e4c77sDXNOn0?N)u**#K`{(jk4M9DMd|LIsp4Ypv z{-eCoXW)H5`OMqW-CD1dXAhsekF*5+K=S{6Hn3S6E-yPT^4;1bBQthbI_HN;9na8EUt$iEFu*yieH;fX3%!~p?g26msRL9R7o4;?c<|h|2>O2eDfiFt$m7Fk1_OT74XsHQ{b+)6zo@kVF9 z3(-hjLqx^`aje96=*(wgd{RkkUo;sBH)f>|_jD)bXIjhG$T6)t<3T>qam2Ilvbt~M zOu|4+3IyN{9rX!*Y?ON05qhYu)5hRc=8q2UWwSn8A;)K@yyL;EtgVRaW|QI77D*@c z&N1{8J{kHG#T6j7$do4u&OltSiJ@Pj!kb;FgRllI4w&>0ml40DnxK7n%Xf}riowOA z5pnAcspsf+B`z?6iwduaUI=`+gJT=li7^Rb17aa#?ZNRKS$jxaj6$duHAyLDNILWT z3*jGbzWxO^jF^96-&!vJeLZIviI->LRqMWQH0t!Q{q2>HF_9?;X$bS4KF*-uoH34v zhs|rZ+!3gg@m0_Csvfb<`qfF{W2h}V@q$5yk_M9*ovDr?C8UTEmVx%fDsa6D)-p^$ zp46DEM_>P1RjidtN_2Y4xX4hLJu%e|nv9)%0lm7snCw?i_8Ki?@@ zskovr2EMihoeos+F#6nv4qPVc((U%+QaJq~8{eqm|a@=93M zrFV~B&q;?~NBm@PLa+-dWl+HzJkT$QMfHQ+8S1hcsWV0>jc1GB-AK--5Rf~O590de zN>B-&c(EfDYFf6}P_{C_DlBKDWn|C9!q9)e7u+saWZ{K?fl~cgowyI(CCnixlt(VD zZgmc8kR}^``92yBRgm|?Pah+Cx9#LE*Faejateit*$_D5%=(ibK~KkMX51&GK;E%C zB=607p{p{hf&!uNc$TC%N9bd^&UW;#Y29#gf(-Kx>N~#jck4JvAPzdaLP$z4vIwNq zW4ld>vyVlCAPfr(=h|w=l7ZS}EOTzX4lb45aFrPN`0*%dBI3Fg1fdrq%y!04ux7?% zHGI8r9iH>=p9<2Cl_qAj8tRubg#Ib)w|Vi8^D@=4)rOsTCcK=LP%C|-XQ%*>QZYA# zU@!*d^{HLwQstt*rqItzpA-S(QnWG74EeSPhSOv+paiF}W? zt^8@jss3gPm(&vo1KvsAA4F48Y}0Pt1DSDD-GQX=!KXZ&&0c03%5&P(A<54)w;=`h z=9v1Tv^X^Va~)rJ;}q;EixRJ({D2}nm*wlt#*@hi1CH8|UN(?78bgw;2Nbf74zj3s z*bK2l>M)OYm){zQztUK19xH>ty+@YBU*bg!Mz`*wO(6g}p--QD`S;OAw5cJBmK_ttyA7dZbEhbSbBQAsG`3bLZNH}$` zzve_3@9MX5c1TONsWGoitUL#!E}!kEozaQsq1jTkuBcd zm@G;Y9R>aD@2?r6^XxzKZnMaMd_&?H{-14ge{K1|gk}8a$3W7*pFc}q{POW3Y)@l6 z)`HG01EW3DqnVb9$FVd2B>=IfhpL{Pto|qLu%s{6KZV-zK6&1>Z~mtwNqh8)AUh-G zrosQ}c~q6p7S~%v1>mG<+e+=+%JW4IQ9}t6I6%@8?!f)KXh&92oy?4zn~EU0{ks0` zEAN(T346X#KA`jX)OMi2sLN_r&+Tq87^8E_0ArLX7qV>ESz4JGd?gv*~}s15en&S#;Io5Gy%5k+Z0 z_o9(a%Z%_vOloxB{_Hqd{m*ni!|Mi@;HEzIS7%atXnjdtZ`AUS_Ky6VSbQ0SV6o=e zZ^#Rg=dV>}#!|aEKU6zxJFv+T*n4#b2*do=9&t*N^GU(^JL7jDbe)jLe;KBmuG*SNlR=~3@M}i1G29&i z4Psr_V`MEr|KBuT=;*$IW-9^}4Zrd^E`XwH zie8C2sxASBjlGS^F5~&!v#Vx$dr}x^40*w2TwXsTv26UNoG_isrBlgvrZqG?${e>{ zy21E-?^9)PvY~gC>W*Wyk9GhlpPDZ=uTHG{dH(&2;v;zE3(t;vlRU&SpEWU85dum_ zLy=B>8n}IvVn+uy8g-DK9dV-}#R5Vn)Nm}%#)E40)wKm3=nU25F*4h+ z+9V$uY+Zuj+)ZF>6I*4W@4fFLM96jbn*7( z{j#cJb038aE~paMm9IFMh`iQt|O9Lw{E zV3tFB3(FYYH9On@cMm&4X?M^(%4_38|>32RngM7Hrr zxdU@lXZm$(Gr9$QMqQFSV8(g))cwY13=B0i>+ge8ai(J@_n#d+0P{_EHl5qY#ZdwC z_4xq~7I4Hsd}hi}4Wb5Td7c{vXqbI{Gc(L=UftnWXAwhC^)Oo;R_ChS4}Fj7)@I%y z(AOdw)`%ZvBSeN&52Nd_2AsA_5nKA4P>g(G`q2>_FHZZ!>NPWZ=OVw{J1}2M6>g)F zlfyk5_wh~n+=n@E{a1+Yxjw{XMtQiqgL-?)bM?5ui4glNL7*)P%xmQ7ew4L&22Ez7 zfAQuhKp6&@c~bV1f;%#rhH>QP6h@1&bY)3;B`-wVbGm100O+E)7=O7;hS`gGMWs({ zz$2x<-{|_aa&MFJ`}c1T$jPwv#z&S`O&d$4(QbJh5X36;gAC^#RbF8=pRiuyF}8cV zO5^e+M`1Nh`!~fKYXC~lM?z!PkWE$ulUmbw$cV`v+Cis_DXALb8~vIcTXZtmO>DE0 z??{qaQc#_pNmBjapLH?xkk><9``6_jHK4?k6>@1KZLi{psl5=Jm#hKovWiaX~ zP~Ze5{F=@Xv{Yh*}}OQ)J= zscHAsg?TOtCq%wZd_y||=XJ?&d*3yV%tSx^QJG-v7{<$;uu_>dbyuUYkTMV^MJ0}X zqCMm8u+;XDupW0@qEDQS?eJe&j0XoPrl0A41kZIaS4ER(FNYOwJ&*zYe8flaf@gXi z$~01HNygh|s{RgZtD5D{*JxRW#`6skdF5&m=HS?xH_wgs1SIFHmj$R?s>xv9q5Z%1DJg#?4Osxv)rjuC zOMjp{9(^N0!Z?gQ51)+ckcic7e8sn%yS?u?Q2Kxt(jx)*S{IxsBiQD=RLiC>%2{YCflcfD6!uH2s(n2ww4tv!jU>F z%4t%FmgZ0W3n$|u*Y%HJt57CgtzPt6IpSo^8tK5AY48i4!{Pvzq7sXT4d>4I?Fh_>23jgXFKTE)Y3@%07-gb)Ly1(;j z`vX;@eAjJP8=EQki}yAY)!e2=Mn(?#0^10^Wu3Kyhdpl(TMu{FMrNp?XKcFSU3VX` zIPt~7%lP0Fjz0}W*0CDhd}kFDo`8CdF!!}{)hwtextCSAiI*h!*SzKuG&o<3O@BYd z5;7%fJ*<{i5dNBAWFv3;u00HOiT&FeA5lIATNmjcHbCzVPrCWos^f%>md!wab0N@O z@HyX~A8E4^swF0(ZPXGY*po9c;lZO36`OKY&`%<-{ry>!TF&*cpoaR-4G0|0kyFD< z0{f3u*U7I0L!;3Ng!%*O z!>S=A%VJJ3QB5%dYZ*HoJorp3-x-_kRK~W&%0!2eCMnokmpy#!=h6acC%#ZIX?@hL zv){SGQ=RroZl!?v8>V+%B2}c9^9(DQAfF=2w!S9xWWl*RkC%G;A=nQTj2kgMwcxhq z+btpN+Xr-K#o0)WB?+eb?IaYXmH<58PvuHmd8t`m3gdItqXp}D4yxtx%aRnlQ%fiJL-70dWdEBY9Kja~w8*C< zNsT8xHt`C>G7NpPJdc&?*^XEyQ`NH7?%PWh{5p7nfOzQkI=by-$cCJ zMbw0H(9Qw6o5az;!76T02xv|#d~QL^GHuIi)iM5N_08&6^F~;g$p9MlL@wJ;&M!r1 zDg-Y7_*?nn^iI$pT2iseb5PGxlDnlp-}HDg#kbYi141W^>2tVRj)=suEjX4MQ^Ir} zn+Pnz0pplo~~oNm}dwM{yuA$#=TPYi&c@Fv5%y@5v21WXU zyiXEo=AtdqR9B6S{1MEneUCu-<#%7Tj! zIbLpEvO%Jdjj9%KrtMG=0~*##<74+j5T( z*2UKX>JdyPY$Nbw(NQ$JJ`ZhQ;C=m6*vQc@kZ(Cf&v#hgUthN%z5d;K@klGHAgN1^ zq8Qb}RSfF+V`}(=sMctua5j3(Tz{MAxGmwAdjTqi5Xa~W5F`O+=qRRlJ*$_v2Rv8l zS$I|^xL*0#B%?sT7a}N{@C*IZo=7qD)&TP@P|e&CeNS@`!hXsP<_U^$62}LT8)|vT zWrh2B{r+J8Uh%KxgQO291_iO5<7OaVL>ZecjR$!;O8-|C6_8&Y)!ZcNY}*qBnZH{= z;BzXQU%*92(r;2$QJo{QnUQqAXRuMAPQE6scJ5`HIinlbYUF=XTh5?Ou|6R-0~mHQ zQInnZ;6XIpnG?z@rE{g97C}C>x&13NWofQj0ABe|fsC#Y4DwmTTxUrTe184+q+kDt zzUQmxn3OruOKmxwK)Q7{FKC5WJXX83$KZY&_6>D$OUT`8hmekvVRYAawtL4fDv(qU zrWB&pJy5r`F|sCJ)C|04(C$V(fciK{x;vHY5lh3P>JI!te-yg3%k+(fA)7p0Dffth z?ugq7{L1;lr@r_&)w&Mosy&^2*Ru67tFIE?o>|KHq}K0s_q?~6zPRRwLpqpeXc5Qp ze3;|bF=u^`PJS>w<{qTo{qd;EfOC^+t9~*j*KNSeM~8w;r@iZS1M}0m0!BafSw#$d z4n}6PPp(tBQZ)o#2#(I0SV4p2Uvj`j-PaT42O1t8Edt)(4Z;5IQ&b^hj*^x(k zvft@ayVg;oo~D&r1k7SoEt8#YFiaYC87E&c?MdkMl9Qo#zT`<>yMRfE--=EPdr6@Z zKJ<>@Qj?=!-Mm*WpukFPdGwEj>uj={kGu2Rn6Pz53kayb$ZS3B5xp~0cw9&|Gbi2N z8n}Y`tNBA?$ncX8&-2$+{VI-Ow+!!u2gZ~lQai5v7je4=zRB~QrJPrtBzn0vt~Ytx zelo?Srdk}F_d&yR7j3^%D6x6(9P}m=h;jM`^6rKgSdci}s?RdeazCXlSisx8qhKH| zX3B}$MT3!Mrq%A#1h-kMUXtOF65&s4mseEw3k(>W?x5di>brTl=(W)0+?j|2+96^% zzQW_QoQ!p!>a6e~Y@YCkYiuwGk$FX=0m96I+gD~Dt851fJ;k#eL#KvQvsj=hVJZGk%UT+jjB6k^A!2Z11l7r=6WH+D75}5zR(Bs5YT+ogU&Y@*1z3Vj= zaALas(`w4aeIbUB>PdY}?){ZJ1Fy@ay7PW^2!MIw-NT7$wb=;SBh#WazlK^MQ6jt& zmt^ekhtt{R8rOqIBss42XX2dy(A2xzH6id?*XI{0d=&fT3YNSf@ckz3^QgYQ68#(bA;ZMkSC)1Wf00A9MREw}rzKL|oaV;_aIXCN z#ru1@fUbe=lQq64^=s}$YK|ImB>ZTxuH)y1u8()-^EttOBuds~+s%kW?~tG%Cd}(i zA`K;aevOp_^TbZR2v-90{N2YEg1?QXDt3#SLj4W`NVC_(6xWkXTsZd2=E=WE5q*J6 zNdpbilLarSIbfcD82^hGwR12ykCD+9YF@{K@Zz+9S5}+4u(R-@ydTbjXnaGwa;{%u zJD;4)ooof>^BW~I%rO`L48-iP2c&{}U3>Czs%`xp_wLBQFSQwJ#Dt2&u_Ste&bSKk ziU&aW+S%qgX6Fr4%J!F~i1xjScVn+vHo{uA z)okZycnX|}UC6D11_1gtW(#MLd^L1aT3tu$8XbjrJnE8W$7`Yg=?z`(DgXXY(!hSyo#D26@lm+zd4JaRTZXzq|zc zAe{8&7X^KjWaq2u5LSlCy)=qsX)7(-tg1urIzgy0om$Tb3id_B^Oh+8DBeP2BGtWC$)PT@x5|!Aop_|XUUqKF{a^?`TS!G z{lku-5p>kYK94r5-`rDLB-yI`y?{uG?rW!f|IsJ&k5nyV$%rpnx_zs$fxTOk zYePwZOp^mRzj#Qy182j-(G=Wm(XFVVKZqLc^Y%u=>*4}XGI@Iq06`y}?42yaK@e73 zvq;?tfKuOdi*0KGJZU(it@&RW5n$c$VZEKpfPFt}eqxqH8hQWdYnmL_q-4Y;852lG z#CPcDye%DGEq;LK57ve;*qE3zV(yvGrX}Y49=;9gy|2l*WFH5nVXEsAiz>bQ47*L& zQ-dHvw~76(`!N+oe3;Xfg?;Ko%sShJ+O`nAVNSh%_$^hw)3iFaz}O28bra@El#}Yp z+5aQ03i3Y-|G@+Gzadf43k*ImKb!on*Eu*#g)59Nk$g%KLMJW`skO|}eKMk&BFoB8 zkjiKCOlU?6_T79*TcH0gtot}V${$Yoml7}Ebx*$ztWSD@S21!@n-arHZ+IajmX;x` z+SdlDA=-lHxFV&z3-=hG$m=}SHYV)6n^kZSO5s$9kpsP8@&U>0y3M*o~amcJ* zDk_dJf-|pF(82V`EFZoCVm3}!c^Xy5*X|a^BKT#7*@N~pjf{RG(^5n`jeHCkbQJE!E#{dc*F@j4^zQ+>w)K~qm)FRX&`0Bg4SLUjAiFFiv zaNa`V$h>EUp`YO=JoR1f9JT8V`Tx!@$?z9TwKLbJB4{dZz!>b*2lFa^V^;>F6aCO@ z{-QeE(fvvVey$daGQnVgr!2N*hVMI$UUpDVB%P55vH_jnuf7D|Sey6OVE&8c37Q+f(KBJa5#(6%oCVp{QE#Lm9!5N-yihZmr-xa8eBCYe zoK`SY^JVhC{ozo7z?oVXGm1FMPR=L<0w)YNusK;@TJlxL=A~L{4<*7ooT#xzmnf8K`>rg4_8hSh?@fKua)WK4?iRCGBx zrKoQKT^pTSZc}+KyS{onte$Vce5iJlsaJJU6N2_59ydV!_r}S}V2a%d5w(og0oPff z(1qJ<bI&2?k$&5<&`z8mYM3?S2PiKPu^$X9^9?7x)zmBdwp6UJnZ!b*^<>hqj->9`xb+0rpnWH_ptADxP@GF zS}f$}2$MX`sJ=V45n9To*1nBD8Rj|NKJ}0T@_tBq6AZ<(AJs- z7PeBQ48{70Ez*=F(qc|5y{brav1UdXql`^mp7=pSH8Axvmu>n^CD>>YiZpoTHK}ZM zyAK{Q3h&$BYJ&W%qc0^RUP{WBSXkUJipWGOsl`4=CI0FIs#nQA3FhZLotX?EEDY5D zsPof|1+_mSlmt-RBh)^_PN7eF3KIV^cb_?+PD+T|P{;}J5##@(vZ6^e1sgq{d;#q< z)b2C@o`_UqS;?%)9gVd`xR9 z=vPyjVV*k<^g`Q}Kwek~(mNyn$!2vyyO9KPCUk-quOx{&)j9h&I*$6fx-q*|t6R~K z#a4pFh>V8sKyQP3biUwf-Ooz!lm@@7+k!l0t}U4N7lxzvv z(wzDrWslZP1{+xy#CDAP`)5hXqTCfvzQMIJ?csKIBc(n_g+_=ppqqPCF6YM_O8Z)} z)TM=hdF@)u`vJ9U^aqXSqxK{)DpzxxIm0m-i>S5yX03Vc-(eSlA2#mxD~~ImUWJaRK;zE&Zh33MA1Ri%(KW3E+)}Qk04;JAdALryv>m}Clcb}hsdAjj* zGZOw{I|=j!N&hW)jPfhn)FbV=Z6tdfMi_AIb-AZI$wN)2ehW=~Ajwwrqi+U)Jb+FX z&T0yFJm$C=$vgz~*M%vBFkTsZK9S=1mKIUPfOiQzaYYS)H&k0)WkZajcV|*>$GQ6p z4;CIyWq|dwa{J2Y$IFH5VJ#uOghzvko#I)}v5G(7+K5x0j!WpAov~lNqmqNU@qcIr zIe_`~&Ym)zjoxIpf=aLEQ=iwkT7{M)jr|ohl};2mrIw`B7J=+Z`hg$ZR;@}o1#`?O zyJcfP}`x=tICDm8v+g^J0w)$1jj$ryRzG*T5RE*RuaHB zs3}-}!h9r27P340-NvsF4T4?P%BCuN^0^-i0N&6W>KR#fbxCejoDmd5bt9N-W07$t zOSbhfgOAW?B$ti*q0V-Izrxhe&Xk8$df7E&!L9$EkokZcYr*aP~kvh?3N1AkHO zafRD`j?Xk`G!tACYZLQ)Avq)}mFlowwplls-h<{{Owy|!g@px}7gqPS^&lqvPT#2+4~#~tl8Ud^Q3p3Yv25^H;-W(QTF(L*T> z*MtAU66OaKXE+{u#Lsd1lSSeu3$||51TVDM;#?myT8W0!BO^rW*WUAZ)3*`IvHFJx zRd^CLlHk73FK;|PEx!4d@z7y0WmDK*JBYjDtbXbgB8T;6D@%;GmcVr!M46k(SG?J5 zh=xSkR;3YC`Z4vJztvx^)eu`Aw%|wb$r0ob=ch;GJ%#vB?O>jvpDy=!6fjUGibShz z!Ie69?DVVhsPgrf6?W;pcoDsad6D&}#Kd$G8Td&HXDYtMAE=hBlFh4I70Udp96Xbc z`pxA;$XZeVc>(7VGAn3=lFF_ke`YzSi=~f@)<=xdFH+2GYUHO+4zq?cg#YmLwdOUI zTX296l6|lA(sPXd4O}C8%cmCf@DG(o{c@R0MH1zX=DpD%Pi-d&auPc&c_<~vZCw_# zM-yj4Rr$VL3EjikC+%o-KwNoQY#s1FNAJ%7=S|Pg;aHKQeq3YXoAA}I{c9k6Df!A6 z86|LC-JCaK2HJl%JvT|PL?Mhc4THaIxjx@!q&l9y2c>S)^ks7E8=4t6a_;O_)$cu; zuWeqDVFHrP$X`p(cRk%41_@_l|MQ8E8>N1?{}zPnd7D=RNEx`OeU@K_eSkd^ zKb0MVo8}ewuf6fTA6cdaSr}(z1}JJooU)i&VIkcd;R3_${4x#_pCwJ*w2=|w4e-WS zrXgAS$l6AJ>tWa4lbw!p@~ahFv`UPLn!c;U3WPhfjyehpSNIyaaWBx}v)=!-e+pql zW0vQm>`ioe5h7itx3%)Y?;~-nWH%({5R^)zhcxJ(OIJ(wffh#6*hu<4M|mrSQzw$_ z7q0QNJeTcN%u7ryn8hVq$6ztUFtkUM+=(M__i@ITvU?C)H`c^ENijn!N2-a#YJ+mx zl+8kSP#wa&658TVNjsMMANhEId`A+gpkh@~IkO9DpRk#E@#Z>~fj z#knIphX%)9QFPVlHYH)JuA*3_|ILlep%GzuEj^nHVq$I9@C^SSFNB$*Bf5R}u0YSjt7{J2guNw@|0s#WaBJ8bwn z!W-sV*h``4B|IboMcz$Hbj`q1WX-IU03cmj)PEYJMm@g<2 z5WDcH+jtT7Y8b{e%X7M1z;+(PO-{p)z7zcMW~Ew7*zU-~f%yT|l0ClV<@E;m6;e8a z#+e74v1Wt__8C`Ef-!GyubXw6VWL~(VIvYcJ+tmT3E&}zZq-GBeB|y!tUGggZfMfa z1ksK@ya0QSGZL}8{^pZ_)P-h37-Z>eTOUCslV_1?9!E$%uGp)#1|HL5{jst43q3V2 z0iSrH`qsPF!-H8RKgO(pf9@|%&YzclVCT!u78_m%d1r3MFN5~5V5Wy=ufJ$H8!7Sk z$u#JrypobCOjUA~(!M88kNj68(h+?UZf@9bDt?N-9ohJ(MG{WYY|3k06U590M7D(G z6Cc@Bg!L5VxjL?Oa9*)}kLc!wRhQpdg5b@Xs%042XH)gx*7~Q%L0*BbK5jSW8p)OP zkvi9{H-AbEBX>*}c3ZwwzDs^p-2R-RZ(8(p2R(SJscYg9SYJ2)ch2DlW?u9AqBYp> zhO9-m$KUEnWX9eYr`qd5T`~PO~TTGR}YGq)^&{N}6**TzK6EpZH9S z5a6!@eZI2kCCzo|^3(A?LVWP+`k*m-e0ig9;8^D_v`Wfdg#;>Po1`&6;! z>G?u^x!kidgm}$EV@@#DxP$y1s2}OcxgKxzb2EBDeMBh$DWnURavamISJ#HmOJimCbn2HrK~lwev_y9a(V+k~F7%x# zME*(8uf@$?eKkdewaduzP3Sv~nNo3)FTWPt6^OEiNHi;hFWMxR*f#UKfv)D%iSY@n z1VWEKJn*fBRC@av^D4;i;bt3;;z8d$F*qxB#NnB#21&8mN3Iz<@)HcOe8m&<<0cZLh}yx z59Oxj8bkZxz%MX7% z06t$CyI8EKNR9Wc^xi~Kw^AV9n9qu!m|k=VJ;5d~*n)m|WmYtqhAjW@0#ibfAP_CY z1b>~t)iXNdk2ovvA-rnlOW!?Zg%uA9Lm86p0@2kS4Sp&fzO$!6)253y1P>C$eN`n5 zrajII3CoKVi?&ID|2Gcd$$v=JTMLIo2_uT--{)53=u6hY}q19_)wgQp= zNZibnzU$n7RyV>9!-b-69WxQR?h3^RjD4*%(u6~YQm>AaA6?(-vW~Y$Fd_RrjfSL^ zI(2mw)OocwssL;Jpta}BqVqOwtUh8k6hk6mxZBSaw``k0MC)JlgwnU^0sQIvuGDK?mZIH@^s@t@{~GA_2j|BBfBr5+AmR9-WC=?R6O+b-T4|w$ zIdAyuNZ=us4fTXO;->(tBTv5}q@sd)yskUplh_hvRTea2cr!(c_L#Pr2C)xON+b9h z(K$vW279y(GU~Zt;8EEAtfpl=c(cPni2s~85@Ab=LfVvjGYbdAqF58=HI&BF%2mdF zZLESgrb&_JosLk5EWZ(=rmGLh2E^)N4}4elD(~H7Z=?#@HKq?|a$`E3Vd1)21Ul$9 z9dMhRM46t533&WD{3j1{aIr@&R##>`+k-q0`gC}sR)Y4t;+=~=j21T41k-&kBZ2zg zA=snCZ>q=df_dS&v}u%_qMuifW1dXqUuX^Fv0GzH}OAX`dra6w?SP!QnX+B@tCdUUt&@Q7$!B$`$rnG&B2t43DI z&18=A_zPv_yT%*v-I~k9a178hD@(?Q4tefHm{D2=5(^x8UPHzsox9;=$x4Tn$~+SP z%8sKmJwQ)TcC-5e;HTT)BKZ*`WZky({06&RyioK%Q+4};DBJRu)&<668FBlPGCx|` z@a$=Nble#!{xH9^_v+#;podVjNGbar^s8Gdkf2-YN_ju~ZY|`J+7q|Eu8STE_Vme8 zvU|QOV3>D%>c7(52a=#4-ly6d@D1MzI&{9JCsQfz>RnoI^oG9Sg6Hya3AbN=FAGj}kd+IeMNM6gO ztY9toD#Ju5(HQ$Oz?Oisz}L{cx(E8inyI98`QTSwXFe@P~BQPA&_~X z>^?FDKZwEVhnPoy5z9VRD)Zx+TpBoPc~Zg92pw~BDfuh2S*ttK^M$WE4C*=1eS2gd z_V7=yph{$pA`cD2|7@}?!*(lz6kTn=M}4epaB4JnLgaa`zpIXLRcH>>JQc}1kEl|d z^x3=z`UX$%%5PwaI&qd2DQ{~7xnZ;w-pY3gtfm{ItbavsuS1ZV%9ac2wQ;@aNA?;d z>oUX@?S;j(RL&)@O->5&KIyMlKq7FD^yZQR5hfO+E95w<#P;p` zF3*dsu4*p2!$U9p!!E-Y!vKD!B)S6lB`HSzcnZKH6!#wa1LRAxKUvYQ4Jt&aEXzGs zK4}cK*J5b_L<1Xn6Cc~8R0hERp9gynsRwjeZ&TuMp^gYkNDZN83wWAU)hWw1nn3XnY9W*WU;J8SD>g+ zZH@jV(LmnQR*%q`EhcLXrMU4COm{H$hh&GAEBuEee%;e zI*!&f!TWaR$mCf6-O?&qw_S|~^A(P-{l2GM!K2uv)Y(MFM2!7U>Sc5ef9g!V?A^B3 z7Kpe_f)Y(IF+?QT&N{`P5hiA*Wio7OYEA~B?q{@u1&PxpG!PnRmljvf@ zq?0xDT@1;lW;!eR^0kQrB}@H(WE+Hgh`jlb^y;Ykq%#Ek@dZW&fmwp?!k4sYw5D6Q z=b?mctFq$ucZ$h{4J%h7DN(UcYe8`GFjx3S?z^E40&2`84LwL_q%;jd>R#^V??UCig|$zBY8So%4%JLd3+)S?%g z7A6o`_hizE225#Q3uzOk#fwwUI#>DJ@bIu<%=5b-51y4VcL4FC`QCpGCFE()-_%Y8 z#)TYBgn>u2PbjAn${mdl^&}!g4Lpz)X3Lghw!h@ZpglB3nOb&8)<&)X&r_)rztKzk zBfBl6@TsOA@qbpY7nN~%wb93=8?F;Pp6T4*|I7WMwWcL`|EeYMV;ygD`fd&KZ&YIH zLY#sARY(BOUwqrbQn!enb4$#o4iY1PwTP!-7L1*wJWaa5*_JDtyK!5mtX}mLiA0ouo*R$3`fQ_@LkI2bX zm3))fp6I*{;HA9tYQ{$8b`4+FcponVe6_#$YAyi@b4nNDcU^kv_lACRSEqxG zh%@HeeUP6`9WlEv>Z??$}nrb;cS`yVra73!J1!ZL`CD%M}3; zEuj@{g*-ovjF)5{vehG=8q>2X`tHJ@|Cg}(l`R%IqT?V{o9ph=p;cPmpdV`#=s9+? zZmxBVUheW<7Pgyp)@Sgs8)B59pXTowZwQ{2GWon$+Pp&U7*=O3u%3ZPp{6lPvQc zqdQbd{G_57hs9WfMBJnkYS$y%*}I>MMCcQN$x#M5zw{l{Km8rRJVmr}>pS2^%$sXV zH880Xvd2a`g6vt;c_WX8@Vo4G?BNuf@hOzdn&Z3v)c(7W1$&$Eys?R=6BUGM(CF(Za2AksI5cK{fkS6uUonB z$Se#NXr!vN6r@U)HS>`O_xQ3Q#$M}eKfoM$0HLnhP;+iaOS`cFr+=7ldivMqCNU75AF-$V+r=VTR#h4<5w7`0ccm-2)? z?M8v^`;BDycit_vvAof6IPUAB5x8F{Kb$^)2=FE9|J^R0>AmcJ`gvYO-zZO_k6D6D zesEZn>2Tk{1}2bk7zFEw=2ua$p3^89EXHDEOX%d1W9j~+i-)WpVPu~hf&BoR-x;2; zbs->IfY-z0D9-v5yIWW`EWWj{%W+$2gfh#|mBNzup&k&3I7$f90w`HxGd#L#`m+Gr zqKvS5Cyw3VtgWYT!h|+!Xt%!0#NPtEEtf1>av)KL5f9*JDU~19KCm97LF0ISnQu0W z)?gzQvpT;iu!JOo1`A@cucvaBIE04bxZ^J#y2Lae{;WM<@uw}wcaj(m?0cu?6U4!+ z*dS6|y3^xI=+x-hugpkpN%hsvO#HMTe0z z(i@kw6Tp2g8$18b5a1JK3ePLPnaO_G3)IE_RbttjqaPhq<1KY9Op-kF+l2UpogaN2 zOP$7zzal_@su-J{Q5wppX=kl-OifQn>UICF=nh@MkmhSYUek;URpt7ts`DF%JaP#YF^P)wk~+62lH5abUjXgWsoV z#4?Xt;N;{1e!2Ra=F&@XZGA~Kz7#KH?_vS|$6ZaUK(deEGwX<|9g|dQWd24u{IbfY z{IVl_A+bYq$>i(Yx2@ck4?#Tsl;2W*XRG8}6-TEo;HMRJWImr!($^q`8^&jCCPkuM z<*r>BOQrEu7YU`N4MM_yuWjt7-~Tb1wZPudk|e_5Qr|U11@~;bKK&msq4e#sud{vc$P?+u2G;JXpx5(d7)a1tj-c={AO# zu%@JrpL*}<7Hm);*|Q=L*@J#OwB{e-DaiWcIM!nWB zGcl34ol0qfI1E&2G~m1SF>jEj_%CSw$DG%*qSl?<_^Q!nLl~GR6lNmq>u7JJn|er7 zsj6{qr%H-vq!58EhpNJyJ=fqLF83DCn9OWuA?Hgz*ssA$5=+77;fiu`^}Ik_Q03KZ zmq3+2`!n8$b&XVgvVG9{0xf`m)_2uVD?U|M+sQ71Y>q5YnQ+$+ne0(=pvG$P9+ z_0jyTh)+jul6aXZl{rSYF^TcB|DOL7w#L==>D>`;Mlf>7{p`e55{jmk5MVu}2BIG( zY^3ZKus>b&h#EANN-&JHShi1u;Qx{{uMN91vQ1t+NlM}fWfCP7@Q#@4mg)COuP01H z7Q_8mtAm)sU|+$7ZPtK%*_>@nj-U@f`}|mt6RiE|zj7ndZ|h);-o^_l>lcJv6)9FU zkJhUEVBI*NL&a4VK$;OFbal4Bntd+3QUK3+{v!HKrYi`QZ2GyM?v6IfaEOYKjwgrIbXE zx>sROp3p>4K0)l`qe0OAps?EV#N2ST!#tiB)vb^U?pMm!E;(KX`Hj<~9zjG!&0ut< z-fTdd7+-D0n^VPMwRqG!cK6oc#9_1gh%8!=%w;BO(fx<8t6Hf(T7@2E&6Y4H?s*CD z?nUFtG1;o;fuU}0Wo1H{XJqjCkxgK4y+00+PfUO)0e@GZC-AvYT3t!pghat_M#IVSA+%^&)>=!2~#ZD(oP$V>;yTIOK@d2E7N3zqD z0KT$?EREv3Fa4DLX|`k$hUO~wTFf~Bzl>(pA79LXp5o3Zn7`0Bj})-I!#;I`c|E(1 zp5>01(>n7Bd735Q9%SK04i4Vk_!f2uEQMj7(z&=0kdN~=`?dEDFfVoAeh&CG8g(M? z%VwTL1`lQZ^1`3_%`!vTcEXc^px>JDniZ<3m}4FgX5-NK4UNv6KelB50la^&@9Yu4 z-+5l7M5IaR5Kq(1|C3^%S^M9I>2c3W&MU|2d^UvveL~immY5~e?lN5V)c~Lu@fvyf zb=>U))m%$ie*LU4aSLe-_?zrv&U(U^8l-CJ@$Ip9*He3Oqc}J9+#>?n*(7-_)u*># z*5xhKOilrR*zz~|ER&Rb{JUK~XGBo@;Y&<9&sfJ*#%As9`a0A_BmsOuL)qq}{FC1z zgT;W)#0?%mSy2@|T7d=<;I*vaxm&oaT^5l(UYh`)8ZCBh%Kn8{e8{BO0Y6OwQXPNm zd;Fy93O`#v3=$oBMrWW*IJ;qnrwFq(!We05pf7SyWDxpbs~0*U8`2O_G-Y%vdsWpY zjbJyyn{-)754D7;DS2L9b@{;)WLeAnN4Sqg= zAix$qEM}s?sG)*QfXZ#@~^RgC>X` zX-_Xn7RZ%MeH840XB~by z^qkXJs@wl`S4e2*{{F8iXJxv}Udj1TU_zPV)cR0mzTZjQiQ@sM;>Gxb#;j;XishRd zB_gxpI;efrgck6MWG1uYh?d~`Ej(Z(vGXJ2Q z5faOHy!0DmcIWY`b(UBGP+|fH@C86EXeaU8NoyvWYgRjnOa^|{c&Vy2D$qYYmG59E zyK65W_$=2Vs|gK0v1WLmm8dJ+@~}c5T)p@!)7m(8y)A zqC}h|Yg_dVpqJf$F(sQ|0kdC~BNtKvMGfG7G)9YvztJL_h-_?+nH^QX;soi&E-dt3 z8z>jDv(1>-6b#}kVR~!IhG;?J!DKjg{C@mGLghK;ZaX19j;e1p`AVnrMUlR=?GGV4 z443I`W2|P=w8Cx)xXk08UwVH5cN;UPa<$|G9^`k}V$J-LmE?{2jJX9)6NH-0qA^~f z^%D(Kh?_auGQ$dLHI~69Q*eEX_x%J+t!jY#O#Jkw17cGs8K~$ zx#;0MDbD}gkBy7^k}-+_q*`ylZ$D2axyWfS;2SDhnb;J;5EWcrz@v>nC%i<3i{o> z(~P69r-fcd5>7yQtAj=-4(hskSFt$DiL+vnac6LVjm_#@uqWZ6dqUzD<1&! zsaa`+3M;jVxul8wWGNnenojIEpy!2BA@8QAWeH*PNv|2Xyqf~WB59-pXRD?AuWB-m*b2Mp3KK_IrUX*n$5=0-~)b)XrHUIM@2!_ zZc0THfn#t@s!!Pr>1**^n0|7o%go-(NiYXWvE)uYv zMgadqbWgO&(FuD4@&*J-!O*@yEAw0MR#sagB&)_Jj!eS1ZyLASTBq`ddC@<{)Y$+J zEQ5V$lY|Yj%M}5>Mze`{SrBGI{|pVHvzd&3V`@2}tYdm*1LV=13pch0{A*Um-+%6f zk?z#?#vh2$2YwJjcn9D+EPnQ@+aEx9Q7^7Z7BDVAgrsdDQ3_;Bz`tr}Zb?9i{$PJ1 z(~$^_$H;x7~V_NTN8I50Pmkkqepp>PY`Od1)?@84u3%0pX86La{c;;DzDb(Udf`R zW_e6_$Gtzshk3?h7BvND;oXYLhVO{r{{#7xVX6kHMtQU+J8l3zOW*pVpFO$o`eNXlmLl?eZc0?^)T&URWJ?2h%o6+a#h2|Gc|NLKuzU1eI*Sxn8VE+r?gYyWU zR#{Qh-@1&3EYil*?Wdrqjr^J&=7BfGT_R=)J<}bg05^KHVb>+@{>wUOaFVd|#fclY zFTAuTaZi}NX)65nJdgF=y#^D%o)+D#-%s%G3m9m+Jo{LXp-iA%8_%SIy z-$j)owFb#F^RqNN*dLXv{ul)hDFp)W({jybUEzW-*La9+McMku4#bJ?( zMsKH;&)!@z?Ws%ty*CKxY0I8!<#x)$=kH(Q=UUdrz`I|SMBCDhl5-{t%Xeq-_z!S7 z8;*6@e>4e?s?LM;KAJ?2;%O+mt5J>ec|W0cKOE5ixF4&V-Nwo3?-3I7c2sXx9E3HP}_%Q4*z0!!cysTnrvYPw=J|ejD$U!)}_^RLd;Z z=@I`!g+vUJW$P(yJyT{I`i^7?;=FB%DM1Qjuyq9F;rzs7^>WqB6>nC3y%ByG>JHt7 zI-K{t_+Hn-yE*ShN8X)>tdbAU@T<8FTaxx3V7+Gne2*znPQ$Cf_AA85 zyudt()%*%EQAQc;N4NwHHDe3R7U)~@AIfBqOyroJw<|M~Rzbc%syZbf$_ybjCJMiY z2)Z;fZLFqIH}0a=5Mf$n5OKiFP2{ZWl^~sY%WRs2mJk#^y2Ao^Tvo-ODZhdHfHb!w z8f;lv%UvFUG67;y;UJiQNe*$V5pMJ5V0{F4;p`78g#3;;=;obK5E*y;?XSQ8il>9T zHQBim_~k8v2B%=+y}=h;FMD%^>`$gaBM0<~?3!~p#G>GQRBge6&eDr3q4>Rc|1IYj zk&I}`cY-dZcX!qMOG*Q{Z*fN=_K=N`m0rFlt2YHt9jWxi%L*G&50m425S)pae~Q29 zZ1o;}IjS63RGMp_TRG>5cG0~6@c11+D3v99_gocm6V}U{rdJr)0Y3^g^BT%Hbt%`)Qw=f^5IZ{pA5R%=oq42xQNmswLcKD9 z|K{*2nnIK`Zan$ZwOfpGeY8Iv8_HDrn!GtJ#y(NT^*Otm2bem{wGHHniAPl^j6ThR zq~^~p<8XqwJ&S34IZ2}C(SYq~v>IK&JwS1NG^J)-0bB7(z%l_I1j1`ZNM~QlhuV<7 zHi-wFEntULzxQ5E8eq&JOT=A0ERxG5gRaK>{GXv&uLSMfG5w3O3Y^cxC`sy{@lf|i zIHbF`n6{kDNjBLX^0WHeFiGF?4(r#$2+WU*^RZ1MJ0 zvO_78G{C)pHn={o9&A^P$e1 zU;z99Xa)S_;FG3^)@GRj3cw4vV4kF@%llsN3&-0-*hqXP15Ix_6p+`ra(7uM3gj^9 zHZ)yVk!AAOWza|ea0G)t2sL|~TvdDFxei(MDADOdBt;d%ZljMUdxRnbOEMj0a*#Nizt5ptR* zT}%lMYTtqi^bVf>R~=z<8OHuiGC2|1vPCv9d3~pbW3fXQHWg zgFy1Yw_CZA0{n%vU-Rbm9wkx7kCGrU{F%}WBc~+&v0!cVP23YlG!*)e&ZflyQ`BnN zx5dnMwvqWuKiksFPxDD_r=q-|=lvPCM*eF8U|5nmhEU<1j_ zBFNry=$jrcoaG2JPgV5s$~!uXW;LbXefbgTZBY02OIblmrdb6>c_5B4dyQK*C@NX( z`g=?1V|}$JmM7tA?S0O?PHVno=yd&GFaf*qWl1#I;F`BY(xbKVH=vKXPEM(uDPq5{ z>yY+BIAV_9xLdLjdu6M%3TwoD4fYd)W^QMy-6cvJ3<-|}=e?Go2@mdLC1o6}GVrg& z!}YD4#i`}4b9c9GNCE!Pulj7RzE-z(@_}j$Od$OaKQrpJv95P)sz>V~?O`7Ov-)F@ zuQi|bF4O_^YbI_R(I1>fUBO_;tMk#p;`Z7Rb&4}2<-uAi9ACMM=AE41|0U+^VXku6 zdL10_6aEHcoxrd2Ou_H%>s`Q)ciT&G?jq1HC@)OEQV0G(|25;uPGx~Z8M!KVb*o+4 zf#eZw<#7^iDql_Mtrdte#>n(OlLx)Jp&pM!D*|kAw}H2%#Z=lfR9zU|z0Grhw-qYeTx+ude=Y0$f|pwhyv9{tWFcgQj}87$j&eTL`<)59$8t zq#Kh|SJw3-d5sizeu^2))IZG5^q!LYm7f(uLSgqqQ%NgTUz&Ea1ns{|8DAbyPt~9X zNv7`#hSHA@>z*k5?#D`C+~q)`@Q z7iC8Ye?4$QLT#e-)&=)t|6>-`n64Pr<=q$VOT1b0KB?C`VPorg8zI8A`>!9(ht}1v zC@_(mDO0`BE&s6FhZsZNKM{I9>YueRqeT|stw@FfiX$=v2ktY^OpNZbfBIVx#3CMAY0T8RBBjB*M!nn z!*1Pg)GNpMXL5b-To=n~JT+wOFnPJ|#lRDT;DT-)4(zu*FxP>%U!Hhph4J{``W4?f z6eGJeWgqZQ!G!QUm_B|A?wXcVDD(;D2=S!2*ui~n!SvV`tyS3^$b#3ONq_CGn}`U+ zF)aT9eu)(K8E5s}>rV?GwYknjqKo(TRBXsLEC_csmp3ao@;oav`Q`n0>la%pl}l6| zd4j02lEw6E1*E&`cSyl#EmJR?5X^h?Sr5*|?*sGoX(iI+LG{wYwQG;1@Y_J%HdCK+ zaB#_&PByl+ct63f4PvS-N}c2eN;RxiuL&J)Ad~{w$X?EqU7m&>?*w>>0oT5w?=|+} z+pJ|G=#Jc)u6g-vY1$>vOa4?U973sQdW60*@JtG%6Pm^O0(?uXNx*=FuMeBO`>oW# z1g}gU!OePcFZ*uR9jXt&|D{PZx{ZN_T7JSOPJ*K>ION6k z^NLDF{$l+0P7HQG>WYMWSkvlBHq`Aopmx{+Hgaz(J3{xgp0K+Kcy(kXE^4TGEwPyP zg11AG-J-!Jd&5}On6$tGD8mM+MHgv4iCBpGheI$BcCBWTTy|>6%`&PqOkBc%eo(j4 zYtL=Wyy59=`|B`4#^}xy5f>#L%H5jM=YzJ;EL?}i7)edif~iay3WMq4Tg;xSQST#v zX1}Je>;H~1YDu>t>`;jAB=qCTPS9M53vltVG1&E9?fb$u^$Kx&5!2YN5BH_={v0_Q z;{l$3BHAThLMqIBlaz?_hS)~^Fg@zi4rb6F zIZv<6$gKY9Tp11KqtQ&p5dBFyCw9Lx(^?>Up30;TDz=6_9CyjZZKCV+_q`k5r;Zs{ z+5WOcycIaCbqS%x~oKzTdrCnzAHItD^J8l2TF7*!=6G%iI1bi9)nW~2T&(_Hb zQjA2^&G)Il@)Nou{H0R08|iM#GmNzegxC0)C?ja8FHv8Zf2xZh_Ud!gZj_1wNB zFnSdKn!da4{0A1*LPjkV%KhH&5jtp+b23Ov4UPB|$g!mWeWiMl%Vyi76vHaezw{FD zDIU?SifM`oN&$c$UHzi7*%i|~@A27VMpDR4#g~S@rt4r09&Y=IVZ;m;kwc;eOe&^t z*!Dr4~lEjH=EpN{v*qR|Hp=FLM?HRVk~Ud(^;{HS5$oHO^y+d*IX8sBeD zD<#45E_X@Jv&wZ#7$a$|PdotMqoHJYD@ZIM6vgjS$6tm&rn_M|p0>;4!Lm!_fK&-X ztg&V0oKa{yHh9dKmZD(zqIfodq^}|%CUm_&X7}{LmqznyOxidJ9h2BriUaep6tT=t z7w{30S^8GMuchE%V!kQ?@(y^`HiP^0T(_7D6RBZe*Z3b577{&nz>HQYP6D#00ACqv zK-+<~aIJ_)lhndOM0XhK%;oQ`Q{9hy+p?9!xyPSsM)fEvnj*uLD79X$4ikLEYiuO7 zB{&CR^D`MO(Ei%Ggv`Li$Xjlho&fnP<#rq-8F?#_Z=a*lr6G{!B@Av-hdFTDl|Nq|R?C$KG+1Z)d+1<~UT#R4L0dA5s2|vKa z{FQ!Xu%jISps5LX2mk=C0Vs%Q0K^1{oDd;IH~zxc32+7xGvEp#CnCgrB9{NaWkmM@ zB!BUQT0|m$>2DCq)kLCy;oAhbllbOe8e#&5i1^n3(1$w$NdMLa08s6D05Ssn2LK>F zB;I;HxcwuF%XhyD&E;Idt!FarPkU;mL)L|FJM(ZA^lEq^8c$Ew!=UrGLf zuL8ba`3EK~`_AxhdP1+|{EH9KWjE&hOD}?h5MbiJI$k66$j{t=_!IEC#Q&~)36uO6 zO!|+Uq&>M;{*C7(&|Lj5nt$}YtovX3=KiDaC7%4h=qdjV6XeXj^Vj}&3GxiBvjHx> z@>0xy0>NX6_z4A8LL~p+dBXn=N1!E0c-c3%_y{&#CBW?eDicxwfZz@PDa#UI^8fKl z0DywfJjq`XE(gq|u`Zoin9v)SM!6L2GF~eD4@^UVE@=UTUS)zyDGZcn}7aDZuvP9Ka3m2LuAB0UiLrV-;d@qTc|XYd4hU002FGB7sYh z|KGx6Wm0ayI6?ncSFQrch`a!R+k`-JId0|vfID{xjoiP)8oeagVgx`lq6D~T-2?2A zDgn-($piWf&j2q}X#g+w1^^h60YHoRe>L<(pHLtsS|nN^nj@MengsxqUjhgzf#<(` zll0Qs|E}P*3L#u)JDQuSV~;xjjg1l@c$AnYrS+Q%pYo+0AW!H0vBom zTYAFpUo1%ecK+p*bU7_vhD(S0>nenl0#HgwDK8NKs=p~Up#3r@`a4Pf9X~gS-VqSj zX$jrUOQ=EfuUv{ia9RFAV0xJo5&ZQsw;(ionMV`qT;{!m;dq%ZvHUA1p#ML^#U$V< z!Csf)G9|tYq$HQ=6;e_X(yLdlUi}-%u3x)KcAf0%)oT>ju9IH|0z^qcPI+0lWb&`7 z#8*g2u8@;mCHqVAf0SIb0BElPS_lJ#=q7-emWYIw=%O9KacQ5w;{C^a2@@#km8)a~ zF(?QGaKeH^L_$Ik`U*i}0(Ic!1WQVL<>paAP9C0K z-acSozYiZn!@?sXqacY%$tkIyzodQ7$<50zC@dW}|Ddv)6$a}*ii&(;aE@&`Kl6gUok$u};z z`_jr|z+HCkCQ(Na|JfCc1Y*nB$9G;ST8T-pswP0rzA!NSRbwnOdiR*&t>n*DNRS=m zUjEZchqnN?c%CdbJ*J)cHAU0)>7=(O`KZxz0gn}1Q#zUZ1$1Lec>4{@NTsrnSMh@+;XNc%iUlM&9KtP+1uNS_rZKmu^*e*>V~+6KMq=~)rFR* zND2)P6s61wVwPE-&p&S?S#J0Rg!`Dv-lu=FLP6&Qu%R4c_bHyB03O#L(^JAC=e!q^HRG>Ka z^TH^d7SRovJv!H%6}dhaTMpY(VYG z&kxmZ)Mom~+LJADKVNz0F^hq9Thibs@!Z&*4k_&0AOL2j5gojFfB)AOlM7Gw)<$<- zl*G-uX$qmQSb6?*XkDihM%Sq4P0Oh!s{u>)*AQQnEbNP zNgQwByr{B+>yngLHX-hw(s+v^QO*kV_e2v!Sdz zw7F zX@}3!&Sf74#Elw*oY5HkH@4WBLX`Dmx%0H+J7UFE{@$A^Z@ThD;`lgToPWo&;%4V9 z+vCHeEbh{-Yn4@7T~yyi-XS5TE-JRN1S2u$q%UZ4xjd*I$FuzH-Ca@t7V zZcxCiwP((J>^=7DV1Q&b&8V%B9YllkXlM3J@zFCg?{o7kjv2cN@p=2Guvm@s9bnxp zMS3~-%|K)<7IEk>v~%`E*S}YKamb6eduoe(i?aRTO}g3hn(B%Q0dapBcT#NQ+G-MiCptcQA z|4ppkDM1;uXdN|iqA3RU2{Z`t!~=`urh<0Wq}0nrh?Nb@wsc>Gz9n|0 z_5%BG_qfVeWfT=Tf@}FajLnV3>wp!~j_6{&@AD5V&OMvws#Kf6PP@Q9slHSlL|wAY zq!{WKlMY5oh&`Nhj1k6?*sg?01M795v_EX!15Wd5CX{-fcBc2%CQ=H|M@-em*mKO> z8ADJ~{@5SV4%%(j)+%`BHdt6?(4j ziB}A3nlM=j{+<^As}vzol~;pb=wH-r6CK@O;dDJ6UJ1{zY=E1?kV}jDA_+w-UAL?S1G*4;d7jld$ zY#h&KKl3>+5C@?w55L!Bt#rulnc|F|IxPNy17qd1epJ`R^mBWks(;~%c2ah2dg=Rk z4s*qis^IKq|LoIYxPX!JdG5hCH?#nE)xa%t$>`e^v(K!b^;CU~_)N+Imh-R_N^`At zi)-1suASAVwc9{91T^J!iS}QAI4ojtzw=K(ZyzwPE<4fkHvS$?$pTAXxiHBWc*Pbg zkO^OIEdkUEUiR*EW30iE)|ACp>RhQFmCTJ8H?tZOkyk$K zMht=akoAL&V^+^~^{TWe5bU5tYVkaOUj-*JZ~5`zAICbu1o0tonXQ^2_lS@Uce<|r zYkHJh{DUYfV=3Y|C7Bx}_f%bt8^*m10p&fyM6`MeZncpL zfd26q1oiw@4a~FU!Zj08Wp0C86$QJ+dif$a#sS`1gWmV(569 z^;8u5H&DRfv7Yg9$nx`#QHiSezPJG2(y_cx{Qeg0kKB9Qsfp&dd! zA{R$tljRK!$x*4yKz{?n>s%g`2*F{=nRQJC%?D9QYkMZUXTc=%*`Z3r&?ez^W~I>m zX+b_?L9gz6HD4g6pR3Y;?>@v!!+_0JeU79u^RIsuSRgi=2h96ag4RHq$B1eaVJ%LmQR zu_{`})H;jFwx5b&eMga3B4F-$53DLCJoKDO7Nf?}Os7j`)XXiFk5N;`@f97KBOlFA z9EIv<8kNbT4IOH~3USf&@|;elhcP=+`w&^jot>2IF{L%vJQ){3jc| zn%Aw@`BA_p+&t31F93}YrTNxlS6^P%;DciA8|x@9bQ{R^^Q(o4I%&MwLWVUK<`l0t z>{It6jp1ix6VHrLco}yMgeB7E)ApybVZEa;UotL#2(TWYp(Tvze>A^;q`Tlg&}VJO|V4?Mykcz?*cGxjvuZbEN_6I9qsrLb|Su!HUEBCynF*BPLPXha%93SUTTs z?&>;@2WQXgkPRGYXYJM?mZ9p&*%?q(rFkDbXT1x?r1Z7y16t|l^qb)5YM(l<4HJ`B zDYZ3UwkbjUL9dCqTd6fz?X9L`ZYcX2e{>wJ%wPBo15QGl#kx9-+R*%K!={s}Ya>Ag zBNqUwCK(3{UaWT>mRc#C!|D$R{@lWurfpBUQqMzBr0yBF-WRQwHkNo^2QrrVf)4?{ z9+)xJ)cNp^Ns3?FIa&Qm6poTmufSV;zn+5)+1lxK_DEvO3O>09>54n*S2<}mvQ@+L z&IY|F4&VbkRkeKg^S+x05XSa^8OE}GV>Vnih9Co;9mn~y4k=(&8~u2=xte|#Mf|I@ zbg-FLPASO;=1`7+f`#5m%(8Gdj^>|))(mHknnDH9qI8g9&<^WfJ9bJ(1jgsy!_}gJ929FgZE4ErzM%3< zpx6AX>mMSmp1)@|`u%G0<~6VV4qu@tyomYa^tzv60@yRSd<|o9^~=wlxW{3%Wm0TQ zYLoB#$-Wl-wDMRde_0S76R@sP@kaN*JmE4txsw+Kt|PGH|bhO z4p%yJS3}fv;=+|uZ||P<7$hZ|>Vwi}>eROf^3pc94`r5^2NPXRjA6qk9t#6oZ1a}P z;BDy!jd?x=$xeY=Th-;=#QibIE)WxcKQ)ss#}l)itBc5KHqoMawG*7;1IZ3bQ` zUCX|9K!b3q<4&Ib9CmWM5?(jV>>+NOO4RgLnO&o|CWctpW2ng*j@$Bgbg2_KuU_zd zTf|euI%#Lyy)G-vp1g_dc`!NAX=s8P(>&B2DR7DFPE(;Pt(q*_KMDy5{5lOThaIq_ z$CFEB9wm!8;?7sToHo`!jTibtm(9qczmU~LV~>VDQMm7fVu+|x6JI-w4t#QSE35*%MHHHWnB}NWQu~p)=no$Y4jN-h2v)D z@2!zXaui0!?Z zVA+`AKh=S#aY?zKF;B8L5aH3z^s$yAL46z; zgo{=>6c?=LD6n-9n!wB^!EVI(>4(mzxUWQ?z;`m{NDQK1@zbeWp!0L2yC-DS?!$SK zw-17V!~J>J)gP!8OaxnEy4GQvvkSAg14xJD)pAx5m71h}furB6-F}5yENszsd}?em zT+9lA^z6*vPVrOuc4%(W&vEkw$`(ypE*rqfTRjKqH>ilP1m zBGqv^EuZZm=>wT}kmw{kQmsZ!hv820{3H53gL})q&WtM>zI}z0lWyy(qd+_FxtTwEV zX~SR;Ep9_kYPU0GhGA4bUf|d3ZlJrz!PM7yKTP)Zpz%?51#Dw{8s@{wI{wKC&;NF-aM%;~HKH5bQ*`Fsea2^a0Wco=UIB4@ z7W5*J#E3uNOD_gi{!0w!Gb2yF+QvFQ=``MqeB{k>jz5z& zjldV|&e`o9{B_W`GY4P1V!&-o)D-Y#QEIydRWq2bM~Mt6<#kY#Pg%X1?vJ(}898z4_`AhIST> z;P4sdQE}<2^GB0TenHdIq~F|=VSAKf`oBTb0N1K!`UpOvso{Bty&5{HF3oV5Xq3wRjOP#ec;BoPEl0tWH1aAR`U8c`94b~J#bzv31v<{Npwcyn&vo5(Jcg~^M2}kT1WEKy z;(L`S@T{G6qwvD%NqFY4kCdW$L$G7o^oIu1f z`i3#I^HZApO}^C&8_fmBSzZeXSUxmwyKjTn0+ouh?uJg?slg(0nyH}f%>|$#p_X4O zFwm_92EsJPN1r{#AbW6@r*epx&phicht zWkRacg#=+|@g^x&Ce=yKw^PlPZgn`z(+Z4yOfWOAP&@oW8nJ5qx_PE`Q@YQ)nc-mI z1~8b(2Dv}`;Uq5uMc3dL)R?a-$z5J&pvUjQC+s}&`pKN+jJC;v`(~$EkzsUmaj+83 z+R1p>`u@+{Koh^f$xMzG)j@fpua>-#Z<-t7jtoo4&GxqH>vtAe_XkY2s%8OT>lZj* zNhoz%HG<;0+P%=h1@5H7#UCVzS>bd$U34E}n#BDmZmHxCS^9}Epvl6{6s~mU&5wVr z`|;(AGwQJ4p>s2XZ?Iyp7q zZe3K=L_PyeW&N)7y4@?VqCpA187gRoZEg>gG8z?-*YJ@{<%U8VyT%3W9hv!@2W5J` zFG$PZSJ0Z7DDztqZv>&~psA7YyZj>G;ubbB6(yU~M`4?+HiFw3ls>SG4r{E}K?$22 zU$ZvOpF_-43~G#!Q|4Rq39i>ae`~X@gaz-gJR4~Mpr8%jyHi`$R#$r7(^6V<%y1`d z{o1hj<~efUwZq7vMedtp3_+K!C@d#e00k5k{L%6N|(W2cTa*+|Psi$AJ5 zP3Q{Eu(s9dR<55mzW}5jC(Dyyqvv@e6&YrlKQ`MG&W%(B%JmL|wm^bQ4)zXDGM~(t zZ?H;H{_3(8WNcEqD>H^mm>+N8_RT%h!3oXpTy?yskzYL=AZKkY-s^GGxpT39B>hbV z#MIl=buDT=Cp1>QKiBL#dUG>PV`w%RrSL`P5OQR>aQ^CeF+g=}pM$k)fhC)2%J|hf z_Bh3LeElW~Y4a}9Woe^^t&RV>^=#UHJcGk53tl`xJr2l5@-qBI6q*96{S$u=pXUc;Er*sB2Rj7})O@92Grq7$ z`QSO|6Wd07KXCG!Y)4_0Q_!F{dFgxA9`ARoA&*I|W6+K1x;Q=O1=I8`6T*QRS@`m> zczQ?ksDuF7t5bn?TH%+UY3qqe-<7<+0bqOR_@{U3Yo1Z6{^rQZ+Tg6ln$NOEiIzwM zC?A#6a}dbaaS|S;XE<~wk{?l4Mv9qq5~~t>Rdt`}W@$PJ1LfCd8S)xH%-FCcz(t-5 zv`~YK%;)a>TJ17gzRvp_0f#3~e%7$Qn;ep{&n8UG*E%3(rednoD^Tztf4i~ccH(rw zc2(LMkWs(W;G58l%GhKX-u_n;MYQ-Sj|FvhL4;g07>4W!Q;fd9i7PxslF@ zI06w`@z_AwV|US=dZEi>ayt*x944>C4tFe@YUB6_-;k&tf>t#?B4c8-Pte}c>31cE zG}d&2M%C_-YfMa_O`wj?AKA^vhP;_}WQm ziOOTWOl0QGaqzgKFm4w~q&v@qyf%B`x{u1iCUxEJSi+njFrGak*7 zitk(zSLDFh9vs$xU#k>3HCTdWHp{=3<#~Fi-?z2T|ELW{SZdHaacRisnDgw2Jb2Xh zv`tFaQ^;GfTDc6BZ*NpxA9Kw;%<||6*cV~%$CMbWb@}=ufF)H%+B~-E)jYZlprDgk zjJ!b*dn;O-c5)o-fi_G9ftEqT;?O5E%1vIoNmoUak8> z4=5t^Zbj5KrW5Ius7ldI{*G;ot6XkZM|z5`M!M{`KtQZjrmC)@$!ySCGjgaCwlQ)K z678s1a!`E%5L?)}6(EIuFuW|)M3b_+zB{d)Sw$U&el~g9oy-)@7x#sUJ-?}K(6c#a zh5>8iH`eA=TY=?4d+`-|zOqT#rjG_n#|{^Vkzbt?`y{hrhWUM8J-TYae?lO&@Az0q zPaUEIi6(rJjPCS|BKxx0!5Z@cX{?e#cd>LGXD1S!MQrf=RA0~+lvV6$PkG>1>Z`&c z8llP^jx~vxhS!T*?y>3pNEoc2d)GP z!hM1jnzx3mbqmCt9nXVJv%X+d7w`!f*dwI#63$YvRtgnSK6LP2K6_%f!VQv8qDX!1 zNpSp7$r&R?t8w1^j#Ds$Py=siD<~k=-WAo+SC&`MOZ!{rEZsVpwa{+A<@f>+)=(Ne z90)1sFYvWco5aGK4xlmR>_d7D$yG?r;M=8sVenIp=?z82F(EHL;|3w$Qk9ZIuubl# z$D}{+L=#6|;|{3IaC_GV0PuAwa^QP7;ImsFu)64-xs0@>kra5~;g-g!)BQOF!YC&4T>{ znqdo22SL5qfxSYl*)|Q8DXyz;eR|AAS!5#lzD6?7?|7;aHx7Q34iiLKf$xHmP4pUq0SPslis|-)80gKEs)D zKyzTD>yK-ban}6C!`fH_o3zU5rZts8jU|stl2jN}C3p5-?DPd7{k!4F*>+sL>fp9` zpx{7jnz*Pc2uN!hDS6g^0ch_UPBiDPqRF3n;arwWC44-QbByr0Gd8XD$~#r?+(+@d zPHTxe%fT%Eda_r}S`o`*G@E#px{=*P+_K`r33&^O_paqbP4GNbiMovvUTlCD@os{*fu?a7)MR|4ch2Xt{Yid z`vOUJ^B}0eVysQ~ai`dT<+n1EVJE94JA1?H{*_Z35)^E59d*FcPb&RbQ4N;+_w=p8 zR1QdQ@-b+Qa#yAIMQTmg5^iacvC95p14Sx2BOHlJRRgW*u&X#^CkN@7ghGFK4&h_P zTENPMp)Wi<(nNJpTJ`7L(Y;0_o#`>c3Z5_ycy@S&ysV{ zH6?M}e9QG{1e7C2FyrisBb;P_hn$l^;~ zys|?z?LpsX+k7Q!?fRA;%RMt>@G&orD$|LkKc@L(M3#5vf?Gka#LWz$)vADfCemA* zE_5#}tz|@xw}sh-UAx?5u0gjfMNG+bGZgtGvn>@%fSKTcAvj|pS6OlyBsf?fALPfL zhb8N1CiM)y2Ci&xW^JIE>lT1tQ zBRB8UIY0#*I`d6i?$U-=QgAn=yB&hPSfFL`cV3XX(%sG{TB@we4>Jmh%HvlNO758H zu7~Gm`n}yjd>c`TLU?FUgz^4{7HQjO*S9jCYKBnU6MoFw|5WZFQ>QaNK-3W@~6lbiKEFL+_m+_I5&2 zD_2Hpm*P3!+Wp|y!%2;qGICCtOFO2B#vFW^w>C}kPpzAp#+E9x`H1SKIu0%TOcn!< z19NJK5$c!?MLOWQn^1@NhZz6W^6uP*Rh5a~FWHTbp^g3eF4G~z5sD@xP zN5qi1lcHE?PW9kE8Bn^XaGE27i^*Y(qJ_K-#Moya)i&5}i(N{3v$Ml1IbCmAa+<19 z+1)Om@A+m#L!(&xU<2ouIToqpB*botE72IS#q@=T9PqJhorFctIjsDMTUA*nHhW6> z{;{$$ClhLFF=J(^(ON69ZAJ#7O$SCA4!We+R-Up|CG>KBi}(2)qN@>5>=BbilgNs=6;9RPM||HKQL6uQ;&FID*t~_{c+uJ)JsX8ul`1*H z1NZ{n(6B8kpb27R27S1`)HT5WTSvp*<7z)T$Y75A*uK*b@gabMEi*4Qj=hx>J76nV zth+9k?s%;|-R-n3gPy1&_cg1q#DF%<7S}fhFf?nxrFwQ{-J`gd_JOiy&3>K6PQ}@z zT(lC06GAf4;=>^iJWMtFlzeun@m&)~fokg}S%9I>J1-gvoRo1@eI>TR@1U_A$>qar zkP*r9%Z9S_uAN2Jo%=O1SWO&^{fjQyIV3r`h7fd&wPFQ|CuQsZxR z(#t|&QP6`|`)Vng8c(y^c37n(zrQip)t!*BeCgxKw&9~ci01@vv>ibvoa&M@8~fUY z)tXN3TT{yHWtQ*0`@*kE`c7_?!=&h;SneXk&m1OzGz>~hP@SlCO2w0mjp12~778U) z-b+wy8KPUOMsBGVOsXvlE0YotOR7;o#CATI{k9NKZ=2zwZ@M#hpwTC#aoz#lQ|0b} zwlN+taj5`g!{jOVI{4+?XuoGV`olC1G@65Pq^iaqnkGUO+L*?{eMG3@Z6`(6QLZWw zy=Ebw=`>5M;>pN->Q?>L?&~_04ycz9lKV0wg|I6>a2xqxP#1%JC#vfEx->duFc*8T zE!mFSxJKGtWweC}08s%>w+H&LFZucAHhTO~}@2O6GtQ z))uKbKdoejOULBT8a0Fsw;V#Iqbmwx?(B`Qg<;>dICpbpd01$Juu{6!SWih>)e@&M zP0#X4)KllfilP3RJ?a)ZBbS|Yia3uyhFL$8<+=1emhC$|*j*d#89e}1uTZ^EysI{> zfpxk7$Yo_>FeW%5lX#s!;-(VjCL9$`793-xL-T^pzh3{iuFqEBGq9U+*p03e&~C~3 zS(kb!0qd~h5_$e|S`FCP*FUwz^2*t4F4zJXPnI5NQK7$?T2j4ZYB4Jp#7okt5A`oO zfi@tg{W54`;O94oG-6p=EbhUPUQi{itGoT(XQ!rM+9p%m)5nH)je|#u78I^`=-|dM zIh`{!_+KO9Y&^j?x=J*z0Yi~1$xy*aeqDKcxp%E^uSoNfWE}Dj8Kf_oIA4|ghOKT;pg~ih;@%LR?MxxH<7Qbn`dsx%xwBPH#h(NEXiwpA4!3^BuDu{8 zM(GVbe9tUNkkpykSZSihP!1yVAVRn6nXbUs=9<{H#5=YEHz+A2$&?l1RQs*5bVVc4 z(U8I>I*NV2q2rW`1x)S*2wIqz0MxMu&TcpJ5jimg*-TMm^h@l28O z_)_`Mq_`mIF=|gF38uohNU|qU&Z?~IzLL%F3ECYr%~`Djj-N1pv16csnsZKoqAEB0f6E9h;+?j6)u(ii2wDCryI4u784 zG#F#0=BKmUny;OSXj7L$xzy~vtZ&B^PoM-ipYI_uCnv-1Z1JV7WO$^FM>%=1qPLnN z1tiBzC&bjW;@Cg5KY2Lm$P(OAzwTwEgqX>h7oyX&p99@mdxJ*pF&LS!e{MIMWa#?D zz6cQ&oj|qB5gqL|6XAGO|G;6iGbX-NUY;rWBKFXUV`q>E?=A7D$J2iTazYK?fJfb`-xh_l4bz2hvuoIY6>mGg;uX2K9@Hla|stG znjJ5zK6j$8y;dW2zqYQBAxJ`|O14>4qGAXis++Y-!F!*6xc=Gj=d^xPUE@rn>}sGb z*7WcbCcl^`{_!yM>?zv_Ec?8uaFvJR*P^Y!y?bK5ADz#KZk@@8H%1Bdb;W!GV*_zhN--JbR{DQ&XR@ zyy=78KL;gBMC*!oF>ftIht3{EArVieD{ z+H|XFxC%x|IG(lCH-77-skl4aFQQ^|B|%>hUBJs#fJK^UM+Nv4OglDl3|d>3Pg=fELK5HK{iz&B z2R{BmW0sgPmXo@8+~j5=T6k>D#-d%2jP%*rm2(w$1xE#w)sJZiHJr2^2H|K3k6zv@ zQou1j@Ao^ZD-eW(D?Z!&PkY_+pBk?M>zmVUi{j{y%ikm5K_%hen8kC{(+1h|WIfkZ zQ^J6457YTdb+BL0%e0t!g*1gyYEIFXjh-d4X3)lf$5+CR zw;s#024^$og7n{tXftCgY}w5N%Z15}8DNS>M`?WplFDJfNq=<8W&8kjN$kJc?7a1j z!-|e&XSP7MGN321fN}oy*Q^sDvTR_F4Q4X%CLpq>xZtE(=ZqIoTX+T(Z1lft^_El; zpWT&)j^mgd3>ajK^MAz@%jqqFK2`_a`2oi? zK-MZysC3gJ`ro6udJA6)`9e0HCyb4TD)b^?G%zi=_@`&T5M9M{}hM_s?*@67Xa zLsjLSww;|pO}Qf%AhR{o-1Lq}|LwS|*P*t_$RfI4O_K*s2n)hvSM%q|>f=!X!zf3E zU~Qo%P45eZqLzowG||l*W@|$OLKA1JL9doRW--%Gxgq|<&>|I_5YgfT6w@0fD&>Ti zONfZl<#Ch#o)bCnCJ$u|wdD5e$E2QzbAzn%7;KdhKL`=LF}z&z5R$xzx;&0%oTWcg z!WKM=TX>4IU#M52b=-T7ot_0ky4d=&7M4Jq2`MBB{s}uq0owUa?rRAf%us0dcp3Cr zcOfv9=;(RBYRb23wk|3%t>bE&eA;aQCzG6f0j!%L`X3{{^tMMqO2i zHl@=pe;R^t;#Q?p9#W#QSL};z+LqRpCap$hxS79__x!Qe!M&Vjc&0Re7TvZWGHkxq z(`!1-$9MRAuj@SF?1kd34GU&$7}O;|so*?WK#->~488n7-^B+}t4sW7KH2*r=JCS4 zy4jln53xa+3v3K6b+*g!BAmuFyAl|3eS{xI5k-ZBi;(cZ*cPdUb5#QZHzjb~to?J2z~ zk&inaxKmjUo!u6H>r5O%b#LJDVA{6YMCrDPYW%6<6Ui`dfM^KO0$0HFyRwM+Cb(&q zt`X9l8DGyP3e{uI*i1KV7TiJnLSiM;{jHP^Q(|-15Y>L|c2qAEFfo%;AK&b5y@sys zefV>e%%jx=Y{B;QQMf8L_2-seJ0kJLpc+)Qh)d+E47ZV9tdV!Aq`C@w1 zL`iU3ZAOk(PNlzBvv#`nzQV0T{V`9=*b~C8Pk{m`8yqd}KZgpe9sveE8Do5Kl}ML} zG-SfMwN=kJ&Hf{2)1Uc24ZI%3U_S@0INi7A7M4U^t( zIdcRjFvj~8>n}Mg=r;=liw!6W;7-x@ZJ-D|Ge$eYlV!_ZHEW)ESFLK0CRJ1la?iO` zC#q?5`jc1&Wej!Q*t}}I$sCTQGw4pZ&albxwgfOz@I8VZ{BV8HreZ!xc>TKjWA&i5qp3Y3%Tqba(xy;1-O zd{GW&9M+#ovdvGO97P53j&uq4Skv(x5+*W@jO~wv=`4uWLH2uY%89|jR$YnNtI6{s zD;vu+6|ZA!>-rd0*jS!YWHTl7jBdedewIvQX|+;~b#yePVrsI70kY6D&RDMX)#kzK zbEdNALv5q&>hW-0NTs7fB5Hj{Yx)H~FZIphin@uw+e4!R)o);-&McAiM~Gxk4JuWG zDtO8m{^l(T=lY(ryoRpdF?o-KLaxix{<Q zQUu#DpypRM8C@5Do%n|PQ1s|YF3OS@*%!FEw?HYxr$3!L=_k}$5GZGyl~@420CXH? z)8&lpXGf=IvBk;B`SRy}u@=eeT-Fm@BO=XC)^e3zz1{~tgkfh;oqb+PlwkLxmtovJ zWqDjO4){Z+AGg_S8QEmIBwG2|Ou#UEV98G9gvHdD5XHf!JR}TaG-9mOm@^|l*d}OU zo5Lk&t`XYol>KcqDg~J`cZ&Qzx!r8bO*+Wd^x7(e&t-x49gD=AC4CTYz~e#rdq~q& zXR6V{qVsgG+=^eJpK};nEEisSC%n%pc0(52N_e&v5t6G^00D+ikhHAx$uDZGlyWe~YK8au+q~957O!dD~U|$LXk^`=D{XWrg#` zkr3w}#XE#`{sI0DaF)I*_!=GEtWr4Oy;a-K)srAf#LlV^sGIx-Qmjn2ws_LCR`C7=zV>N?eI9hRDg z&3t(OsScd+0$)6t3kF|;RUBQvS3D|a>cHuc>cV8#d-jfYJO-Z}kGa*xE{R*fUI`HK zpMTbXm8=^$WC6;(v_jmJao&CK;@-V1EdYxzZ*#fIt#-Y`pjIt+OGop933oWcpR@0L zaoxW+&3w#KAigGeE=%za4!aN^&5(#I!*%DEg})hvk#Y+xTw(d`(7FO-tKrJ{IC%1fDz;bA(>$%KmWD6>>&W zVDaQG0>s;$$nHV(DJVTmBpd28yi&CNWO?{Or{dYBpX#RZ=_4=&T0S+6Jslg?)t>h8 zE%Us9iw=sX^Z7$+qgCIIpvU;%7B_Kg7$NOy49$B*I&k`9-1~f|pK20YvON-zy(1WB zZD6`us(U+HAR$^~@HYUZQAo16YaJhk|fq`)n*+(Y3(#W?P|{ zU`BaIOty*=a{%AfbaWo_4fAhrcTjjgMYrDju`)UT;i}qFcdewNMokXX>Eod@y?Jwn zXT$oLbtv<)(N5%T&;VXuy0kgkq;Vih-pl&CdBu4O{i0vib+Vy`jLD;6Yh}La*=@q% zq~F8~bB2dw)hVX~cbc)BIBWGfe7{}V@@#~TewfV4w|%CvF0^V0f39f1xz*n76zAHX z3(bw-4FSS#@x{>tbm1_3aE}A!1)RO9fu8n#Qu(}MieCnmbpbec+b~ycSGc8#8I9%6 ztt&40!g6nGsr#Dt9Zyy_Gl>e+c#Kii+rtOwqGI(7oed7sX2)q><6(i#ea#?;vU>)G z;k{FWoKJf=4$Cvx&-n;fv^GK$(WYYodU=GoQUgnbKuDEH4&^3CP|i1_?!+hAGp!6Y zC5DSpPHsdc%@2f|s8v<1c}>8CYF0Y=5fo}g;-?)YJG+C&Ia`4w9ezGhFsc((aY>O% z-&pXRT(zt-ho>QLbPzi(SR2zjfdgNua}4OFD~tNV(j@9O@C{bm9;i0~57!+Gn1%S9 z(`3W=Sqp3a=&Dcq9rKoDb9%I$B*`Z21!nGMSyAlSbHZIO05naUU>7yQ<+A7bV`c}_ z-V_O|WUEfTpN9~rPBnBfV9P1N-CY(Gi3*L2#O$@FRp(>7sG6d^_rzo9oIsdhSOS3$}gBh@j@*@{}uoLA7+G*uE6s+=@7{cZ35nTOYm zsyDIrImyySRzXDNFkTL<)J}i05-u^RV1BS>9pcMm+5JArdAdHCBBXcuwF~Mi30&E$ zmo{cmT{t?>NTONaUMrcST*urKcy@HA;myt%-DZ;VCEwAg&L#0`RRx>(?(@Z1mt1i7 z`J&+0GU4KDuGe%zndrXP#kc!&+?pPMt(%x7i2q!9hBVyiOAwT-*-idjwyVFwnf1!8 zx^@G-aA09-Uhk#Vzo9X!-MQEkFjSSBw)6i0_&^80eZE%H+%_1+55bpikr_3Lr8$o2p9nE5CcRw0!FYI-%4*26wT{l{|SZr2U zix!_5f88o&c{nmN0021^n~bpaTe`B<+ei7Yyqb92L}=PDjh>dXz5457pTXaWx?hCk ze+6iAY8tecSBx$1FZB@LV3@l+Z5PU1A_Ib~Nn$h7u77HLak9OzA`MX6fHBdV%LD2gyPG<`QAA-5BNFhwxntpYF0@^D zNz|T6^-I~E*6t?G{n4s|HU{LJfCdjWu3?J$ynUq?7i5;3xBegU2-KrmhWaI~owe_8 z!(#schf`c{6x-VYs%pGb9qic8PdwCqu!bZK7H_iApuFmEs z#NP~Dcpt&i>mC%gyp97H9sk0VjLd#$ycn;J=999CaTETl_bg>cYw!<_R6fpv;#SLpXL=)j;{M>-;RDHplm?*FF_| zUGUGsonrGs@y4eYi5egDpAX939mT+qZe8yRmRDdLDzFIODJ1o;(Bm8}T94gn-_FlZ zlKf9*btp=H){P6jRnl(G{j84_yzn25E@p4|KJ_w$x<3)&#=F0}?jqrk8U^ z8xz|sHZbD7N8)e9%?ILC`c#N&G>fpA+bMeD7i`^gU85YppF@ z=P=r~!a|T6c_V_ufHPV97}VvBN50J@wfo!qj)_r}WT8`D&1v%fu5rF3(>ztFJ+6b| ze}{9)s>`D8)U^4v63~d*m__r6cPKa|fg>z2&pDpY;opfoEB^oz-D*91!}j)Ywcev^ zs7&%lJks+LuJ<97p&j-sHU~A)91R%D+ChFd*YW(cCk=&+6LGV<^SAstI>GSr%fVW1 zgW~yZrP1TJF~c34r}ya#9mW;q8Pjq3KZqy*j8nBwhJOm>)wKTr7wbMAStPlW?6D>-0gh?nmlqQ2jCj)>v?oc*Jd*L3XH%S8VG6Oi!N zk3Jdrqf?(uj?Y}zG>B)B#jcqLlW{yML0^@N5bcrIC3D{xuS-t^Tlh!BHyU@r4Nme+ zKS;QVQ&f*mG0iJ&RxP-x+aj|Sb#j)~*2RJ7cU&1~b_|4$;*0oOzc+&1kwTpC1 zrfW2fZQ}DEKVG0O0=sU5w>U9M7MwojUq{w>^T> zT)go9pK;-z6lw6^SZVCgL8?c5jBe3JJjPIi0I<#vP<<=TS4OzK_+542tv})N-D^hh zE#|3b3qv+TF6h^ARCvNF1mFe7R>(E;_}sq{cvDikOXAU8{r>=UmA^B^mQRPJi;UqY z#V30^w*LSh@;ySwTlj(EO-jb&!kT@Z-kBzw1>6>J$NTAG2+VeyBuS*qGAN9q01By-YbYcfjtyNrp;f2Irk|5ncDC0~ z?)=X~`#w0AcO>I}-8bIt{{UJaO#aJS_l>`3?;cI?W8sg%ElXOu4RvpQ;+W=+D@!17 zQb`U9gpFI~-}5lrw{bOB;V*uSYNBujj6ThewFF^bH-!6xCFgN~DiAZyssdkH$M)F6UHXblSAw$C5!Ds2qP6PQ9z=tp~>%r-Zfd_)e_! zEmu{#w!4v=?7DojF!MUT0PJ(qry1hDXB}C6Vw~gft@nDLQnfj@|AY=~yhil_khcsV|zB=&Fz%3wK$*1^M!|l4ls2*97NC8+5!zGWY z#eD`#!??`itW@2)zKS>L=KQX_&vz}AI?-~joGI*@-M=UQ008Pf2mE&NW%tGH7sH+l z)|%Tx);6}AXK=Xkpg`nE1_J{oIR}DAt!#K(!`eTIe0`_tx)+Ho^vjJc<2RR^R3wN# z?%zhp+Cb_KeQWj%E=QH+8Er^6X*Hvgy1$ZN<#XsN(#qqI)lM9<`!bSk^z5~7t=C-- zZ1G3L%~Mg<#)mbO5;TgU>0!xmJDhX?`q#{VwzusaC+zL<^TNLfJQw3Tf9P-Mkv9ckr{*el?c<@rJ$d*ZBF!+Fbt+V8M%?ZD%S+p$UA5f&*D%a*a>BM)5c$yfMdt21`D%{RiQg%K!{{VvKXqUb<`2PUme}~$ai>8J@4ESa(M@zG4 zrfA~SZXQMRglfpM{^>RmxUz;l0~+A}0N}Sj7``KEf463E$miDFl`DHg*grj1_RTlBv+Pc9~KkE z*75v0@NS0MrSGAnY}Tgk;yBqA2y`cn+eSG)rx-bR z_TDGf{6GDp;QI?(DInBtEiLq!HJ!H+%^Y}?zu#t1#2_G%k+^YOl_P@3O(iFFr)2Nj zvDr$ToKz~*>_#Jcy!PakNSPM@Q+k(-G1OGm!@E3}GsM!Q!L zaHIyRyWi(PwGvGK3N{{V_c>rGpKH-3$x`N^oi zm`|50GMu3K^BRvZV~}eX!=D-LJ|OtJT=1uYJY{F$cd=q^tb9MD`M}_Q^6DUws3E~S zcqh3uVzBi+%29{AF0$U;eSTkYrD}EQ>n75deSI}ib~`Og#a}s5BNy zCB)j+ne{xw;wM$v4dC+ZB52(?j_Bc0 zrVONz*z3haDc7k@K{dKbbhmD=%VWvpdb(-ZZ`ap!iud2^x9HO;zhZ#5*8 zU?2^*_iP(3#DjrRiNfLCK3PXp^nE=3?#ER+Je1W+jkve9?7n;chjsA&**~^!rq^v{ z5>2g19;&F1Zr4zi4wKt#Igtm-ypl=Cz&uxi!nzm60}^mfGtxbUd_6R|6|Hplzn7Kwx7>G1o(B(G-)+U()|ca@w)q~VZ*$=< z8u)iyyzu3wsU6DO`LW$<5;S`iTOLXfO1M=Uv(E$y^G^(X8`nMtYCrIw=yw`~(B4|y z#jHbnd%D1@9o}h)$L@-syMR9P0tw_PQB@@gQMzgLR^Q6)S$vN-5n7z2`6bTpv$}Tj z)a!h0qi8-9S$sos*GXe6QJB}u3njyRoVdWwTLc0@09Pet@teawA=E9+mx^s|tpdno zmG8XG#wNjyv(OGtIpZJ>IOwN?rwluUrF8r2uidw$k;f@xC0Cinr?r*-UEi6h;f->` zTJZ!e7NL73{;B1(iwtoyM-sDs@i0SdCki+Xj=gJ&yzx$zVXkSiX&xND`(~eSEv3s{ zT^6;tk)tHW{{Su|gp^PeWMd~MjDDLc5sP-$T@TTvg_Nnvn$dLf`;Na+z1I8|Be_j8 zM~mz-O!gAY(%Y*1;bpdE+RTo3C_->IWE05G3uc%4wzzL@JVrFTi`Y^N%UgTqh8bC7 zRAxK_BC#APCnWLeRA}BdmfLPsJT#p?TDqpJ{XYKy_$#^5c!uM^Hg>VTk9^;6o^meX zHw3IJ}tPD1u@}58Mo)TNo?H24fiAxIMIX*5B8e zGN}n{@91}$2BYD~-eKXr64h>`!^5p>U+HYe1mV>eY7_F1{{UPbc;fyUczZ_G=bOS> zhN{p=*5-G%WftNX*ee1?HkRW65u5|I?kJltfl7Uqg}^waLy{%5R5 zC;TK5HOGi`6^>iWz2Z3ISl|a}46!6^6a^p=-;Q!~o)28`pNhOGq2Dfrs4H7td2m@x z<|-R`T}b7e^$ZyE$IHmC0#V1*sGR23x_tit6U(PhFveCB=&ToTp{0+tFyhBdY<5r%J0;X!};|l5bDLTfZi4e%4PR4k9QCfs6zXL3??qm6-M6GyZTCG4MP-45jWm?!t5=?aIR{24x$16+PSDy}YN2Dq&P<;yva3(U$&J@91(< z&8kK!H5z_<{%6xa@JzpfJ}>xj;ah(kYaS+tT=539RXoc!W{TYr+0{;ZDwboDgOIAtcp_~sOY4O)+rw<{Zn+EdB4iX?@BjoJyjRuK;%ui86;3rde42au zo}L#l!l`pgOPjmU{cQMo;{8KP(IahRP%5_hE#^vFJo@lC7#l#y?dxBaUl9KQ;G^C* z_@$^x@Xy1a5H7qmrs_>Ks$J;1EYe-Os;j7uMq(j25+OT5P5<{lv48ij*eK${>n;L-L9X9FNon2g6Dj0^y$9e8mZNr;o>sTFJ6s=nImZ9PpRiFkT` z{BJKUzE{<+GxZ0+-yDBt%Z*~!z+MOO8fzNG*nhI=7Aj?#)mL+|3!SQ}GD*N3cOtxJ z{s`sbPln$IU&Ox>el6WuX!?e#ww78o#oi>DExvc10-u=>lo=-k<#I3qqX)!zIdlDD zaEgaU^>^x1^O zui%l-D&3LNwmv|`B`n*i9$2m74}(fD+egXq+toBuAQ%R`mTq$Lmg8O zPVkDCuBXU83e$D}0E8bEd|Bfk7c4hg#5y|N+-cCvNVvCf*hMEBn0al}zB_SQzwl8l zcjG65KWK<;{1Jbm+xU5`Y-hfj^4!aDX5pL?sf-kme57YM4S}9(#^P@h^L`hpJS`ej z+$99lZZeGoHK4J&NJ%rub9=8-S@qceQd6hUP{Nv)Wz4tR+U^t+~w0< zy%pE_7(ca-!_NWu1Ihicd|h{^>%J+sxQ5+yfA~lzk+j>TZKa@d8o7;rMe@j1A=sW6 z3e)(JvvItYd%~Lu2)>%l-+g zacMWeT_jk_8D2Cox2ge!8SnCe&wBox$#E3(48D#fU2{b}ciXAzQkOL@Z@BZ{+JE*x z_!IF<_Ka^E{?T3{ypGQIR<*a$;nP`WwRQ5=DPgz@rbuUHRssIvt8zi<)_&5Tx99AQ zqx?s>@TRar_E&Gl&=L8 zteu+C?|Y}N!F(JVa6*2_5Q}#?RV*)5ZS)5aiT!&x=~aOR8GW=4pCttRX7OtAM0!2Z5J7k<%6N z_&i0cICD@- zwP2f?G|KsMLmB|jhB zSp3Fkk8+-c0u~&D$S0w$cV3%C)cjK&_0{x0Xpwxl-Z@U^NBOrZsKTE49e#tJ`j~sk zsH@5J*4lYq%kw%EqS8{-=I(wc{8_T`Mx~|rpIo)l@0Ku--(Og~UQX5^Mq^;><&eO9 z-FPFZBc}03!Ji#?l+wC>Ikx_xQ$~Rv4mjA6+zrV2cG%GdpTt(Q*l~G zTiHL#_BX@f@chwoir>=z0N1AGdidkxPQRzMpToZm>3R*um23^;S&MsX%|H*C{O>BUpS(@UaVwebgm*300> zh88PWZ?2J>Rd?MC<7x-$|NccBv;(cCCVrFep z^4~=b2Mt#|3^f6NAp)GpfN2Z%wTlaTIsW{h?B9@E00K^ZxoTwfD^um2OHWHBt=9Uj zJxZZYKF5_b=K5QkO|?g*ThH;|!di}>qX?m2}6f;Ao>)MkvmkOXTS0{efel~&#{odZ4JnSw95h+PZ zqE_qh?0B-m)5LQ(IK|%DtA0tAH8{0xbHsOgAN(RZ8(e*j-up`mBWsuryZLP4P~&Rw<=YLFATK7n zsMEtL&NH3lo$Q*`bk@(-&vQx`cvDWFv|P#WeH!xC>-E^|yglOk%?|zS?mQ!)>Q=KV z`Nr-=c1gCKl9XJ!s`HcuK^XcT&%z!bxVefo()8GV&u6jz)vC5*OntHNz=5buA^nNzj+LQBRey%$Q1)J zFCzr;)oWh}4XG}l)WglCI(N{G>d#-o5j?I#&E$1K+_gt;8o%;KJ9 zftR~EFU;Y5SK_N564+-5IFbXlakf0tw$tS5C+XtfNR!h_K+^Tto4pOq0 zZS?%?d4`3qc&5Wfn#BA)wbbq2Z!xYfFC~eahw*2;A}?b`$)3Z zE;U^~IR609ZuKdIEw^#WmnwGgz}me2wJ$NtBVO}Rt~gMgV&&?io;gcGS|-k0Fh$H;`?e|5tC2Vt!^RKCJA?M z98j{Mn`uai+)9Gmo2evi;Ae{4@b|#27UK48OIOwH);oZr;yIW}A(94CrC&H;$+bxs z;PeF7n~cU`sKKW+yuCYloHZ)oV&1m?zUzLcooVyxwsx=fofb(N2`(k`7jvk{1>2Lm z1gXY3uWZwNA7`R?t#w(Xf8Q^dZCh-X>Pm3RG4dUtC?_}=9CSZ9TDB5Td#BOqZJFlc zs$t`;?)w?u0PqHwH0yteW(%8(#khN44}G2bB?0AZC3YFz0xgh z+WDZhwzpM=7*T^q6G^$9Fu0OdAcn{X3NeA2&+FKFG{1GM<-6>=C;8YY<7r_ib5VDz zF2BmWyT3BNf#D4z3%k!QCXhiKa(RlTHkXj!Hqn*~vy}jb$4)# ztT|$pm-(-ryP8IwN5A4<@n425b?L^RrRytaAih{$^3}E$18!9@^AnxRfxyA^tlbmF zGw6C%j-8_D_P%ArZ6fLjOo}8AfIPecji;TA##v70;EaS# zq>woGz!h@Mu@KcS*I!M&G%KE@T5)Hoe#!d8pR-58{{Rh!gEO6Mp!X(df$r7W; z-zfxS5Y3Pd{0!o|NpyQ{E5h@5-}nDEOhGorr?!D<*m8U?Ch^3iYG}Vo6L=NlB^NH8-P&D$-vw1isxoaElp(p z)ZHXDcT49iSY<^(3a=f`GC{$~IXEXgIE+WM_ob%y?fC2e00iZz?)7>dw}$mE5qOz@ z;Uc%5>fYDScADO1P%%Q*|YV7{s~|4I!_k-F4HEvxc&Ww^xAFC={JU5!btvX z4B=c4!n|kx2~Fb_&}H$L!n^I?YO&Px{Z2WXg)q$-P7gwNLd4^sAa*tSM+4%yvWa2! z{nWkPZ|=Q*>)kITeO5<^`o$*~(VL&}Rey>eANXhSwmlcb7cFlc){hPCoYv#_-fFz4 z8GA7UpglJpoog@t3)SJD3t9L(!Jo9Dx@NrlLGabpykxLNlr+gUF@ujTKypXk80mxK zc!89xZAv)(Y?mavZ+N%&v}Kx5t4^wE=>GsS;g1%0sig5kU-&M<>KP$M*?MmsWEtn~ zkCd@&s3c_W9dTY}JVB~Igzh!p3~CZwM!REc%{C(=Td3~wjJl39_i^v(@K}5<7E+a= zXkB^V*P-M%)`M35zT)qR6U1=Mb$PG&!6TP{{P;-&Xgp%{bF7tqk$kib^#SceV9NC-c#HTHVi^#B)-uD82VykNyes zaPTLH;GQ|+*R=?h%ztUwb@!AWNGuKs^bLWIYd*r?;&+HIqtLXd^(i7-h~v4N$v#5G z%gX*{;s60oG6zgo%0~-dn^-5?MSsG_Bxg9gyL|ed$>D#A>ESy#d@{1hrQ6!+yLBn8 z7bkXvpJ;t9{q$2H=xd{wiKT}t6Cv|CxGiaU6jRL$}$e4<$pmB_|Aj&s=7 z@sHKwxo;URi>vFXu`$EScc%uE?(g?@{{SPl(&zA0emIWf#d-ye)OQ~%UI>|*SsSKC zGQOZ>V>sfld@E<-N%dvdwLMba>PZyFtkIa(42+V>ytH`8z{gN>F3#w59+GaRx-i{bLKZi;V!#NTLy|!l$!z=aTn?-Ku-7f2 z=>9y0`pvJJ%=DkcV6Aed|)s^JmeRc1yo{fCY$i_6I zs!vTDcl-|1TKJ{$245B4*%RTf3ryA}F0L)?R^Yr!M%Wm!QW#;efu4JFS$gf3q2ezO zF0F6jnJ!?DPgrM`3#OTM0*X>qlgkaTuI@57kid*$y;>2%)2Nz?OIxkq+}Gb@3074i zp(K;}Jv^`1@hRySz9RU2tq+L40oEnDwA93g82;Dh+g(GnyJQiF^1*-r@IOlW6^+%Z zXqKKIi6PN$?pdrYzGk(!Axn8Fk-c}Z``8PSfO{OP#*QcVZF`rGPrq;J8&l?~7Y3A) z***UNubJHIpAq~`;r{>%S$sgY)3l!p-d$aNpX~bO!rDUj5(STcpS8CPZ2^JC8`N>f zd}r})o#P*e*1iz&kHb0dCbN_6H&I+&WmjWiLQa_qcAcc09A_cB`gv2s%2I?Iwc1vd z;;q$dd+*%h!_nr(IMr5;-5$L+f2q0gBgQt`SHsO0z&{&&K-YTJ)x%A5ccDmTu)2xU zY8jVn2Z4(!{HNUH0&C7RFBE(!wD4d2B38Z|n@Uk{w#Q4j1ly#4!UL}Wk&Zay85Pw$ z!i+z0N)A_gyC?YQ{Zcek)jUM67p{q`CHP z@CMxA=9OxSi-Pu7QogG1(K{y6`4v)8s~b0})2jJzV!R&~=G3)s6CV`ZTH5KN>e_7& zREJb+q!Ixlyk9a7NFkpG3QC-UYtX!Fr)eJ_JT5*R_^-q3XQ^Jp_6ao`3l+AyvXPf| zWR0?+2^ieD1d-DQl`KT4({pf)V!kVCyr(%1Dn8^05<(IoLMxvxbW0^bX79BdeFW7UZSf^o?7 z$vCbG2rfKhs9dyKTikty;2W#!Xk<3=rbsLaWyc=4Uc6(2jXXTuo05L0#!*nTTRNM+ zityW8$^QTd_1qIl=0=vv%FL*wa?$M^9=`YloD5cGjQ%6nZzAyDg*-Ji(_Av7b4H{t z=5-7X&{Ncc0~~cY>CY(ERk>2t`M>MrX3mn!$J)<}Z2rw8wsFGl5^3YOb10B<0s+_5 zoE@VXt#1{43iy}ctq1#7?%i$HJ}v&m3+^n(gnf#f9B@W*P7QC%i;BM^)$Y?@iZCHEg5(dH(>wG>qphoxf9})%-yRjWqkO5yz+{y|tS{ zkjA&0jE|fJUUsS#1)HhIUTc`s;PCdX;wXF@1X47RU9dMEa5nIV+N&N8(AiPY9y6YD zD?B`B8~aDn_kY*S(l3@ty;qrUNbzrvZ}mtmA6L^ZrZ)wQHgXp75ORd%00Mw87ruEs zRt}k{cyGdb)P4%^&F9Ty<^8%#Ecv$+1Cbv29Fn zscS_g>2LTye?zd={Bh#h{6M;Xps_4)DUr#Q;w`-}?bXSnekDSxHG zq2DdN&BDqx1#QTn@#Lr{00tPxB>n9C*{qc{7&%2hSAXmJ(84kI6^^6C9x8(PjXT5f z!5oFvh17Rh<6|nTDdl%zhf*>JdgU~W3+qXvmgiAbl0*x7zHDskpSYqj7`gT1fxssQ zu$CQ3B-`Hle2I^{WR1Nx$5YZGx?dFQutzQIj?EiP#aJ-N3K!H4N8!QAIt_2(? zVI#OycS}3gn3!UiuJC%OKfG)Kxcs9%b4Tx$lebmr+1%0AsV=2mL9`zTYMN=&Y-E}_ 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 literal 0 HcmV?d00001 From a31fe8fcd80f44b85fa55580c74623eec84eddca Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Fri, 7 Jul 2023 16:54:53 +1000 Subject: [PATCH 098/421] Fix GCC/Clang compile errors. --- CesiumGltfReader/test/TestGltfReader.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CesiumGltfReader/test/TestGltfReader.cpp b/CesiumGltfReader/test/TestGltfReader.cpp index cc18874b3..57b96e6df 100644 --- a/CesiumGltfReader/test/TestGltfReader.cpp +++ b/CesiumGltfReader/test/TestGltfReader.cpp @@ -456,7 +456,7 @@ TEST_CASE("Can correctly interpret mipmaps in KTX2 files") { CHECK(image.mipPositions[0].byteSize > 0); CHECK( image.mipPositions[0].byteSize == - image.width * image.height * image.channels); + size_t(image.width * image.height * image.channels)); CHECK(image.mipPositions[0].byteSize == image.pixelData.size()); } @@ -489,7 +489,7 @@ TEST_CASE("Can correctly interpret mipmaps in KTX2 files") { CHECK(image.mipPositions[0].byteSize > 0); CHECK( image.mipPositions[0].byteSize == - image.width * image.height * image.channels); + size_t(image.width * image.height * image.channels)); CHECK(image.mipPositions[0].byteSize < image.pixelData.size()); size_t smallerThan = image.mipPositions[0].byteSize; From e6ce4a9a1daca5c825dda93fbb94059f8f530003 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Mon, 10 Jul 2023 09:23:24 +1000 Subject: [PATCH 099/421] Don't let zlib dirty its repo on configure. --- extern/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/extern/CMakeLists.txt b/extern/CMakeLists.txt index 9fee817b1..70deb3bd6 100644 --- a/extern/CMakeLists.txt +++ b/extern/CMakeLists.txt @@ -160,6 +160,9 @@ add_subdirectory(libmorton) add_subdirectory(zlib) +# Undo zlib's unnecessary and repo-dirtying rename. +file(RENAME ${CMAKE_CURRENT_SOURCE_DIR}/zlib/zconf.h.included ${CMAKE_CURRENT_SOURCE_DIR}/zlib/zconf.h) + if(DEFINED CMAKE_TOOLCHAIN_FILE) if(NOT IS_ABSOLUTE ${CMAKE_TOOLCHAIN_FILE}) set(TJ_TOOLCHAIN_FILE ${CMAKE_SOURCE_DIR}/${CMAKE_TOOLCHAIN_FILE}) From baa611e19b15409fb497c6f94bed14a3ac99d6d6 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Tue, 11 Jul 2023 10:39:13 +1000 Subject: [PATCH 100/421] Hopefully fix problem on macOS. --- extern/CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/extern/CMakeLists.txt b/extern/CMakeLists.txt index 70deb3bd6..c5a6279d1 100644 --- a/extern/CMakeLists.txt +++ b/extern/CMakeLists.txt @@ -158,7 +158,12 @@ set(CESIUM_NATIVE_LIBMORTON_INCUDE_DIR "${CMAKE_CURRENT_LIST_DIR}/libmorton/incl set(BUILD_TESTING OFF CACHE INTERNAL "Disable libmorton Testing") add_subdirectory(libmorton) +# Be sure the include directories zlib adds are added before any existing directories. +# We need to make sure it finds zconf.h in the build directory not the source directory. +set(ORIGINAL_CMAKE_INCLUDE_DIRECTORIES_BEFORE ${CMAKE_INCLUDE_DIRECTORIES_BEFORE}) +set(CMAKE_INCLUDE_DIRECTORIES_BEFORE ON) add_subdirectory(zlib) +set(CMAKE_INCLUDE_DIRECTORIES_BEFORE ${ORIGINAL_CMAKE_INCLUDE_DIRECTORIES_BEFORE}) # Undo zlib's unnecessary and repo-dirtying rename. file(RENAME ${CMAKE_CURRENT_SOURCE_DIR}/zlib/zconf.h.included ${CMAKE_CURRENT_SOURCE_DIR}/zlib/zconf.h) From fd4b97bbf6757f57a412e0c069c151f1826ae710 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Tue, 11 Jul 2023 11:26:45 +1000 Subject: [PATCH 101/421] A different solution to zlib's dirty repo. --- CesiumUtility/CMakeLists.txt | 2 +- extern/CMakeLists.txt | 13 ++++--------- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/CesiumUtility/CMakeLists.txt b/CesiumUtility/CMakeLists.txt index 36ae9c04d..bb704b8d0 100644 --- a/CesiumUtility/CMakeLists.txt +++ b/CesiumUtility/CMakeLists.txt @@ -35,8 +35,8 @@ target_include_directories( ${CESIUM_NATIVE_RAPIDJSON_INCLUDE_DIR} PRIVATE ${CMAKE_CURRENT_LIST_DIR}/src - ${CMAKE_CURRENT_LIST_DIR}/../extern/zlib ${CMAKE_CURRENT_BINARY_DIR}/../extern/zlib + ${CMAKE_CURRENT_BINARY_DIR}/../extern/zlib-src ) # GLM erroneously does not declare its include a `SYSTEM` include, so diff --git a/extern/CMakeLists.txt b/extern/CMakeLists.txt index c5a6279d1..c7b6220b9 100644 --- a/extern/CMakeLists.txt +++ b/extern/CMakeLists.txt @@ -158,15 +158,10 @@ set(CESIUM_NATIVE_LIBMORTON_INCUDE_DIR "${CMAKE_CURRENT_LIST_DIR}/libmorton/incl set(BUILD_TESTING OFF CACHE INTERNAL "Disable libmorton Testing") add_subdirectory(libmorton) -# Be sure the include directories zlib adds are added before any existing directories. -# We need to make sure it finds zconf.h in the build directory not the source directory. -set(ORIGINAL_CMAKE_INCLUDE_DIRECTORIES_BEFORE ${CMAKE_INCLUDE_DIRECTORIES_BEFORE}) -set(CMAKE_INCLUDE_DIRECTORIES_BEFORE ON) -add_subdirectory(zlib) -set(CMAKE_INCLUDE_DIRECTORIES_BEFORE ${ORIGINAL_CMAKE_INCLUDE_DIRECTORIES_BEFORE}) - -# Undo zlib's unnecessary and repo-dirtying rename. -file(RENAME ${CMAKE_CURRENT_SOURCE_DIR}/zlib/zconf.h.included ${CMAKE_CURRENT_SOURCE_DIR}/zlib/zconf.h) +# The zlib cmake build makes its working directory dirty. +# So we make a copy of it to avoid that annoyance. +file(COPY "${CMAKE_CURRENT_LIST_DIR}/zlib/" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/zlib-src") +add_subdirectory(${CMAKE_CURRENT_BINARY_DIR}/zlib-src ${CMAKE_CURRENT_BINARY_DIR}/zlib) if(DEFINED CMAKE_TOOLCHAIN_FILE) if(NOT IS_ABSOLUTE ${CMAKE_TOOLCHAIN_FILE}) From b1bd5d237ca86847c066eb6f934dab473161ac68 Mon Sep 17 00:00:00 2001 From: Joseph Kaile Date: Thu, 13 Jul 2023 14:07:18 -0400 Subject: [PATCH 102/421] add the meshoptimizer library --- .gitmodules | 3 +++ ThirdParty.json | 6 ++++++ extern/meshoptimizer | 1 + 3 files changed, 10 insertions(+) create mode 160000 extern/meshoptimizer diff --git a/.gitmodules b/.gitmodules index 244d6e8ba..4c42c9ceb 100644 --- a/.gitmodules +++ b/.gitmodules @@ -65,3 +65,6 @@ [submodule "extern/zlib"] path = extern/zlib url = https://github.com/madler/zlib.git +[submodule "extern/meshoptimizer"] + path = extern/meshoptimizer + url = https://github.com/zeux/meshoptimizer diff --git a/ThirdParty.json b/ThirdParty.json index 41622a037..c3753c2ac 100644 --- a/ThirdParty.json +++ b/ThirdParty.json @@ -71,6 +71,12 @@ "version": "2.1.91", "license": ["IJG", "BSD-3-Clause", "zlib"] }, + { + "name": "meshoptimizer", + "url": "https://github.com/zeux/meshoptimizer", + "version": "0.19", + "license": ["MIT"] + }, { "name": "modp_b64", "url": "https://github.com/chromium/chromium/tree/15996b5d2322b634f4197447b10289bddc2b0b32/third_party/modp_b64", diff --git a/extern/meshoptimizer b/extern/meshoptimizer new file mode 160000 index 000000000..fbe26ff91 --- /dev/null +++ b/extern/meshoptimizer @@ -0,0 +1 @@ +Subproject commit fbe26ff91317bd89c67c3890e038965d55f3e354 From e790dbb6ae8284889a208ca91a63e973a24f4682 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Mon, 17 Jul 2023 19:32:05 +1000 Subject: [PATCH 103/421] Fix unconditionally-refined leaf tile bug. --- CHANGES.md | 6 ++++++ Cesium3DTilesSelection/src/Tile.cpp | 4 +++- Cesium3DTilesSelection/src/Tileset.cpp | 4 +++- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 586b3c9de..6e9087b3d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,11 @@ # Change Log +### ? - ? + +##### Fixes :wrench: + +- Fixed a bug in the 3D Tiles selection algorithm that could cause missing detail if a tileset had a leaf tile that was considered "unconditionally refined" due to have a geometric error larger than its parent's. + ### v0.25.1 - 2023-07-03 ##### Additions :tada: diff --git a/Cesium3DTilesSelection/src/Tile.cpp b/Cesium3DTilesSelection/src/Tile.cpp index 0763c7185..87df7636c 100644 --- a/Cesium3DTilesSelection/src/Tile.cpp +++ b/Cesium3DTilesSelection/src/Tile.cpp @@ -188,7 +188,9 @@ bool Tile::isRenderable() const noexcept { } if (getState() == TileLoadState::Done) { - if (!getUnconditionallyRefine()) { + // An unconditionally-refined tile is never renderable... UNLESS it has no + // children, in which case waiting longer will be futile. + if (!getUnconditionallyRefine() || this->_children.empty()) { return std::all_of( this->_rasterTiles.begin(), this->_rasterTiles.end(), diff --git a/Cesium3DTilesSelection/src/Tileset.cpp b/Cesium3DTilesSelection/src/Tileset.cpp index 7305c5ee0..b005e2532 100644 --- a/Cesium3DTilesSelection/src/Tileset.cpp +++ b/Cesium3DTilesSelection/src/Tileset.cpp @@ -261,7 +261,9 @@ Tileset::updateViewOffline(const std::vector& frustums) { // TODO: fix the fading for offline case // (https://github.com/CesiumGS/cesium-native/issues/549) this->updateView(frustums, 0.0f); - while (this->_pTilesetContentManager->getNumberOfTilesLoading() > 0) { + while (this->_pTilesetContentManager->getNumberOfTilesLoading() > 0 || + this->_updateResult.mainThreadTileLoadQueueLength > 0 || + this->_updateResult.workerThreadTileLoadQueueLength > 0) { this->_externals.pAssetAccessor->tick(); this->updateView(frustums, 0.0f); } From 311489838a17616f0f8ff065e8ddb2f39a6174eb Mon Sep 17 00:00:00 2001 From: Joseph Kaile Date: Tue, 18 Jul 2023 21:22:00 -0400 Subject: [PATCH 104/421] initial commit for meshopt-compression --- CesiumGltfReader/CMakeLists.txt | 2 + .../include/CesiumGltfReader/GltfReader.h | 2 + CesiumGltfReader/src/GltfReader.cpp | 18 +++ CesiumGltfReader/src/decodeMeshOpt.cpp | 103 +++++++++++++++ CesiumGltfReader/src/decodeMeshOpt.h | 10 ++ CesiumGltfReader/src/decodeQuantized.cpp | 119 ++++++++++++++++++ CesiumGltfReader/src/decodeQuantized.h | 10 ++ extern/CMakeLists.txt | 6 + 8 files changed, 270 insertions(+) create mode 100644 CesiumGltfReader/src/decodeMeshOpt.cpp create mode 100644 CesiumGltfReader/src/decodeMeshOpt.h create mode 100644 CesiumGltfReader/src/decodeQuantized.cpp create mode 100644 CesiumGltfReader/src/decodeQuantized.h diff --git a/CesiumGltfReader/CMakeLists.txt b/CesiumGltfReader/CMakeLists.txt index 8720b254a..7c6d460ad 100644 --- a/CesiumGltfReader/CMakeLists.txt +++ b/CesiumGltfReader/CMakeLists.txt @@ -46,6 +46,7 @@ target_include_directories( SYSTEM PUBLIC # Necessary for `draco/draco_features.h` ${CESIUM_NATIVE_DRACO_INCLUDE_DIR} + ${CESIUM_NATIVE_MESHOPTIMIZER_INCLUDE_DIR} ${CMAKE_CURRENT_LIST_DIR}/include ${CMAKE_CURRENT_LIST_DIR}/generated/include PRIVATE @@ -66,6 +67,7 @@ target_link_libraries(CesiumGltfReader webp webpdecoder turbojpeg + meshoptimizer ) install(TARGETS CesiumGltfReader diff --git a/CesiumGltfReader/include/CesiumGltfReader/GltfReader.h b/CesiumGltfReader/include/CesiumGltfReader/GltfReader.h index 5795779e6..55a92339b 100644 --- a/CesiumGltfReader/include/CesiumGltfReader/GltfReader.h +++ b/CesiumGltfReader/include/CesiumGltfReader/GltfReader.h @@ -101,6 +101,8 @@ struct CESIUMGLTFREADER_API GltfReaderOptions { */ bool decodeDraco = true; + bool decodeQuantized = true; + /** * @brief For each possible input transmission format, this struct names * the ideal target gpu-compressed pixel format to transcode to. diff --git a/CesiumGltfReader/src/GltfReader.cpp b/CesiumGltfReader/src/GltfReader.cpp index 080896bc1..5ece2f048 100644 --- a/CesiumGltfReader/src/GltfReader.cpp +++ b/CesiumGltfReader/src/GltfReader.cpp @@ -3,10 +3,13 @@ #include "ModelJsonHandler.h" #include "decodeDataUrls.h" #include "decodeDraco.h" +#include "decodeMeshOpt.h" +#include "decodeQuantized.h" #include "registerExtensions.h" #include #include +#include #include #include #include @@ -300,6 +303,21 @@ void postprocess( if (options.decodeDraco) { decodeDraco(readGltf); } + + if (std::find( + model.extensionsUsed.begin(), + model.extensionsUsed.end(), + "EXT_meshopt_compression") != model.extensionsUsed.end()) { + decodeMeshOpt(model); + } + + if (options.decodeQuantized && + std::find( + model.extensionsUsed.begin(), + model.extensionsUsed.end(), + "KHR_mesh_quantization") != model.extensionsUsed.end()) { + decodeQuantized(model); + } } } // namespace diff --git a/CesiumGltfReader/src/decodeMeshOpt.cpp b/CesiumGltfReader/src/decodeMeshOpt.cpp new file mode 100644 index 000000000..66f34f2be --- /dev/null +++ b/CesiumGltfReader/src/decodeMeshOpt.cpp @@ -0,0 +1,103 @@ +#include "decodeMeshOpt.h" + +#include "CesiumGltfReader/GltfReader.h" + +#include +#include + +#include + +using namespace std; +using namespace CesiumGltf; + +namespace CesiumGltfReader { + +namespace { + +void decodeBufferView( + Accessor& accessor, + Buffer& destination, + const gsl::span& buffer, + BufferView& bufferView, + ExtensionBufferViewExtMeshoptCompression& meshOpt) { + + destination.byteLength = + static_cast(meshOpt.byteStride * accessor.count); + destination.cesium.data.resize(destination.byteLength); + bufferView.byteLength = destination.byteLength; + bufferView.byteStride = meshOpt.byteStride; + bufferView.byteOffset = 0; + + if (meshOpt.mode == + ExtensionBufferViewExtMeshoptCompression::Mode::ATTRIBUTES) { + if (meshopt_decodeVertexBuffer( + destination.cesium.data.data(), + accessor.count, + bufferView.byteStride.value(), + reinterpret_cast(buffer.data()), + buffer.size())) { + return; + } + } else if ( + meshOpt.mode == + ExtensionBufferViewExtMeshoptCompression::Mode::TRIANGLES) { + if (meshopt_decodeIndexBuffer( + reinterpret_cast(destination.cesium.data.data()), + accessor.count, + sizeof(uint16_t), + reinterpret_cast(buffer.data()), + buffer.size())) { + return; + } + } else if ( + meshOpt.mode == ExtensionBufferViewExtMeshoptCompression::Mode::INDICES) { + if (meshopt_decodeIndexSequence( + reinterpret_cast(destination.cesium.data.data()), + accessor.count, + sizeof(uint16_t), + reinterpret_cast(buffer.data()), + buffer.size())) { + return; + } + } +} + +void decodeAccessor(int32_t accessor, Model& model) { + Accessor* pAccessor = Model::getSafe(&model.accessors, accessor); + if (pAccessor) { + BufferView* pBufferView = + Model::getSafe(&model.bufferViews, pAccessor->bufferView); + if (pBufferView) { + ExtensionBufferViewExtMeshoptCompression* meshOpt = + pBufferView->getExtension(); + if (meshOpt) { + pBufferView->buffer = static_cast(model.buffers.size()); + Buffer* pDest = &model.buffers.emplace_back(); + Buffer* pBuffer = model.getSafe(&model.buffers, meshOpt->buffer); + if (pBuffer) { + decodeBufferView( + *pAccessor, + *pDest, + gsl::span( + pBuffer->cesium.data.data() + meshOpt->byteOffset, + static_cast(meshOpt->byteLength)), + *pBufferView, + *meshOpt); + } + } + } + } +} +} // namespace + +void CesiumGltfReader::decodeMeshOpt(Model& model) { + for (Mesh& mesh : model.meshes) { + for (MeshPrimitive& primitive : mesh.primitives) { + decodeAccessor(primitive.indices, model); + for (pair& attribute : primitive.attributes) { + decodeAccessor(attribute.second, model); + } + } + } +} +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/src/decodeMeshOpt.h b/CesiumGltfReader/src/decodeMeshOpt.h new file mode 100644 index 000000000..9961c6f44 --- /dev/null +++ b/CesiumGltfReader/src/decodeMeshOpt.h @@ -0,0 +1,10 @@ +#pragma once + +namespace CesiumGltf { +struct Model; +} + +namespace CesiumGltfReader { + +void decodeMeshOpt(CesiumGltf::Model& model); +} // namespace CesiumGltfReader diff --git a/CesiumGltfReader/src/decodeQuantized.cpp b/CesiumGltfReader/src/decodeQuantized.cpp new file mode 100644 index 000000000..edbf947e9 --- /dev/null +++ b/CesiumGltfReader/src/decodeQuantized.cpp @@ -0,0 +1,119 @@ +#include "decodeQuantized.h" + +#include "CesiumGltfReader/GltfReader.h" + +#include + +#include + +using namespace std; +using namespace CesiumGltf; + +namespace CesiumGltfReader { + +namespace { +template +using xVec = AccessorView>; + +template +void unquantizeFloat(float* fPtr, xVec& quantizedView) { + for (int i = 0; i < quantizedView.size(); i++) { + const auto& q = quantizedView[i]; + for (int j = 0; j < q.length(); j++) { + *fPtr++ = intToFloat(q[j]); + } + } +} + +template float intToFloat(T t) { return intToFloat(t); } + +float intToFloat(int8_t c) { return max(c / 127.0f, -1.0f); } + +float intToFloat(uint8_t c) { return c / 127.0f; } + +float intToFloat(int16_t c) { return max(c / 65535.0f, -1.0f); } + +float intToFloat(unsigned short c) { return c / 65535.0f; } + +template +void decodeAccessor(Model& model, Accessor& accessor) { + + xVec quantizedView(model, accessor); + accessor.componentType = AccessorSpec::ComponentType::FLOAT; + accessor.byteOffset = 0; + accessor.count = accessor.count; + + for (double& d : accessor.min) { + d = intToFloat(static_cast(d)); + } + for (double& d : accessor.max) { + d = intToFloat(static_cast(d)); + } + + BufferView* pBufferView = + Model::getSafe(&model.bufferViews, accessor.bufferView); + if (!pBufferView) { + accessor.bufferView = static_cast(model.bufferViews.size()); + pBufferView = &model.bufferViews.emplace_back(); + } + pBufferView->byteOffset = 0; + pBufferView->byteStride = N * sizeof(float); + pBufferView->byteLength = accessor.count * *pBufferView->byteStride; + pBufferView->buffer = static_cast(model.buffers.size()); + + Buffer& buffer = model.buffers.emplace_back(); + buffer.byteLength = pBufferView->byteLength; + buffer.cesium.data.resize(buffer.byteLength); + + unquantizeFloat( + reinterpret_cast(buffer.cesium.data.data()), + quantizedView); +} + +template void decodeAccessor(Model& model, Accessor& accessor) { + switch (accessor.componentType) { + case Accessor::ComponentType::BYTE: + decodeAccessor(model, accessor); + break; + case Accessor::ComponentType::UNSIGNED_BYTE: + decodeAccessor(model, accessor); + break; + case Accessor::ComponentType::SHORT: + decodeAccessor(model, accessor); + break; + case Accessor::ComponentType::UNSIGNED_SHORT: + decodeAccessor(model, accessor); + break; + } +} + +void decodeAccessor(Model& model, Accessor& accessor) { + int8_t numberOfComponents = accessor.computeNumberOfComponents(); + switch (numberOfComponents) { + case 2: + decodeAccessor<2>(model, accessor); + break; + case 3: + decodeAccessor<3>(model, accessor); + break; + case 4: + decodeAccessor<4>(model, accessor); + break; + } +} +} // namespace + +void CesiumGltfReader::decodeQuantized(Model& model) { + for (Mesh& mesh : model.meshes) { + for (MeshPrimitive& primitive : mesh.primitives) { + for (pair& attribute : primitive.attributes) { + Accessor* pAccessor = + Model::getSafe(&model.accessors, attribute.second); + if (pAccessor) { + decodeAccessor(model, *pAccessor); + } + } + } + } +} +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/src/decodeQuantized.h b/CesiumGltfReader/src/decodeQuantized.h new file mode 100644 index 000000000..72af87680 --- /dev/null +++ b/CesiumGltfReader/src/decodeQuantized.h @@ -0,0 +1,10 @@ +#pragma once + +namespace CesiumGltf { +struct Model; +} + +namespace CesiumGltfReader { + +void decodeQuantized(CesiumGltf::Model& model); +} // namespace CesiumGltfReader diff --git a/extern/CMakeLists.txt b/extern/CMakeLists.txt index 9fee817b1..17b475f3e 100644 --- a/extern/CMakeLists.txt +++ b/extern/CMakeLists.txt @@ -46,6 +46,8 @@ endif() set(CESIUM_NATIVE_DRACO_LIBRARY ${CESIUM_NATIVE_DRACO_LIBRARY} PARENT_SCOPE) +add_subdirectory(meshoptimizer) + if (NOT TARGET glm) add_subdirectory(glm GLM) endif() @@ -155,6 +157,10 @@ set(CESIUM_NATIVE_LIBMORTON_INCUDE_DIR "${CMAKE_CURRENT_LIST_DIR}/libmorton/incl "Include directory for libmorton libraries" ) +set(CESIUM_NATIVE_MESHOPTIMIZER_INCLUDE_DIR "${CMAKE_CURRENT_LIST_DIR}/meshoptimizer/src/" CACHE INTERNAL + "Include directory for meshoptimizer" +) + set(BUILD_TESTING OFF CACHE INTERNAL "Disable libmorton Testing") add_subdirectory(libmorton) From b41f6da1fc2a7e7ea36af5cfc166745e01cd6886 Mon Sep 17 00:00:00 2001 From: Janine Liu <32226860+j9liu@users.noreply.github.com> Date: Wed, 19 Jul 2023 11:22:41 -0400 Subject: [PATCH 105/421] Fix typos --- CHANGES.md | 2 +- CesiumGltfReader/src/GltfReader.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 72500d100..80209b0c2 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -4,7 +4,7 @@ ##### Fixes :wrench: -- Fixed a bug where `GltfReader::readImage` would always populate `mipPositions` when reading KTX2 images, even when the KTX2 file indicated that it had no mip levels that and they should be created, if necessary, from the base image. As a result, `generateMipMaps` wouldn't generate any mipmaps for the image. +- Fixed a bug where `GltfReader::readImage` would always populate `mipPositions` when reading KTX2 images, even when the KTX2 file indicated that it had no mip levels and that they should be created, if necessary, from the base image. As a result, `generateMipMaps` wouldn't generate any mipmaps for the image. ### v0.25.1 - 2023-07-03 diff --git a/CesiumGltfReader/src/GltfReader.cpp b/CesiumGltfReader/src/GltfReader.cpp index 564c62aa5..c0ce5155c 100644 --- a/CesiumGltfReader/src/GltfReader.cpp +++ b/CesiumGltfReader/src/GltfReader.cpp @@ -597,7 +597,7 @@ ImageReaderResult GltfReader::readImage( } // In the KTX2 spec, there's a distinction between "this image has no - // mipmaps, so they should be generated at runtime" and and "this + // mipmaps, so they should be generated at runtime" and "this // image has no mipmaps because it makes no sense to create a mipmap // for this type of image." It is, confusingly, encoded in the // `levelCount` property: From 6ab9f41ad5729245b0c13b3e34421c243e94043e Mon Sep 17 00:00:00 2001 From: Brian L <130494071+csciguy8@users.noreply.github.com> Date: Wed, 19 Jul 2023 10:27:35 -0600 Subject: [PATCH 106/421] Add stale-while-revalidate support to ResponseCacheControl class --- CesiumAsync/src/ResponseCacheControl.cpp | 16 +++++++++++++--- CesiumAsync/src/ResponseCacheControl.h | 9 ++++++++- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/CesiumAsync/src/ResponseCacheControl.cpp b/CesiumAsync/src/ResponseCacheControl.cpp index b2618dc27..2931e8cd2 100644 --- a/CesiumAsync/src/ResponseCacheControl.cpp +++ b/CesiumAsync/src/ResponseCacheControl.cpp @@ -15,7 +15,8 @@ ResponseCacheControl::ResponseCacheControl( bool accessControlPrivate, bool proxyRevalidate, int maxAge, - int sharedMaxAge) + int sharedMaxAge, + int staleWhileRevalidate) : _mustRevalidate{mustRevalidate}, _noCache{noCache}, _noStore{noStore}, @@ -24,7 +25,8 @@ ResponseCacheControl::ResponseCacheControl( _accessControlPrivate{accessControlPrivate}, _proxyRevalidate{proxyRevalidate}, _maxAge{maxAge}, - _sharedMaxAge{sharedMaxAge} {} + _sharedMaxAge{sharedMaxAge}, + _staleWhileRevalidate{staleWhileRevalidate} {} /*static*/ std::optional ResponseCacheControl::parseFromResponseHeaders(const HttpHeaders& headers) { @@ -87,6 +89,13 @@ ResponseCacheControl::parseFromResponseHeaders(const HttpHeaders& headers) { sharedMaxAge = std::stoi(sharedMaxAgeIter->second); } + int staleWhileRevalidate = 0; + std::map::const_iterator + staleWhileRevalidateIter = parameterizedDirectives.find("stale-while-revalidate"); + if (staleWhileRevalidateIter != parameterizedDirectives.end()) { + staleWhileRevalidate = std::stoi(staleWhileRevalidateIter->second); + } + return ResponseCacheControl( mustRevalidate, noCache, @@ -96,7 +105,8 @@ ResponseCacheControl::parseFromResponseHeaders(const HttpHeaders& headers) { accessControlPrivate, proxyRevalidate, maxAge, - sharedMaxAge); + sharedMaxAge, + staleWhileRevalidate); } std::string trimSpace(const std::string& str) { diff --git a/CesiumAsync/src/ResponseCacheControl.h b/CesiumAsync/src/ResponseCacheControl.h index 7c95a046c..159ebc5be 100644 --- a/CesiumAsync/src/ResponseCacheControl.h +++ b/CesiumAsync/src/ResponseCacheControl.h @@ -40,7 +40,8 @@ class ResponseCacheControl { bool accessControlPrivate, bool proxyRevalidate, int maxAge, - int sharedMaxAge); + int sharedMaxAge, + int staleWhileRevalidate); /** * @brief Must-Revalidate directive that appears in the Cache-Control header. @@ -91,6 +92,11 @@ class ResponseCacheControl { */ int sharedMaxAge() const noexcept { return _sharedMaxAge; } + /** + * @brief Stale-While-Revalidate directive that appears in the Cache-Control header. + */ + int staleWhileRevalidate() const noexcept { return _staleWhileRevalidate; } + /** * @brief Parse response cache control from the response's headers. */ @@ -107,5 +113,6 @@ class ResponseCacheControl { bool _proxyRevalidate; int _maxAge; int _sharedMaxAge; + int _staleWhileRevalidate; }; } // namespace CesiumAsync From 2b0911d4a008968d76d99768d8c5907f3661e31a Mon Sep 17 00:00:00 2001 From: Brian L <130494071+csciguy8@users.noreply.github.com> Date: Wed, 19 Jul 2023 10:50:17 -0600 Subject: [PATCH 107/421] Run clang-format --- CesiumAsync/src/ResponseCacheControl.cpp | 3 ++- CesiumAsync/src/ResponseCacheControl.h | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CesiumAsync/src/ResponseCacheControl.cpp b/CesiumAsync/src/ResponseCacheControl.cpp index 2931e8cd2..004d4b539 100644 --- a/CesiumAsync/src/ResponseCacheControl.cpp +++ b/CesiumAsync/src/ResponseCacheControl.cpp @@ -91,7 +91,8 @@ ResponseCacheControl::parseFromResponseHeaders(const HttpHeaders& headers) { int staleWhileRevalidate = 0; std::map::const_iterator - staleWhileRevalidateIter = parameterizedDirectives.find("stale-while-revalidate"); + staleWhileRevalidateIter = + parameterizedDirectives.find("stale-while-revalidate"); if (staleWhileRevalidateIter != parameterizedDirectives.end()) { staleWhileRevalidate = std::stoi(staleWhileRevalidateIter->second); } diff --git a/CesiumAsync/src/ResponseCacheControl.h b/CesiumAsync/src/ResponseCacheControl.h index 159ebc5be..309c8ca4a 100644 --- a/CesiumAsync/src/ResponseCacheControl.h +++ b/CesiumAsync/src/ResponseCacheControl.h @@ -93,7 +93,8 @@ class ResponseCacheControl { int sharedMaxAge() const noexcept { return _sharedMaxAge; } /** - * @brief Stale-While-Revalidate directive that appears in the Cache-Control header. + * @brief Stale-While-Revalidate directive that appears in the Cache-Control + * header. */ int staleWhileRevalidate() const noexcept { return _staleWhileRevalidate; } From fa24d9fd92dbc0ff2bd1af423a6b98a9da1db254 Mon Sep 17 00:00:00 2001 From: Brian L <130494071+csciguy8@users.noreply.github.com> Date: Wed, 19 Jul 2023 11:26:37 -0600 Subject: [PATCH 108/421] Let stale-while-revalidate requests now go to disk cache --- CesiumAsync/src/CachingAssetAccessor.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/CesiumAsync/src/CachingAssetAccessor.cpp b/CesiumAsync/src/CachingAssetAccessor.cpp index c3bd25135..986ab098e 100644 --- a/CesiumAsync/src/CachingAssetAccessor.cpp +++ b/CesiumAsync/src/CachingAssetAccessor.cpp @@ -311,17 +311,21 @@ bool shouldCacheRequest( // check if cache control contains no-store or no-cache directives int maxAge = 0; + int staleWhileRevalidate = 0; if (cacheControl) { if (cacheControl->noStore() || cacheControl->noCache()) { return false; } maxAge = cacheControl->maxAge(); + staleWhileRevalidate = cacheControl->staleWhileRevalidate(); } - // check response header contains expires if maxAge is not specified - const HttpHeaders& responseHeaders = pResponse->headers(); - if (maxAge == 0) { + // Prefer cache control directives over older Expires header + if (maxAge != 0 || staleWhileRevalidate != 0) { + // The response is cacheable + } else { + const HttpHeaders& responseHeaders = pResponse->headers(); HttpHeaders::const_iterator expiresHeader = responseHeaders.find("Expires"); if (expiresHeader == responseHeaders.end()) { return false; @@ -343,8 +347,14 @@ std::string calculateCacheKey(const IAssetRequest& request) { std::time_t calculateExpiryTime( const IAssetRequest& request, const std::optional& cacheControl) { + + // Prefer cache control directives over older Expires header if (cacheControl) { - if (cacheControl->maxAge() != 0) { + // A specified maxAge indicates exact expiration. + // A specified staleWhileRevalidate still indicates expiration with maxAge, + // even if it is 0. + if (cacheControl->maxAge() != 0 || + cacheControl->staleWhileRevalidate() != 0) { return std::time(nullptr) + cacheControl->maxAge(); } } From 7f482ef98d754655bf297e18bfeda40b2d34715a Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Thu, 20 Jul 2023 16:15:46 +0200 Subject: [PATCH 109/421] Remove credits argument --- .../src/CesiumIonTilesetLoader.cpp | 3 +- .../src/LayerJsonTerrainLoader.cpp | 26 ++----- .../src/LayerJsonTerrainLoader.h | 4 +- .../src/TilesetContentManager.cpp | 1 - .../test/TestLayerJsonTerrainLoader.cpp | 67 +++++-------------- 5 files changed, 26 insertions(+), 75 deletions(-) diff --git a/Cesium3DTilesSelection/src/CesiumIonTilesetLoader.cpp b/Cesium3DTilesSelection/src/CesiumIonTilesetLoader.cpp index 269adba7a..27b25994c 100644 --- a/Cesium3DTilesSelection/src/CesiumIonTilesetLoader.cpp +++ b/Cesium3DTilesSelection/src/CesiumIonTilesetLoader.cpp @@ -167,8 +167,7 @@ mainThreadLoadLayerJsonFromAssetEndpoint( externals, contentOptions, url, - requestHeaders, - showCreditsOnScreen) + requestHeaders) .thenImmediately([credits = std::move(credits), requestHeaders, ionAssetID, diff --git a/Cesium3DTilesSelection/src/LayerJsonTerrainLoader.cpp b/Cesium3DTilesSelection/src/LayerJsonTerrainLoader.cpp index fa44d9bcf..46b0aa3c8 100644 --- a/Cesium3DTilesSelection/src/LayerJsonTerrainLoader.cpp +++ b/Cesium3DTilesSelection/src/LayerJsonTerrainLoader.cpp @@ -257,7 +257,6 @@ Future loadLayersRecursive( const rapidjson::Document& layerJson, const QuadtreeTilingScheme& tilingScheme, bool useWaterMask, - bool showCreditsOnScreen, LoadLayersResult&& loadLayersResult) { std::string version; const auto tilesetVersionIt = layerJson.FindMember("version"); @@ -350,7 +349,6 @@ Future loadLayersRecursive( pAssetAccessor, tilingScheme, useWaterMask, - showCreditsOnScreen, loadLayersResult = std::move(loadLayersResult)]( std::shared_ptr&& pCompletedRequest) mutable { const CesiumAsync::IAssetResponse* pResponse = @@ -403,7 +401,6 @@ Future loadLayersRecursive( layerJson, tilingScheme, useWaterMask, - showCreditsOnScreen, std::move(loadLayersResult)); }); } @@ -417,8 +414,7 @@ Future loadLayerJson( const std::string& baseUrl, const std::vector& requestHeaders, const rapidjson::Document& layerJson, - bool useWaterMask, - bool showCreditsOnScreen) { + bool useWaterMask) { // Use the projection and tiling scheme of the main layer. // Any underlying layers must use the same. std::string projectionString = @@ -472,7 +468,6 @@ Future loadLayerJson( layerJson, tilingScheme, useWaterMask, - showCreditsOnScreen, std::move(loadLayersResult)); } @@ -482,8 +477,7 @@ Future loadLayerJson( const std::string& baseUrl, const std::vector& requestHeaders, const gsl::span& layerJsonBinary, - bool useWaterMask, - bool showCreditsOnScreen) { + bool useWaterMask) { rapidjson::Document layerJson; layerJson.Parse( reinterpret_cast(layerJsonBinary.data()), @@ -503,8 +497,7 @@ Future loadLayerJson( baseUrl, requestHeaders, layerJson, - useWaterMask, - showCreditsOnScreen); + useWaterMask); } } // namespace @@ -514,8 +507,7 @@ LayerJsonTerrainLoader::createLoader( const TilesetExternals& externals, const TilesetContentOptions& contentOptions, const std::string& layerJsonUrl, - const std::vector& requestHeaders, - bool showCreditsOnScreen) { + const std::vector& requestHeaders) { bool useWaterMask = contentOptions.enableWaterMask; return externals.pAssetAccessor @@ -523,8 +515,7 @@ LayerJsonTerrainLoader::createLoader( .thenInWorkerThread( [asyncSystem = externals.asyncSystem, pAssetAccessor = externals.pAssetAccessor, - useWaterMask, - showCreditsOnScreen]( + useWaterMask]( std::shared_ptr&& pCompletedRequest) { const CesiumAsync::IAssetResponse* pResponse = pCompletedRequest->response(); @@ -560,8 +551,7 @@ LayerJsonTerrainLoader::createLoader( pCompletedRequest->url(), flatHeaders, pResponse->data(), - useWaterMask, - showCreditsOnScreen); + useWaterMask); }) .thenInMainThread([](LoadLayersResult&& loadLayersResult) { return convertToTilesetContentLoaderResult(std::move(loadLayersResult)); @@ -575,7 +565,6 @@ Cesium3DTilesSelection::LayerJsonTerrainLoader::createLoader( const TilesetContentOptions& contentOptions, const std::string& layerJsonUrl, const std::vector& requestHeaders, - bool showCreditsOnScreen, const rapidjson::Document& layerJson) { return loadLayerJson( asyncSystem, @@ -583,8 +572,7 @@ Cesium3DTilesSelection::LayerJsonTerrainLoader::createLoader( layerJsonUrl, requestHeaders, layerJson, - contentOptions.enableWaterMask, - showCreditsOnScreen) + contentOptions.enableWaterMask) .thenInMainThread([](LoadLayersResult&& loadLayersResult) { return convertToTilesetContentLoaderResult(std::move(loadLayersResult)); }); diff --git a/Cesium3DTilesSelection/src/LayerJsonTerrainLoader.h b/Cesium3DTilesSelection/src/LayerJsonTerrainLoader.h index baed32094..4698be683 100644 --- a/Cesium3DTilesSelection/src/LayerJsonTerrainLoader.h +++ b/Cesium3DTilesSelection/src/LayerJsonTerrainLoader.h @@ -34,8 +34,7 @@ class LayerJsonTerrainLoader : public TilesetContentLoader { const TilesetExternals& externals, const TilesetContentOptions& contentOptions, const std::string& layerJsonUrl, - const std::vector& requestHeaders, - bool showCreditsOnScreen); + const std::vector& requestHeaders); static CesiumAsync::Future> createLoader( @@ -44,7 +43,6 @@ class LayerJsonTerrainLoader : public TilesetContentLoader { const TilesetContentOptions& contentOptions, const std::string& layerJsonUrl, const std::vector& requestHeaders, - bool showCreditsOnScreen, const rapidjson::Document& layerJson); struct Layer { diff --git a/Cesium3DTilesSelection/src/TilesetContentManager.cpp b/Cesium3DTilesSelection/src/TilesetContentManager.cpp index 5d4a065ed..418ae973b 100644 --- a/Cesium3DTilesSelection/src/TilesetContentManager.cpp +++ b/Cesium3DTilesSelection/src/TilesetContentManager.cpp @@ -702,7 +702,6 @@ TilesetContentManager::TilesetContentManager( contentOptions, url, flatHeaders, - showCreditsOnScreen, tilesetJson) .thenImmediately( [](TilesetContentLoaderResult&& diff --git a/Cesium3DTilesSelection/test/TestLayerJsonTerrainLoader.cpp b/Cesium3DTilesSelection/test/TestLayerJsonTerrainLoader.cpp index f5afc0ef8..69635a9ca 100644 --- a/Cesium3DTilesSelection/test/TestLayerJsonTerrainLoader.cpp +++ b/Cesium3DTilesSelection/test/TestLayerJsonTerrainLoader.cpp @@ -94,12 +94,8 @@ TEST_CASE("Test create layer json terrain loader") { pMockedAssetAccessor->mockCompletedRequests.insert( {"layer.json", createMockAssetRequest(layerJsonPath)}); - auto loaderFuture = LayerJsonTerrainLoader::createLoader( - externals, - {}, - "layer.json", - {}, - true); + auto loaderFuture = + LayerJsonTerrainLoader::createLoader(externals, {}, "layer.json", {}); asyncSystem.dispatchMainThreadTasks(); @@ -183,12 +179,8 @@ TEST_CASE("Test create layer json terrain loader") { pMockedAssetAccessor->mockCompletedRequests.insert( {"layer.json", createMockAssetRequest(layerJsonPath)}); - auto loaderFuture = LayerJsonTerrainLoader::createLoader( - externals, - {}, - "layer.json", - {}, - true); + auto loaderFuture = + LayerJsonTerrainLoader::createLoader(externals, {}, "layer.json", {}); asyncSystem.dispatchMainThreadTasks(); @@ -207,12 +199,8 @@ TEST_CASE("Test create layer json terrain loader") { pMockedAssetAccessor->mockCompletedRequests.insert( {"layer.json", createMockAssetRequest(layerJsonPath)}); - auto loaderFuture = LayerJsonTerrainLoader::createLoader( - externals, - {}, - "layer.json", - {}, - true); + auto loaderFuture = + LayerJsonTerrainLoader::createLoader(externals, {}, "layer.json", {}); asyncSystem.dispatchMainThreadTasks(); @@ -231,12 +219,8 @@ TEST_CASE("Test create layer json terrain loader") { pMockedAssetAccessor->mockCompletedRequests.insert( {"layer.json", createMockAssetRequest(layerJsonPath)}); - auto loaderFuture = LayerJsonTerrainLoader::createLoader( - externals, - {}, - "layer.json", - {}, - true); + auto loaderFuture = + LayerJsonTerrainLoader::createLoader(externals, {}, "layer.json", {}); asyncSystem.dispatchMainThreadTasks(); @@ -264,12 +248,8 @@ TEST_CASE("Test create layer json terrain loader") { pMockedAssetAccessor->mockCompletedRequests.insert( {"layer.json", createMockAssetRequest(layerJsonPath)}); - auto loaderFuture = LayerJsonTerrainLoader::createLoader( - externals, - {}, - "layer.json", - {}, - true); + auto loaderFuture = + LayerJsonTerrainLoader::createLoader(externals, {}, "layer.json", {}); asyncSystem.dispatchMainThreadTasks(); @@ -311,12 +291,8 @@ TEST_CASE("Test create layer json terrain loader") { pMockedAssetAccessor->mockCompletedRequests.insert( {"./Parent/layer.json", createMockAssetRequest(parentJsonPath)}); - auto loaderFuture = LayerJsonTerrainLoader::createLoader( - externals, - {}, - "layer.json", - {}, - true); + auto loaderFuture = + LayerJsonTerrainLoader::createLoader(externals, {}, "layer.json", {}); asyncSystem.dispatchMainThreadTasks(); @@ -349,12 +325,8 @@ TEST_CASE("Test create layer json terrain loader") { pMockedAssetAccessor->mockCompletedRequests.insert( {"layer.json", createMockAssetRequest(layerJsonPath)}); - auto loaderFuture = LayerJsonTerrainLoader::createLoader( - externals, - {}, - "layer.json", - {}, - true); + auto loaderFuture = + LayerJsonTerrainLoader::createLoader(externals, {}, "layer.json", {}); asyncSystem.dispatchMainThreadTasks(); @@ -376,12 +348,8 @@ TEST_CASE("Test create layer json terrain loader") { pMockedAssetAccessor->mockCompletedRequests.insert( {"layer.json", createMockAssetRequest(layerJsonPath)}); - auto loaderFuture = LayerJsonTerrainLoader::createLoader( - externals, - {}, - "layer.json", - {}, - true); + auto loaderFuture = + LayerJsonTerrainLoader::createLoader(externals, {}, "layer.json", {}); asyncSystem.dispatchMainThreadTasks(); @@ -406,8 +374,7 @@ TEST_CASE("Test create layer json terrain loader") { externals, options, "layer.json", - {}, - true); + {}); asyncSystem.dispatchMainThreadTasks(); From b7da6add9932e8c00e55a4c1397daad4825a7558 Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Thu, 20 Jul 2023 16:57:36 +0200 Subject: [PATCH 110/421] Removed unused lambda capture --- Cesium3DTilesSelection/src/TilesetContentManager.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Cesium3DTilesSelection/src/TilesetContentManager.cpp b/Cesium3DTilesSelection/src/TilesetContentManager.cpp index 418ae973b..79ceb1130 100644 --- a/Cesium3DTilesSelection/src/TilesetContentManager.cpp +++ b/Cesium3DTilesSelection/src/TilesetContentManager.cpp @@ -634,8 +634,7 @@ TilesetContentManager::TilesetContentManager( [pLogger = externals.pLogger, asyncSystem = externals.asyncSystem, pAssetAccessor = externals.pAssetAccessor, - contentOptions = tilesetOptions.contentOptions, - showCreditsOnScreen = tilesetOptions.showCreditsOnScreen]( + contentOptions = tilesetOptions.contentOptions]( const std::shared_ptr& pCompletedRequest) { // Check if request is successful From 35b23187d774f3576e30b3e051b381d926bc4ea7 Mon Sep 17 00:00:00 2001 From: Joseph Kaile Date: Thu, 20 Jul 2023 15:32:43 -0400 Subject: [PATCH 111/421] transform texture coordinates on cpu --- .../include/CesiumGltfReader/GltfReader.h | 2 + CesiumGltfReader/src/GltfReader.cpp | 10 +- CesiumGltfReader/src/transformTexture.cpp | 96 +++++++++++++++++++ CesiumGltfReader/src/transformTexture.h | 10 ++ 4 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 CesiumGltfReader/src/transformTexture.cpp create mode 100644 CesiumGltfReader/src/transformTexture.h diff --git a/CesiumGltfReader/include/CesiumGltfReader/GltfReader.h b/CesiumGltfReader/include/CesiumGltfReader/GltfReader.h index 55a92339b..1a696a563 100644 --- a/CesiumGltfReader/include/CesiumGltfReader/GltfReader.h +++ b/CesiumGltfReader/include/CesiumGltfReader/GltfReader.h @@ -103,6 +103,8 @@ struct CESIUMGLTFREADER_API GltfReaderOptions { bool decodeQuantized = true; + bool transformTexture = true; + /** * @brief For each possible input transmission format, this struct names * the ideal target gpu-compressed pixel format to transcode to. diff --git a/CesiumGltfReader/src/GltfReader.cpp b/CesiumGltfReader/src/GltfReader.cpp index 5ece2f048..26cf103a2 100644 --- a/CesiumGltfReader/src/GltfReader.cpp +++ b/CesiumGltfReader/src/GltfReader.cpp @@ -6,10 +6,10 @@ #include "decodeMeshOpt.h" #include "decodeQuantized.h" #include "registerExtensions.h" +#include "transformTexture.h" #include #include -#include #include #include #include @@ -318,6 +318,14 @@ void postprocess( "KHR_mesh_quantization") != model.extensionsUsed.end()) { decodeQuantized(model); } + + if (options.transformTexture && + std::find( + model.extensionsUsed.begin(), + model.extensionsUsed.end(), + "KHR_texture_transform") != model.extensionsUsed.end()) { + transformTexture(model); + } } } // namespace diff --git a/CesiumGltfReader/src/transformTexture.cpp b/CesiumGltfReader/src/transformTexture.cpp new file mode 100644 index 000000000..a46414242 --- /dev/null +++ b/CesiumGltfReader/src/transformTexture.cpp @@ -0,0 +1,96 @@ +#include "transformTexture.h" + +#include "CesiumGltf/ExtensionKhrTextureTransform.h" +#include "CesiumGltfReader/GltfReader.h" + +#include +using namespace glm; +using namespace CesiumGltf; +namespace CesiumGltfReader { +namespace { +void transformBufferView( + Model& model, + const Accessor& accessor, + Buffer& buffer, + ExtensionKhrTextureTransform& textureTransform) { + + const AccessorView accessorView(model, accessor); + if (accessorView.status() == AccessorViewStatus::Valid) { + vec2 Offset(textureTransform.offset[0], textureTransform.offset[1]); + vec2 Scale(textureTransform.scale[0], textureTransform.scale[1]); + float Rotation = static_cast(textureTransform.rotation); + mat3 translation = mat3(1, 0, 0, 0, 1, 0, Offset.x, Offset.y, 1); + mat3 rotation = mat3( + cos(Rotation), + sin(Rotation), + 0, + -sin(Rotation), + cos(Rotation), + 0, + 0, + 0, + 1); + mat3 scale = mat3(Scale.x, 0, 0, 0, Scale.y, 0, 0, 0, 1); + mat3 matrix = translation * rotation * scale; + + vec2* uvs = reinterpret_cast(buffer.cesium.data.data()); + + for (int i = 0; i < accessorView.size(); i++) { + *uvs++ = glm::vec2((matrix * glm::vec3(accessorView[i], 1))); + } + } +} + +void processTextureInfo( + Model& model, + MeshPrimitive& primitive, + TextureInfo& textureInfo) { + ExtensionKhrTextureTransform* textureTransform = + textureInfo.getExtension(); + if (textureTransform) { + int64_t texCoord = 0; + if (textureTransform->texCoord) { + texCoord = *textureTransform->texCoord; + } + auto find = + primitive.attributes.find("TEXCOORD_" + std::to_string(texCoord)); + if (find != primitive.attributes.end()) { + const Accessor* pAccessor = + Model::getSafe(&model.accessors, find->second); + if (pAccessor) { + const BufferView* pBufferView = + Model::getSafe(&model.bufferViews, pAccessor->bufferView); + if (pBufferView) { + find->second = static_cast(model.accessors.size()); + Accessor& accessor = model.accessors.emplace_back(*pAccessor); + Buffer& buffer = model.buffers.emplace_back(); + buffer.cesium.data.resize(pBufferView->byteLength); + transformBufferView(model, accessor, buffer, *textureTransform); + accessor.bufferView = static_cast(model.bufferViews.size()); + BufferView& bufferView = model.bufferViews.emplace_back(*pBufferView); + bufferView.buffer = static_cast(model.buffers.size() - 1); + } + } + } + } +} +} // namespace + +void CesiumGltfReader::transformTexture(Model& model) { + for (Mesh& mesh : model.meshes) { + for (MeshPrimitive& primitive : mesh.primitives) { + Material* pMaterial = + Model::getSafe(&model.materials, primitive.material); + if (pMaterial) { + if (pMaterial->pbrMetallicRoughness) { + std::optional textureInfo = + pMaterial->pbrMetallicRoughness->baseColorTexture; + if (textureInfo) { + processTextureInfo(model, primitive, *textureInfo); + } + } + } + } + } +} +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/src/transformTexture.h b/CesiumGltfReader/src/transformTexture.h new file mode 100644 index 000000000..6485782fe --- /dev/null +++ b/CesiumGltfReader/src/transformTexture.h @@ -0,0 +1,10 @@ +#pragma once + +namespace CesiumGltf { +struct Model; +} + +namespace CesiumGltfReader { + +void transformTexture(CesiumGltf::Model& model); +} // namespace CesiumGltfReader From 39b5cc79f84e299af71ffe41633ecbd2ab72aa55 Mon Sep 17 00:00:00 2001 From: Joseph Kaile Date: Fri, 21 Jul 2023 12:39:14 -0400 Subject: [PATCH 112/421] fix linux compile errors --- CesiumGltfReader/src/decodeMeshOpt.cpp | 14 +++--- CesiumGltfReader/src/decodeQuantized.cpp | 52 ++++++++++++----------- CesiumGltfReader/src/transformTexture.cpp | 7 +-- 3 files changed, 39 insertions(+), 34 deletions(-) diff --git a/CesiumGltfReader/src/decodeMeshOpt.cpp b/CesiumGltfReader/src/decodeMeshOpt.cpp index 66f34f2be..4d19167cd 100644 --- a/CesiumGltfReader/src/decodeMeshOpt.cpp +++ b/CesiumGltfReader/src/decodeMeshOpt.cpp @@ -23,7 +23,7 @@ void decodeBufferView( destination.byteLength = static_cast(meshOpt.byteStride * accessor.count); - destination.cesium.data.resize(destination.byteLength); + destination.cesium.data.resize(static_cast(destination.byteLength)); bufferView.byteLength = destination.byteLength; bufferView.byteStride = meshOpt.byteStride; bufferView.byteOffset = 0; @@ -32,8 +32,8 @@ void decodeBufferView( ExtensionBufferViewExtMeshoptCompression::Mode::ATTRIBUTES) { if (meshopt_decodeVertexBuffer( destination.cesium.data.data(), - accessor.count, - bufferView.byteStride.value(), + static_cast(accessor.count), + static_cast(meshOpt.byteStride), reinterpret_cast(buffer.data()), buffer.size())) { return; @@ -43,7 +43,7 @@ void decodeBufferView( ExtensionBufferViewExtMeshoptCompression::Mode::TRIANGLES) { if (meshopt_decodeIndexBuffer( reinterpret_cast(destination.cesium.data.data()), - accessor.count, + static_cast(accessor.count), sizeof(uint16_t), reinterpret_cast(buffer.data()), buffer.size())) { @@ -53,7 +53,7 @@ void decodeBufferView( meshOpt.mode == ExtensionBufferViewExtMeshoptCompression::Mode::INDICES) { if (meshopt_decodeIndexSequence( reinterpret_cast(destination.cesium.data.data()), - accessor.count, + static_cast(accessor.count), sizeof(uint16_t), reinterpret_cast(buffer.data()), buffer.size())) { @@ -90,7 +90,7 @@ void decodeAccessor(int32_t accessor, Model& model) { } } // namespace -void CesiumGltfReader::decodeMeshOpt(Model& model) { +void decodeMeshOpt(Model& model) { for (Mesh& mesh : model.meshes) { for (MeshPrimitive& primitive : mesh.primitives) { decodeAccessor(primitive.indices, model); @@ -100,4 +100,4 @@ void CesiumGltfReader::decodeMeshOpt(Model& model) { } } } -} // namespace CesiumGltfReader \ No newline at end of file +} // namespace CesiumGltfReader diff --git a/CesiumGltfReader/src/decodeQuantized.cpp b/CesiumGltfReader/src/decodeQuantized.cpp index edbf947e9..de2d05bdb 100644 --- a/CesiumGltfReader/src/decodeQuantized.cpp +++ b/CesiumGltfReader/src/decodeQuantized.cpp @@ -6,36 +6,39 @@ #include -using namespace std; using namespace CesiumGltf; namespace CesiumGltfReader { namespace { -template +template using xVec = AccessorView>; -template +template float intToFloat(T t) { return intToFloat(t); } + +template <> float intToFloat(std::int8_t c) { + return std::max(c / 127.0f, -1.0f); +} + +template <> float intToFloat(std::uint8_t c) { return c / 127.0f; } + +template <> float intToFloat(std::int16_t c) { + return std::max(c / 65535.0f, -1.0f); +} + +template <> float intToFloat(std::uint16_t c) { return c / 65535.0f; } + +template void unquantizeFloat(float* fPtr, xVec& quantizedView) { for (int i = 0; i < quantizedView.size(); i++) { const auto& q = quantizedView[i]; - for (int j = 0; j < q.length(); j++) { + for (unsigned int j = 0; j < q.length(); j++) { *fPtr++ = intToFloat(q[j]); } } } -template float intToFloat(T t) { return intToFloat(t); } - -float intToFloat(int8_t c) { return max(c / 127.0f, -1.0f); } - -float intToFloat(uint8_t c) { return c / 127.0f; } - -float intToFloat(int16_t c) { return max(c / 65535.0f, -1.0f); } - -float intToFloat(unsigned short c) { return c / 65535.0f; } - -template +template void decodeAccessor(Model& model, Accessor& accessor) { xVec quantizedView(model, accessor); @@ -63,26 +66,26 @@ void decodeAccessor(Model& model, Accessor& accessor) { Buffer& buffer = model.buffers.emplace_back(); buffer.byteLength = pBufferView->byteLength; - buffer.cesium.data.resize(buffer.byteLength); + buffer.cesium.data.resize(static_cast(buffer.byteLength)); unquantizeFloat( reinterpret_cast(buffer.cesium.data.data()), quantizedView); } -template void decodeAccessor(Model& model, Accessor& accessor) { +template void decodeAccessor(Model& model, Accessor& accessor) { switch (accessor.componentType) { case Accessor::ComponentType::BYTE: - decodeAccessor(model, accessor); + decodeAccessor(model, accessor); break; case Accessor::ComponentType::UNSIGNED_BYTE: - decodeAccessor(model, accessor); + decodeAccessor(model, accessor); break; case Accessor::ComponentType::SHORT: - decodeAccessor(model, accessor); + decodeAccessor(model, accessor); break; case Accessor::ComponentType::UNSIGNED_SHORT: - decodeAccessor(model, accessor); + decodeAccessor(model, accessor); break; } } @@ -103,10 +106,11 @@ void decodeAccessor(Model& model, Accessor& accessor) { } } // namespace -void CesiumGltfReader::decodeQuantized(Model& model) { +void decodeQuantized(Model& model) { for (Mesh& mesh : model.meshes) { for (MeshPrimitive& primitive : mesh.primitives) { - for (pair& attribute : primitive.attributes) { + for (std::pair& attribute : + primitive.attributes) { Accessor* pAccessor = Model::getSafe(&model.accessors, attribute.second); if (pAccessor) { @@ -116,4 +120,4 @@ void CesiumGltfReader::decodeQuantized(Model& model) { } } } -} // namespace CesiumGltfReader \ No newline at end of file +} // namespace CesiumGltfReader diff --git a/CesiumGltfReader/src/transformTexture.cpp b/CesiumGltfReader/src/transformTexture.cpp index a46414242..46b323515 100644 --- a/CesiumGltfReader/src/transformTexture.cpp +++ b/CesiumGltfReader/src/transformTexture.cpp @@ -64,7 +64,8 @@ void processTextureInfo( find->second = static_cast(model.accessors.size()); Accessor& accessor = model.accessors.emplace_back(*pAccessor); Buffer& buffer = model.buffers.emplace_back(); - buffer.cesium.data.resize(pBufferView->byteLength); + buffer.cesium.data.resize( + static_cast(pBufferView->byteLength)); transformBufferView(model, accessor, buffer, *textureTransform); accessor.bufferView = static_cast(model.bufferViews.size()); BufferView& bufferView = model.bufferViews.emplace_back(*pBufferView); @@ -76,7 +77,7 @@ void processTextureInfo( } } // namespace -void CesiumGltfReader::transformTexture(Model& model) { +void transformTexture(Model& model) { for (Mesh& mesh : model.meshes) { for (MeshPrimitive& primitive : mesh.primitives) { Material* pMaterial = @@ -93,4 +94,4 @@ void CesiumGltfReader::transformTexture(Model& model) { } } } -} // namespace CesiumGltfReader \ No newline at end of file +} // namespace CesiumGltfReader From 4f4901c717e6dd76fc7b08bba703482cb892fffd Mon Sep 17 00:00:00 2001 From: Joseph Kaile Date: Fri, 21 Jul 2023 14:43:51 -0400 Subject: [PATCH 113/421] address pull request comments --- CHANGES.md | 4 +++ CesiumGltfReader/CMakeLists.txt | 1 - .../include/CesiumGltfReader/GltfReader.h | 12 +++++++-- CesiumGltfReader/src/GltfReader.cpp | 10 +++---- CesiumGltfReader/src/decodeMeshOpt.cpp | 17 ++++++------ ...Texture.cpp => transformTextureCoords.cpp} | 19 +++++++------- ...formTexture.h => transformTextureCoords.h} | 0 ...deQuantized.cpp => unquantizeMeshData.cpp} | 26 +++++++++---------- ...decodeQuantized.h => unquantizeMeshData.h} | 2 +- extern/CMakeLists.txt | 4 --- 10 files changed, 51 insertions(+), 44 deletions(-) rename CesiumGltfReader/src/{transformTexture.cpp => transformTextureCoords.cpp} (82%) rename CesiumGltfReader/src/{transformTexture.h => transformTextureCoords.h} (100%) rename CesiumGltfReader/src/{decodeQuantized.cpp => unquantizeMeshData.cpp} (80%) rename CesiumGltfReader/src/{decodeQuantized.h => unquantizeMeshData.h} (69%) diff --git a/CHANGES.md b/CHANGES.md index 80209b0c2..c81c63239 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,6 +2,10 @@ ### ? - ? +##### Additions :tada: + +- Added support for the `EXT_meshopt_compression` extension, which allows decompressing mesh data using the meshoptimizer library. Also added support for the `KHR_mesh_quantization` and `KHR_texture_transform` extensions, which are often used together with the `EXT_meshopt_compression` extension to optimize the size and performance of glTF files. + ##### Fixes :wrench: - Fixed a bug where `GltfReader::readImage` would always populate `mipPositions` when reading KTX2 images, even when the KTX2 file indicated that it had no mip levels and that they should be created, if necessary, from the base image. As a result, `generateMipMaps` wouldn't generate any mipmaps for the image. diff --git a/CesiumGltfReader/CMakeLists.txt b/CesiumGltfReader/CMakeLists.txt index 7c6d460ad..d88137563 100644 --- a/CesiumGltfReader/CMakeLists.txt +++ b/CesiumGltfReader/CMakeLists.txt @@ -46,7 +46,6 @@ target_include_directories( SYSTEM PUBLIC # Necessary for `draco/draco_features.h` ${CESIUM_NATIVE_DRACO_INCLUDE_DIR} - ${CESIUM_NATIVE_MESHOPTIMIZER_INCLUDE_DIR} ${CMAKE_CURRENT_LIST_DIR}/include ${CMAKE_CURRENT_LIST_DIR}/generated/include PRIVATE diff --git a/CesiumGltfReader/include/CesiumGltfReader/GltfReader.h b/CesiumGltfReader/include/CesiumGltfReader/GltfReader.h index 1a696a563..feba7d80b 100644 --- a/CesiumGltfReader/include/CesiumGltfReader/GltfReader.h +++ b/CesiumGltfReader/include/CesiumGltfReader/GltfReader.h @@ -101,9 +101,17 @@ struct CESIUMGLTFREADER_API GltfReaderOptions { */ bool decodeDraco = true; - bool decodeQuantized = true; + /** + * @brief Whether the quantized and compressed mesh data are unquantized on + * the CPU or not, according to the KHR_mesh_quantization extension + */ + bool unquantizeMeshData = true; - bool transformTexture = true; + /** + * @brief Whether the texture coordinates of a texture are transformed or + * not, according to the KHR_texture_transform extension + */ + bool applyTextureTransform = true; /** * @brief For each possible input transmission format, this struct names diff --git a/CesiumGltfReader/src/GltfReader.cpp b/CesiumGltfReader/src/GltfReader.cpp index 8243e9438..01d033741 100644 --- a/CesiumGltfReader/src/GltfReader.cpp +++ b/CesiumGltfReader/src/GltfReader.cpp @@ -4,9 +4,9 @@ #include "decodeDataUrls.h" #include "decodeDraco.h" #include "decodeMeshOpt.h" -#include "decodeQuantized.h" +#include "unquantizeMeshData.h" #include "registerExtensions.h" -#include "transformTexture.h" +#include "transformTextureCoords.h" #include #include @@ -311,15 +311,15 @@ void postprocess( decodeMeshOpt(model); } - if (options.decodeQuantized && + if (options.unquantizeMeshData && std::find( model.extensionsUsed.begin(), model.extensionsUsed.end(), "KHR_mesh_quantization") != model.extensionsUsed.end()) { - decodeQuantized(model); + unquantizeMeshData(model); } - if (options.transformTexture && + if (options.applyTextureTransform && std::find( model.extensionsUsed.begin(), model.extensionsUsed.end(), diff --git a/CesiumGltfReader/src/decodeMeshOpt.cpp b/CesiumGltfReader/src/decodeMeshOpt.cpp index 4d19167cd..9bd557cfc 100644 --- a/CesiumGltfReader/src/decodeMeshOpt.cpp +++ b/CesiumGltfReader/src/decodeMeshOpt.cpp @@ -68,21 +68,22 @@ void decodeAccessor(int32_t accessor, Model& model) { BufferView* pBufferView = Model::getSafe(&model.bufferViews, pAccessor->bufferView); if (pBufferView) { - ExtensionBufferViewExtMeshoptCompression* meshOpt = + ExtensionBufferViewExtMeshoptCompression* pMeshOpt = pBufferView->getExtension(); - if (meshOpt) { - pBufferView->buffer = static_cast(model.buffers.size()); - Buffer* pDest = &model.buffers.emplace_back(); - Buffer* pBuffer = model.getSafe(&model.buffers, meshOpt->buffer); + if (pMeshOpt) { + Buffer* pBuffer = model.getSafe(&model.buffers, pMeshOpt->buffer); if (pBuffer) { + pBufferView->buffer = static_cast(model.buffers.size()); + Buffer* pDest = &model.buffers.emplace_back(); + pBuffer = model.getSafe(&model.buffers, pMeshOpt->buffer); decodeBufferView( *pAccessor, *pDest, gsl::span( - pBuffer->cesium.data.data() + meshOpt->byteOffset, - static_cast(meshOpt->byteLength)), + pBuffer->cesium.data.data() + pMeshOpt->byteOffset, + static_cast(pMeshOpt->byteLength)), *pBufferView, - *meshOpt); + *pMeshOpt); } } } diff --git a/CesiumGltfReader/src/transformTexture.cpp b/CesiumGltfReader/src/transformTextureCoords.cpp similarity index 82% rename from CesiumGltfReader/src/transformTexture.cpp rename to CesiumGltfReader/src/transformTextureCoords.cpp index 46b323515..f258b8b15 100644 --- a/CesiumGltfReader/src/transformTexture.cpp +++ b/CesiumGltfReader/src/transformTextureCoords.cpp @@ -1,10 +1,9 @@ -#include "transformTexture.h" +#include "transformTextureCoords.h" #include "CesiumGltf/ExtensionKhrTextureTransform.h" #include "CesiumGltfReader/GltfReader.h" #include -using namespace glm; using namespace CesiumGltf; namespace CesiumGltfReader { namespace { @@ -14,13 +13,13 @@ void transformBufferView( Buffer& buffer, ExtensionKhrTextureTransform& textureTransform) { - const AccessorView accessorView(model, accessor); + const AccessorView accessorView(model, accessor); if (accessorView.status() == AccessorViewStatus::Valid) { - vec2 Offset(textureTransform.offset[0], textureTransform.offset[1]); - vec2 Scale(textureTransform.scale[0], textureTransform.scale[1]); + glm::vec2 Offset(textureTransform.offset[0], textureTransform.offset[1]); + glm::vec2 Scale(textureTransform.scale[0], textureTransform.scale[1]); float Rotation = static_cast(textureTransform.rotation); - mat3 translation = mat3(1, 0, 0, 0, 1, 0, Offset.x, Offset.y, 1); - mat3 rotation = mat3( + glm::mat3 translation = glm::mat3(1, 0, 0, 0, 1, 0, Offset.x, Offset.y, 1); + glm::mat3 rotation = glm::mat3( cos(Rotation), sin(Rotation), 0, @@ -30,10 +29,10 @@ void transformBufferView( 0, 0, 1); - mat3 scale = mat3(Scale.x, 0, 0, 0, Scale.y, 0, 0, 0, 1); - mat3 matrix = translation * rotation * scale; + glm::mat3 scale = glm::mat3(Scale.x, 0, 0, 0, Scale.y, 0, 0, 0, 1); + glm::mat3 matrix = translation * rotation * scale; - vec2* uvs = reinterpret_cast(buffer.cesium.data.data()); + glm::vec2* uvs = reinterpret_cast(buffer.cesium.data.data()); for (int i = 0; i < accessorView.size(); i++) { *uvs++ = glm::vec2((matrix * glm::vec3(accessorView[i], 1))); diff --git a/CesiumGltfReader/src/transformTexture.h b/CesiumGltfReader/src/transformTextureCoords.h similarity index 100% rename from CesiumGltfReader/src/transformTexture.h rename to CesiumGltfReader/src/transformTextureCoords.h diff --git a/CesiumGltfReader/src/decodeQuantized.cpp b/CesiumGltfReader/src/unquantizeMeshData.cpp similarity index 80% rename from CesiumGltfReader/src/decodeQuantized.cpp rename to CesiumGltfReader/src/unquantizeMeshData.cpp index de2d05bdb..2161a9f6b 100644 --- a/CesiumGltfReader/src/decodeQuantized.cpp +++ b/CesiumGltfReader/src/unquantizeMeshData.cpp @@ -1,4 +1,4 @@ -#include "decodeQuantized.h" +#include "unquantizeMeshData.h" #include "CesiumGltfReader/GltfReader.h" @@ -39,7 +39,7 @@ void unquantizeFloat(float* fPtr, xVec& quantizedView) { } template -void decodeAccessor(Model& model, Accessor& accessor) { +void unquantizeAccessor(Model& model, Accessor& accessor) { xVec quantizedView(model, accessor); accessor.componentType = AccessorSpec::ComponentType::FLOAT; @@ -73,40 +73,40 @@ void decodeAccessor(Model& model, Accessor& accessor) { quantizedView); } -template void decodeAccessor(Model& model, Accessor& accessor) { +template void unquantizeAccessor(Model& model, Accessor& accessor) { switch (accessor.componentType) { case Accessor::ComponentType::BYTE: - decodeAccessor(model, accessor); + unquantizeAccessor(model, accessor); break; case Accessor::ComponentType::UNSIGNED_BYTE: - decodeAccessor(model, accessor); + unquantizeAccessor(model, accessor); break; case Accessor::ComponentType::SHORT: - decodeAccessor(model, accessor); + unquantizeAccessor(model, accessor); break; case Accessor::ComponentType::UNSIGNED_SHORT: - decodeAccessor(model, accessor); + unquantizeAccessor(model, accessor); break; } } -void decodeAccessor(Model& model, Accessor& accessor) { +void unquantizeAccessor(Model& model, Accessor& accessor) { int8_t numberOfComponents = accessor.computeNumberOfComponents(); switch (numberOfComponents) { case 2: - decodeAccessor<2>(model, accessor); + unquantizeAccessor<2>(model, accessor); break; case 3: - decodeAccessor<3>(model, accessor); + unquantizeAccessor<3>(model, accessor); break; case 4: - decodeAccessor<4>(model, accessor); + unquantizeAccessor<4>(model, accessor); break; } } } // namespace -void decodeQuantized(Model& model) { +void unquantizeMeshData(Model& model) { for (Mesh& mesh : model.meshes) { for (MeshPrimitive& primitive : mesh.primitives) { for (std::pair& attribute : @@ -114,7 +114,7 @@ void decodeQuantized(Model& model) { Accessor* pAccessor = Model::getSafe(&model.accessors, attribute.second); if (pAccessor) { - decodeAccessor(model, *pAccessor); + unquantizeAccessor(model, *pAccessor); } } } diff --git a/CesiumGltfReader/src/decodeQuantized.h b/CesiumGltfReader/src/unquantizeMeshData.h similarity index 69% rename from CesiumGltfReader/src/decodeQuantized.h rename to CesiumGltfReader/src/unquantizeMeshData.h index 72af87680..14a20f855 100644 --- a/CesiumGltfReader/src/decodeQuantized.h +++ b/CesiumGltfReader/src/unquantizeMeshData.h @@ -6,5 +6,5 @@ struct Model; namespace CesiumGltfReader { -void decodeQuantized(CesiumGltf::Model& model); +void unquantizeMeshData(CesiumGltf::Model& model); } // namespace CesiumGltfReader diff --git a/extern/CMakeLists.txt b/extern/CMakeLists.txt index f27994e6d..0a6545035 100644 --- a/extern/CMakeLists.txt +++ b/extern/CMakeLists.txt @@ -157,10 +157,6 @@ set(CESIUM_NATIVE_LIBMORTON_INCUDE_DIR "${CMAKE_CURRENT_LIST_DIR}/libmorton/incl "Include directory for libmorton libraries" ) -set(CESIUM_NATIVE_MESHOPTIMIZER_INCLUDE_DIR "${CMAKE_CURRENT_LIST_DIR}/meshoptimizer/src/" CACHE INTERNAL - "Include directory for meshoptimizer" -) - set(BUILD_TESTING OFF CACHE INTERNAL "Disable libmorton Testing") add_subdirectory(libmorton) From 2e966fbeae0b55bfc058bc904473f70d0521bdcf Mon Sep 17 00:00:00 2001 From: Brian L <130494071+csciguy8@users.noreply.github.com> Date: Fri, 21 Jul 2023 14:58:30 -0600 Subject: [PATCH 114/421] Modify ResponseCacheControl to support checking for existence of directives with values Add additional logic to shouldCacheRequest: - Return false if no etag or last-modified header exists, can't revalidate - If cache control and max age (or s max age) exists, ignore Expires header --- CesiumAsync/src/CachingAssetAccessor.cpp | 79 ++++++++++++------------ CesiumAsync/src/ResponseCacheControl.cpp | 57 +++++++++-------- CesiumAsync/src/ResponseCacheControl.h | 56 +++++++++++++---- 3 files changed, 115 insertions(+), 77 deletions(-) diff --git a/CesiumAsync/src/CachingAssetAccessor.cpp b/CesiumAsync/src/CachingAssetAccessor.cpp index 986ab098e..b048b7b6c 100644 --- a/CesiumAsync/src/CachingAssetAccessor.cpp +++ b/CesiumAsync/src/CachingAssetAccessor.cpp @@ -180,16 +180,17 @@ Future> CachingAssetAccessor::get( std::vector newHeaders = headers; const CacheResponse& cacheResponse = cacheItem.cacheResponse; const HttpHeaders& responseHeaders = cacheResponse.headers; - HttpHeaders::const_iterator lastModifiedHeader = - responseHeaders.find("Last-Modified"); HttpHeaders::const_iterator etagHeader = responseHeaders.find("Etag"); if (etagHeader != responseHeaders.end()) { newHeaders.emplace_back("If-None-Match", etagHeader->second); - } else if (lastModifiedHeader != responseHeaders.end()) { - newHeaders.emplace_back( - "If-Modified-Since", - lastModifiedHeader->second); + } else { + HttpHeaders::const_iterator lastModifiedHeader = + responseHeaders.find("Last-Modified"); + if (lastModifiedHeader != responseHeaders.end()) + newHeaders.emplace_back( + "If-Modified-Since", + lastModifiedHeader->second); } return pAssetAccessor->get(asyncSystem, url, newHeaders) @@ -309,27 +310,31 @@ bool shouldCacheRequest( return false; } - // check if cache control contains no-store or no-cache directives - int maxAge = 0; - int staleWhileRevalidate = 0; + // If no headers exist that allow us to revalidate, don't put it in the cache + const HttpHeaders& headers = pResponse->headers(); + bool hasEtag = headers.find("Etag") != headers.end(); + bool hasLastModified = headers.find("Last-Modified") != headers.end(); + bool canRevalidate = hasEtag || hasLastModified; + if (!canRevalidate) + return false; + + // Check cache control header if it exists + bool ignoreExpiresHeader = false; if (cacheControl) { - if (cacheControl->noStore() || cacheControl->noCache()) { + if (cacheControl->noStore() || cacheControl->noCache()) return false; - } - maxAge = cacheControl->maxAge(); - staleWhileRevalidate = cacheControl->staleWhileRevalidate(); + // If there is a Cache-Control header with the max-age or s-maxage directive + // in the response, the Expires header is ignored + ignoreExpiresHeader = + cacheControl->maxAgeExists() || cacheControl->sharedMaxAgeExists(); } - // Prefer cache control directives over older Expires header - if (maxAge != 0 || staleWhileRevalidate != 0) { - // The response is cacheable - } else { - const HttpHeaders& responseHeaders = pResponse->headers(); - HttpHeaders::const_iterator expiresHeader = responseHeaders.find("Expires"); - if (expiresHeader == responseHeaders.end()) { + // Check older Expires header + if (!ignoreExpiresHeader) { + HttpHeaders::const_iterator expiresHeader = headers.find("Expires"); + if (expiresHeader == headers.end()) return false; - } return std::difftime( convertHttpDateToTime(expiresHeader->second), @@ -348,25 +353,23 @@ std::time_t calculateExpiryTime( const IAssetRequest& request, const std::optional& cacheControl) { - // Prefer cache control directives over older Expires header - if (cacheControl) { - // A specified maxAge indicates exact expiration. - // A specified staleWhileRevalidate still indicates expiration with maxAge, - // even if it is 0. - if (cacheControl->maxAge() != 0 || - cacheControl->staleWhileRevalidate() != 0) { - return std::time(nullptr) + cacheControl->maxAge(); - } - } + // If there is a Cache-Control header with the max-age or s-maxage directive + // in the response, the Expires header is ignored + bool preferCacheControl = + cacheControl && + (cacheControl->maxAgeExists() || cacheControl->sharedMaxAgeExists()); - const IAssetResponse* pResponse = request.response(); - const HttpHeaders& responseHeaders = pResponse->headers(); - HttpHeaders::const_iterator expiresHeader = responseHeaders.find("Expires"); - if (expiresHeader != responseHeaders.end()) { - return convertHttpDateToTime(expiresHeader->second); - } + if (preferCacheControl) { + return std::time(nullptr) + cacheControl->maxAgeValue(); + } else { + const IAssetResponse* pResponse = request.response(); + const HttpHeaders& responseHeaders = pResponse->headers(); + HttpHeaders::const_iterator expiresHeader = responseHeaders.find("Expires"); + if (expiresHeader != responseHeaders.end()) + return convertHttpDateToTime(expiresHeader->second); - return std::time(nullptr); + return std::time(nullptr); + } } std::unique_ptr diff --git a/CesiumAsync/src/ResponseCacheControl.cpp b/CesiumAsync/src/ResponseCacheControl.cpp index 004d4b539..84084a02c 100644 --- a/CesiumAsync/src/ResponseCacheControl.cpp +++ b/CesiumAsync/src/ResponseCacheControl.cpp @@ -14,9 +14,12 @@ ResponseCacheControl::ResponseCacheControl( bool accessControlPublic, bool accessControlPrivate, bool proxyRevalidate, - int maxAge, - int sharedMaxAge, - int staleWhileRevalidate) + bool maxAgeExists, + int maxAgeValue, + bool sharedMaxAgeExists, + int sharedMaxAgeValue, + bool staleWhileRevalidateExists, + int staleWhileRevalidateValue) : _mustRevalidate{mustRevalidate}, _noCache{noCache}, _noStore{noStore}, @@ -24,9 +27,12 @@ ResponseCacheControl::ResponseCacheControl( _accessControlPublic{accessControlPublic}, _accessControlPrivate{accessControlPrivate}, _proxyRevalidate{proxyRevalidate}, - _maxAge{maxAge}, - _sharedMaxAge{sharedMaxAge}, - _staleWhileRevalidate{staleWhileRevalidate} {} + _maxAgeExists{maxAgeExists}, + _maxAgeValue{maxAgeValue}, + _sharedMaxAgeExists{sharedMaxAgeExists}, + _sharedMaxAgeValue{sharedMaxAgeValue}, + _staleWhileRevalidateExists{staleWhileRevalidateExists}, + _staleWhileRevalidateValue{staleWhileRevalidateValue} {} /*static*/ std::optional ResponseCacheControl::parseFromResponseHeaders(const HttpHeaders& headers) { @@ -75,27 +81,21 @@ ResponseCacheControl::parseFromResponseHeaders(const HttpHeaders& headers) { bool proxyRevalidate = directives.find("proxy-revalidate") != directives.end(); - int maxAge = 0; std::map::const_iterator - maxAgeIter = parameterizedDirectives.find("max-age"); - if (maxAgeIter != parameterizedDirectives.end()) { - maxAge = std::stoi(maxAgeIter->second); - } + mapIter; - int sharedMaxAge = 0; - std::map::const_iterator - sharedMaxAgeIter = parameterizedDirectives.find("s-maxage"); - if (sharedMaxAgeIter != parameterizedDirectives.end()) { - sharedMaxAge = std::stoi(sharedMaxAgeIter->second); - } + mapIter = parameterizedDirectives.find("max-age"); + bool maxAgeExists = mapIter != parameterizedDirectives.end(); + int maxAgeValue = maxAgeExists ? std::stoi(mapIter->second) : 0; - int staleWhileRevalidate = 0; - std::map::const_iterator - staleWhileRevalidateIter = - parameterizedDirectives.find("stale-while-revalidate"); - if (staleWhileRevalidateIter != parameterizedDirectives.end()) { - staleWhileRevalidate = std::stoi(staleWhileRevalidateIter->second); - } + mapIter = parameterizedDirectives.find("s-maxage"); + bool sharedMaxAgeExists = mapIter != parameterizedDirectives.end(); + int sharedMaxAgeValue = sharedMaxAgeExists ? std::stoi(mapIter->second) : 0; + + mapIter = parameterizedDirectives.find("stale-while-revalidate"); + bool staleWhileRevalidateExists = mapIter != parameterizedDirectives.end(); + int staleWhileRevalidateValue = + staleWhileRevalidateExists ? std::stoi(mapIter->second) : 0; return ResponseCacheControl( mustRevalidate, @@ -105,9 +105,12 @@ ResponseCacheControl::parseFromResponseHeaders(const HttpHeaders& headers) { accessControlPublic, accessControlPrivate, proxyRevalidate, - maxAge, - sharedMaxAge, - staleWhileRevalidate); + maxAgeExists, + maxAgeValue, + sharedMaxAgeExists, + sharedMaxAgeValue, + staleWhileRevalidateExists, + staleWhileRevalidateValue); } std::string trimSpace(const std::string& str) { diff --git a/CesiumAsync/src/ResponseCacheControl.h b/CesiumAsync/src/ResponseCacheControl.h index 309c8ca4a..474143fc5 100644 --- a/CesiumAsync/src/ResponseCacheControl.h +++ b/CesiumAsync/src/ResponseCacheControl.h @@ -39,9 +39,12 @@ class ResponseCacheControl { bool accessControlPublic, bool accessControlPrivate, bool proxyRevalidate, - int maxAge, - int sharedMaxAge, - int staleWhileRevalidate); + bool maxAgeExists, + int maxAgeValue, + bool sharedMaxAgeExists, + int sharedMaxAgeValue, + bool staleWhileRevalidateExists, + int staleWhileRevalidateValue); /** * @brief Must-Revalidate directive that appears in the Cache-Control header. @@ -83,20 +86,43 @@ class ResponseCacheControl { inline bool proxyRevalidate() const noexcept { return _proxyRevalidate; } /** - * @brief Max-Age directive that appears in the Cache-Control header. + * @brief Existence of Max-Age directive that appears in the Cache-Control + * header. + */ + bool maxAgeExists() const noexcept { return _maxAgeExists; } + + /** + * @brief Value of Max-Age directive that appears in the Cache-Control header. */ - int maxAge() const noexcept { return _maxAge; } + int maxAgeValue() const noexcept { return _maxAgeValue; } /** - * @brief S-Maxage directive that appears in the Cache-Control header. + * @brief Existence of S-Maxage directive that appears in the Cache-Control + * header. */ - int sharedMaxAge() const noexcept { return _sharedMaxAge; } + bool sharedMaxAgeExists() const noexcept { return _sharedMaxAgeExists; } /** - * @brief Stale-While-Revalidate directive that appears in the Cache-Control + * @brief Value of S-Maxage directive that appears in the Cache-Control * header. */ - int staleWhileRevalidate() const noexcept { return _staleWhileRevalidate; } + int sharedMaxAgeValue() const noexcept { return _sharedMaxAgeValue; } + + /** + * @brief Existence of Stale-While-Revalidate directive that appears in the + * Cache-Control header. + */ + bool staleWhileRevalidateExists() const noexcept { + return _staleWhileRevalidateExists; + } + + /** + * @brief Value of Stale-While-Revalidate directive that appears in the + * Cache-Control header. + */ + int staleWhileRevalidateValue() const noexcept { + return _staleWhileRevalidateValue; + } /** * @brief Parse response cache control from the response's headers. @@ -112,8 +138,14 @@ class ResponseCacheControl { bool _accessControlPublic; bool _accessControlPrivate; bool _proxyRevalidate; - int _maxAge; - int _sharedMaxAge; - int _staleWhileRevalidate; + + bool _maxAgeExists; + int _maxAgeValue; + + bool _sharedMaxAgeExists; + int _sharedMaxAgeValue; + + bool _staleWhileRevalidateExists; + int _staleWhileRevalidateValue; }; } // namespace CesiumAsync From 4802c6a8137deba7e6addbbcf4bf1c1ff7d85bb8 Mon Sep 17 00:00:00 2001 From: Joseph Kaile Date: Fri, 21 Jul 2023 15:21:17 -0400 Subject: [PATCH 115/421] use 32 bit indices when numVertices > 65535 --- CesiumGltfReader/src/GltfReader.cpp | 2 +- CesiumGltfReader/src/decodeMeshOpt.cpp | 106 +++++++++--------- .../src/transformTextureCoords.cpp | 73 ++++++------ 3 files changed, 94 insertions(+), 87 deletions(-) diff --git a/CesiumGltfReader/src/GltfReader.cpp b/CesiumGltfReader/src/GltfReader.cpp index 01d033741..eec7bfc64 100644 --- a/CesiumGltfReader/src/GltfReader.cpp +++ b/CesiumGltfReader/src/GltfReader.cpp @@ -4,9 +4,9 @@ #include "decodeDataUrls.h" #include "decodeDraco.h" #include "decodeMeshOpt.h" -#include "unquantizeMeshData.h" #include "registerExtensions.h" #include "transformTextureCoords.h" +#include "unquantizeMeshData.h" #include #include diff --git a/CesiumGltfReader/src/decodeMeshOpt.cpp b/CesiumGltfReader/src/decodeMeshOpt.cpp index 9bd557cfc..008766b23 100644 --- a/CesiumGltfReader/src/decodeMeshOpt.cpp +++ b/CesiumGltfReader/src/decodeMeshOpt.cpp @@ -7,57 +7,52 @@ #include -using namespace std; using namespace CesiumGltf; namespace CesiumGltfReader { namespace { -void decodeBufferView( - Accessor& accessor, - Buffer& destination, - const gsl::span& buffer, - BufferView& bufferView, +template +int decodeIndices( + void* data, + const gsl::span& buffer, ExtensionBufferViewExtMeshoptCompression& meshOpt) { - - destination.byteLength = - static_cast(meshOpt.byteStride * accessor.count); - destination.cesium.data.resize(static_cast(destination.byteLength)); - bufferView.byteLength = destination.byteLength; - bufferView.byteStride = meshOpt.byteStride; - bufferView.byteOffset = 0; - if (meshOpt.mode == - ExtensionBufferViewExtMeshoptCompression::Mode::ATTRIBUTES) { - if (meshopt_decodeVertexBuffer( - destination.cesium.data.data(), - static_cast(accessor.count), - static_cast(meshOpt.byteStride), - reinterpret_cast(buffer.data()), - buffer.size())) { - return; - } - } else if ( - meshOpt.mode == ExtensionBufferViewExtMeshoptCompression::Mode::TRIANGLES) { - if (meshopt_decodeIndexBuffer( - reinterpret_cast(destination.cesium.data.data()), - static_cast(accessor.count), - sizeof(uint16_t), - reinterpret_cast(buffer.data()), - buffer.size())) { - return; - } + return meshopt_decodeIndexBuffer( + reinterpret_cast(data), + static_cast(meshOpt.count), + reinterpret_cast(buffer.data()), + buffer.size()); } else if ( meshOpt.mode == ExtensionBufferViewExtMeshoptCompression::Mode::INDICES) { - if (meshopt_decodeIndexSequence( - reinterpret_cast(destination.cesium.data.data()), - static_cast(accessor.count), - sizeof(uint16_t), - reinterpret_cast(buffer.data()), - buffer.size())) { - return; + return meshopt_decodeIndexSequence( + reinterpret_cast(data), + static_cast(meshOpt.count), + reinterpret_cast(buffer.data()), + buffer.size()); + } + return -1; +} + +int decodeBufferView( + void* pDest, + const gsl::span& buffer, + ExtensionBufferViewExtMeshoptCompression& meshOpt) { + if (meshOpt.mode == + ExtensionBufferViewExtMeshoptCompression::Mode::ATTRIBUTES) { + return meshopt_decodeVertexBuffer( + pDest, + static_cast(meshOpt.count), + static_cast(meshOpt.byteStride), + reinterpret_cast(buffer.data()), + buffer.size()); + } else { + if (meshOpt.count > std::numeric_limits::max()) { + return decodeIndices(pDest, buffer, meshOpt); + } else { + return decodeIndices(pDest, buffer, meshOpt); } } } @@ -71,19 +66,27 @@ void decodeAccessor(int32_t accessor, Model& model) { ExtensionBufferViewExtMeshoptCompression* pMeshOpt = pBufferView->getExtension(); if (pMeshOpt) { - Buffer* pBuffer = model.getSafe(&model.buffers, pMeshOpt->buffer); + Buffer* pBuffer = model.getSafe(&model.buffers, pMeshOpt->buffer); if (pBuffer) { - pBufferView->buffer = static_cast(model.buffers.size()); Buffer* pDest = &model.buffers.emplace_back(); pBuffer = model.getSafe(&model.buffers, pMeshOpt->buffer); - decodeBufferView( - *pAccessor, - *pDest, - gsl::span( - pBuffer->cesium.data.data() + pMeshOpt->byteOffset, - static_cast(pMeshOpt->byteLength)), - *pBufferView, - *pMeshOpt); + pDest->byteLength = + static_cast(pMeshOpt->byteStride * pMeshOpt->count); + pDest->cesium.data.resize(static_cast(pDest->byteLength)); + pBufferView->byteLength = pDest->byteLength; + pBufferView->byteStride = pMeshOpt->byteStride; + pBufferView->byteOffset = 0; + if (decodeBufferView( + pDest->cesium.data.data(), + gsl::span( + pBuffer->cesium.data.data() + pMeshOpt->byteOffset, + static_cast(pMeshOpt->byteLength)), + *pMeshOpt) == 0) { + pBufferView->buffer = + static_cast(model.buffers.size() - 1); + pBufferView->extensions.erase( + ExtensionBufferViewExtMeshoptCompression::ExtensionName); + } } } } @@ -95,7 +98,8 @@ void decodeMeshOpt(Model& model) { for (Mesh& mesh : model.meshes) { for (MeshPrimitive& primitive : mesh.primitives) { decodeAccessor(primitive.indices, model); - for (pair& attribute : primitive.attributes) { + for (std::pair attribute : + primitive.attributes) { decodeAccessor(attribute.second, model); } } diff --git a/CesiumGltfReader/src/transformTextureCoords.cpp b/CesiumGltfReader/src/transformTextureCoords.cpp index f258b8b15..6612c05e5 100644 --- a/CesiumGltfReader/src/transformTextureCoords.cpp +++ b/CesiumGltfReader/src/transformTextureCoords.cpp @@ -8,48 +8,45 @@ using namespace CesiumGltf; namespace CesiumGltfReader { namespace { void transformBufferView( - Model& model, - const Accessor& accessor, + const AccessorView& accessorView, Buffer& buffer, ExtensionKhrTextureTransform& textureTransform) { - const AccessorView accessorView(model, accessor); - if (accessorView.status() == AccessorViewStatus::Valid) { - glm::vec2 Offset(textureTransform.offset[0], textureTransform.offset[1]); - glm::vec2 Scale(textureTransform.scale[0], textureTransform.scale[1]); - float Rotation = static_cast(textureTransform.rotation); - glm::mat3 translation = glm::mat3(1, 0, 0, 0, 1, 0, Offset.x, Offset.y, 1); - glm::mat3 rotation = glm::mat3( - cos(Rotation), - sin(Rotation), - 0, - -sin(Rotation), - cos(Rotation), - 0, - 0, - 0, - 1); - glm::mat3 scale = glm::mat3(Scale.x, 0, 0, 0, Scale.y, 0, 0, 0, 1); - glm::mat3 matrix = translation * rotation * scale; + glm::vec2 Offset(textureTransform.offset[0], textureTransform.offset[1]); + glm::vec2 Scale(textureTransform.scale[0], textureTransform.scale[1]); + float Rotation = static_cast(textureTransform.rotation); + glm::mat3 translation = glm::mat3(1, 0, 0, 0, 1, 0, Offset.x, Offset.y, 1); + glm::mat3 rotation = glm::mat3( + cos(Rotation), + sin(Rotation), + 0, + -sin(Rotation), + cos(Rotation), + 0, + 0, + 0, + 1); + glm::mat3 scale = glm::mat3(Scale.x, 0, 0, 0, Scale.y, 0, 0, 0, 1); + glm::mat3 matrix = translation * rotation * scale; - glm::vec2* uvs = reinterpret_cast(buffer.cesium.data.data()); + glm::vec2* uvs = reinterpret_cast(buffer.cesium.data.data()); - for (int i = 0; i < accessorView.size(); i++) { - *uvs++ = glm::vec2((matrix * glm::vec3(accessorView[i], 1))); - } + for (int i = 0; i < accessorView.size(); i++) { + *uvs++ = glm::vec2((matrix * glm::vec3(accessorView[i], 1))); } } +} // namespace void processTextureInfo( Model& model, MeshPrimitive& primitive, TextureInfo& textureInfo) { - ExtensionKhrTextureTransform* textureTransform = + ExtensionKhrTextureTransform* pTextureTransform = textureInfo.getExtension(); - if (textureTransform) { + if (pTextureTransform) { int64_t texCoord = 0; - if (textureTransform->texCoord) { - texCoord = *textureTransform->texCoord; + if (pTextureTransform->texCoord) { + texCoord = *pTextureTransform->texCoord; } auto find = primitive.attributes.find("TEXCOORD_" + std::to_string(texCoord)); @@ -60,20 +57,26 @@ void processTextureInfo( const BufferView* pBufferView = Model::getSafe(&model.bufferViews, pAccessor->bufferView); if (pBufferView) { - find->second = static_cast(model.accessors.size()); Accessor& accessor = model.accessors.emplace_back(*pAccessor); Buffer& buffer = model.buffers.emplace_back(); buffer.cesium.data.resize( static_cast(pBufferView->byteLength)); - transformBufferView(model, accessor, buffer, *textureTransform); - accessor.bufferView = static_cast(model.bufferViews.size()); - BufferView& bufferView = model.bufferViews.emplace_back(*pBufferView); - bufferView.buffer = static_cast(model.buffers.size() - 1); + const AccessorView accessorView(model, accessor); + if (accessorView.status() == AccessorViewStatus::Valid) { + transformBufferView(accessorView, buffer, *pTextureTransform); + accessor.bufferView = + static_cast(model.bufferViews.size()); + BufferView& bufferView = + model.bufferViews.emplace_back(*pBufferView); + bufferView.buffer = static_cast(model.buffers.size() - 1); + find->second = static_cast(model.accessors.size() - 1); + textureInfo.extensions.erase( + ExtensionKhrTextureTransform::ExtensionName); + } } } } } -} } // namespace void transformTexture(Model& model) { @@ -83,7 +86,7 @@ void transformTexture(Model& model) { Model::getSafe(&model.materials, primitive.material); if (pMaterial) { if (pMaterial->pbrMetallicRoughness) { - std::optional textureInfo = + std::optional& textureInfo = pMaterial->pbrMetallicRoughness->baseColorTexture; if (textureInfo) { processTextureInfo(model, primitive, *textureInfo); From 67335352186c56db3555ace246a3896b5e297ab1 Mon Sep 17 00:00:00 2001 From: Brian L <130494071+csciguy8@users.noreply.github.com> Date: Fri, 21 Jul 2023 15:34:51 -0600 Subject: [PATCH 116/421] Handle noCache properly Let it store to cache, but always indicate stale (needs to revalidate) --- CesiumAsync/src/CachingAssetAccessor.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/CesiumAsync/src/CachingAssetAccessor.cpp b/CesiumAsync/src/CachingAssetAccessor.cpp index b048b7b6c..aee2ea234 100644 --- a/CesiumAsync/src/CachingAssetAccessor.cpp +++ b/CesiumAsync/src/CachingAssetAccessor.cpp @@ -269,13 +269,14 @@ bool shouldRevalidateCache(const CacheItem& cacheItem) { std::optional cacheControl = ResponseCacheControl::parseFromResponseHeaders( cacheItem.cacheResponse.headers); - if (cacheControl) { - if (isCacheStale(cacheItem) && cacheControl->mustRevalidate()) { - return true; - } - } + if (cacheControl && cacheControl->noCache()) + return true; + + bool cacheIsStale = isCacheStale(cacheItem); + if (cacheControl && cacheIsStale && cacheControl->mustRevalidate()) + return true; - return isCacheStale(cacheItem); + return cacheIsStale; } bool isCacheStale(const CacheItem& cacheItem) noexcept { @@ -321,7 +322,7 @@ bool shouldCacheRequest( // Check cache control header if it exists bool ignoreExpiresHeader = false; if (cacheControl) { - if (cacheControl->noStore() || cacheControl->noCache()) + if (cacheControl->noStore()) return false; // If there is a Cache-Control header with the max-age or s-maxage directive From ce3bd6ff5a251892ce544118ce50d69ae3f83e9d Mon Sep 17 00:00:00 2001 From: Brian L <130494071+csciguy8@users.noreply.github.com> Date: Fri, 21 Jul 2023 15:45:49 -0600 Subject: [PATCH 117/421] Fix for unit tests (function rename) --- CesiumAsync/test/TestCacheAssetAccessor.cpp | 8 ++++---- CesiumAsync/test/TestDiskCache.cpp | 4 ++-- CesiumAsync/test/TestResponseCacheControl.cpp | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/CesiumAsync/test/TestCacheAssetAccessor.cpp b/CesiumAsync/test/TestCacheAssetAccessor.cpp index c6821d7cc..e45ea6b61 100644 --- a/CesiumAsync/test/TestCacheAssetAccessor.cpp +++ b/CesiumAsync/test/TestCacheAssetAccessor.cpp @@ -593,8 +593,8 @@ TEST_CASE("Test serving cache item") { REQUIRE(cacheControl->accessControlPublic() == false); REQUIRE(cacheControl->accessControlPrivate() == true); REQUIRE(cacheControl->proxyRevalidate() == false); - REQUIRE(cacheControl->maxAge() == 100); - REQUIRE(cacheControl->sharedMaxAge() == 0); + REQUIRE(cacheControl->maxAgeValue() == 100); + REQUIRE(cacheControl->sharedMaxAgeValue() == 0); }) .wait(); } @@ -687,8 +687,8 @@ TEST_CASE("Test serving cache item") { REQUIRE(cacheControl->accessControlPublic() == false); REQUIRE(cacheControl->accessControlPrivate() == true); REQUIRE(cacheControl->proxyRevalidate() == false); - REQUIRE(cacheControl->maxAge() == 300); - REQUIRE(cacheControl->sharedMaxAge() == 0); + REQUIRE(cacheControl->maxAgeValue() == 300); + REQUIRE(cacheControl->sharedMaxAgeValue() == 0); }) .wait(); } diff --git a/CesiumAsync/test/TestDiskCache.cpp b/CesiumAsync/test/TestDiskCache.cpp index d8285746c..4481ba857 100644 --- a/CesiumAsync/test/TestDiskCache.cpp +++ b/CesiumAsync/test/TestDiskCache.cpp @@ -166,8 +166,8 @@ TEST_CASE("Test disk cache with Sqlite") { REQUIRE(cacheControl->accessControlPublic() == true); REQUIRE(cacheControl->accessControlPrivate() == false); REQUIRE(cacheControl->proxyRevalidate() == true); - REQUIRE(cacheControl->maxAge() == 0); - REQUIRE(cacheControl->sharedMaxAge() == 0); + REQUIRE(cacheControl->maxAgeValue() == 0); + REQUIRE(cacheControl->sharedMaxAgeValue() == 0); } } diff --git a/CesiumAsync/test/TestResponseCacheControl.cpp b/CesiumAsync/test/TestResponseCacheControl.cpp index d1cc7e7e5..75bb240b4 100644 --- a/CesiumAsync/test/TestResponseCacheControl.cpp +++ b/CesiumAsync/test/TestResponseCacheControl.cpp @@ -32,8 +32,8 @@ TEST_CASE("Test parsing cache-control header") { REQUIRE(cacheControl->accessControlPublic()); REQUIRE(cacheControl->accessControlPrivate()); REQUIRE(cacheControl->proxyRevalidate()); - REQUIRE(cacheControl->maxAge() == 1000); - REQUIRE(cacheControl->sharedMaxAge() == 10); + REQUIRE(cacheControl->maxAgeValue() == 1000); + REQUIRE(cacheControl->sharedMaxAgeValue() == 10); } SECTION("Header has cache-control header with only some directive") { @@ -53,7 +53,7 @@ TEST_CASE("Test parsing cache-control header") { REQUIRE(cacheControl->accessControlPublic()); REQUIRE(cacheControl->accessControlPrivate()); REQUIRE(cacheControl->proxyRevalidate() == false); - REQUIRE(cacheControl->maxAge() == 1000); - REQUIRE(cacheControl->sharedMaxAge() == 10); + REQUIRE(cacheControl->maxAgeValue() == 1000); + REQUIRE(cacheControl->sharedMaxAgeValue() == 10); } } From 2227dd6cd9f59dcb6bbe02bf8fd4a5c79e7b0142 Mon Sep 17 00:00:00 2001 From: Brian L <130494071+csciguy8@users.noreply.github.com> Date: Fri, 21 Jul 2023 16:58:25 -0600 Subject: [PATCH 118/421] Back out check for etag and last-modified We can still cache requests without this. Logic error caught by unit tests. --- CesiumAsync/src/CachingAssetAccessor.cpp | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/CesiumAsync/src/CachingAssetAccessor.cpp b/CesiumAsync/src/CachingAssetAccessor.cpp index aee2ea234..944ee6cfd 100644 --- a/CesiumAsync/src/CachingAssetAccessor.cpp +++ b/CesiumAsync/src/CachingAssetAccessor.cpp @@ -311,14 +311,6 @@ bool shouldCacheRequest( return false; } - // If no headers exist that allow us to revalidate, don't put it in the cache - const HttpHeaders& headers = pResponse->headers(); - bool hasEtag = headers.find("Etag") != headers.end(); - bool hasLastModified = headers.find("Last-Modified") != headers.end(); - bool canRevalidate = hasEtag || hasLastModified; - if (!canRevalidate) - return false; - // Check cache control header if it exists bool ignoreExpiresHeader = false; if (cacheControl) { @@ -333,6 +325,7 @@ bool shouldCacheRequest( // Check older Expires header if (!ignoreExpiresHeader) { + const HttpHeaders& headers = pResponse->headers(); HttpHeaders::const_iterator expiresHeader = headers.find("Expires"); if (expiresHeader == headers.end()) return false; From 1779ca87f3ac03d9bdf4e44a58d8aa155da13788 Mon Sep 17 00:00:00 2001 From: Joseph Kaile Date: Fri, 21 Jul 2023 19:04:04 -0400 Subject: [PATCH 119/421] add warning messages when meshopt fails --- CesiumGltfReader/src/GltfReader.cpp | 2 +- CesiumGltfReader/src/decodeMeshOpt.cpp | 113 ++++++++++++++++--------- CesiumGltfReader/src/decodeMeshOpt.h | 8 +- 3 files changed, 79 insertions(+), 44 deletions(-) diff --git a/CesiumGltfReader/src/GltfReader.cpp b/CesiumGltfReader/src/GltfReader.cpp index eec7bfc64..94a757b84 100644 --- a/CesiumGltfReader/src/GltfReader.cpp +++ b/CesiumGltfReader/src/GltfReader.cpp @@ -308,7 +308,7 @@ void postprocess( model.extensionsUsed.begin(), model.extensionsUsed.end(), "EXT_meshopt_compression") != model.extensionsUsed.end()) { - decodeMeshOpt(model); + decodeMeshOpt(model, readGltf); } if (options.unquantizeMeshData && diff --git a/CesiumGltfReader/src/decodeMeshOpt.cpp b/CesiumGltfReader/src/decodeMeshOpt.cpp index 008766b23..4a6c4ba09 100644 --- a/CesiumGltfReader/src/decodeMeshOpt.cpp +++ b/CesiumGltfReader/src/decodeMeshOpt.cpp @@ -15,25 +15,25 @@ namespace { template int decodeIndices( - void* data, + T* data, const gsl::span& buffer, ExtensionBufferViewExtMeshoptCompression& meshOpt) { if (meshOpt.mode == ExtensionBufferViewExtMeshoptCompression::Mode::TRIANGLES) { return meshopt_decodeIndexBuffer( - reinterpret_cast(data), + data, static_cast(meshOpt.count), reinterpret_cast(buffer.data()), buffer.size()); } else if ( meshOpt.mode == ExtensionBufferViewExtMeshoptCompression::Mode::INDICES) { return meshopt_decodeIndexSequence( - reinterpret_cast(data), + data, static_cast(meshOpt.count), reinterpret_cast(buffer.data()), buffer.size()); } - return -1; + return -1; // invalid mode; } int decodeBufferView( @@ -49,58 +49,87 @@ int decodeBufferView( reinterpret_cast(buffer.data()), buffer.size()); } else { - if (meshOpt.count > std::numeric_limits::max()) { - return decodeIndices(pDest, buffer, meshOpt); + if (meshOpt.byteStride == sizeof(std::uint16_t)) { + return decodeIndices( + reinterpret_cast(pDest), + buffer, + meshOpt); + } else if (meshOpt.byteStride == sizeof(std::uint32_t)) { + return decodeIndices( + reinterpret_cast(pDest), + buffer, + meshOpt); } else { - return decodeIndices(pDest, buffer, meshOpt); + return -1; } } } -void decodeAccessor(int32_t accessor, Model& model) { +void decodeAccessor( + int32_t accessor, + Model& model, + CesiumGltfReader::GltfReaderResult& readGltf) { Accessor* pAccessor = Model::getSafe(&model.accessors, accessor); - if (pAccessor) { - BufferView* pBufferView = - Model::getSafe(&model.bufferViews, pAccessor->bufferView); - if (pBufferView) { - ExtensionBufferViewExtMeshoptCompression* pMeshOpt = - pBufferView->getExtension(); - if (pMeshOpt) { - Buffer* pBuffer = model.getSafe(&model.buffers, pMeshOpt->buffer); - if (pBuffer) { - Buffer* pDest = &model.buffers.emplace_back(); - pBuffer = model.getSafe(&model.buffers, pMeshOpt->buffer); - pDest->byteLength = - static_cast(pMeshOpt->byteStride * pMeshOpt->count); - pDest->cesium.data.resize(static_cast(pDest->byteLength)); - pBufferView->byteLength = pDest->byteLength; - pBufferView->byteStride = pMeshOpt->byteStride; - pBufferView->byteOffset = 0; - if (decodeBufferView( - pDest->cesium.data.data(), - gsl::span( - pBuffer->cesium.data.data() + pMeshOpt->byteOffset, - static_cast(pMeshOpt->byteLength)), - *pMeshOpt) == 0) { - pBufferView->buffer = - static_cast(model.buffers.size() - 1); - pBufferView->extensions.erase( - ExtensionBufferViewExtMeshoptCompression::ExtensionName); - } - } - } - } + if (!pAccessor) { + return; + } + BufferView* pBufferView = + Model::getSafe(&model.bufferViews, pAccessor->bufferView); + if (!pBufferView) { + return; + } + ExtensionBufferViewExtMeshoptCompression* pMeshOpt = + pBufferView->getExtension(); + if (!pMeshOpt) { + return; + } + Buffer* pBuffer = model.getSafe(&model.buffers, pMeshOpt->buffer); + if (!pBuffer) { + readGltf.warnings.emplace_back( + "The ext_meshopt_compression extension has an invalid buffer " + "index."); + return; + } + if (pMeshOpt->byteOffset < 0 || pMeshOpt->byteLength < 0 || + pMeshOpt->byteOffset + pMeshOpt->byteLength > + static_cast(pBuffer->cesium.data.size())) { + readGltf.warnings.emplace_back( + "The ext_meshopt_compression extension has a bufferView that " + "extends beyond its buffer."); + return; + } + Buffer* pDest = &model.buffers.emplace_back(); + pBuffer = model.getSafe(&model.buffers, pMeshOpt->buffer); + int64_t byteLength = pMeshOpt->byteStride * pMeshOpt->count; + pDest->byteLength = byteLength; + pDest->cesium.data.resize(static_cast(byteLength)); + pBufferView->byteLength = byteLength; + pBufferView->byteStride = pMeshOpt->byteStride; + pBufferView->byteOffset = 0; + if (decodeBufferView( + pDest->cesium.data.data(), + gsl::span( + pBuffer->cesium.data.data() + pMeshOpt->byteOffset, + static_cast(pMeshOpt->byteLength)), + *pMeshOpt) != 0) { + readGltf.warnings.emplace_back( + "The ext_meshopt_compression extension has a corrupted or " + "incompatible meshopt compression buffer."); + return; } + pBufferView->buffer = static_cast(model.buffers.size() - 1); + pBufferView->extensions.erase( + ExtensionBufferViewExtMeshoptCompression::ExtensionName); } } // namespace -void decodeMeshOpt(Model& model) { +void decodeMeshOpt(Model& model, CesiumGltfReader::GltfReaderResult& readGltf) { for (Mesh& mesh : model.meshes) { for (MeshPrimitive& primitive : mesh.primitives) { - decodeAccessor(primitive.indices, model); + decodeAccessor(primitive.indices, model, readGltf); for (std::pair attribute : primitive.attributes) { - decodeAccessor(attribute.second, model); + decodeAccessor(attribute.second, model, readGltf); } } } diff --git a/CesiumGltfReader/src/decodeMeshOpt.h b/CesiumGltfReader/src/decodeMeshOpt.h index 9961c6f44..e5a9fb8aa 100644 --- a/CesiumGltfReader/src/decodeMeshOpt.h +++ b/CesiumGltfReader/src/decodeMeshOpt.h @@ -4,7 +4,13 @@ namespace CesiumGltf { struct Model; } +namespace CesiumGltfReader { +struct GltfReaderResult; +} + namespace CesiumGltfReader { -void decodeMeshOpt(CesiumGltf::Model& model); +void decodeMeshOpt( + CesiumGltf::Model& model, + CesiumGltfReader::GltfReaderResult& readGltf); } // namespace CesiumGltfReader From 030c5462b4e8b8ee72500d0dd7382b72ba27098d Mon Sep 17 00:00:00 2001 From: Joseph Kaile Date: Fri, 21 Jul 2023 20:17:40 -0400 Subject: [PATCH 120/421] add testing data and test --- .../src/transformTextureCoords.cpp | 80 +++++++++--------- CesiumGltfReader/src/unquantizeMeshData.cpp | 34 ++++---- CesiumGltfReader/test/TestGltfReader.cpp | 21 +++++ .../DucksMeshopt/Duck-vp-12-vt-12-vn-12.glb | Bin 0 -> 49852 bytes .../DucksMeshopt/Duck-vp-15-vt-15-vn-15.glb | Bin 0 -> 56840 bytes .../data/DucksMeshopt/Duck-vp-3-vt-3-vn-3.glb | Bin 0 -> 28748 bytes .../data/DucksMeshopt/Duck-vp-6-vt-6-vn-6.glb | Bin 0 -> 34664 bytes .../data/DucksMeshopt/Duck-vp-9-vt-9-vn-9.glb | Bin 0 -> 42956 bytes .../test/data/DucksMeshopt/README.md | 7 ++ 9 files changed, 85 insertions(+), 57 deletions(-) create mode 100644 CesiumGltfReader/test/data/DucksMeshopt/Duck-vp-12-vt-12-vn-12.glb create mode 100644 CesiumGltfReader/test/data/DucksMeshopt/Duck-vp-15-vt-15-vn-15.glb create mode 100644 CesiumGltfReader/test/data/DucksMeshopt/Duck-vp-3-vt-3-vn-3.glb create mode 100644 CesiumGltfReader/test/data/DucksMeshopt/Duck-vp-6-vt-6-vn-6.glb create mode 100644 CesiumGltfReader/test/data/DucksMeshopt/Duck-vp-9-vt-9-vn-9.glb create mode 100644 CesiumGltfReader/test/data/DucksMeshopt/README.md diff --git a/CesiumGltfReader/src/transformTextureCoords.cpp b/CesiumGltfReader/src/transformTextureCoords.cpp index 6612c05e5..a56bc94a9 100644 --- a/CesiumGltfReader/src/transformTextureCoords.cpp +++ b/CesiumGltfReader/src/transformTextureCoords.cpp @@ -40,42 +40,43 @@ void transformBufferView( void processTextureInfo( Model& model, MeshPrimitive& primitive, - TextureInfo& textureInfo) { + std::optional& textureInfo) { + if (!textureInfo) { + return; + } ExtensionKhrTextureTransform* pTextureTransform = - textureInfo.getExtension(); - if (pTextureTransform) { - int64_t texCoord = 0; - if (pTextureTransform->texCoord) { - texCoord = *pTextureTransform->texCoord; - } - auto find = - primitive.attributes.find("TEXCOORD_" + std::to_string(texCoord)); - if (find != primitive.attributes.end()) { - const Accessor* pAccessor = - Model::getSafe(&model.accessors, find->second); - if (pAccessor) { - const BufferView* pBufferView = - Model::getSafe(&model.bufferViews, pAccessor->bufferView); - if (pBufferView) { - Accessor& accessor = model.accessors.emplace_back(*pAccessor); - Buffer& buffer = model.buffers.emplace_back(); - buffer.cesium.data.resize( - static_cast(pBufferView->byteLength)); - const AccessorView accessorView(model, accessor); - if (accessorView.status() == AccessorViewStatus::Valid) { - transformBufferView(accessorView, buffer, *pTextureTransform); - accessor.bufferView = - static_cast(model.bufferViews.size()); - BufferView& bufferView = - model.bufferViews.emplace_back(*pBufferView); - bufferView.buffer = static_cast(model.buffers.size() - 1); - find->second = static_cast(model.accessors.size() - 1); - textureInfo.extensions.erase( - ExtensionKhrTextureTransform::ExtensionName); - } - } - } - } + textureInfo->getExtension(); + if (!pTextureTransform) { + return; + } + int64_t texCoord = 0; + if (pTextureTransform->texCoord) { + texCoord = *pTextureTransform->texCoord; + } + auto find = primitive.attributes.find("TEXCOORD_" + std::to_string(texCoord)); + if (find == primitive.attributes.end()) { + return; + } + const Accessor* pAccessor = Model::getSafe(&model.accessors, find->second); + if (!pAccessor) { + return; + } + const BufferView* pBufferView = + Model::getSafe(&model.bufferViews, pAccessor->bufferView); + if (!pBufferView) { + return; + } + Accessor& accessor = model.accessors.emplace_back(*pAccessor); + Buffer& buffer = model.buffers.emplace_back(); + buffer.cesium.data.resize(static_cast(pBufferView->byteLength)); + const AccessorView accessorView(model, accessor); + if (accessorView.status() == AccessorViewStatus::Valid) { + transformBufferView(accessorView, buffer, *pTextureTransform); + accessor.bufferView = static_cast(model.bufferViews.size()); + BufferView& bufferView = model.bufferViews.emplace_back(*pBufferView); + bufferView.buffer = static_cast(model.buffers.size() - 1); + find->second = static_cast(model.accessors.size() - 1); + textureInfo->extensions.erase(ExtensionKhrTextureTransform::ExtensionName); } } // namespace @@ -86,11 +87,10 @@ void transformTexture(Model& model) { Model::getSafe(&model.materials, primitive.material); if (pMaterial) { if (pMaterial->pbrMetallicRoughness) { - std::optional& textureInfo = - pMaterial->pbrMetallicRoughness->baseColorTexture; - if (textureInfo) { - processTextureInfo(model, primitive, *textureInfo); - } + processTextureInfo( + model, + primitive, + pMaterial->pbrMetallicRoughness->baseColorTexture); } } } diff --git a/CesiumGltfReader/src/unquantizeMeshData.cpp b/CesiumGltfReader/src/unquantizeMeshData.cpp index 2161a9f6b..03b60979c 100644 --- a/CesiumGltfReader/src/unquantizeMeshData.cpp +++ b/CesiumGltfReader/src/unquantizeMeshData.cpp @@ -29,10 +29,10 @@ template <> float intToFloat(std::int16_t c) { template <> float intToFloat(std::uint16_t c) { return c / 65535.0f; } template -void unquantizeFloat(float* fPtr, xVec& quantizedView) { +void unquantizeFloat(float* fPtr, const xVec& quantizedView) { for (int i = 0; i < quantizedView.size(); i++) { const auto& q = quantizedView[i]; - for (unsigned int j = 0; j < q.length(); j++) { + for (unsigned int j = 0; j < N; j++) { *fPtr++ = intToFloat(q[j]); } } @@ -40,12 +40,20 @@ void unquantizeFloat(float* fPtr, xVec& quantizedView) { template void unquantizeAccessor(Model& model, Accessor& accessor) { - xVec quantizedView(model, accessor); + if (quantizedView.status() != AccessorViewStatus::Valid) { + return; + } + if (quantizedView.size() != accessor.count) { + return; + } + Buffer& buffer = model.buffers.emplace_back(); + int64_t byteLength = accessor.count * N * sizeof(float); + buffer.byteLength = byteLength; + buffer.cesium.data.resize(byteLength); + accessor.componentType = AccessorSpec::ComponentType::FLOAT; accessor.byteOffset = 0; - accessor.count = accessor.count; - for (double& d : accessor.min) { d = intToFloat(static_cast(d)); } @@ -55,20 +63,12 @@ void unquantizeAccessor(Model& model, Accessor& accessor) { BufferView* pBufferView = Model::getSafe(&model.bufferViews, accessor.bufferView); - if (!pBufferView) { - accessor.bufferView = static_cast(model.bufferViews.size()); - pBufferView = &model.bufferViews.emplace_back(); - } + pBufferView->buffer = static_cast(model.buffers.size() - 1); pBufferView->byteOffset = 0; pBufferView->byteStride = N * sizeof(float); - pBufferView->byteLength = accessor.count * *pBufferView->byteStride; - pBufferView->buffer = static_cast(model.buffers.size()); - - Buffer& buffer = model.buffers.emplace_back(); - buffer.byteLength = pBufferView->byteLength; - buffer.cesium.data.resize(static_cast(buffer.byteLength)); + pBufferView->byteLength = byteLength; - unquantizeFloat( + unquantizeFloat( reinterpret_cast(buffer.cesium.data.data()), quantizedView); } @@ -109,7 +109,7 @@ void unquantizeAccessor(Model& model, Accessor& accessor) { void unquantizeMeshData(Model& model) { for (Mesh& mesh : model.meshes) { for (MeshPrimitive& primitive : mesh.primitives) { - for (std::pair& attribute : + for (std::pair attribute : primitive.attributes) { Accessor* pAccessor = Model::getSafe(&model.accessors, attribute.second); diff --git a/CesiumGltfReader/test/TestGltfReader.cpp b/CesiumGltfReader/test/TestGltfReader.cpp index 57b96e6df..0635cb6bd 100644 --- a/CesiumGltfReader/test/TestGltfReader.cpp +++ b/CesiumGltfReader/test/TestGltfReader.cpp @@ -108,6 +108,27 @@ TEST_CASE("CesiumGltfReader::GltfReader") { CHECK(model.meshes[0].primitives[0].targets[0]["NORMAL"] == 11); } +TEST_CASE("Can decompress meshes using EXT_meshopt_compression") { + std::vector filenames; + for (int n = 3; n <= 15; n += 3) { + std::string filename = CesiumGltfReader_TEST_DATA_DIR + + std::string("/DucksMeshopt/Duck") + "-vp-" + + std::to_string(n) + "-vt-" + std::to_string(n) + + "-vn-" + std::to_string(n) + ".glb"; + if (std::filesystem::exists(filename)) { + filenames.push_back(filename); + } + } + + for (const std::string& filename : filenames) { + std::vector data = readFile(filename); + GltfReader reader; + GltfReaderResult result = reader.readGltf(data); + REQUIRE(result.model); + REQUIRE(result.warnings.empty()); + } +} + TEST_CASE("Read TriangleWithoutIndices") { std::filesystem::path gltfFile = CesiumGltfReader_TEST_DATA_DIR; gltfFile /= diff --git a/CesiumGltfReader/test/data/DucksMeshopt/Duck-vp-12-vt-12-vn-12.glb b/CesiumGltfReader/test/data/DucksMeshopt/Duck-vp-12-vt-12-vn-12.glb new file mode 100644 index 0000000000000000000000000000000000000000..b744a6f105d58a51c1003124884c89aedafdff29 GIT binary patch literal 49852 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 $Slv?^cDwMV@%R=V-1P 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

eyHfWmb?OG8fO==q#sfnJG68Cgl+X)lYo zHa`@${P}%osq|(B(Mm?xXU1+>`G`{v?YB+`1g-k`ww z$`#hv!qTIrST#7MeRGW$S2gFY2miT;YGm|!sg`|H1A||&GxOX{aQF=pUXQjC3po|R zBQ)V+E!$!{ghj7rEo?`#2_u|)sytRSJLSxMg{Uzsw>!Pq;ThU+B*)h#FU?|CKtKTL z_3PIMjf|F7O>hgNndT9GL20apXhFcBzl(=QhD8htg=)wiwZsemIEtz%{n*b9U+j;} zhh6yTUDwvy6)$>Z;58k9Kny3vjQ*&u!YeI66w@UQOpTbQ%37OxrsGaEf&)#n2NiHGo&WjC8;pVTg2l&YqnVU zmARU#s&*-D{J-`PwWZ;dNMW#-mlry6(9Re;)fh8hTsWN_(f>7l+R`L;b*@gl$>Tgi zKodzj7|F=Jkd%~^3gV8~){@%8NLDwwaFZl0PO=~YDndtnOl`3} zE!*o8#=hN;-!|tWC@&Gv(-C97RE9zABn4R$&g~8e4n9&vHNxc5EibvdD+AUy4xNlK{QQxl5WBkp~sDtkfECi)8D#^>#k1!DmOjJvctT&%nT71t^n&fNekC zcl}d=qcaLMA;--dJELg57tGT@bLK}?K|w*LiNDhVy9MS^hD@J_5yc|4s>jMKkV%WIc+NyVf zqFdq3ApdAudbP4KhQiB>c6qdUtbt=UA>8D zNyYSPBy+i3XHQQma$UT&WDmyDD4b3zHa>vJ)qJFnFr;hEDSmUvIGU*EkkL+$;*qE3 zm|fK_ZXO;5l7?>0b*JTKwOXl^L82 zogC#mvp{#FbIEGXOz3_c6+?8UMVq;GB%jWs713yhu~u_NE{|?GIyw$Anasdh*E~t9 zq4q?jr13Gpa2G7g?rg~Do8L}`jDGyfbh~RiClp}5_(iFI=7A-j^z6+2f&<%5?yjz` ztCv6KJw725v*~&pNb9B9{p|j literal 0 HcmV?d00001 diff --git a/CesiumRasterOverlays/test/data/Cesium_Logo_Color/3/1/4.png b/CesiumRasterOverlays/test/data/Cesium_Logo_Color/3/1/4.png new file mode 100644 index 0000000000000000000000000000000000000000..5f793794e35b7dae39f54083c4199096457e3f7c GIT binary patch literal 7940 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 literal 0 HcmV?d00001 diff --git a/CesiumRasterOverlays/test/data/Cesium_Logo_Color/3/2/4.png b/CesiumRasterOverlays/test/data/Cesium_Logo_Color/3/2/4.png new file mode 100644 index 0000000000000000000000000000000000000000..ce57afc9d9d91d80e34d37c2138e9bb6a35c2669 GIT binary patch literal 4567 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 literal 0 HcmV?d00001 diff --git a/CesiumRasterOverlays/test/data/Cesium_Logo_Color/4/2/8.png b/CesiumRasterOverlays/test/data/Cesium_Logo_Color/4/2/8.png new file mode 100644 index 0000000000000000000000000000000000000000..cbefe0f1da24cb9cefee1a31258bfdd7806571f9 GIT binary patch literal 406 zcmeAS@N?(olHy`uVBq!ia0y~yU<5K5893O0R7}x|G$1F|)5S5QV$Rzuj)E-?0tXH} z=K4SD+RdcK`wTC`CWQCN?>TU<+TXn9V!ai^jARCZGzw6eANv8>?6szMfwqqX;9tnt X>%y+L)$Zw2kas;@{an^LB{Ts5KKL(t literal 0 HcmV?d00001 diff --git a/CesiumRasterOverlays/test/data/Cesium_Logo_Color/4/2/9.png b/CesiumRasterOverlays/test/data/Cesium_Logo_Color/4/2/9.png new file mode 100644 index 0000000000000000000000000000000000000000..4a2860eb5d61386a0d2c64df135923803b4edabc GIT binary patch literal 8229 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 AHe(;OhRtgSJ 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 literal 0 HcmV?d00001 diff --git a/CesiumRasterOverlays/test/data/Cesium_Logo_Color/4/5/8.png b/CesiumRasterOverlays/test/data/Cesium_Logo_Color/4/5/8.png new file mode 100644 index 0000000000000000000000000000000000000000..1243871b5e60f02d39a33412c00ad082c753665b GIT binary patch literal 416 zcmeAS@N?(olHy`uVBq!ia0y~yU<5K5893O0R7}x|G$1G4)5S5QV$Rz;j=YBr7+4N2 z(=s$RS1~S7_#%8VsEqC4-fxxH?^V}VT#-mVWBL50?1%H;zc4(IVQes<0BxB6`_=Qc jTd%$31v+E|fCJTcuP5<+Qa>zg2=c9`tDnm{r-UW|%gsG8 literal 0 HcmV?d00001 diff --git a/CesiumRasterOverlays/test/data/Cesium_Logo_Color/4/5/9.png b/CesiumRasterOverlays/test/data/Cesium_Logo_Color/4/5/9.png new file mode 100644 index 0000000000000000000000000000000000000000..25f75f80322bebcb4a83aa85c9a6bdb087eb0fc1 GIT binary patch literal 2794 zcmeHJc{r5q9)4y9qmd;QK9w0Q2wA@9%hpUHk}Z+Qc0Sp)nk+SDUR!a5B3VQEN}14v z?1L;FNuw+kLL~T`R1#( zLbblU-?j&@?yM-;cwvbc#D8`Vq& zQ?B-@{zoeBUpA<{Flnf)G+{?K+t;g2EUnyHx>9feM*ffJ9m&5B$4S|}K@xDBw6e0= zWhOIz!hL0fME?|LoolO>5wxlV+0>?}0H{+HK>rW}dUpZ1y$!VJA%W6K0NNyhj}iiG z-S_{G{|yPrp1_omF&&knB5_ahP>Kpcu3)zsJP&@_hCFg?)rwuH z=LYx{NQ3Y!1>dCHCv{f`yt5*lYf2;7Qdr}#| zuwi!Y#(mV3fhLZ2bdlF)U7AQ6PVx>c1XKH$J`}(x2!fH*-xtGBNVHF6WGy#{;CAzeYp%?-CvDf>jER>dDvUEbl5?}OeOR*lEjkd9t26gy z_87OHq2P{d%CsyD*Ce|NX-JU%m{|#$1)RJBJjIJa1BvG?wV-B^Ft0=iwisLjquxxc z(RGU~9nW;cVIlsArwzjtVh$XL{3=UjMt`QLtcEi9C3m^Vt zA-;k}qval1JeqpFE`Mlv*iDGC3+$`}>J9}jH)aI?+^J=tr{~BqMSvdPA8!*}!RXj!!HBFr| zp$JKSm2PVI-u7o=;~yWH7+?SSuLkVZxF)j?&4DwUOU+*ty-8;_B@z90*TOeX3mPJp zX1`SibYL}hU^1_KxRH$J^ENk6>{f$rYDlzP+nth<5+o7|ZVGreTsDP5=@S>;gnoKv zmuJ351NxO&1}=}(K1R!`I9IDuyql}5U8t@VG66&|`#y8@J1t_o1z|?wI;JBF5x*G9 z#E6Kwl0q`G5if@-$Di?r*(S}qvh483)0m8$!&b1)q$1!{yj@f0_V(Tr^5%3;Ezt#W zUaiwn1qz4U|8lGJAMkqx-VW>)tiNTzP&OEHi8l+=$~K+6UMF`4T~y$~U>g`4mrlG( zLklNATg68el%%H0T>APdMjasZf6${h&jozU|CFKbfOPN>O$+)cuvEW%etJ5jK3<7H z9HG(lJ?W&V_h%``+VO-LE0_WScOosQ9ZYP4okN6vQIBZ*mr-!fe4_ryGF3vnBn&>HfaHwqtm? z6E5hM^`1Y*0)Mx83 zusE)%Vfjv%Y`ZJ0PEmPt=n-mbYis>h48Z`ofPCO3%!7?BVsYd(1JQok5~0z=vPz%o z$tx+O2y>ET+dVAdRXpKjcNUdOO&%u4WK$zyPvDF_GKnZ{YHH#dL6I1u0?bu)O-;zc zP_-=yhtR~p&Lfo&%bEXH>~L)3;t&NbryKJT`Ipl{FCJpQbrD!yUk~|Se`8Hq3QBY# zDc~|~SkwwSGe<;-b#6qSheb*k@=T7_6a(U?`fG%=S(w3<4O6rTiq8!fA9q+?T)f;< zV0JZPw&fiG<#WM4J_J~@0nJ$J zS3xL8jUD|10|%0#DY&j@(k-x+C8zNv?s@b{;Ig`5r{0<^55a}H@Vz1HDc+~$0U(Bl z2QM=_Qj}nF>V#}z^cZ=w>=8BwQn_ z+mJ{APbMDH*DGpjT+7PJq*dgh=e5Eb%8@)53Ptn)J5I#i^XTw|3_}Jr9@#r=AX3qw zYtly4w0P_4RY&>lDzDlzg$*keB_$=Fd=YZ`e`It8&JC84*eHp*kinOq-e+pT4G@qX z7p`>c!Z~@+zKznNTvJiu@WRj)Fw=U>fIx3Tal|17) z#{F%OPGQBb?*m2QH>3&K6+77K$6!sG-fv`^+}$$gKspBqk$blglt#y-fn&p0F+kw{ duiGw;Zo{_ONipO2&VRR&r!4I)%FTUZ{|1}**4F?4 literal 0 HcmV?d00001 diff --git a/CesiumRasterOverlays/test/data/Cesium_Logo_Color/tilemapresource.xml b/CesiumRasterOverlays/test/data/Cesium_Logo_Color/tilemapresource.xml new file mode 100644 index 000000000..e6ac2fe36 --- /dev/null +++ b/CesiumRasterOverlays/test/data/Cesium_Logo_Color/tilemapresource.xml @@ -0,0 +1,17 @@ + + + Cesium_Logo_Color.jpg + + EPSG:900913 + + + + + + + + + + + + \ No newline at end of file diff --git a/CesiumRasterOverlays/test/data/Shadow_Tester.glb b/CesiumRasterOverlays/test/data/Shadow_Tester.glb new file mode 100644 index 0000000000000000000000000000000000000000..a51d5faff2aa8dafd169322f21a0673454a424d4 GIT binary patch literal 42976 zcmd752bfjGviH5#+S^f35K&Nqf&+*OlG6aQ*G2>cNs>g8f&|GqNKimf5lkQ|C@Khw z5mdy$%-&=bF$0RA7?7lZB4%OU-@m6D7oKzPInVce@AJIwTh-OIYE@TNcUN_<;ojSC zP=}_0=XqZ~<#{*8J+DRkHmxUQ_8d8~$6Eget8PaD&&rw51WY*8T>c$a6hYTHA zwjZyQy?jugA-(&IDBHYho3dl7SE*K|c4p_2n}vNfty%bZj>bL0)YpIN_} zrAB7f?>ZrKNYBCWGTWPHl{1G7?cGNYs&${#{qNp{%)xy|-pH$B4(>Us&xrm#2j#!t zd+6w1gZi}Z-@8xm%=)87jP6r8b6Bqtt@@1WIcQM-8`=#W-S5UBeMTZXA+uM{k$oBu z9W-=A)1EgNC%RUzY<^N|UvQ7ASI!*a-aWy?hdzV*j~v;5OdrSVTCFla6!aW4?8ct0 zupVdHv~6^C$0nJ^SnM_qO~@QJqW|Fjqj)#p@SdYajp*NN^eA;FWVUV7zIlh{ZCYp6 zXXMsx+O=xbGP8aS26WzYf;=Qp3bnJB<`ix}jGSg8twW`;vp;g@N3D3K{d26zd?q-`UU6`7?_BRZuY^|;T+%BAF6EW> z&ZjNyUEr0Wy#P2LsswEbudH_=_(EjMc$rY;;FN$WXP%YkNd>PwymGWKvjB`c<(asWpHcIuLV@~YJ+P-*YN6ib-jADb%9#+vW#37y?XR&dG*2d zy#_q51Fpqe4Z#iJ)%LP!tAa%(3|$SXBD8Qe7A3sUOY$TEt_ZDuBkyv0A-E#6`i;FS z=rsaYgjU+VWXj=l!>9zLSK(z*1 zLA5YXuJYRQq`7xB_-g2ONVf;tf;%AB65JAQN1zkX9()bFc3^Qk1J?qbz+K>V1dH1h z=muO1?hdarSlk}KbwD@p_3*la#qB}Ar`LK!Q^I6_K{ANJi z1AQ-Ww|5`-KInUZ+5Bce&4!)>+y|Zw+|O@@q2_uIK+gfs@gDRZ0zc%20i0eFG;C{O2s=X;OAdmKEU_a8U?g>WA87I{yA zpYRsKU1X#ddW-2j;Vl6#fnI>zlfYu|0`DnrDf$+G7tnu--;>_c;HSN3;5`L?(tDQP zlgK{@e$Mos^`57F&U*p;0`xOTECZegKLaf1Hv{S!@KX9OdN09Q2404YdM|n}11r2& zz^_0*4=w)7-b#Kmpk4wmhkn_66?nya4g4DPO5k;VGYs{Hw+i}I@T)w{0q1z)z2W5o zIbI5!f?frr`OSb@1%93WYHtmkTyQQjYr$&`Z?(6McCEJ_ydHWDuz}wUs5Rg;{f*w6 zaMpp>A@dgaEqEKfx4lj9Hi9?8c?bNC;l1r`rhNyz33!*^45&@uH=#Fs?*Z?F-vR6S zd)^29W01!Tftj- zItV<7UHvIAg5M0NPr;wCmuJu)3m(qiodI3s$D6*9ZLuc5y7z65{iedFy0@8-$Z-nUTS!V!0mw->yZC*OMep!UHL zcfWT4e1Iq4LGOY8o#B4(9i)GdC*OI8pbmN8gTMCPI-=L;Yx;9if&S z23pfU0(HduiP~}mXalDKR0BBTCaFt50WILvhpG=pxFKy_ct3haX@B&7rqEbd>xKHxj>F?fG~#r=!eeC-{l z{TkRu?-=cIFAw+{{57%81KwwS=7HbhXq5-fqwo2WTTlBI*!Mj@@O^(h`zHYVe$0>4 zj~jjrD(0W!AM;{B9LWs&8F0is)&J8w1vutq_@~i74UV`4{NKG(fj_;|{DSlg!V&j$ z|5vX7@Vi&gKZE`maKtU-|LmO({OT2kJ{^8x!_6Y1h5UNNudsh6PtNqS=%48qp{>LT zC(A41R|22q7lnHkP}naG?@aooy)*r@{d3@+4HSbaYTC1qDGslgf3ANX?YTg4sB>sb zK$QfFflKkEIJh|65`Jm_eE$O4^MR7|%J8HFxCGqN_?^L%(%{lOEeI|MuPnR_a0XG! z@h*hQ@yhxssPmyy(1HINy&UpjrhlRT3RqMbbY=qLl!GbZ zlgfTosH$+pt>#w;SLaC$=*!^OFx;AcE&8>1Qp2weRokx#uIbnD>w@d@q_$rVsvaD1 zv;6wt`aG%UH-KsYN8E<+8u-~z+0g1I;576@KM79K%k~>VHG(7AgrDgp;FklJO76WXTWBz89i zSLDvn1l)vPMV>SRH}kIqU+FgoH}_kBTliOjuku@hTl%fQt^C&D)_xms8^0~Mt$#K6 zYQG)0o!=hZ-tPeJ;CBRf^gDq&`PYE2@jHV%`#OzRSwJ5)E?>!BI|+2Ec$=??A=w--MO#^eg($ zc>};pz|V2+smM-P%xUU5&Q<;4KH&{S%ObF-L1-Nah%*>!FdT7*V8tMyADp33L*a-! z46BC#ec%j-8V*O?5qLQaxB<>csF85Q9Ys_|06qQD&?EdYv?F*r#ylHK97h2?;EshF z>yPtq0^bC0v_BqdJRET+_!Gesc{1Lg1a&ibR=X--6=XB1kz<&^YKhN$4i<*n<{eUPeVNoN4O2`Gqg*=ZD^mR72THJbN=&C&jHVpncC7mPy3Aj0{x}_GM+pO zewO|VjJDWcPP-UbO7BIUya0Xy?n}U8@M5fgnRb!C!heN!1@JPIBvwK_4lDw{%9EGD zFT;HecocXX{5rgqU~ylg|A_wv?IUnrgL(sMzQ2lgzMtdg(&hlGpkAj_W*0+b}tP*6?{8*HP5DiMXf=0H6YF!s5Smt##jU7`0Jq8 zz+Y#$>lt}1kPCkU)CPY&c)h<7Kh^`U!+8_xO*rDdh2I;2SK+)3^)?)FHxZ+^fE94w zfqDmyxSNUZCg2q~??Sx`N8I<|z3aaZ^**%v%iz4{f542r2Q2eHgno~wAA(=u*>b3l z;C<|Gf%+JD-`@)7L;73Ivu!-x>TiR$&Hn^?8}+FjxE;N%{-;o%!jaS`tddXQKM(yp zqqSqrJkQe(Jm2nr2DKg73e|yj2feNSPXBY-oxl$N3!ZGJ{{?s#{Vl*&@RvN_0p0=k zE8t^b3;1hzyTIap1MCJq27e3hE3mkGfW5$O@IH9ofW_Sp902x$zk|02EbjNfLEr%R z5WM|haliKu`#<p|0gC*-`&z{}&?qGw>z+_B?6p{X)Mz z-`w=NwO&bX7zH`07o#o4ec%jkJIA;=d9=s5FSh0j;sM$YKr8TGde;CQz`N;n z0j>e>2D)>9-EDApZpJ(5UGM$j|LOnb9|Qlv?=Sy2__&`3=MS(K{7KITAM*pSAH={x z5C_MCQ^4^c1AIzwDmWuJ4SZ@)0DM|d5L_TQ9b7Ot1AKZ=2z*9R7+feg6I?hb0zNZ1 z3tS{93O*}18(cIv2Yhx=417*d99%3o7hF6z4}5M=0(@Rj5?mt4!}5~BUtljd4$i{| zQOBT427YkN;F7!(&=W3!mKd$@dFYMP3ZKhJr_c%)XY34G;bK^GDy{H2*moMO@Yz^g zfL6FDJ`|)CJ`1l-rxh-OpJ&htpNZ#%XbT6Wg3`hH;PZnEf-% z1ZVQRY)}rW92{}W!>a(k5KaZtzbL3kzaspLgNvXl(3TG_2`;5~2~Y{Rl;;-(m4nMn z?=mDR)33twNOi53+%JK?0nBZh+4rIOJ&(xR*95A9+w%M>@KtcH2I>Gc!R_F+0gHPzzP1lK z(6$Ha(Cf&PtHD>pZBM@;zbl~HgFAsc!EcB+SMX~bTm!xaUSs^}4DL+ta{Rd#d@a2u zcycY!4BMLmUBF%7bcJpX)dH$DAi8VNE$ANfpzUtB-M~G9>*%!xx5mTkXaBDLho99>IUvcuN$5WHSN$~ zSTLO4FrYiU9`t(A>mG~rZ=4S)?ga_TY(Y5bowLdPX~{IHzK$#xSjSk zU<}kW+B={|2Q%P}0gs_S9uP;=oxx0l$7A_Scxq?Cy%T&F_^#k?elwva1o!alCh#oi zNx{9~d+ALK?xQyeJR3Y4-p#=rxD&zmLEl1q3x3=Wz8`urUfmB&4d$A5F6~r2xS#d` zV0!SNX&*%PeyBOXeZYgkL*R!PLA}}Zrw8-E^Md){`N6|Hdk{Pi{Zr#FAkPKEe4)|T10y{ z)J$Lo_(`5T0e-?n?5^M`+NbEx3YHSZyTDHot*7bF0xyNWm-b#fc?SFp^lUtM82m83 zhq3xu@Uy{lruQ8E1@xW|UZ8y*c#PfxdduiN7Ay~5q+JdygL;8>5!9o=W8jx~vJAWo z?#r~oi}2xPU~#a5c5(1Zu#)x_U8+;!0=*Z5HKu)mc5SeZ z{#sx;)Ee6L^p*!3f{nBrfb~%8XjekL1S|)?$&>Zq^>E*!6<&!4Z_~aKYzp3?-2}W1 z^(O6W!Di^UdG;E3GtXYbug!SyF6|rO_rUKNP7b|Q!Tb1^1O5Q~f#Ia+<>E<--|FB) z@P}|#6Wt@F~qIKg5qM;4RQw@L~^mPq5eY z_Ts}Hc%KCOp!Pv;#q$01_tX0%I1qeC{{XNZYCr84^tK1z2M1}t2fhdn@#FygL*T>k zz5suL<(~&XKph7E2>vlR!tZ;i&++Sr;HTgy?Jn9c`5lF`JNTL2SG2-EnP)!-zd-%M z^WAv*Gd}%F`wjRv@NaOw4St9FHTYNPy|jDr;t%j2!Jnr0C!YKPZ-4L?)L+p1u=p7L zWAyf8_c8j1g5$J#{8N!b;6wQ1#f}@U9}A#@*fBUUs94Mg`>}ZJ6sS{p8jEE>Wx(-c zr^1PY$#S6pEb% zJ`4Ixps0CLl(tChY~U>L4?)q`kHFcnb4)9IPOMn$2=iDB-r2F@;Nr1!;hh6MD|Q~d zv%w|6C3tdLtR&n*v}f@v87l=Y#q-mMMafv{*!i@j0mbNio|L356}tdlMyyP%ENvO! zg4l&TElvMIa3=j2kO3~o^9#Tiz%37WKnz>~UM5)F^0B<&qF6=RivW+_#XKn=yBMfQ z?;z{(BHHg+nHR-`i^VSC$wm07R{e8hm*V54u}VBC20jE`xh6xH$e+#N?7;JWn7 z0A<1TcwQ4+6K)nz4k!bz53ep*+$?;&IM#snVxSzohCImvXThxmw<7JuP?exA0bd%+ z24}|-{3LNHUS$K7V<9+Vw;we!EzExE*5cz*ocV0B#@a2<{l`#IG$>Hr}=a zw=;4{o+s#a;MV}E18+4z;u`qv!JWaKc{@o&t_5EUFC-RSz+LDy!tW;FCiI%%XIIm9 zg=!Y-2JRN?4)02E59ns#W_W)cZPVEG;Ok;N;rD><3GPL|1<(|HL##K{4L~ob>uLKy zwFX*%`|_k0xEI_$#H4MkA8lKpHN6{o(g)lJZa=u4V*P15#RkCZ2ku9|Q*0o(KX?Fi zN7{~fItV-n`kL5adY!>Tz(e3&gSUf$F8FgTFc|0_8*192P~GsLJ1`7hckuAo2ztW- z`8Fap5?)X6D0tV$MuSJi#=svAJqA3MesAD<@VMAbP~(8HP@`$bL)`%M22bG0Snyc5 z6M??K4d6-e#)HM3h^GT$H`5LP`qI0FClkRF;SP*VhCcv21w6%Y2GJXYr&GaGp$Fr~ z5PrAP8v;EIJdNH^{Fnxej7>M~bf^({FcP>8-bnE6u{-G94veNZhTaT%qhoi*X42jX z%z(Osb{5o4z-aJYJedKW0rzfT9B>o(9(c3B;@%5P0LFpugLgMr+OnVEzIkEe}_rsqYn+tCWcneb`J>=R;VuU50qz1Xfwu@O?vucMz&+rn;4KD=yEOJR_!-(K`P~QgB%G)4 z`B`|gX@!>>iDzTaK|ROwIkD$?azFT4{C$D`9Psnd57NE>UIsiATMk|h{Q~ej@FMbR zp9Pl3UIM>lcrV6YrhO^40=xqHS>QQ-FUMX1R>W3?|5_|Xe--$(SlZy%W2>Q8n_fD$hIVyqEqE<- z4if8tHL>-kU5}<5sNC2FVwnp(1zs217<&^=u4(n`EoNW?umt{FrvEl`^%n31oJ~-h zVsC@rj=jUYz70GEXEW5haNdS`*F1ZVIe!Ouoc?=I@5SDy_ipS1s1Kmk{}9ec;P=7r z$3BL?h4y{$R(kJ)7xHvl>=WQa@Q27PgkHdJn^}qTX&(kYVQ%I@EeBrW_b{A?;AqA^ zVK!EPKLUS5>{o(627k;9ybj(9-b&=QGVZ6~Ph;D`+liuhYh$0m*#h2zeLHABi0zDh zPP-G>5&MFtpV9vUyo>(3zz5(jdAdF*iP2in8HA*h42KSJ#UJ_jG+$szC|xIY130z1J+ z;r$2}_h;a1;7jl?@O}b|`zx>;_!|5hyr041{vP`SyodHze!HQ5h4Ux)PbBu@)$icn zjl}-gUr>MX{9AZ?X#a{G10O?nU+g%&{j`7aJ08me=ka78zI*X~z;UPp{9~r?055(3 z@BH`=u^_JYAZ;ujrym0jLk6^`z&jkvh@VQE0h|IAr#%ho2u}~=cLAQ95-$Lp7B2`K zjh)7`qp{QBofbdcaL?e$FR_9=`vrc%_!;p(V}-!KVfPudzu@T^@j~&!(1qh?@;lwg z9E%k}vItL(;mcXzv*JbYqi8&je$n{Zv}ec9g5$-D;*%dg2Ye2+kNxLhYcbOXw5P_4 z(?1s~4s|a0ym$$?=K&c|#SNW-)g|E-h?k0&rY!}OgepOMK2!m40jxV8I5U0$?V0g1 z@v^jKfD52X8eZY}g*>?sPN8@vJ{1OE2wjA>2o{$EmxDeFAIgKv(<>UUK<{kuMc|9z z6~%`NKrwIy@VVe~c~UWcF>OUao?INi1m3ye67Y)0F9nx~SAt&=x)Qi_yfU21^h(4p zi&vq48BhwUGHq3QrQ+4%)oH5%RiUcT)_^Jtlmb_ytr4#ouLUkcTZ3O&s2Xr;^Q#6` zCtjB)7t+?m?+XpCX{77M>(Q%+Y-T)*=b3=yvf}l@^?5H7|FeMd@dn@q(B<*z67VJT zF2R>d;7aj^rq?i@4SgxSN>B+vbap(9C*zH1lZG3D8^tf@`K9s3@Dktz{VG6Z;8O4v z@Gb|7Tb;H_ya{bJ@D;R8X+<}qm(AZ1nt+?YO$5#0Hsx9E_?5J^UcYPR|E3AUA#R{8i6~&OU66KFAJ^)UrqnApdD>9sK!7N+=(Y0z#ZT= zqkjc{b%Lq~PVl#r+O)O#TTDlAN91bo_nMCJO8kvSdMl%^JtI~Q+5;B{ZRuUZuYLUD zpbb`Z=GT_~Ran{?+!>Esg0I2nmho$WYrw65F2tY}xC?Z-;9B?F@$T`iK#%x!@$2I~!9C-> z;y1*5$NPZ$#QVnk#cz!F2ltN;h!2bpiVp@4jt_|sjSq_t2M>>rh>whqijM}5j*p3t zjgO1p1imRgK0YBnF+K@ADSmVOmiXlO6!4Vz)cCFOY4Pdc>G9j*x5w{@&j8Pe-x;47 zpB29gd{_ML_&xD^siI=QG+uBF3&(pFDed`C;?zj^mx zWTNtpj_5ICS^mG+>G=PYcXmc>Fz-5k#C~Isv(w4%Tiq+Ua{714`{pl5UUbEE$seok zPCnP;$z;(9?F@Z$?h->wX3)Ej7<$9#qsgbn&P~o)&?hPS>l+_Pei9pJ=+-soC7l$5r{s-2 zcO)~iR+)E2Kl$Y<^RDQDUOwGC{49C?)Q!p4kF`1>Kl8cfC&tQqv5C?D0=^__UvBJ~ zgO5+vYGh`&5MXMP3hK-+1zFDrk(YbNU z6Um$Jsd2(z@fE|dot7EC^t7L~GWp9b=Ng%N;!h|0l`UjqrFYvMD`n_$s}>~N9jR>S zw#2Y4eCb)zYDKaS@`}H7wnaud5B~akvLSxSw(8fVlJ|8E4L#@l)yWF=vJJhxaFxb(S7a&MEg{^6(pnjh;zOw|DdwxYeL*E-2V&bL^vJV{}UXiX}UfFBW*+ z@FgQXXY?py^hm$tuR7;*qepzn$eyeA6l)}Vq(^+|k-Ye_M>dF-4T{69c_kaUIEa=W z@%8SVOZFsN6CaKBdegneN6E;?@9SS#dd51+nwS1(^S((=WRFNjc52<~U5)z^drkaX@aq_R zO?<^9lf5QAdH5oo(les!ej_iwd|%97*1L+Q_Nn-@h}#VIviOR>#?`yU3LZ4$ieA$H zu*oNqUqx({>m;u^6)hXyV?8Lhh_220Q67^_Q*EGmpw?&NjU( znYpYDP z9IdT7(eOJmr(UX`;cLDIO`K-*Xnq&mdb^?ZZm%^r8+pn6as3R#*E(rjsK0qvdEnyU zPQ%xF9{$)ZMqc#joAYHvpHpG>iFI}BhI$|QHP^?R%{O%PUG04* zqnzOEQ9kKy?Nsh^whbkxt+)Ov*ExSB<9t-kboR6(C%ZT($9{carm=H0Io8Ef@-Chl z*Ug3Ab@7*-Zcg8zUR1YpBt33U>r*e>oXQusHdOE28c-c{>tr$Y&aJnfsGDw$#i@yI z?YSDQc~?Evy46~8^;&B{wNiCWGOE9BJ*&35{i6D)nkX4p6QxHrQTs@GRDV?yC8L_? z_Nl9hl2=Vs9h8jXau)4 zZ0oY-LVU^V?58;rtutYaS|j~)IfG>}U!r$&ZX7_IWt+~HpE5_HpXO{SdHLn!MN3}# z6+@?AwDjvtrTFVirL&geud|lco@jSY6Rmf3CKRnRq0U`;SLZIB$3*KqrZp-3buYa? zIfu0>{vK?2lX@q6`WCs*oV6s=8~QcYo9yorzc;yub!g^g!(B#R{!0EO)}CZ8e0rAA zulPv6D0K2xyIEVm&^scD_Sz08EYiDS31RSM6Q<1v*?kG_{VRXY51~7 zeC2u3z0g0D94;B@$;<6${1spCN=7=a+LP9Ykqat z5Usn0a;cjO+VWAyQ=QHyOrW2TJ=ctt2is3T4Rc#a-HT*^P)VdxfZRupfw|T)r(X}!f}Eb(QJWE4-y%TDo~J&L(>YMrP)I{PKBeW5r= z-o-@m*I1I*SULwPK9bSCQ#?iMj3-*_%EeIgr5vTXkc`%+@94Pq$;^c{_37`xt&wpJJ#C0 zultCbQ{69I-q5{6@`|&Q7cF@=r*16Gk;Za!Dtk1p=2zp&W|Kd$`8=C5b?5rh=FCyt zo2uCyD_U{TyKAmId?J79epJ-vOx=%MEo(W<;Y2R&^zbi6UTZ`0E`Q!P z>(>+cP&ze7l99aZQLJQx^lLpxziifekj=79GO|rJYkoBsq9vnuU4GMi>5MHI#lht| z#m9Y9QVhkHjAE$VB3f})ZqZs&{Fjh3rC&BH7m1c%S|_qccDnCf9q`5F&)8?bC9k*n zbIVVEnp`J;XD0GaM*1$~>=a-A zN?zw`$;dD9HNVm?c^4D$6;IjV&N)uMVlF#nk7VSd6?}8(OkIug7(_RgZcLb#Z%v)6gS1necN>3mlQYUVT~(V-=O;4Iw+i1tU_|d>gSUD zpY-xJ-#I(EDDSo8+H)ot{@67yB@;V37`lJGr;@pr|3P*-+1yMx|ch;1p$ zm;KjSdD&mX>iK-*rsPjnfAaD7liTgMr#<{}vdH0ldkWvXE%}DEvwp8nll?DTR5RcH zPm}9691~Mt8wcu->|yQGli2ARz4i&(;3Xkx_6z7 z_yao}3^kuEkF8#P7@5b{-yL3j!=@ATg45O-dO)!r;m=do82JwGPcrsMXI|cQ;VqxP zeS-hVHE*AgA2VRBv1jd$rs1hI(}o_B+bE0;PZ^oQJvxU??_O){oZK~Gbjr4ZbFK_e zAD=e->Z7wl=}{b{=kHi0jZX2UQ+AFbKC&}krf^bvG?w@pOMJypHs{L}H)~mb)p@3P zQhv#Pr|0<3Z=D!d@e$v}$IL-#<(e2CXgW4L<-8mds|l^M!ivAYVPbXkNb9iq@YjtDYdJh+N{$)pj$YOA z<#j`U)%2pUKt;(M3%9*|8T?nxymvd+$moHSg{zFg?6$(@LW=bLTzbte&DXlBGYIWB9U1e96mS$xDycp7hjT z{<5+2>H#-}k1t+f=prkthmC5#WMt};xGMa0&`RT1m$GHTyt7|5`uDdxFI@NP3PZoL z?$j{-^UL|Ze_Y7O%SXwJZ~TQX{qoW2mtWHV$eGKKDV{vSeqHv>i$?+e1OH!L%B z*NMHuI~qJ^=zEu3Wo(un**qZYC8Phl?YF^y(b&`X-a8FnzKbtC+Aq>0{i3B`<0=js zx9a8>jr}!i9Zih8DPR7hZl{HxFOmL?a>GBKmp1aUNAl;idJcZ^ zB_lg)yuQ%9 z`$d;S#uxGBi)@y>Y@Rjlc_Y*9$u@~`i=Q#??%LBYasL#>BqRCthNebF@sZ4?-T8C$ z!^F3;C9m}$`MVD0&-K}R_h+x&qP0;t86W;(cAH=1d-3GsOHWA@&0A>XS9C9)xUBf& zM&|ii3yqzUmz^tCiJy`D>cCMGXW6VcOJ4p;USo-`v7}$VNWaFFjKYTSyE?@nXmrf}c$>u-v zb|I6WqmJd}rCjcM=aW)t%h^J_L}V3(dCedkNj19++I^`6+`)@7%H}!YsFUfOJ4Rza&OAzs`sa8YkafjnMqMT3>i&bu*{Zqd9doMe%WG z0L4UWKz3>kxZJClxSFE%qgtkM&#u!gJ=*5*>P>s4U5yies;ycIQ1tUOK7!hA&!tcLva0OOJe! z9-X^HcRkh!e*U>=>&xBKk+WRdoe6Jhot4&^O|km%Nb9sa6L#Z%6FCQ_-T82Ouj)pR z{NlW4^oW)ocNS9oWrOyYY>w_ok0>PF5T^52qvdg>>W|k&fI)(ntTkkEA2_g>>XjVf4sf?g&PY zXzmfl2Kmdqz~~gs-ND!(S~f(^!0E`nEIsn?dzqo-7k937B)cJq0*82m(^qLL}}eeG?whozrX0tn6$LH?-&{9W0j9|f5}L4w=y!CJJ}Gq zNE-?`(Qke8i`v*w!nk+IFm$Y#gSx5wOP)BfN0Q>)+HZ4E8^xvLr(7q^>! zdl$bBhTJvMll}KktbyT&Hyc`WH2AX3#&^wQ%YGl2_0OFpUFq`dFgTML448TYb^Ptv7}$Il787HA7z`y60Nahr&}k}*5&Kb z+K$}2({~j70C{sCOiM=oN~XZrkB$A@gNVt;#>eZ}clDQVHNI?Tuhn1viQ%^=KMctF z$h^Cq{kvt=HuJ9bY|TpBjl6PW{pGSPBl%6!i_)W8ZZWj-^Q51)n0J+jrAPiskNBd+ zmkshoHb}pG&(8^4ktvRRcQck~jV1dflW+eg=3UvjcW%DUa^$+b$MbFA4ruuDUHk$S zW%IG{5V>*o{2j&@<-_$8w;Nh@^c0o;j0!Gzv(lhQ|G0bJ9ijbXRQLCDL%(Sov$9eJ72%fD$*m{q(?HMB_o~k zQ930rza+n+?^nnaPpWP%Nq%E!oegB0onKQA5GV|>?{M|{bPUt`QZ!yn7p zulPLqRXq6{XTrHxN>AbBUz`;m?R?PaJnig?hA(@>&sv_(*SY%B1>c*obbjvHbHCB~ zJ!fX|rAPcfSAA*thdDo&8zA3_Cui(EBfc{Hq1+et?ahzdFzy0hexZ5HNFL$7@X}1J zG3J!}g=A!dWS*HI`HW;e?iZ2~Uosj?YfodzFXdSIrLn}m9_s8;f zT-_h*FVFW;?>c{V&y}A1*yigIEj=1bYgqCww$drv6m#*VU$pegPRVPI++6G4_kdjk zy6ZXLbq5q*aTedj`AzQH=h`@b$^G5UQB&^bZqMpFhTF5c@4NYZkGs6v1K&hGYtzC|>(d*Na3?`|LIzTevJqix)U z-M-V^U-I%@@@}0-r&~)J*X1;Q!%_axn$hOq^UpW(Y!6Pw#B@m3DOl)I$oFY>nN_xaxC@)&cM&^i=f-(fb8$3(B@Tg~-0cfH9sF_*9Oea7V(ee-eo z#C+?pIY%;XUno{?&1)>#AirdT^lL9j|4X(Wy~H=km9~C;^l>3W%NNnE{_6XrtJnIT zxq;fGb=a0~gWGLAI>@&{)q2gZzE!#!t#6p$Q(q*1C*R*D*ql6$?@^c9y!sm7wZzwa ziLdy`W)~mPijT(C{&nNZ7mX|Z;!D3fduUvD_K-d9j3HkJ+A~J}_vidGiM~tgjG?*E zx8>IMeDwq0oQK&n*f_pR>--}h^-b8-UwtptSxE2dTWe)|2J6bV=)Y{O$>6)(ep_qw zO|qb^(faP{{hnWUD)JrgM_Z%y4O8zbCVJPML-NPUKZE6em(D+@$!6K0Sjh(2FIx7? z9>q%b$am?K@AAvU9KVLyv*7y`Vj7afb>c zXHM+NKYQpq^L~5Q;vO3AvuCg}d~a7wWP^)|XvIW2WxsUF2FCL(X>h>WvYdK?{<`RBXNPVpVBcTd>kng1q}=iPt*6`e+Ua{e2W z{{wBFTjl=^ZAHER$`0)QZ{?g`*Zw!QOiur?y-;Fm!&>Qyt!t#;UbHKFNoI%i>X~QA zXYVZNzOBwQ^b}<7*j(Gtou?E^pW3No`shs;C7y;J)$rIgc zwQTmKZ;nsT`lxv-n7Sal2K1nk3G?oujpNh(HnlP|aWS?apX=lqE7y&MFS*Vad`vkX z87t++!iJQyfw6MkSaqN6mKugVH(szL_0gi;xh1gS{F0SY7uJ6)wGErAU3DlmFYoPC zZG3$3;&bJ1%K7`L7v>wEdyqm2xp|v5t){SDWbrhj)rM2@*wHwouwyjNdeWPANYff>~8f=t1DE$I_>0H)!O!|`9 zO;eeFG)_GAT9#_ zhei1JIpUpOzi?=M4CP~;Pp?Q<-?la}etIml@d*_lts&M@$eMDo3Kc86enQqy!p=*m zIkIsMH7|CZgxZUC?S-tpgk5hT>n&l|SjZa7wrecZ9<}Qs)LysiKcr3AH6PN3b}y(+ z*mWDyChT4d87E=)TF5vFyVpW&N!Yy>;)ndvo()+GvQ=w4#P)>U?;-xl4&{LmyApP9 zDo*lKc_74Z6KmFU$htN(Yd&P{n|Wd{gzSlg%`GAOAR*tJPQ{LOZpO0t%E)N0T)tKu zoozOcg~Uw$CfL8mU(Jp4MX_eT8-EpF_OgkC;zbTIaZ^mmEhe^#on+K1cG79*$nd2@ zaghzOMPri_%zP=P8cSm;jeWzABFLQNGH5@taC_R@;AUAHOGc0C)uUBlAj_JZ`NRysYZk6Oc~HrTZ)d))pq z^~2c{*{AYFHBsx+)kLjZSMSVTCq5~=J{1$yM6Fv_zqD3e%~C#bb;|5%>9Xrn^Qc;; zb?a)D+24wnUAI~rsykY%S`VsmTC2IXmYH>B>zAQzy)$$~K3ij0e-pN@8JS3))S6WN z(zP0$IznmV` zzEq@+q$73E*`pfj^rWae<_u@+sk0$Ptubd5omF&Zv2{mvIc4WUbvboi--_vV)LFH9 zhdO0+G@4sI{RA~vt;V3nnKAY6PPKN`Qs)dUd#!vTBA<}2R(~R*-_%2UrV7W(=x zXH_B`6HLb&ipYY}VPu`63=;nKO>gD=rS=Gp;$;h*sW?+yzX0>|Mab zQ!>i=+E1znw*G1^bVo4f7oAJo9O>+$GmPS|a|35)ogZ{o4mmTM`Q=$?&pBFWA?wYp zhfwDt?Hkrj$a-_@C)Alr>xXAXMr%|5CRz5`wP$FpJ>-nM)~VIw)~)zv-6Ch?wPvji zZp}--S@Twp*;CejcjlLmWJ_en)PdAV(u5#U$gIQeMu{q+L~q7xUDs5n}>B~ zNbAgHYm>Rl*m`H~6fakQC9!4e+EgLdp3bQ{_bPwbHDK1Bty$*oW9yx}YpC|QyN1pc zrdHTmZ0;BKJYd#fgs*d>lh3xbReG```V*0H6SgMm4yf~(yBnG{t2;~NoR-!&HisK| z)fL&Pacn)3{q7uV##feCG;!M01)6m{o6zkC5HMCbldrwKR56!w#E@3|#S~rUb1O^qOTgxZ_RC04)g zAgV{QP4@-mh){c0Yn|K?YF~?v$kYG(JA<{y+;^;eO7anVwEv~k@FVu?PN%b+nfFLc z5)t23TXhaJGWI-aXnV%f{ZV&D7sG@-XBruM_Dkzr6Oq>)L}xgH5;J(mM@zjJ3--ML)ORIYS6Q)dyEW6k~0=GZWjKf_4Q4CR~6rC}sb zhVsYeQYWw680wCyH6s5a`OwKLXF7Y71D$^5N#%FhZ1TO$|H_T#o@2kI=ng3Rl@HBb z$mTcQ6BGJQqPdOax`aJr>zh(SbFV$Cdw@MZ>l>H3=V;An?bv#xZ&v2sqW!BiXlt3y zMCPudZx>qEwkE1}hmrglM(S@E$)#bW&W4eC?cOzO$G)o??ey#H;cPJTPkzxiH`Q6E z-|VeOJ$3eVsed%{+8zZl0$`kgiV(b*ho=N1_NqIu~ zL3zT;o4h4?ntKZ};@s$&l8zN^OGcNZUeTS4^?mM12>vQHe^QauBJghmgXFM~H z_AX=Q-QI)DTx%?S<5FH>o#-2aStqO|-8$3KjJ2Pv4rDeDFSL8gYy=nN`qgIc} zC6TkJ)1$MHv%%yQtH-Q8Ymd%R&IXh7Y;4Wii=2(Mm*tn))9h_yzj6cn+4yL4qrOM! z+@c)izF{g4n>cIl5?>Q%?Opb&nG5aRKG(Mlr+(t4|7!KLSyQe)?fa#3_y+to^DC!5 zJ5)F1|Jiu@gLi&TMflNo6PxRF4;LY?*u@5w4wpjH>Yaq%MTf>nXe`E*-h}j{&%C2| z(P74OeA(pWHMY|uy-vUMI@_dI@_J8to&C}){jx2>m;FvY5z&)~=no^~h7o&0#l_kg zO0TtFeCLNcJi&( z?#XRl+fP?1-8FsZrrxO`&^^X=Ojqp^q*n26(S|>zB6g;wvoSK4c-_;lO_^rwuhVR7 z+Ug0VM>6^UO?Em%>9lr+vQy8|6H1RAE7VxxV{<5*MPqX)->qN#UAk&RYroOMxY^im z#a~%-uuv|4dkZD#A~Tf3IJs zK}}*icFLs<$6WMu>MP>#d*znt+gBx1#fbmqi}$52@ozUW{rqwbBe^L3(D6Lu5C01! z>0QTnrk>|r{%J${fz1d=ntj;;^24Vm)a6%zLffkb)t5A$43)$@UefV_Y#};znZ8B zt=8Hc%I0dQAe+`B3umM{8U0Q+zdFinr#K^$=>FWrzHdT{{xzhYzz>)$SftExZ`I z-u0)30@30T^H5`9i{q!nw=qepwd1BWuGW^;q1Kum*Q`ftPg?fq`-pUEEo)tB&1;?7 zcSG}TK0g#+>yJGWYCUSbvM)ldU#%ytL#;3ECG9osIjvpEYb|S@SYx5)P4l7o()`FD z*&zQkXQDNSs{NwX+W4f@+PI|^Hyf*z;$-!wBl=U)Z*5M?W@~3!c4|McUsBp1))%uE zt<7oKth}b2sobgjrJSjJrkp6-lq0Q7C>e6S;VY+F`B3u8iR9!^IZ`=PIZZiL`A+LZ z`A>O`oUh!Ut=y#CrCeq0H2XVx=dy85E6!FvC3!oR$;VcITKes{CYLKOE6>XZcIwNtfN zGRoa*mCIf2R86+;rWGIiZc6XkanpJy;$wCs4%v};Nkrx@5t&~{YaSDkxr?shRgv|- zDzg5gYu?m#>sM}M9;pw1e~XCtWzG#cKgfO?E8|OK?%s&_o;#raPpP`pl$O^9C;iPaw7$4{r+SnV z$x$ZH+MKMMZ+ukFkL0d&B%hdiLw-~KG3N~Svi7FSpUOq6BDpAa z%Bo1N*4e`bC=F((Q{Fb>YqK!nfhnXgr?@%GhxD>&C=wC9IXlMW37?M z`Owr+^ys|j^ynOD&I+6{bQVfP&VGr=nb6r2vPW|w=RJ28)tN9Sa_&v52H112sdLn2 zo%c*_&^jb1xih)W|Iu@Ajy)&qJe-Q$7fh|P_XtzN?ENBP&+WR8$OhxPazNxxVd^3^ zRCffYN9TD{7pbSZ7bGHgheYJ89z6r+s0Jvu%H{T+m7oqdT6x9P277NZ`$K0}>O+p+ zvv(zL*?ALun_bIb}rPt+kd*5*~#>a@wM&90iFWOEg8kx+NQgY__EpXWr@AdDkkoJYV_!ttL^r#>SRpZIP0vk zcOR{R6z4ay1~|JU>^(7|x@hl@TK|d2`Zw?XV;!D!2T>hzd&lZA>%sEPUa(`C^(q;Z&DtR9+L+wU-zTX+7QYHtKZ}V z#IZ!o8wMn^=Gjz)3L+VI^8mc={ zf_h=%pjNeqdYYyd8C&iB$k=D^M<<=tBQ?>~U{j0#ephu&HA`cew)Dm~6J6O0k2Rm? z##OyqWo^@WU-c+u?|r7G+PmIK_wGndR2@lEuT4#{@9KV}ds&X=ORkd zAI*W*gXSbs>vh&LdssC`x!c~Abaya&L2K6PG4pI?%o)t;H1llb%^uP^vv)(CEpxQC zwZ^S&p=?u~v-c)vzwVW*R3qJ)M008HQ@SIi?OoEv!PHaLU}XVYizQ=ZRfEM>{H#t> z(<5g^vsT=BCVFR7O|^F@v$nMMwALbLY*TNv@3jB4UhO^6@e|@}58FFqO8Z=K(7snr zcW3Fy8Q$b9_~s1h@|t|$9+7ruY2`OZUoEb}UnW?7X`(p~*|y-!>+?-=(ab zCf{4XRO@o>`Z4D&yH0dIN~xA;ohv5XDblKUqPgoO++C0RUFhzB)-Tzd8?jCIQ=><9 z-qktjiTIncdP4Cd@yX@Rn26jNU2Jo$ZALy4|N7QW!;j3hsqyZf81Y?qK&=gP2aLqo ztsi%P*F0*D^nJtaS=}$(p8X&1ERi+f-Zf{p$og@5%y}@fZW~1Qa)Zb^%#N({L}V`{ zbS6{%(%H@KDRbYC?4y(JdRAV0N_S6FuXS#UtR;Q_aXC%*i-f*I>Wmr5X(!!ZREONR zuSgzCS-!b{SUu+ZmCmd>yXp=R$z%G?D)fq!` zc+wp_Qls@v&($DP=j=H|_Z@czYiMoOz093+(yTAtkKFlF_aJxvY-rC{=G%MZ%&D`G z$^EKbk@Kg?%k~bEir%3jXHI=%H#Lj>s=JA)Q|!6K|8&=joWWd7bPjPbQ7zSXQg{AL z*mJM$V!H1bU&tj5?O5h-6V_()x0}cvBptb5=-%hfw_)VG7e?=8Rz~^8A$~UMQg$TOy_I=pP&Cz-{0RJ|93l_-V?S@&gYo-{}Ubc`@f)1jFCS??@J)ekaG`KRy4azW- Date: Wed, 15 Nov 2023 11:36:48 -0500 Subject: [PATCH 372/421] Minor doc changes --- .../include/Cesium3DTilesContent/TileBoundingVolumes.h | 2 +- CesiumGeometry/include/CesiumGeometry/BoundingSphere.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/TileBoundingVolumes.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/TileBoundingVolumes.h index 0d76aa83e..bb2b9b74d 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/TileBoundingVolumes.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/TileBoundingVolumes.h @@ -36,7 +36,7 @@ class TileBoundingVolumes { * * @param boundingVolume The bounding volume from which to get the region. * @return The region, or `std::nullopt` if the bounding volume does not - * define a region. The box is defined in geographic coordinates. + * define a region. The region is defined in geographic coordinates. */ static std::optional getBoundingRegion(const Cesium3DTiles::BoundingVolume& boundingVolume); diff --git a/CesiumGeometry/include/CesiumGeometry/BoundingSphere.h b/CesiumGeometry/include/CesiumGeometry/BoundingSphere.h index c910a2057..3f0827f27 100644 --- a/CesiumGeometry/include/CesiumGeometry/BoundingSphere.h +++ b/CesiumGeometry/include/CesiumGeometry/BoundingSphere.h @@ -67,7 +67,7 @@ class CESIUMGEOMETRY_API BoundingSphere final { * 4x4 matrix. * * If the transformation has non-uniform scale, the bounding sphere's radius - * is scaled by the scale of the transformation's axis with the largest scale. + * is scaled by the largest scale value among the transformation's axes. * * @param transformation The transformation. * @return The bounding sphere in the new coordinate system. From ebf44a1fd75a3880ae968dbdd81dcb90845549c6 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Wed, 15 Nov 2023 12:04:03 -0500 Subject: [PATCH 373/421] Add findTile to TestImplicitQuadtreeLoader --- .../test/TestImplicitQuadtreeLoader.cpp | 102 ++++++++---------- 1 file changed, 46 insertions(+), 56 deletions(-) diff --git a/Cesium3DTilesSelection/test/TestImplicitQuadtreeLoader.cpp b/Cesium3DTilesSelection/test/TestImplicitQuadtreeLoader.cpp index 00209e323..5e7b1b145 100644 --- a/Cesium3DTilesSelection/test/TestImplicitQuadtreeLoader.cpp +++ b/Cesium3DTilesSelection/test/TestImplicitQuadtreeLoader.cpp @@ -198,6 +198,31 @@ TEST_CASE("Test implicit quadtree loader") { } } +namespace { + +const Tile& +findTile(const gsl::span& children, const QuadtreeTileID& tileID) { + auto it = std::find_if( + children.begin(), + children.end(), + [tileID](const Tile& tile) { + const QuadtreeTileID* pID = + std::get_if(&tile.getTileID()); + if (!pID) + return false; + return *pID == tileID; + }); + REQUIRE(it != children.end()); + return *it; +} + +const Tile& +findTile(const std::vector& children, const QuadtreeTileID& tileID) { + return findTile(gsl::span(children), tileID); +} + +} // namespace + TEST_CASE("Test tile subdivision for implicit quadtree loader") { Cesium3DTilesContent::registerAllTileContentTypes(); @@ -238,10 +263,7 @@ TEST_CASE("Test tile subdivision for implicit quadtree loader") { const auto& tileChildren = tileChildrenResult.children; CHECK(tileChildren.size() == 4); - const auto& tile_1_0_0 = tileChildren[0]; - CHECK( - std::get(tile_1_0_0.getTileID()) == - QuadtreeTileID(1, 0, 0)); + const auto& tile_1_0_0 = findTile(tileChildren, QuadtreeTileID(1, 0, 0)); const auto& box_1_0_0 = std::get(tile_1_0_0.getBoundingVolume()); CHECK(box_1_0_0.getCenter() == glm::dvec3(-10.0, -10.0, 0.0)); @@ -249,10 +271,7 @@ TEST_CASE("Test tile subdivision for implicit quadtree loader") { CHECK(box_1_0_0.getHalfAxes()[1] == glm::dvec3(0.0, 10.0, 0.0)); CHECK(box_1_0_0.getHalfAxes()[2] == glm::dvec3(0.0, 0.0, 20.0)); - const auto& tile_1_1_0 = tileChildren[1]; - CHECK( - std::get(tile_1_1_0.getTileID()) == - QuadtreeTileID(1, 1, 0)); + const auto& tile_1_1_0 = findTile(tileChildren, QuadtreeTileID(1, 1, 0)); const auto& box_1_1_0 = std::get(tile_1_1_0.getBoundingVolume()); CHECK(box_1_1_0.getCenter() == glm::dvec3(10.0, -10.0, 0.0)); @@ -260,10 +279,7 @@ TEST_CASE("Test tile subdivision for implicit quadtree loader") { CHECK(box_1_1_0.getHalfAxes()[1] == glm::dvec3(0.0, 10.0, 0.0)); CHECK(box_1_1_0.getHalfAxes()[2] == glm::dvec3(0.0, 0.0, 20.0)); - const auto& tile_1_0_1 = tileChildren[2]; - CHECK( - std::get(tile_1_0_1.getTileID()) == - QuadtreeTileID(1, 0, 1)); + const auto& tile_1_0_1 = findTile(tileChildren, QuadtreeTileID(1, 0, 1)); const auto& box_1_0_1 = std::get(tile_1_0_1.getBoundingVolume()); CHECK(box_1_0_1.getCenter() == glm::dvec3(-10.0, 10.0, 0.0)); @@ -271,10 +287,7 @@ TEST_CASE("Test tile subdivision for implicit quadtree loader") { CHECK(box_1_0_1.getHalfAxes()[1] == glm::dvec3(0.0, 10.0, 0.0)); CHECK(box_1_0_1.getHalfAxes()[2] == glm::dvec3(0.0, 0.0, 20.0)); - const auto& tile_1_1_1 = tileChildren[3]; - CHECK( - std::get(tile_1_1_1.getTileID()) == - QuadtreeTileID(1, 1, 1)); + const auto& tile_1_1_1 = findTile(tileChildren, QuadtreeTileID(1, 1, 1)); const auto& box_1_1_1 = std::get(tile_1_1_1.getBoundingVolume()); CHECK(box_1_1_1.getCenter() == glm::dvec3(10.0, 10.0, 0.0)); @@ -294,10 +307,7 @@ TEST_CASE("Test tile subdivision for implicit quadtree loader") { const auto& tileChildren = tileChildrenResult.children; CHECK(tileChildren.size() == 4); - const auto& tile_2_2_0 = tileChildren[0]; - CHECK( - std::get(tile_2_2_0.getTileID()) == - QuadtreeTileID(2, 2, 0)); + const auto& tile_2_2_0 = findTile(tileChildren, QuadtreeTileID(2, 2, 0)); const auto& box_2_2_0 = std::get(tile_2_2_0.getBoundingVolume()); CHECK(box_2_2_0.getCenter() == glm::dvec3(5.0, -15.0, 0.0)); @@ -305,10 +315,7 @@ TEST_CASE("Test tile subdivision for implicit quadtree loader") { CHECK(box_2_2_0.getHalfAxes()[1] == glm::dvec3(0.0, 5.0, 0.0)); CHECK(box_2_2_0.getHalfAxes()[2] == glm::dvec3(0.0, 0.0, 20.0)); - const auto& tile_2_3_0 = tileChildren[1]; - CHECK( - std::get(tile_2_3_0.getTileID()) == - QuadtreeTileID(2, 3, 0)); + const auto& tile_2_3_0 = findTile(tileChildren, QuadtreeTileID(2, 3, 0)); const auto& box_2_3_0 = std::get(tile_2_3_0.getBoundingVolume()); CHECK(box_2_3_0.getCenter() == glm::dvec3(15.0, -15.0, 0.0)); @@ -316,10 +323,7 @@ TEST_CASE("Test tile subdivision for implicit quadtree loader") { CHECK(box_2_3_0.getHalfAxes()[1] == glm::dvec3(0.0, 5.0, 0.0)); CHECK(box_2_3_0.getHalfAxes()[2] == glm::dvec3(0.0, 0.0, 20.0)); - const auto& tile_2_2_1 = tileChildren[2]; - CHECK( - std::get(tile_2_2_1.getTileID()) == - QuadtreeTileID(2, 2, 1)); + const auto& tile_2_2_1 = findTile(tileChildren, QuadtreeTileID(2, 2, 1)); const auto& box_2_2_1 = std::get(tile_2_2_1.getBoundingVolume()); CHECK(box_2_2_1.getCenter() == glm::dvec3(5.0, -5.0, 0.0)); @@ -327,10 +331,7 @@ TEST_CASE("Test tile subdivision for implicit quadtree loader") { CHECK(box_2_2_1.getHalfAxes()[1] == glm::dvec3(0.0, 5.0, 0.0)); CHECK(box_2_2_1.getHalfAxes()[2] == glm::dvec3(0.0, 0.0, 20.0)); - const auto& tile_2_3_1 = tileChildren[3]; - CHECK( - std::get(tile_2_3_1.getTileID()) == - QuadtreeTileID(2, 3, 1)); + const auto& tile_2_3_1 = findTile(tileChildren, QuadtreeTileID(2, 3, 1)); const auto& box_2_3_1 = std::get(tile_2_3_1.getBoundingVolume()); CHECK(box_2_3_1.getCenter() == glm::dvec3(15.0, -5.0, 0.0)); @@ -379,7 +380,7 @@ TEST_CASE("Test tile subdivision for implicit quadtree loader") { const auto& tileChildren = tileChildrenResult.children; CHECK(tileChildren.size() == 4); - const auto& tile_1_0_0 = tileChildren[0]; + const auto& tile_1_0_0 = findTile(tileChildren, QuadtreeTileID(1, 0, 0)); const auto& region_1_0_0 = std::get(tile_1_0_0.getBoundingVolume()); CHECK(region_1_0_0.getRectangle().getWest() == Approx(-Math::OnePi)); @@ -389,7 +390,7 @@ TEST_CASE("Test tile subdivision for implicit quadtree loader") { CHECK(region_1_0_0.getMinimumHeight() == Approx(0.0)); CHECK(region_1_0_0.getMaximumHeight() == Approx(100.0)); - const auto& tile_1_1_0 = tileChildren[1]; + const auto& tile_1_1_0 = findTile(tileChildren, QuadtreeTileID(1, 1, 0)); const auto& region_1_1_0 = std::get(tile_1_1_0.getBoundingVolume()); CHECK(region_1_1_0.getRectangle().getWest() == Approx(0.0)); @@ -399,7 +400,7 @@ TEST_CASE("Test tile subdivision for implicit quadtree loader") { CHECK(region_1_1_0.getMinimumHeight() == Approx(0.0)); CHECK(region_1_1_0.getMaximumHeight() == Approx(100.0)); - const auto& tile_1_0_1 = tileChildren[2]; + const auto& tile_1_0_1 = findTile(tileChildren, QuadtreeTileID(1, 0, 1)); const auto& region_1_0_1 = std::get(tile_1_0_1.getBoundingVolume()); CHECK(region_1_0_1.getRectangle().getWest() == Approx(-Math::OnePi)); @@ -409,7 +410,7 @@ TEST_CASE("Test tile subdivision for implicit quadtree loader") { CHECK(region_1_0_1.getMinimumHeight() == Approx(0.0)); CHECK(region_1_0_1.getMaximumHeight() == Approx(100.0)); - const auto& tile_1_1_1 = tileChildren[3]; + const auto& tile_1_1_1 = findTile(tileChildren, QuadtreeTileID(1, 1, 1)); const auto& region_1_1_1 = std::get(tile_1_1_1.getBoundingVolume()); CHECK(region_1_1_1.getRectangle().getWest() == Approx(0.0)); @@ -431,7 +432,7 @@ TEST_CASE("Test tile subdivision for implicit quadtree loader") { const auto& tileChildren = tileChildrenResult.children; CHECK(tileChildren.size() == 4); - const auto& tile_2_2_0 = tileChildren[0]; + const auto& tile_2_2_0 = findTile(tileChildren, QuadtreeTileID(2, 2, 0)); const auto& region_2_2_0 = std::get(tile_2_2_0.getBoundingVolume()); CHECK(region_2_2_0.getRectangle().getWest() == Approx(0.0)); @@ -442,7 +443,7 @@ TEST_CASE("Test tile subdivision for implicit quadtree loader") { CHECK(region_2_2_0.getMinimumHeight() == Approx(0.0)); CHECK(region_2_2_0.getMaximumHeight() == Approx(100.0)); - const auto& tile_2_3_0 = tileChildren[1]; + const auto& tile_2_3_0 = findTile(tileChildren, QuadtreeTileID(2, 3, 0)); const auto& region_2_3_0 = std::get(tile_2_3_0.getBoundingVolume()); CHECK(region_2_3_0.getRectangle().getWest() == Approx(Math::PiOverTwo)); @@ -453,7 +454,7 @@ TEST_CASE("Test tile subdivision for implicit quadtree loader") { CHECK(region_2_3_0.getMinimumHeight() == Approx(0.0)); CHECK(region_2_3_0.getMaximumHeight() == Approx(100.0)); - const auto& tile_2_2_1 = tileChildren[2]; + const auto& tile_2_2_1 = findTile(tileChildren, QuadtreeTileID(2, 2, 1)); const auto& region_2_2_1 = std::get(tile_2_2_1.getBoundingVolume()); CHECK(region_2_2_1.getRectangle().getWest() == Approx(0.0)); @@ -464,7 +465,7 @@ TEST_CASE("Test tile subdivision for implicit quadtree loader") { CHECK(region_2_2_1.getMinimumHeight() == Approx(0.0)); CHECK(region_2_2_1.getMaximumHeight() == Approx(100.0)); - const auto& tile_2_3_1 = tileChildren[3]; + const auto& tile_2_3_1 = findTile(tileChildren, QuadtreeTileID(2, 3, 1)); const auto& region_2_3_1 = std::get(tile_2_3_1.getBoundingVolume()); CHECK(region_2_3_1.getRectangle().getWest() == Approx(Math::PiOverTwo)); @@ -511,34 +512,23 @@ TEST_CASE("Test tile subdivision for implicit quadtree loader") { const auto& tileChildren = tileChildrenResult.children; CHECK(tileChildren.size() == 4); - const auto& tile_1_0_0 = tileChildren[0]; - CHECK( - std::get(tile_1_0_0.getTileID()) == - QuadtreeTileID(1, 0, 0)); + const auto& tile_1_0_0 = findTile(tileChildren, QuadtreeTileID(1, 0, 0)); const auto& box_1_0_0 = std::get(tile_1_0_0.getBoundingVolume()); CHECK(box_1_0_0.getCellID().toToken() == "04"); - const auto& tile_1_1_0 = tileChildren[1]; - CHECK( - std::get(tile_1_1_0.getTileID()) == - QuadtreeTileID(1, 1, 0)); + const auto& tile_1_1_0 = findTile(tileChildren, QuadtreeTileID(1, 1, 0)); const auto& box_1_1_0 = std::get(tile_1_1_0.getBoundingVolume()); CHECK(box_1_1_0.getCellID().toToken() == "1c"); - const auto& tile_1_0_1 = tileChildren[2]; - CHECK( - std::get(tile_1_0_1.getTileID()) == - QuadtreeTileID(1, 0, 1)); + const auto& tile_1_0_1 = findTile(tileChildren, QuadtreeTileID(1, 0, 1)); + ; const auto& box_1_0_1 = std::get(tile_1_0_1.getBoundingVolume()); CHECK(box_1_0_1.getCellID().toToken() == "0c"); - const auto& tile_1_1_1 = tileChildren[3]; - CHECK( - std::get(tile_1_1_1.getTileID()) == - QuadtreeTileID(1, 1, 1)); + const auto& tile_1_1_1 = findTile(tileChildren, QuadtreeTileID(1, 1, 1)); const auto& box_1_1_1 = std::get(tile_1_1_1.getBoundingVolume()); CHECK(box_1_1_1.getCellID().toToken() == "14"); From 96b632b0ef2fc51eadd18927ddc92fe40b4ca160 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Wed, 15 Nov 2023 13:37:28 -0500 Subject: [PATCH 374/421] Doc tweaks --- .../ImplicitTilingUtilities.h | 45 ++++++++++--------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTilingUtilities.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTilingUtilities.h index a71f3e385..16b56ef97 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTilingUtilities.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTilingUtilities.h @@ -288,55 +288,60 @@ class ImplicitTilingUtilities { } /** - * @brief Computes the bounding volume for an implicit tile with the given ID. + * @brief Computes the bounding volume for an implicit quadtree tile with the + * given ID as a bounding region. * - * @param rootBoundingVolume The bounding volume of the root tile. - * @param tileID The tile ID for each to compute the bounding volume. - * @return The bounding volume for the given implicit tile. + * @param rootBoundingVolume The bounding region of the root tile. + * @param tileID The tile ID for which to compute the bounding region. + * @return The bounding region for the given implicit tile. */ static CesiumGeospatial::BoundingRegion computeBoundingVolume( const CesiumGeospatial::BoundingRegion& rootBoundingVolume, const CesiumGeometry::QuadtreeTileID& tileID) noexcept; /** - * @brief Computes the bounding volume for an implicit tile with the given ID. + * @brief Computes the bounding volume for an implicit octree tile with the + * given ID as a bounding region. * - * @param rootBoundingVolume The bounding volume of the root tile. - * @param tileID The tile ID for each to compute the bounding volume. - * @return The bounding volume for the given implicit tile. + * @param rootBoundingVolume The bounding region of the root tile. + * @param tileID The tile ID for which to compute the bounding region. + * @return The bounding region for the given implicit tile. */ static CesiumGeospatial::BoundingRegion computeBoundingVolume( const CesiumGeospatial::BoundingRegion& rootBoundingVolume, const CesiumGeometry::OctreeTileID& tileID) noexcept; /** - * @brief Computes the bounding volume for an implicit tile with the given ID. + * @brief Computes the bounding volume for an implicit quadtree tile + * with the given ID as an oriented bounding box. * - * @param rootBoundingVolume The bounding volume of the root tile. - * @param tileID The tile ID for each to compute the bounding volume. - * @return The bounding volume for the given implicit tile. + * @param rootBoundingVolume The oriented bounding box of the root tile. + * @param tileID The tile ID for which to compute the oriented bounding box. + * @return The oriented bounding box for the given implicit tile. */ static CesiumGeometry::OrientedBoundingBox computeBoundingVolume( const CesiumGeometry::OrientedBoundingBox& rootBoundingVolume, const CesiumGeometry::QuadtreeTileID& tileID) noexcept; /** - * @brief Computes the bounding volume for an implicit tile with the given ID. + * @brief Computes the bounding volume for an implicit octree tile with + * the given ID as an oriented bounding box. * - * @param rootBoundingVolume The bounding volume of the root tile. - * @param tileID The tile ID for each to compute the bounding volume. - * @return The bounding volume for the given implicit tile. + * @param rootBoundingVolume The oriented bounding box of the root tile. + * @param tileID The tile ID for which to compute the oriented bounding box. + * @return The oriented bounding box for the given implicit tile. */ static CesiumGeometry::OrientedBoundingBox computeBoundingVolume( const CesiumGeometry::OrientedBoundingBox& rootBoundingVolume, const CesiumGeometry::OctreeTileID& tileID) noexcept; /** - * @brief Computes the bounding volume for an implicit tile with the given ID. + * @brief Computes the bounding volume for an implicit quadtree tile + * with the given ID as an S2 cell bounding volume. * - * @param rootBoundingVolume The bounding volume of the root tile. - * @param tileID The tile ID for each to compute the bounding volume. - * @return The bounding volume for the given implicit tile. + * @param rootBoundingVolume The S2 cell bounding volume of the root tile. + * @param tileID The tile ID for which to compute the S2 cell bounding volume. + * @return The S2 cell bounding volume for the given implicit tile. */ static CesiumGeospatial::S2CellBoundingVolume computeBoundingVolume( const CesiumGeospatial::S2CellBoundingVolume& rootBoundingVolume, From bf812c9da1e2ae1fb1034d92fc58f36395bfbba8 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Wed, 15 Nov 2023 13:55:36 -0500 Subject: [PATCH 375/421] Fix changelog entry --- CHANGES.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index f8e8ce675..54dade1a5 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -22,9 +22,6 @@ ##### Fixes :wrench: - Fixed a bug in `OrientedBoundingBox::contains` where it didn't account for the bounding box's center. - -##### Fixes :wrench: - - Fixed compiler error when calling `PropertyAttributeView::forEachProperty`. ### v0.29.0 - 2023-11-01 From 581d6c9ef640baa9c9ab6ef0f92723821ebad7e9 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Thu, 16 Nov 2023 23:32:12 +1100 Subject: [PATCH 376/421] Get raster overlay baking working. --- .../src/LayerJsonTerrainLoader.cpp | 5 +- .../src/TilesetContentManager.cpp | 5 +- .../include/CesiumGltfContent/GltfUtilities.h | 9 ++ .../CesiumGltfContent/ImageManipulation.h | 20 ++++ CesiumGltfContent/src/GltfUtilities.cpp | 40 +++++++ CesiumGltfContent/src/ImageManipulation.cpp | 38 ++++++ .../RasterOverlayUtilities.h | 5 +- .../src/RasterOverlayUtilities.cpp | 17 ++- .../test/TestAddRasterOverlayToGltf.cpp | 109 ++++++++++++++++-- 9 files changed, 228 insertions(+), 20 deletions(-) diff --git a/Cesium3DTilesSelection/src/LayerJsonTerrainLoader.cpp b/Cesium3DTilesSelection/src/LayerJsonTerrainLoader.cpp index 27b43fbc4..38f10ff33 100644 --- a/Cesium3DTilesSelection/src/LayerJsonTerrainLoader.cpp +++ b/Cesium3DTilesSelection/src/LayerJsonTerrainLoader.cpp @@ -215,11 +215,12 @@ void generateRasterOverlayUVs( RasterOverlayUtilities::createRasterOverlayTextureCoordinates( *pModel, tileTransform, - 0, pParentRegion ? std::make_optional( pParentRegion->getRectangle()) : std::nullopt, - {projection}); + {projection}, + "_CESIUMOVERLAY_", + 0); } } diff --git a/Cesium3DTilesSelection/src/TilesetContentManager.cpp b/Cesium3DTilesSelection/src/TilesetContentManager.cpp index cbcac9add..5c9fc848a 100644 --- a/Cesium3DTilesSelection/src/TilesetContentManager.cpp +++ b/Cesium3DTilesSelection/src/TilesetContentManager.cpp @@ -382,9 +382,10 @@ void calcRasterOverlayDetailsInWorkerThread( RasterOverlayUtilities::createRasterOverlayTextureCoordinates( model, tileLoadInfo.tileTransform, - firstRasterOverlayTexCoord, pRegion ? std::make_optional(pRegion->getRectangle()) : std::nullopt, - std::move(projections)); + std::move(projections), + "_CESIUMOVERLAY_", + firstRasterOverlayTexCoord); if (pRegion && overlayDetails) { // If the original bounding region was wrong, report it. diff --git a/CesiumGltfContent/include/CesiumGltfContent/GltfUtilities.h b/CesiumGltfContent/include/CesiumGltfContent/GltfUtilities.h index 267bb8255..96a925b8b 100644 --- a/CesiumGltfContent/include/CesiumGltfContent/GltfUtilities.h +++ b/CesiumGltfContent/include/CesiumGltfContent/GltfUtilities.h @@ -92,5 +92,14 @@ struct CESIUMGLTFCONTENT_API GltfUtilities { */ static std::vector parseGltfCopyright(const CesiumGltf::Model& gltf); + + /** + * @brief Merges all of the glTF's buffers into a single buffer. + * + * This is useful when writing the glTF as a GLB. + * + * @param gltf The glTF in which to merge buffers. + */ + static void mergeBuffers(CesiumGltf::Model& gltf); }; } // namespace CesiumGltfContent diff --git a/CesiumGltfContent/include/CesiumGltfContent/ImageManipulation.h b/CesiumGltfContent/include/CesiumGltfContent/ImageManipulation.h index 8c338f118..82b3e2466 100644 --- a/CesiumGltfContent/include/CesiumGltfContent/ImageManipulation.h +++ b/CesiumGltfContent/include/CesiumGltfContent/ImageManipulation.h @@ -95,6 +95,26 @@ class CESIUMGLTFCONTENT_API ImageManipulation { const PixelRectangle& targetPixels, const CesiumGltf::ImageCesium& source, const PixelRectangle& sourcePixels); + + /** + * @brief Saves an image to a new byte buffer in PNG format. + * + * @param image The image to save. + * @return The byte buffer containing the image. If the buffer is empty, the + * image could not be written. + */ + static std::vector savePng(const CesiumGltf::ImageCesium& image); + + /** + * @brief Saves an image to an existing byte buffer in PNG format. + * + * @param image The image to save. + * @param output The buffer in which to store the PNG. The image is written to + * the end of the buffer. If the buffer size is unchanged on return the image + * could not be written. + */ + static void + savePng(const CesiumGltf::ImageCesium& image, std::vector& output); }; } // namespace CesiumGltfContent diff --git a/CesiumGltfContent/src/GltfUtilities.cpp b/CesiumGltfContent/src/GltfUtilities.cpp index 0f9b5db1a..78d9c4021 100644 --- a/CesiumGltfContent/src/GltfUtilities.cpp +++ b/CesiumGltfContent/src/GltfUtilities.cpp @@ -8,6 +8,8 @@ #include +using namespace CesiumGltf; + namespace CesiumGltfContent { /*static*/ glm::dmat4x4 GltfUtilities::applyRtcCenter( const CesiumGltf::Model& gltf, @@ -150,4 +152,42 @@ GltfUtilities::parseGltfCopyright(const CesiumGltf::Model& gltf) { return result; } + +/*static*/ void GltfUtilities::mergeBuffers(CesiumGltf::Model& gltf) { + if (gltf.buffers.empty()) + return; + + Buffer& mainBuffer = gltf.buffers[0]; + + // Copy all other buffers into the main one, and keep track of where we put + // them. + std::vector oldBufferStarts; + + for (size_t i = 1; i < gltf.buffers.size(); ++i) { + const Buffer& otherBuffer = gltf.buffers[i]; + + oldBufferStarts.emplace_back(mainBuffer.cesium.data.size()); + + mainBuffer.cesium.data.insert( + mainBuffer.cesium.data.end(), + otherBuffer.cesium.data.begin(), + otherBuffer.cesium.data.end()); + } + + mainBuffer.byteLength = mainBuffer.cesium.data.size(); + + // Update all the bufferViews to refer to the main buffer. + for (BufferView& bufferView : gltf.bufferViews) { + if (bufferView.buffer <= 0 || bufferView.buffer >= gltf.buffers.size()) + continue; + + size_t oldBufferStart = oldBufferStarts[bufferView.buffer - 1]; + bufferView.buffer = 0; + bufferView.byteOffset += oldBufferStart; + } + + // Remove all the old buffers + gltf.buffers.resize(1); +} + } // namespace CesiumGltfContent diff --git a/CesiumGltfContent/src/ImageManipulation.cpp b/CesiumGltfContent/src/ImageManipulation.cpp index aa5971a24..fcc45ffb6 100644 --- a/CesiumGltfContent/src/ImageManipulation.cpp +++ b/CesiumGltfContent/src/ImageManipulation.cpp @@ -6,6 +6,8 @@ #define STB_IMAGE_RESIZE_IMPLEMENTATION #include +#define STB_IMAGE_WRITE_IMPLEMENTATION +#include namespace CesiumGltfContent { @@ -113,4 +115,40 @@ bool ImageManipulation::blitImage( return true; } + +namespace { +void writePngToVector(void* context, void* data, int size) { + std::vector* pVector = + reinterpret_cast*>(context); + size_t previousSize = pVector->size(); + pVector->resize(previousSize + size); + std::memcpy(pVector->data() + previousSize, data, size); +} +} // namespace + +/*static*/ void ImageManipulation::savePng( + const CesiumGltf::ImageCesium& image, + std::vector& output) { + if (image.bytesPerChannel != 1) { + // Only 8-bit images can be written. + return; + } + + stbi_write_png_to_func( + writePngToVector, + &output, + image.width, + image.height, + image.channels, + image.pixelData.data(), + 0); +} + +/*static*/ std::vector +ImageManipulation::savePng(const CesiumGltf::ImageCesium& image) { + std::vector result; + savePng(image, result); + return result; +} + } // namespace CesiumGltfContent diff --git a/CesiumRasterOverlays/include/CesiumRasterOverlays/RasterOverlayUtilities.h b/CesiumRasterOverlays/include/CesiumRasterOverlays/RasterOverlayUtilities.h index 3112930ef..ed0a4a262 100644 --- a/CesiumRasterOverlays/include/CesiumRasterOverlays/RasterOverlayUtilities.h +++ b/CesiumRasterOverlays/include/CesiumRasterOverlays/RasterOverlayUtilities.h @@ -59,9 +59,10 @@ struct CESIUMRASTEROVERLAYS_API RasterOverlayUtilities { createRasterOverlayTextureCoordinates( CesiumGltf::Model& gltf, const glm::dmat4& modelToEcefTransform, - int32_t firstTextureCoordinateID, const std::optional& globeRectangle, - std::vector&& projections); + std::vector&& projections, + const std::string& textureCoordinateAttributeBaseName = "TEXCOORD_", + int32_t firstTextureCoordinateID = 0); /** * @brief Computes the desired screen pixels for a raster overlay texture. diff --git a/CesiumRasterOverlays/src/RasterOverlayUtilities.cpp b/CesiumRasterOverlays/src/RasterOverlayUtilities.cpp index dc8d65cab..88865d659 100644 --- a/CesiumRasterOverlays/src/RasterOverlayUtilities.cpp +++ b/CesiumRasterOverlays/src/RasterOverlayUtilities.cpp @@ -15,9 +15,10 @@ namespace CesiumRasterOverlays { RasterOverlayUtilities::createRasterOverlayTextureCoordinates( CesiumGltf::Model& model, const glm::dmat4& modelToEcefTransform, - int32_t firstTextureCoordinateID, const std::optional& globeRectangle, - std::vector&& projections) { + std::vector&& projections, + const std::string& textureCoordinateAttributeBaseName, + int32_t firstTextureCoordinateID) { if (projections.empty()) { return std::nullopt; } @@ -79,7 +80,7 @@ RasterOverlayUtilities::createRasterOverlayTextureCoordinates( // them. for (size_t i = 0; i < projections.size(); ++i) { std::string attributeName = - "_CESIUMOVERLAY_" + + textureCoordinateAttributeBaseName + std::to_string(firstTextureCoordinateID + int32_t(i)); primitive.attributes[attributeName] = firstTextureCoordinateAccessorIndex + int32_t(i); @@ -154,13 +155,15 @@ RasterOverlayUtilities::createRasterOverlayTextureCoordinates( uvAccessor.componentType = CesiumGltf::Accessor::ComponentType::FLOAT; uvAccessor.count = int64_t(positionView.size()); uvAccessor.type = CesiumGltf::Accessor::Type::VEC2; + uvAccessor.min = {0.0, 0.0}; + uvAccessor.max = {1.0, 1.0}; [[maybe_unused]] CesiumGltf::AccessorWriter& uvWriter = uvWriters.emplace_back(gltf, uvAccessorId); assert(uvWriter.status() == CesiumGltf::AccessorViewStatus::Valid); std::string attributeName = - "_CESIUMOVERLAY_" + + textureCoordinateAttributeBaseName + std::to_string(firstTextureCoordinateID + int32_t(i)); primitive.attributes[attributeName] = uvAccessorId; } @@ -249,8 +252,10 @@ RasterOverlayUtilities::createRasterOverlayTextureCoordinates( 0.0, 1.0), CesiumUtility::Math::clamp( - (projectedPosition.y - rectangle.minimumY) / - rectangle.computeHeight(), + // TODO: this "1.0 minus" will break existing shaders in + // game engines! + 1.0 - (projectedPosition.y - rectangle.minimumY) / + rectangle.computeHeight(), 0.0, 1.0)); diff --git a/CesiumRasterOverlays/test/TestAddRasterOverlayToGltf.cpp b/CesiumRasterOverlays/test/TestAddRasterOverlayToGltf.cpp index 3fc41bcee..5884df8b4 100644 --- a/CesiumRasterOverlays/test/TestAddRasterOverlayToGltf.cpp +++ b/CesiumRasterOverlays/test/TestAddRasterOverlayToGltf.cpp @@ -1,7 +1,9 @@ +#include #include #include #include #include +#include #include #include #include @@ -17,7 +19,11 @@ #include #include +#include +#include + using namespace CesiumAsync; +using namespace CesiumGeometry; using namespace CesiumGeospatial; using namespace CesiumGltf; using namespace CesiumGltfContent; @@ -44,7 +50,20 @@ TEST_CASE("Add raster overlay to glTF") { glm::scale(glm::dmat4(1.0), glm::dvec3(100000.0, 100000.0, 100000.0)); glm::dmat4 modelToEcef = enuToFixed * scale; - // Set up some mock resources. + // Find the first unused texture coordinate set across all + // primitives. + int32_t textureCoordinateIndex = 0; + for (const Mesh& mesh : gltf.meshes) { + for (const MeshPrimitive& primitive : mesh.primitives) { + while (primitive.attributes.find( + "TEXCOORD_" + std::to_string(textureCoordinateIndex)) != + primitive.attributes.end()) { + ++textureCoordinateIndex; + } + } + } + + // Set up some mock resources for the raster overlay. auto pMockTaskProcessor = std::make_shared(); CesiumAsync::AsyncSystem asyncSystem{pMockTaskProcessor}; @@ -70,7 +89,7 @@ TEST_CASE("Add raster overlay to glTF") { auto pMockAssetAccessor = std::make_shared(std::move(mapUrlToRequest)); - // Create a raster overlay to drape over the glTF. + // Create the raster overlay to drape over the glTF. std::string tmr = "file:///" + std::filesystem::directory_entry( dataDir / "Cesium_Logo_Color" / "tilemapresource.xml") @@ -88,7 +107,7 @@ TEST_CASE("Add raster overlay to glTF") { nullptr, spdlog::default_logger(), nullptr) - .thenInMainThread([&gltf, &modelToEcef]( + .thenInMainThread([&gltf, &modelToEcef, textureCoordinateIndex]( RasterOverlay::CreateTileProviderResult&& tileProviderResult) { REQUIRE(tileProviderResult); @@ -100,9 +119,10 @@ TEST_CASE("Add raster overlay to glTF") { RasterOverlayUtilities::createRasterOverlayTextureCoordinates( gltf, modelToEcef, - 0, std::nullopt, - {pTileProvider->getProjection()}); + {pTileProvider->getProjection()}, + "TEXCOORD_", + textureCoordinateIndex); REQUIRE(details); REQUIRE(details->rasterOverlayProjections.size() == 1); REQUIRE(details->rasterOverlayRectangles.size() == 1); @@ -126,12 +146,15 @@ TEST_CASE("Add raster overlay to glTF") { details->rasterOverlayRectangles[0], targetScreenPixels); - // And go load the texture. + // Go load the texture. return pTileProvider->loadTile(*pRasterTile); }) - .thenInMainThread([&gltf](TileProviderAndTile&& loadResult) { + .thenInMainThread([&gltf, textureCoordinateIndex]( + TileProviderAndTile&& loadResult) { + // Create the image, sampler, and texture for the raster overlay Image& image = gltf.images.emplace_back(); image.cesium = loadResult.pTile->getImage(); + image.mimeType = Image::MimeType::image_png; Sampler& sampler = gltf.samplers.emplace_back(); sampler.magFilter = Sampler::MagFilter::LINEAR; @@ -142,11 +165,81 @@ TEST_CASE("Add raster overlay to glTF") { Texture& texture = gltf.textures.emplace_back(); texture.sampler = int32_t(gltf.samplers.size() - 1); texture.source = int32_t(gltf.images.size() - 1); + + Buffer& buffer = !gltf.buffers.empty() + ? gltf.buffers.front() + : gltf.buffers.emplace_back(); + size_t imageStart = buffer.cesium.data.size(); + + // PNG-encode the raster overlay image and store it in the main + // buffer. + ImageManipulation::savePng(image.cesium, buffer.cesium.data); + + BufferView& bufferView = gltf.bufferViews.emplace_back(); + bufferView.buffer = 0; + bufferView.byteOffset = imageStart; + bufferView.byteLength = buffer.cesium.data.size() - imageStart; + + buffer.byteLength = buffer.cesium.data.size(); + + image.bufferView = int32_t(gltf.bufferViews.size() - 1); + + // TODO: the below will replace any existing color texture with + // the raster overlay, because glTF only allows one color + // texture. However, it doesn't clean up previous textures or + // texture coordinates, leaving the model bigger than it needs + // to be. + + int32_t newMaterialIndex = -1; + + for (Mesh& mesh : gltf.meshes) { + for (MeshPrimitive& primitive : mesh.primitives) { + if (primitive.material < 0 || + primitive.material >= gltf.materials.size()) { + // This primitive didn't previous have a material so assign + // one (creating it if needed). + if (newMaterialIndex < 0) { + newMaterialIndex = int32_t(gltf.materials.size()); + Material& material = gltf.materials.emplace_back(); + MaterialPBRMetallicRoughness& pbr = + material.pbrMetallicRoughness.emplace(); + pbr.metallicFactor = 0.0; + pbr.roughnessFactor = 1.0; + } + primitive.material = newMaterialIndex; + } + + Material& material = gltf.materials[primitive.material]; + if (!material.pbrMetallicRoughness) + material.pbrMetallicRoughness.emplace(); + if (!material.pbrMetallicRoughness->baseColorTexture) + material.pbrMetallicRoughness->baseColorTexture.emplace(); + + TextureInfo& colorTexture = + *material.pbrMetallicRoughness->baseColorTexture; + colorTexture.index = int32_t(gltf.textures.size() - 1); + colorTexture.texCoord = textureCoordinateIndex; + } + } }); waitForFuture(asyncSystem, std::move(future)); + GltfUtilities::mergeBuffers(gltf); + + GltfWriterOptions options; + options.prettyPrint = true; + GltfWriter writer; GltfWriterResult writeResult = - writer.writeGlb(gltf, gltf.buffers[0].cesium.data); + writer.writeGlb(gltf, gltf.buffers[0].cesium.data, options); + + std::ofstream outputFile( + dataDir / "Shadow_Tester_overlays.glb", + std::ios::binary | std::ios::ate); + REQUIRE(outputFile); + + outputFile.write( + reinterpret_cast(writeResult.gltfBytes.data()), + writeResult.gltfBytes.size()); } From 791984c22c391d491f7ee4db32b7aedbf2b1fdec Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Fri, 17 Nov 2023 14:12:21 +1100 Subject: [PATCH 377/421] moveBufferContent, invertVCoordinate. --- .../src/TilesetContentManager.cpp | 1 + .../include/CesiumGltfContent/GltfUtilities.h | 28 ++++++-- CesiumGltfContent/src/GltfUtilities.cpp | 65 +++++++++++++------ .../RasterOverlayUtilities.h | 12 +++- .../src/RasterOverlayUtilities.cpp | 13 ++-- .../test/TestAddRasterOverlayToGltf.cpp | 13 +++- 6 files changed, 98 insertions(+), 34 deletions(-) diff --git a/Cesium3DTilesSelection/src/TilesetContentManager.cpp b/Cesium3DTilesSelection/src/TilesetContentManager.cpp index 5c9fc848a..b8b05a42b 100644 --- a/Cesium3DTilesSelection/src/TilesetContentManager.cpp +++ b/Cesium3DTilesSelection/src/TilesetContentManager.cpp @@ -384,6 +384,7 @@ void calcRasterOverlayDetailsInWorkerThread( tileLoadInfo.tileTransform, pRegion ? std::make_optional(pRegion->getRectangle()) : std::nullopt, std::move(projections), + false, "_CESIUMOVERLAY_", firstRasterOverlayTexCoord); diff --git a/CesiumGltfContent/include/CesiumGltfContent/GltfUtilities.h b/CesiumGltfContent/include/CesiumGltfContent/GltfUtilities.h index 96a925b8b..db3fe0b5e 100644 --- a/CesiumGltfContent/include/CesiumGltfContent/GltfUtilities.h +++ b/CesiumGltfContent/include/CesiumGltfContent/GltfUtilities.h @@ -11,8 +11,9 @@ #include namespace CesiumGltf { +struct Buffer; struct Model; -} +} // namespace CesiumGltf namespace CesiumGltfContent { /** @@ -94,12 +95,31 @@ struct CESIUMGLTFCONTENT_API GltfUtilities { parseGltfCopyright(const CesiumGltf::Model& gltf); /** - * @brief Merges all of the glTF's buffers into a single buffer. + * @brief Merges all of the glTF's buffers into a single buffer (the first + * one). * - * This is useful when writing the glTF as a GLB. + * This is useful when writing the glTF as a GLB, which supports only a single + * embedded buffer. * * @param gltf The glTF in which to merge buffers. */ - static void mergeBuffers(CesiumGltf::Model& gltf); + static void collapseToSingleBuffer(CesiumGltf::Model& gltf); + + /** + * @brief Copies the content of one {@link Buffer} to the end of another, + * updates all {@link BufferView} instances to refer to the destination + * buffer, and clears the contents of the original buffer. + * + * The source buffer is not removed, but it has a `byteLength` of zero after + * this function completes. + * + * @param gltf The glTF model to modify. + * @param destination The destination Buffer into which to move content. + * @param source The source Buffer from which to move content. + */ + static void moveBufferContent( + CesiumGltf::Model& gltf, + CesiumGltf::Buffer& destination, + CesiumGltf::Buffer& source); }; } // namespace CesiumGltfContent diff --git a/CesiumGltfContent/src/GltfUtilities.cpp b/CesiumGltfContent/src/GltfUtilities.cpp index 78d9c4021..1400ac14e 100644 --- a/CesiumGltfContent/src/GltfUtilities.cpp +++ b/CesiumGltfContent/src/GltfUtilities.cpp @@ -153,41 +153,64 @@ GltfUtilities::parseGltfCopyright(const CesiumGltf::Model& gltf) { return result; } -/*static*/ void GltfUtilities::mergeBuffers(CesiumGltf::Model& gltf) { +/*static*/ void GltfUtilities::collapseToSingleBuffer(CesiumGltf::Model& gltf) { if (gltf.buffers.empty()) return; - Buffer& mainBuffer = gltf.buffers[0]; - - // Copy all other buffers into the main one, and keep track of where we put - // them. - std::vector oldBufferStarts; + Buffer& destinationBuffer = gltf.buffers[0]; for (size_t i = 1; i < gltf.buffers.size(); ++i) { - const Buffer& otherBuffer = gltf.buffers[i]; + Buffer& sourceBuffer = gltf.buffers[i]; + GltfUtilities::moveBufferContent(gltf, destinationBuffer, sourceBuffer); + } - oldBufferStarts.emplace_back(mainBuffer.cesium.data.size()); + // Remove all the old buffers + gltf.buffers.resize(1); +} - mainBuffer.cesium.data.insert( - mainBuffer.cesium.data.end(), - otherBuffer.cesium.data.begin(), - otherBuffer.cesium.data.end()); +/*static*/ void GltfUtilities::moveBufferContent( + CesiumGltf::Model& gltf, + CesiumGltf::Buffer& destination, + CesiumGltf::Buffer& source) { + // Assert that the byteLength and the size of the cesium data vector are in + // sync. + assert(source.byteLength == int64_t(source.cesium.data.size())); + assert(destination.byteLength == int64_t(destination.cesium.data.size())); + + int64_t sourceIndex = &source - &gltf.buffers[0]; + int64_t destinationIndex = &destination - &gltf.buffers[0]; + + // Both buffers must exist in the glTF. + if (sourceIndex < 0 || sourceIndex >= int64_t(gltf.buffers.size()) || + destinationIndex < 0 || + destinationIndex >= int64_t(gltf.buffers.size())) { + assert(false); + return; } - mainBuffer.byteLength = mainBuffer.cesium.data.size(); + // Copy the data to the destination and keep track of where we put it. + size_t start = destination.cesium.data.size(); - // Update all the bufferViews to refer to the main buffer. + destination.cesium.data.insert( + destination.cesium.data.end(), + source.cesium.data.begin(), + source.cesium.data.end()); + + source.byteLength = 0; + source.cesium.data.clear(); + source.cesium.data.shrink_to_fit(); + + destination.byteLength = destination.cesium.data.size(); + + // Update all the bufferViews that previously referred to the source Buffer to + // refer to the destination Buffer instead. for (BufferView& bufferView : gltf.bufferViews) { - if (bufferView.buffer <= 0 || bufferView.buffer >= gltf.buffers.size()) + if (bufferView.buffer != sourceIndex) continue; - size_t oldBufferStart = oldBufferStarts[bufferView.buffer - 1]; - bufferView.buffer = 0; - bufferView.byteOffset += oldBufferStart; + bufferView.buffer = int32_t(destinationIndex); + bufferView.byteOffset += start; } - - // Remove all the old buffers - gltf.buffers.resize(1); } } // namespace CesiumGltfContent diff --git a/CesiumRasterOverlays/include/CesiumRasterOverlays/RasterOverlayUtilities.h b/CesiumRasterOverlays/include/CesiumRasterOverlays/RasterOverlayUtilities.h index ed0a4a262..0cab80563 100644 --- a/CesiumRasterOverlays/include/CesiumRasterOverlays/RasterOverlayUtilities.h +++ b/CesiumRasterOverlays/include/CesiumRasterOverlays/RasterOverlayUtilities.h @@ -45,14 +45,21 @@ struct CESIUMRASTEROVERLAYS_API RasterOverlayUtilities { * @param gltf The glTF model. * @param modelToEcefTransform The transformation of this glTF to ECEF * coordinates. - * @param firstTextureCoordinateID The texture coordinate ID of the first - * projection. * @param globeRectangle The rectangle that all projected vertex positions are * expected to lie within. If this parameter is std::nullopt, it is computed * from the vertices. * @param projections The projections for which to generate texture * coordinates. There is a linear relationship between the coordinates of this * projection and the generated texture coordinates. + * @param invertVCoordinate True if the V texture coordinate should be + * inverted so that it is 1.0 at the Southern end of the rectangle and 0.0 at + * the Northern end. This is useful with images that use the typical North-up + * orientation. + * @param textureCoordinateAttributeBaseName The base name to use for the + * texture coordinate attributes, without a number on the end. Defaults to + * "TEXCOORD_0". + * @param firstTextureCoordinateID The texture coordinate ID of the first + * projection. * @return The details of the generated texture coordinates. */ static std::optional @@ -61,6 +68,7 @@ struct CESIUMRASTEROVERLAYS_API RasterOverlayUtilities { const glm::dmat4& modelToEcefTransform, const std::optional& globeRectangle, std::vector&& projections, + bool invertVCoordinate = false, const std::string& textureCoordinateAttributeBaseName = "TEXCOORD_", int32_t firstTextureCoordinateID = 0); diff --git a/CesiumRasterOverlays/src/RasterOverlayUtilities.cpp b/CesiumRasterOverlays/src/RasterOverlayUtilities.cpp index 88865d659..51db0c2ff 100644 --- a/CesiumRasterOverlays/src/RasterOverlayUtilities.cpp +++ b/CesiumRasterOverlays/src/RasterOverlayUtilities.cpp @@ -17,6 +17,7 @@ RasterOverlayUtilities::createRasterOverlayTextureCoordinates( const glm::dmat4& modelToEcefTransform, const std::optional& globeRectangle, std::vector&& projections, + bool invertVCoordinate, const std::string& textureCoordinateAttributeBaseName, int32_t firstTextureCoordinateID) { if (projections.empty()) { @@ -140,6 +141,8 @@ RasterOverlayUtilities::createRasterOverlayTextureCoordinates( uvBuffer.cesium.data.resize( size_t(positionView.size()) * 2 * sizeof(float)); + uvBuffer.byteLength = uvBuffer.cesium.data.size(); + CesiumGltf::BufferView& uvBufferView = gltf.bufferViews[static_cast(uvBufferViewId)]; uvBufferView.buffer = uvBufferId; @@ -252,13 +255,15 @@ RasterOverlayUtilities::createRasterOverlayTextureCoordinates( 0.0, 1.0), CesiumUtility::Math::clamp( - // TODO: this "1.0 minus" will break existing shaders in - // game engines! - 1.0 - (projectedPosition.y - rectangle.minimumY) / - rectangle.computeHeight(), + (projectedPosition.y - rectangle.minimumY) / + rectangle.computeHeight(), 0.0, 1.0)); + if (invertVCoordinate) { + uv.y = 1.0f - uv.y; + } + uvWriters[projectionIndex][positionIndex] = uv; } } diff --git a/CesiumRasterOverlays/test/TestAddRasterOverlayToGltf.cpp b/CesiumRasterOverlays/test/TestAddRasterOverlayToGltf.cpp index 5884df8b4..bee641f28 100644 --- a/CesiumRasterOverlays/test/TestAddRasterOverlayToGltf.cpp +++ b/CesiumRasterOverlays/test/TestAddRasterOverlayToGltf.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -121,6 +122,7 @@ TEST_CASE("Add raster overlay to glTF") { modelToEcef, std::nullopt, {pTileProvider->getProjection()}, + true, "TEXCOORD_", textureCoordinateIndex); REQUIRE(details); @@ -153,7 +155,6 @@ TEST_CASE("Add raster overlay to glTF") { TileProviderAndTile&& loadResult) { // Create the image, sampler, and texture for the raster overlay Image& image = gltf.images.emplace_back(); - image.cesium = loadResult.pTile->getImage(); image.mimeType = Image::MimeType::image_png; Sampler& sampler = gltf.samplers.emplace_back(); @@ -173,7 +174,9 @@ TEST_CASE("Add raster overlay to glTF") { // PNG-encode the raster overlay image and store it in the main // buffer. - ImageManipulation::savePng(image.cesium, buffer.cesium.data); + ImageManipulation::savePng( + loadResult.pTile->getImage(), + buffer.cesium.data); BufferView& bufferView = gltf.bufferViews.emplace_back(); bufferView.buffer = 0; @@ -219,13 +222,17 @@ TEST_CASE("Add raster overlay to glTF") { *material.pbrMetallicRoughness->baseColorTexture; colorTexture.index = int32_t(gltf.textures.size() - 1); colorTexture.texCoord = textureCoordinateIndex; + + // ExtensionKhrTextureTransform& textureTransform = + // colorTexture.addExtension(); + // textureTransform.scale = {1.0, 1.0}; } } }); waitForFuture(asyncSystem, std::move(future)); - GltfUtilities::mergeBuffers(gltf); + GltfUtilities::collapseToSingleBuffer(gltf); GltfWriterOptions options; options.prettyPrint = true; From 9a0f2476d0ab7fc965559b235af3cf9d3aa80cd9 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Fri, 17 Nov 2023 14:50:19 +1100 Subject: [PATCH 378/421] Remove duplicate code. --- .../src/RasterMappedTo3DTile.cpp | 74 ++++--------------- 1 file changed, 15 insertions(+), 59 deletions(-) diff --git a/Cesium3DTilesSelection/src/RasterMappedTo3DTile.cpp b/Cesium3DTilesSelection/src/RasterMappedTo3DTile.cpp index f307e8e87..c31922c0f 100644 --- a/Cesium3DTilesSelection/src/RasterMappedTo3DTile.cpp +++ b/Cesium3DTilesSelection/src/RasterMappedTo3DTile.cpp @@ -8,6 +8,7 @@ #include #include #include +#include using namespace Cesium3DTilesSelection; using namespace CesiumGeometry; @@ -257,41 +258,6 @@ int32_t addProjectionToList( } } -glm::dvec2 computeDesiredScreenPixels( - const Tile& tile, - const Projection& projection, - const Rectangle& rectangle, - double maxHeight, - double maximumScreenSpaceError, - const Ellipsoid& ellipsoid = Ellipsoid::WGS84) { - // We're aiming to estimate the maximum number of pixels (in each projected - // direction) the tile will occupy on the screen. The will be determined by - // the tile's geometric error, because when less error is needed (i.e. the - // viewer moved closer), the LOD will switch to show the tile's children - // instead of this tile. - // - // It works like this: - // * Estimate the size of the projected rectangle in world coordinates. - // * Compute the distance at which tile will switch to its children, based on - // its geometric error and the tileset SSE. - // * Compute the on-screen size of the projected rectangle at that distance. - // - // For the two compute steps, we use the usual perspective projection SSE - // equation: - // screenSize = (realSize * viewportHeight) / (distance * 2 * tan(0.5 * fovY)) - // - // Conveniently a bunch of terms cancel out, so the screen pixel size at the - // switch distance is not actually dependent on the screen dimensions or - // field-of-view angle. - double geometryError = tile.getNonZeroGeometricError(); - glm::dvec2 diameters = computeProjectedRectangleSize( - projection, - rectangle, - maxHeight, - ellipsoid); - return diameters * maximumScreenSpaceError / geometryError; -} - RasterMappedTo3DTile* addRealTile( Tile& tile, RasterOverlayTileProvider& provider, @@ -322,16 +288,6 @@ RasterMappedTo3DTile* addRealTile( RasterMappedTo3DTile(getPlaceholderTile(placeholder), -1)); } - // We can get a more accurate estimate of the real-world size of the projected - // rectangle if we consider the rectangle at the true height of the geometry - // rather than assuming it's on the ellipsoid. This will make basically no - // difference for small tiles (because surface normals on opposite ends of - // tiles are effectively identical), and only a small difference for large - // ones (because heights will be small compared to the total size of a large - // tile). So we're skipping this complexity for now and estimating geometry - // width/height as if it's on the ellipsoid surface. - const double heightForSizeEstimation = 0.0; - const Projection& projection = tileProvider.getProjection(); // If the tile is loaded, use the precise rectangle computed from the content. @@ -346,13 +302,13 @@ RasterMappedTo3DTile* addRealTile( // We have a rectangle and texture coordinates for this projection. int32_t index = int32_t(pRectangle - &overlayDetails.rasterOverlayRectangles[0]); - const glm::dvec2 screenPixels = computeDesiredScreenPixels( - tile, - projection, - *pRectangle, - heightForSizeEstimation, - maximumScreenSpaceError, - Ellipsoid::WGS84); + const glm::dvec2 screenPixels = + RasterOverlayUtilities::computeDesiredScreenPixels( + tile.getNonZeroGeometricError(), + maximumScreenSpaceError, + projection, + *pRectangle, + Ellipsoid::WGS84); return addRealTile(tile, tileProvider, *pRectangle, screenPixels, index); } else { // We don't have a precise rectangle for this projection, which means the @@ -376,13 +332,13 @@ RasterMappedTo3DTile* addRealTile( tileProvider.getProjection(), tile.getBoundingVolume()); if (maybeRectangle) { - const glm::dvec2 screenPixels = computeDesiredScreenPixels( - tile, - projection, - *maybeRectangle, - heightForSizeEstimation, - maximumScreenSpaceError, - Ellipsoid::WGS84); + const glm::dvec2 screenPixels = + RasterOverlayUtilities::computeDesiredScreenPixels( + tile.getNonZeroGeometricError(), + maximumScreenSpaceError, + projection, + *maybeRectangle, + Ellipsoid::WGS84); return addRealTile( tile, tileProvider, From 86fce69b304bc146dbc0dad210d9f9e0c2cd4cd3 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Fri, 17 Nov 2023 16:33:47 +1100 Subject: [PATCH 379/421] Add Future::thenPassThrough. --- CHANGES.md | 1 + CesiumAsync/include/CesiumAsync/Future.h | 24 ++++++++++++++ .../include/CesiumAsync/SharedFuture.h | 22 +++++++++++++ CesiumAsync/test/TestAsyncSystem.cpp | 33 +++++++++++++++++++ 4 files changed, 80 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 54dade1a5..7bd45cf0b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -18,6 +18,7 @@ - Added `TileTransform` class to `Cesium3DTilesContent`, making it easier to create a `glm::dmat4` from the `transform` property of a `Cesium3DTiles::Tile`. - Added `ImplicitTilingUtilities` class to `Cesium3DTilesContent`. - Added overloads of `isTileAvailable` and `isContentAvailable` on the `SubtreeAvailability` class that take the subtree root tile ID and the tile ID of interest, instead of a relative level and Morton index. +- Added `Future::thenPassThrough`, used to easily pass additional values through to the next continuation. ##### Fixes :wrench: diff --git a/CesiumAsync/include/CesiumAsync/Future.h b/CesiumAsync/include/CesiumAsync/Future.h index 7f234f3b8..13347f87a 100644 --- a/CesiumAsync/include/CesiumAsync/Future.h +++ b/CesiumAsync/include/CesiumAsync/Future.h @@ -201,6 +201,30 @@ template class Future final { std::forward(f)); } + /** + * @brief Passes through one or more additional values to the next + * continuation. + * + * The next continuation will receive a tuple with each of the provided + * values, followed by the result of the current Future. + * + * @tparam TPassThrough The types to pass through to the next continuation. + * @param value The values to pass through to the next continuation. + * @return A new Future that resolves to a tuple with the pass-through values, + * followed by the result of the last Future. + */ + template + Future> + thenPassThrough(TPassThrough&&... values) && { + return std::move(*this).thenImmediately( + [values = std::tuple(std::forward(values)...)]( + T&& result) mutable { + return std::tuple_cat( + std::move(values), + std::make_tuple(std::move(result))); + }); + } + /** * @brief Waits for the future to resolve or reject and returns the result. * diff --git a/CesiumAsync/include/CesiumAsync/SharedFuture.h b/CesiumAsync/include/CesiumAsync/SharedFuture.h index 531604424..0ce84ddf0 100644 --- a/CesiumAsync/include/CesiumAsync/SharedFuture.h +++ b/CesiumAsync/include/CesiumAsync/SharedFuture.h @@ -187,6 +187,28 @@ template class SharedFuture final { std::forward(f)); } + /** + * @brief Passes through one or more additional values to the next + * continuation. + * + * The next continuation will receive a tuple with each of the provided + * values, followed by the result of the current Future. + * + * @tparam TPassThrough The types to pass through to the next continuation. + * @param value The values to pass through to the next continuation. + * @return A new Future that resolves to a tuple with the pass-through values, + * followed by the result of the last Future. + */ + template + Future> + thenPassThrough(TPassThrough&&... values) { + return this->thenImmediately( + [values = std::tuple(std::forward(values)...)]( + const T& result) mutable { + return std::tuple_cat(std::move(values), std::make_tuple(result)); + }); + } + /** * @brief Waits for the future to resolve or reject and returns the result. * diff --git a/CesiumAsync/test/TestAsyncSystem.cpp b/CesiumAsync/test/TestAsyncSystem.cpp index ee7354ff2..a6656a2c6 100644 --- a/CesiumAsync/test/TestAsyncSystem.cpp +++ b/CesiumAsync/test/TestAsyncSystem.cpp @@ -539,4 +539,37 @@ TEST_CASE("AsyncSystem") { CHECK(future.isReady()); future.wait(); } + + SECTION("thenPassThrough") { + bool checksCompleted = false; + + asyncSystem.createResolvedFuture(3.1) + .thenPassThrough(std::string("foo"), 4) + .thenImmediately([&](std::tuple&& tuple) { + auto& [s, i, d] = tuple; + CHECK(s == "foo"); + CHECK(i == 4); + CHECK(d == 3.1); + checksCompleted = true; + }); + + CHECK(checksCompleted); + } + + SECTION("thenPassThrough on a SharedFuture") { + bool checksCompleted = false; + + asyncSystem.createResolvedFuture(3.1) + .share() + .thenPassThrough(std::string("foo"), 4) + .thenImmediately([&](std::tuple&& tuple) { + auto& [s, i, d] = tuple; + CHECK(s == "foo"); + CHECK(i == 4); + CHECK(d == 3.1); + checksCompleted = true; + }); + + CHECK(checksCompleted); + } } From d9f7dc2c0f465971bf918c123b29eb1967d45fd9 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Fri, 17 Nov 2023 22:29:31 +1100 Subject: [PATCH 380/421] Compute overlay texture translation and scale. --- .../src/RasterMappedTo3DTile.cpp | 18 +- .../RasterOverlayUtilities.h | 13 ++ .../src/RasterOverlayUtilities.cpp | 18 ++ .../test/TestAddRasterOverlayToGltf.cpp | 169 ++++++++++-------- 4 files changed, 130 insertions(+), 88 deletions(-) diff --git a/Cesium3DTilesSelection/src/RasterMappedTo3DTile.cpp b/Cesium3DTilesSelection/src/RasterMappedTo3DTile.cpp index c31922c0f..3e10e2008 100644 --- a/Cesium3DTilesSelection/src/RasterMappedTo3DTile.cpp +++ b/Cesium3DTilesSelection/src/RasterMappedTo3DTile.cpp @@ -393,17 +393,13 @@ void RasterMappedTo3DTile::computeTranslationAndScale(const Tile& tile) { const CesiumGeometry::Rectangle imageryRectangle = this->_pReadyTile->getRectangle(); - const double terrainWidth = geometryRectangle.computeWidth(); - const double terrainHeight = geometryRectangle.computeHeight(); - - const double scaleX = terrainWidth / imageryRectangle.computeWidth(); - const double scaleY = terrainHeight / imageryRectangle.computeHeight(); - this->_translation = glm::dvec2( - (scaleX * (geometryRectangle.minimumX - imageryRectangle.minimumX)) / - terrainWidth, - (scaleY * (geometryRectangle.minimumY - imageryRectangle.minimumY)) / - terrainHeight); - this->_scale = glm::dvec2(scaleX, scaleY); + glm::dvec4 translationAndScale = + RasterOverlayUtilities::computeTranslationAndScale( + geometryRectangle, + imageryRectangle); + + this->_translation = glm::dvec2(translationAndScale.x, translationAndScale.y); + this->_scale = glm::dvec2(translationAndScale.z, translationAndScale.w); } } // namespace Cesium3DTilesSelection diff --git a/CesiumRasterOverlays/include/CesiumRasterOverlays/RasterOverlayUtilities.h b/CesiumRasterOverlays/include/CesiumRasterOverlays/RasterOverlayUtilities.h index 0cab80563..85a4f5454 100644 --- a/CesiumRasterOverlays/include/CesiumRasterOverlays/RasterOverlayUtilities.h +++ b/CesiumRasterOverlays/include/CesiumRasterOverlays/RasterOverlayUtilities.h @@ -117,6 +117,19 @@ struct CESIUMRASTEROVERLAYS_API RasterOverlayUtilities { const CesiumGeometry::Rectangle& rectangle, const CesiumGeospatial::Ellipsoid& ellipsoid = CesiumGeospatial::Ellipsoid::WGS84); + + /** + * @brief Computes the texture translation and scale necessary to align a + * raster overlay with the given rectangle on geometry whose texture + * coordinates were computed using a different rectangle. + * + * @param geometryRectangle The geometry rectangle used to the compute the texture coordinates. + * @param overlayRectangle The rectangle covered by the raster overlay texture. + * @return The translation in X and Y, and the scale in Z and W. + */ + static glm::dvec4 computeTranslationAndScale( + const CesiumGeometry::Rectangle& geometryRectangle, + const CesiumGeometry::Rectangle& overlayRectangle); }; } // namespace CesiumRasterOverlays diff --git a/CesiumRasterOverlays/src/RasterOverlayUtilities.cpp b/CesiumRasterOverlays/src/RasterOverlayUtilities.cpp index 51db0c2ff..482472e43 100644 --- a/CesiumRasterOverlays/src/RasterOverlayUtilities.cpp +++ b/CesiumRasterOverlays/src/RasterOverlayUtilities.cpp @@ -321,4 +321,22 @@ RasterOverlayUtilities::createRasterOverlayTextureCoordinates( return diameters * maximumScreenSpaceError / geometricError; } +/*static*/ glm::dvec4 RasterOverlayUtilities::computeTranslationAndScale( + const Rectangle& geometryRectangle, + const Rectangle& overlayRectangle) { + const double geometryWidth = geometryRectangle.computeWidth(); + const double geometryHeight = geometryRectangle.computeHeight(); + + const double scaleX = geometryWidth / overlayRectangle.computeWidth(); + const double scaleY = geometryHeight / overlayRectangle.computeHeight(); + glm::dvec2 translation = glm::dvec2( + (scaleX * (geometryRectangle.minimumX - overlayRectangle.minimumX)) / + geometryWidth, + (scaleY * (geometryRectangle.minimumY - overlayRectangle.minimumY)) / + geometryHeight); + glm::dvec2 scale = glm::dvec2(scaleX, scaleY); + + return glm::dvec4(translation, scale); +} + } // namespace CesiumRasterOverlays diff --git a/CesiumRasterOverlays/test/TestAddRasterOverlayToGltf.cpp b/CesiumRasterOverlays/test/TestAddRasterOverlayToGltf.cpp index bee641f28..4baa1b771 100644 --- a/CesiumRasterOverlays/test/TestAddRasterOverlayToGltf.cpp +++ b/CesiumRasterOverlays/test/TestAddRasterOverlayToGltf.cpp @@ -148,87 +148,102 @@ TEST_CASE("Add raster overlay to glTF") { details->rasterOverlayRectangles[0], targetScreenPixels); + glm::dvec4 textureTranslationAndScale = + RasterOverlayUtilities::computeTranslationAndScale( + details->rasterOverlayRectangles[0], + pRasterTile->getRectangle()); + // Go load the texture. - return pTileProvider->loadTile(*pRasterTile); + return pTileProvider->loadTile(*pRasterTile) + .thenPassThrough(std::move(textureTranslationAndScale)); }) - .thenInMainThread([&gltf, textureCoordinateIndex]( - TileProviderAndTile&& loadResult) { - // Create the image, sampler, and texture for the raster overlay - Image& image = gltf.images.emplace_back(); - image.mimeType = Image::MimeType::image_png; - - Sampler& sampler = gltf.samplers.emplace_back(); - sampler.magFilter = Sampler::MagFilter::LINEAR; - sampler.minFilter = Sampler::MinFilter::LINEAR_MIPMAP_LINEAR; - sampler.wrapS = Sampler::WrapS::CLAMP_TO_EDGE; - sampler.wrapT = Sampler::WrapT::CLAMP_TO_EDGE; - - Texture& texture = gltf.textures.emplace_back(); - texture.sampler = int32_t(gltf.samplers.size() - 1); - texture.source = int32_t(gltf.images.size() - 1); - - Buffer& buffer = !gltf.buffers.empty() - ? gltf.buffers.front() - : gltf.buffers.emplace_back(); - size_t imageStart = buffer.cesium.data.size(); - - // PNG-encode the raster overlay image and store it in the main - // buffer. - ImageManipulation::savePng( - loadResult.pTile->getImage(), - buffer.cesium.data); - - BufferView& bufferView = gltf.bufferViews.emplace_back(); - bufferView.buffer = 0; - bufferView.byteOffset = imageStart; - bufferView.byteLength = buffer.cesium.data.size() - imageStart; - - buffer.byteLength = buffer.cesium.data.size(); - - image.bufferView = int32_t(gltf.bufferViews.size() - 1); - - // TODO: the below will replace any existing color texture with - // the raster overlay, because glTF only allows one color - // texture. However, it doesn't clean up previous textures or - // texture coordinates, leaving the model bigger than it needs - // to be. - - int32_t newMaterialIndex = -1; - - for (Mesh& mesh : gltf.meshes) { - for (MeshPrimitive& primitive : mesh.primitives) { - if (primitive.material < 0 || - primitive.material >= gltf.materials.size()) { - // This primitive didn't previous have a material so assign - // one (creating it if needed). - if (newMaterialIndex < 0) { - newMaterialIndex = int32_t(gltf.materials.size()); - Material& material = gltf.materials.emplace_back(); - MaterialPBRMetallicRoughness& pbr = - material.pbrMetallicRoughness.emplace(); - pbr.metallicFactor = 0.0; - pbr.roughnessFactor = 1.0; + .thenInMainThread( + [&gltf, textureCoordinateIndex]( + std::tuple&& tuple) { + auto& [textureTranslationAndScale, loadResult] = tuple; + + // Create the image, sampler, and texture for the raster overlay + Image& image = gltf.images.emplace_back(); + image.mimeType = Image::MimeType::image_png; + + Sampler& sampler = gltf.samplers.emplace_back(); + sampler.magFilter = Sampler::MagFilter::LINEAR; + sampler.minFilter = Sampler::MinFilter::LINEAR_MIPMAP_LINEAR; + sampler.wrapS = Sampler::WrapS::CLAMP_TO_EDGE; + sampler.wrapT = Sampler::WrapT::CLAMP_TO_EDGE; + + Texture& texture = gltf.textures.emplace_back(); + texture.sampler = int32_t(gltf.samplers.size() - 1); + texture.source = int32_t(gltf.images.size() - 1); + + Buffer& buffer = !gltf.buffers.empty() + ? gltf.buffers.front() + : gltf.buffers.emplace_back(); + size_t imageStart = buffer.cesium.data.size(); + + // PNG-encode the raster overlay image and store it in the main + // buffer. + ImageManipulation::savePng( + loadResult.pTile->getImage(), + buffer.cesium.data); + + BufferView& bufferView = gltf.bufferViews.emplace_back(); + bufferView.buffer = 0; + bufferView.byteOffset = imageStart; + bufferView.byteLength = buffer.cesium.data.size() - imageStart; + + buffer.byteLength = buffer.cesium.data.size(); + + image.bufferView = int32_t(gltf.bufferViews.size() - 1); + + // TODO: the below will replace any existing color texture with + // the raster overlay, because glTF only allows one color + // texture. However, it doesn't clean up previous textures or + // texture coordinates, leaving the model bigger than it needs + // to be. + + int32_t newMaterialIndex = -1; + + for (Mesh& mesh : gltf.meshes) { + for (MeshPrimitive& primitive : mesh.primitives) { + if (primitive.material < 0 || + primitive.material >= gltf.materials.size()) { + // This primitive didn't previous have a material so + // assign one (creating it if needed). + if (newMaterialIndex < 0) { + newMaterialIndex = int32_t(gltf.materials.size()); + Material& material = gltf.materials.emplace_back(); + MaterialPBRMetallicRoughness& pbr = + material.pbrMetallicRoughness.emplace(); + pbr.metallicFactor = 0.0; + pbr.roughnessFactor = 1.0; + } + primitive.material = newMaterialIndex; + } + + Material& material = gltf.materials[primitive.material]; + if (!material.pbrMetallicRoughness) + material.pbrMetallicRoughness.emplace(); + if (!material.pbrMetallicRoughness->baseColorTexture) + material.pbrMetallicRoughness->baseColorTexture.emplace(); + + TextureInfo& colorTexture = + *material.pbrMetallicRoughness->baseColorTexture; + colorTexture.index = int32_t(gltf.textures.size() - 1); + colorTexture.texCoord = textureCoordinateIndex; + + ExtensionKhrTextureTransform& textureTransform = + colorTexture + .addExtension(); + textureTransform.offset = { + textureTranslationAndScale.x, + textureTranslationAndScale.y}; + textureTransform.scale = { + textureTranslationAndScale.z, + textureTranslationAndScale.w}; } - primitive.material = newMaterialIndex; } - - Material& material = gltf.materials[primitive.material]; - if (!material.pbrMetallicRoughness) - material.pbrMetallicRoughness.emplace(); - if (!material.pbrMetallicRoughness->baseColorTexture) - material.pbrMetallicRoughness->baseColorTexture.emplace(); - - TextureInfo& colorTexture = - *material.pbrMetallicRoughness->baseColorTexture; - colorTexture.index = int32_t(gltf.textures.size() - 1); - colorTexture.texCoord = textureCoordinateIndex; - - // ExtensionKhrTextureTransform& textureTransform = - // colorTexture.addExtension(); - // textureTransform.scale = {1.0, 1.0}; - } - } - }); + }); waitForFuture(asyncSystem, std::move(future)); From b563542e20d09aa2e5a021a630e734f2c54a3753 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Fri, 17 Nov 2023 22:30:36 +1100 Subject: [PATCH 381/421] Formatting. --- .../include/CesiumGltfContent/SkirtMeshMetadata.h | 2 +- .../include/CesiumRasterOverlays/RasterOverlayUtilities.h | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CesiumGltfContent/include/CesiumGltfContent/SkirtMeshMetadata.h b/CesiumGltfContent/include/CesiumGltfContent/SkirtMeshMetadata.h index 16c291b63..e0dfd5aba 100644 --- a/CesiumGltfContent/include/CesiumGltfContent/SkirtMeshMetadata.h +++ b/CesiumGltfContent/include/CesiumGltfContent/SkirtMeshMetadata.h @@ -33,4 +33,4 @@ struct SkirtMeshMetadata { double skirtEastHeight; double skirtNorthHeight; }; -} // namespace Cesium3DTilesContent +} // namespace CesiumGltfContent diff --git a/CesiumRasterOverlays/include/CesiumRasterOverlays/RasterOverlayUtilities.h b/CesiumRasterOverlays/include/CesiumRasterOverlays/RasterOverlayUtilities.h index 85a4f5454..5caa6616e 100644 --- a/CesiumRasterOverlays/include/CesiumRasterOverlays/RasterOverlayUtilities.h +++ b/CesiumRasterOverlays/include/CesiumRasterOverlays/RasterOverlayUtilities.h @@ -123,8 +123,10 @@ struct CESIUMRASTEROVERLAYS_API RasterOverlayUtilities { * raster overlay with the given rectangle on geometry whose texture * coordinates were computed using a different rectangle. * - * @param geometryRectangle The geometry rectangle used to the compute the texture coordinates. - * @param overlayRectangle The rectangle covered by the raster overlay texture. + * @param geometryRectangle The geometry rectangle used to the compute the + * texture coordinates. + * @param overlayRectangle The rectangle covered by the raster overlay + * texture. * @return The translation in X and Y, and the scale in Z and W. */ static glm::dvec4 computeTranslationAndScale( From 91b8a525c0947f58f8067003d0183e58fb1db4bb Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Fri, 17 Nov 2023 23:05:15 +1100 Subject: [PATCH 382/421] Update CHANGES.md. --- CHANGES.md | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index eb7a538e1..9e7861c95 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,15 +6,19 @@ - Moved `ErrorList`, `CreditSystem`, and `Credit` from `Cesium3DTilesSelection` to `CesiumUtility`. - Moved `GltfUtilities` from `Cesium3DTilesSelection` to `Cesium3DTilesContent`. -- Moved `createRasterOverlayTextureCoordinates` method from `GltfUtilities` to a new `RasterOverlayUtilities` class in the `Cesium3DTilesSelection` library. +- Moved `RasterOverlay`, `RasterOverlayTileProvider`, `RasterOverlayTile`, `QuadtreeRasterOverlayTileProvider`, `RasterOverlayDetails`, and all of the `RasterOverlay`-derived types to a new `CesiumRasterOverlays` library and namespace. +- Moved `createRasterOverlayTextureCoordinates` method from `GltfUtilities` to a new `RasterOverlayUtilities` class in the `CesiumRasterOverlays` library. - `GltfUtilities::parseGltfCopyright` now returns the credits as a vector of string_views. Previously it took a `CreditSystem` and created credits directly. - The `SubtreeAvailability` constructor and `loadSubtree` static method now take an `ImplicitTileSubdivisionScheme` enumeration parameter instead of a `powerOf2` parameter. They also now require a `levelsInSubtree` parameter, which is needed when switching from constant to bitstream availability. Lastly, the constructor now takes a `Subtree` parameter instead of a `std::vector>` representing the buffers. - `SubtreeConstantAvailability`, `SubtreeBufferViewAvailability`, and `AvailabilityView` are now members of `SubtreeAvailability`. -- Moved `RasterOverlay`, `RasterOverlayTileProvider`, `RasterOverlayTile`, `QuadtreeRasterOverlayTileProvider`, and all of the `RasterOverlay`-derived types to a new `CesiumRasterOverlays` library and namespace. +- Moved `ImageManipulation` from `CesiumGltfReader` to `CesiumGltfContent`. +- Added some new parameters to `RasterOverlayUtilities::createRasterOverlayTextureCoordinates` and changed the order of some existing parameters. ##### Additions :tada: -- Added `Cesium3DTilesContent` library and namespace. It has classes for loading, converting, and manipulating 3D Tiles tile content. +- Added new `Cesium3DTilesContent` library and namespace. It has classes for loading, converting, and manipulating 3D Tiles tile content. +- Added new `CesiumGltfContent` library and namespace. It has classes for manipulating in-memory glTF files. +- Added new `CesiumRasterOverlays` library and namespace. It has classes for working with massive textures draped over glTFs and 3D Tiles. - Added `TileBoundingVolumes` class to `Cesium3DTilesContent`, making it easier to create the rich bounding volume types in `CesiumGeometry` and `CesiumGeospatial` from the simple vector representations in `Cesium3DTiles`. - Added `transform` method to `CesiumGeometry::BoundingSphere`. - Added `toSphere`, `fromSphere`, and `fromAxisAligned` methods to `CesiumGeometry::OrientedBoundingBox`. @@ -28,6 +32,10 @@ - Added `rectangleIsWithinPolygons` and `rectangleIsOutsidePolygons` static methods to `CartographicPolygon`. - Raster overlays now use `IPrepareRasterOverlayRendererResources`, which contains only overlay-related methods, instead of `IPrepareRendererResources`, which contains tileset-related methods as well. `IPrepareRendererResources` derives from `IPrepareRasterOverlayRendererResources` so existing code should continue to work without modification. - Added `Future::thenPassThrough`, used to easily pass additional values through to the next continuation. +- Added `collapseToSingleBuffer` and `moveBufferContent` methods to `GltfUtilities`. +- Added `savePng` method to `ImageManipulation`. +- `RasterOverlayTileProvider::loadTile` now returns a future that resolves when the tile is done loading. +- Added `computeDesiredScreenPixels` and `computeTranslationAndScale` methods to `RasterOverlayUtilities`. ##### Fixes :wrench: From 7854e3221e77991233bf84b029b07f5adc99ffa0 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Sat, 18 Nov 2023 00:14:13 +1100 Subject: [PATCH 383/421] Work around dodgy expected-lite warning. --- Cesium3DTilesSelection/CMakeLists.txt | 14 ++++++++++++-- CesiumRasterOverlays/CMakeLists.txt | 7 ++++++- .../test/TestAddRasterOverlayToGltf.cpp | 2 +- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/Cesium3DTilesSelection/CMakeLists.txt b/Cesium3DTilesSelection/CMakeLists.txt index b51400cc6..473afb851 100644 --- a/Cesium3DTilesSelection/CMakeLists.txt +++ b/Cesium3DTilesSelection/CMakeLists.txt @@ -55,13 +55,23 @@ target_link_libraries(Cesium3DTilesSelection CesiumUtility spdlog # PRIVATE - tinyxml2 uriparser libmorton - expected-lite ${CESIUM_NATIVE_DRACO_LIBRARY} ) +target_link_libraries_system( + Cesium3DTilesSelection + PUBLIC + expected-lite +) + +target_link_libraries_system( + Cesium3DTilesSelection + PRIVATE + tinyxml2 +) + install(TARGETS Cesium3DTilesSelection LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/Cesium3DTilesSelection diff --git a/CesiumRasterOverlays/CMakeLists.txt b/CesiumRasterOverlays/CMakeLists.txt index 284ddf06b..1e95340f4 100644 --- a/CesiumRasterOverlays/CMakeLists.txt +++ b/CesiumRasterOverlays/CMakeLists.txt @@ -50,8 +50,13 @@ target_link_libraries(CesiumRasterOverlays target_link_libraries_system( CesiumRasterOverlays - PRIVATE + PUBLIC expected-lite +) + +target_link_libraries_system( + CesiumRasterOverlays + PRIVATE tinyxml2 ) diff --git a/CesiumRasterOverlays/test/TestAddRasterOverlayToGltf.cpp b/CesiumRasterOverlays/test/TestAddRasterOverlayToGltf.cpp index 4baa1b771..347e8d486 100644 --- a/CesiumRasterOverlays/test/TestAddRasterOverlayToGltf.cpp +++ b/CesiumRasterOverlays/test/TestAddRasterOverlayToGltf.cpp @@ -114,7 +114,7 @@ TEST_CASE("Add raster overlay to glTF") { REQUIRE(tileProviderResult); IntrusivePointer pTileProvider = - tileProviderResult.value(); + *tileProviderResult; std::optional details = RasterOverlayUtilities::createRasterOverlayTextureCoordinates( From 413f977e318a4b291ecd1e05831612b12e39f9e4 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Sat, 18 Nov 2023 19:35:25 +1100 Subject: [PATCH 384/421] Fix clang warnings. --- CesiumGltfContent/src/GltfUtilities.cpp | 2 +- CesiumGltfContent/src/ImageManipulation.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CesiumGltfContent/src/GltfUtilities.cpp b/CesiumGltfContent/src/GltfUtilities.cpp index 1400ac14e..e271d22c4 100644 --- a/CesiumGltfContent/src/GltfUtilities.cpp +++ b/CesiumGltfContent/src/GltfUtilities.cpp @@ -200,7 +200,7 @@ GltfUtilities::parseGltfCopyright(const CesiumGltf::Model& gltf) { source.cesium.data.clear(); source.cesium.data.shrink_to_fit(); - destination.byteLength = destination.cesium.data.size(); + destination.byteLength = int64_t(destination.cesium.data.size()); // Update all the bufferViews that previously referred to the source Buffer to // refer to the destination Buffer instead. diff --git a/CesiumGltfContent/src/ImageManipulation.cpp b/CesiumGltfContent/src/ImageManipulation.cpp index fcc45ffb6..d9c029ed9 100644 --- a/CesiumGltfContent/src/ImageManipulation.cpp +++ b/CesiumGltfContent/src/ImageManipulation.cpp @@ -121,8 +121,8 @@ void writePngToVector(void* context, void* data, int size) { std::vector* pVector = reinterpret_cast*>(context); size_t previousSize = pVector->size(); - pVector->resize(previousSize + size); - std::memcpy(pVector->data() + previousSize, data, size); + pVector->resize(previousSize + size_t(size)); + std::memcpy(pVector->data() + previousSize, data, size_t(size)); } } // namespace From c77a0442c48125b5cc82e4f8a26ac682fa32f7af Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Sat, 18 Nov 2023 19:37:05 +1100 Subject: [PATCH 385/421] Fix another warning. --- CesiumGltfContent/src/GltfUtilities.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CesiumGltfContent/src/GltfUtilities.cpp b/CesiumGltfContent/src/GltfUtilities.cpp index e271d22c4..80d688926 100644 --- a/CesiumGltfContent/src/GltfUtilities.cpp +++ b/CesiumGltfContent/src/GltfUtilities.cpp @@ -209,7 +209,7 @@ GltfUtilities::parseGltfCopyright(const CesiumGltf::Model& gltf) { continue; bufferView.buffer = int32_t(destinationIndex); - bufferView.byteOffset += start; + bufferView.byteOffset += int64_t(start); } } From b21922e87d12506006f0a742d4efa2b27498d061 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Sat, 18 Nov 2023 19:39:07 +1100 Subject: [PATCH 386/421] Yet another warning. --- CesiumRasterOverlays/src/RasterOverlayUtilities.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CesiumRasterOverlays/src/RasterOverlayUtilities.cpp b/CesiumRasterOverlays/src/RasterOverlayUtilities.cpp index 482472e43..3d36bea98 100644 --- a/CesiumRasterOverlays/src/RasterOverlayUtilities.cpp +++ b/CesiumRasterOverlays/src/RasterOverlayUtilities.cpp @@ -141,7 +141,7 @@ RasterOverlayUtilities::createRasterOverlayTextureCoordinates( uvBuffer.cesium.data.resize( size_t(positionView.size()) * 2 * sizeof(float)); - uvBuffer.byteLength = uvBuffer.cesium.data.size(); + uvBuffer.byteLength = int64_t(uvBuffer.cesium.data.size()); CesiumGltf::BufferView& uvBufferView = gltf.bufferViews[static_cast(uvBufferViewId)]; From e83dca7892b3c6a066076c59f7cfb45312e6b364 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Sat, 18 Nov 2023 19:53:13 +1100 Subject: [PATCH 387/421] Add missing parameter. --- Cesium3DTilesSelection/src/LayerJsonTerrainLoader.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Cesium3DTilesSelection/src/LayerJsonTerrainLoader.cpp b/Cesium3DTilesSelection/src/LayerJsonTerrainLoader.cpp index 38f10ff33..cec84813b 100644 --- a/Cesium3DTilesSelection/src/LayerJsonTerrainLoader.cpp +++ b/Cesium3DTilesSelection/src/LayerJsonTerrainLoader.cpp @@ -219,6 +219,7 @@ void generateRasterOverlayUVs( pParentRegion->getRectangle()) : std::nullopt, {projection}, + false, "_CESIUMOVERLAY_", 0); } From 42f964d98cc78af494821fc1d0993a9837022b1e Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Sat, 18 Nov 2023 21:38:32 +1100 Subject: [PATCH 388/421] Fix more warnings. --- .../test/TestAddRasterOverlayToGltf.cpp | 172 +++++++++--------- 1 file changed, 86 insertions(+), 86 deletions(-) diff --git a/CesiumRasterOverlays/test/TestAddRasterOverlayToGltf.cpp b/CesiumRasterOverlays/test/TestAddRasterOverlayToGltf.cpp index 347e8d486..f4a65afd1 100644 --- a/CesiumRasterOverlays/test/TestAddRasterOverlayToGltf.cpp +++ b/CesiumRasterOverlays/test/TestAddRasterOverlayToGltf.cpp @@ -157,93 +157,93 @@ TEST_CASE("Add raster overlay to glTF") { return pTileProvider->loadTile(*pRasterTile) .thenPassThrough(std::move(textureTranslationAndScale)); }) - .thenInMainThread( - [&gltf, textureCoordinateIndex]( - std::tuple&& tuple) { - auto& [textureTranslationAndScale, loadResult] = tuple; - - // Create the image, sampler, and texture for the raster overlay - Image& image = gltf.images.emplace_back(); - image.mimeType = Image::MimeType::image_png; - - Sampler& sampler = gltf.samplers.emplace_back(); - sampler.magFilter = Sampler::MagFilter::LINEAR; - sampler.minFilter = Sampler::MinFilter::LINEAR_MIPMAP_LINEAR; - sampler.wrapS = Sampler::WrapS::CLAMP_TO_EDGE; - sampler.wrapT = Sampler::WrapT::CLAMP_TO_EDGE; - - Texture& texture = gltf.textures.emplace_back(); - texture.sampler = int32_t(gltf.samplers.size() - 1); - texture.source = int32_t(gltf.images.size() - 1); - - Buffer& buffer = !gltf.buffers.empty() - ? gltf.buffers.front() - : gltf.buffers.emplace_back(); - size_t imageStart = buffer.cesium.data.size(); - - // PNG-encode the raster overlay image and store it in the main - // buffer. - ImageManipulation::savePng( - loadResult.pTile->getImage(), - buffer.cesium.data); - - BufferView& bufferView = gltf.bufferViews.emplace_back(); - bufferView.buffer = 0; - bufferView.byteOffset = imageStart; - bufferView.byteLength = buffer.cesium.data.size() - imageStart; - - buffer.byteLength = buffer.cesium.data.size(); - - image.bufferView = int32_t(gltf.bufferViews.size() - 1); - - // TODO: the below will replace any existing color texture with - // the raster overlay, because glTF only allows one color - // texture. However, it doesn't clean up previous textures or - // texture coordinates, leaving the model bigger than it needs - // to be. - - int32_t newMaterialIndex = -1; - - for (Mesh& mesh : gltf.meshes) { - for (MeshPrimitive& primitive : mesh.primitives) { - if (primitive.material < 0 || - primitive.material >= gltf.materials.size()) { - // This primitive didn't previous have a material so - // assign one (creating it if needed). - if (newMaterialIndex < 0) { - newMaterialIndex = int32_t(gltf.materials.size()); - Material& material = gltf.materials.emplace_back(); - MaterialPBRMetallicRoughness& pbr = - material.pbrMetallicRoughness.emplace(); - pbr.metallicFactor = 0.0; - pbr.roughnessFactor = 1.0; - } - primitive.material = newMaterialIndex; - } - - Material& material = gltf.materials[primitive.material]; - if (!material.pbrMetallicRoughness) - material.pbrMetallicRoughness.emplace(); - if (!material.pbrMetallicRoughness->baseColorTexture) - material.pbrMetallicRoughness->baseColorTexture.emplace(); - - TextureInfo& colorTexture = - *material.pbrMetallicRoughness->baseColorTexture; - colorTexture.index = int32_t(gltf.textures.size() - 1); - colorTexture.texCoord = textureCoordinateIndex; - - ExtensionKhrTextureTransform& textureTransform = - colorTexture - .addExtension(); - textureTransform.offset = { - textureTranslationAndScale.x, - textureTranslationAndScale.y}; - textureTransform.scale = { - textureTranslationAndScale.z, - textureTranslationAndScale.w}; + .thenInMainThread([&gltf, textureCoordinateIndex]( + std::tuple&& + tuple) { + auto& [textureTranslationAndScale, loadResult] = tuple; + + // Create the image, sampler, and texture for the raster overlay + Image& image = gltf.images.emplace_back(); + image.mimeType = Image::MimeType::image_png; + + Sampler& sampler = gltf.samplers.emplace_back(); + sampler.magFilter = Sampler::MagFilter::LINEAR; + sampler.minFilter = Sampler::MinFilter::LINEAR_MIPMAP_LINEAR; + sampler.wrapS = Sampler::WrapS::CLAMP_TO_EDGE; + sampler.wrapT = Sampler::WrapT::CLAMP_TO_EDGE; + + Texture& texture = gltf.textures.emplace_back(); + texture.sampler = int32_t(gltf.samplers.size() - 1); + texture.source = int32_t(gltf.images.size() - 1); + + Buffer& buffer = !gltf.buffers.empty() + ? gltf.buffers.front() + : gltf.buffers.emplace_back(); + size_t imageStart = buffer.cesium.data.size(); + + // PNG-encode the raster overlay image and store it in the main + // buffer. + ImageManipulation::savePng( + loadResult.pTile->getImage(), + buffer.cesium.data); + + BufferView& bufferView = gltf.bufferViews.emplace_back(); + bufferView.buffer = 0; + bufferView.byteOffset = int64_t(imageStart); + bufferView.byteLength = + int64_t(buffer.cesium.data.size() - imageStart); + + buffer.byteLength = int64_t(buffer.cesium.data.size()); + + image.bufferView = int32_t(gltf.bufferViews.size() - 1); + + // TODO: the below will replace any existing color texture with + // the raster overlay, because glTF only allows one color + // texture. However, it doesn't clean up previous textures or + // texture coordinates, leaving the model bigger than it needs + // to be. + + int32_t newMaterialIndex = -1; + + for (Mesh& mesh : gltf.meshes) { + for (MeshPrimitive& primitive : mesh.primitives) { + if (primitive.material < 0 || + size_t(primitive.material) >= gltf.materials.size()) { + // This primitive didn't previous have a material so + // assign one (creating it if needed). + if (newMaterialIndex < 0) { + newMaterialIndex = int32_t(gltf.materials.size()); + Material& material = gltf.materials.emplace_back(); + MaterialPBRMetallicRoughness& pbr = + material.pbrMetallicRoughness.emplace(); + pbr.metallicFactor = 0.0; + pbr.roughnessFactor = 1.0; } + primitive.material = newMaterialIndex; } - }); + + Material& material = gltf.materials[size_t(primitive.material)]; + if (!material.pbrMetallicRoughness) + material.pbrMetallicRoughness.emplace(); + if (!material.pbrMetallicRoughness->baseColorTexture) + material.pbrMetallicRoughness->baseColorTexture.emplace(); + + TextureInfo& colorTexture = + *material.pbrMetallicRoughness->baseColorTexture; + colorTexture.index = int32_t(gltf.textures.size() - 1); + colorTexture.texCoord = textureCoordinateIndex; + + ExtensionKhrTextureTransform& textureTransform = + colorTexture.addExtension(); + textureTransform.offset = { + textureTranslationAndScale.x, + textureTranslationAndScale.y}; + textureTransform.scale = { + textureTranslationAndScale.z, + textureTranslationAndScale.w}; + } + } + }); waitForFuture(asyncSystem, std::move(future)); @@ -263,5 +263,5 @@ TEST_CASE("Add raster overlay to glTF") { outputFile.write( reinterpret_cast(writeResult.gltfBytes.data()), - writeResult.gltfBytes.size()); + int64_t(writeResult.gltfBytes.size())); } From ffee3424945a86788f681aeba7dd5551366a9869 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Sat, 18 Nov 2023 22:27:24 +1100 Subject: [PATCH 389/421] Very basic assertions. --- .../test/TestAddRasterOverlayToGltf.cpp | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/CesiumRasterOverlays/test/TestAddRasterOverlayToGltf.cpp b/CesiumRasterOverlays/test/TestAddRasterOverlayToGltf.cpp index f4a65afd1..8f9dd04f2 100644 --- a/CesiumRasterOverlays/test/TestAddRasterOverlayToGltf.cpp +++ b/CesiumRasterOverlays/test/TestAddRasterOverlayToGltf.cpp @@ -256,12 +256,31 @@ TEST_CASE("Add raster overlay to glTF") { GltfWriterResult writeResult = writer.writeGlb(gltf, gltf.buffers[0].cesium.data, options); - std::ofstream outputFile( - dataDir / "Shadow_Tester_overlays.glb", - std::ios::binary | std::ios::ate); + std::filesystem::path outputFilename = + std::filesystem::temp_directory_path() / "Shadow_Tester_overlays.glb"; + + std::ofstream outputFile(outputFilename, std::ios::binary | std::ios::ate); REQUIRE(outputFile); outputFile.write( reinterpret_cast(writeResult.gltfBytes.data()), int64_t(writeResult.gltfBytes.size())); + + outputFile.close(); + + // Read it back and verify everything still looks good. + std::vector bytesBack = readFile(outputFilename); + GltfReaderResult resultBack = reader.readGltf(bytesBack); + REQUIRE(resultBack.model); + + const Model& gltfBack = *resultBack.model; + + REQUIRE(gltfBack.images.size() == 1); + CHECK(!gltfBack.images[0].cesium.pixelData.empty()); + + REQUIRE(!gltfBack.meshes.empty()); + REQUIRE(!gltfBack.meshes[0].primitives.empty()); + + const MeshPrimitive& primitive = gltfBack.meshes[0].primitives[0]; + CHECK(primitive.attributes.find("TEXCOORD_0") != primitive.attributes.end()); } From 75451e7efb2263f424c33f28efa840c163f28066 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Mon, 20 Nov 2023 14:25:12 +1100 Subject: [PATCH 390/421] More assertions in TestAddRasterOverlayToGltf. --- .../test/TestAddRasterOverlayToGltf.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/CesiumRasterOverlays/test/TestAddRasterOverlayToGltf.cpp b/CesiumRasterOverlays/test/TestAddRasterOverlayToGltf.cpp index 8f9dd04f2..43dcdd729 100644 --- a/CesiumRasterOverlays/test/TestAddRasterOverlayToGltf.cpp +++ b/CesiumRasterOverlays/test/TestAddRasterOverlayToGltf.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -282,5 +283,17 @@ TEST_CASE("Add raster overlay to glTF") { REQUIRE(!gltfBack.meshes[0].primitives.empty()); const MeshPrimitive& primitive = gltfBack.meshes[0].primitives[0]; - CHECK(primitive.attributes.find("TEXCOORD_0") != primitive.attributes.end()); + + auto texCoordIt = primitive.attributes.find("TEXCOORD_0"); + REQUIRE(texCoordIt != primitive.attributes.end()); + + AccessorView texCoordView(gltfBack, texCoordIt->second); + CHECK(texCoordView.size() > 0); + + for (int64_t i = 0; i < texCoordView.size(); ++i) { + CHECK(texCoordView[i].x >= 0.0); + CHECK(texCoordView[i].x <= 1.0); + CHECK(texCoordView[i].y >= 0.0); + CHECK(texCoordView[i].y <= 1.0); + } } From 88bfbf508fb471a908f2b5ed77f9bace9ae23cdb Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Mon, 20 Nov 2023 14:41:45 +1100 Subject: [PATCH 391/421] Add a comment. --- CesiumRasterOverlays/test/TestAddRasterOverlayToGltf.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CesiumRasterOverlays/test/TestAddRasterOverlayToGltf.cpp b/CesiumRasterOverlays/test/TestAddRasterOverlayToGltf.cpp index 43dcdd729..319b41e5c 100644 --- a/CesiumRasterOverlays/test/TestAddRasterOverlayToGltf.cpp +++ b/CesiumRasterOverlays/test/TestAddRasterOverlayToGltf.cpp @@ -139,7 +139,7 @@ TEST_CASE("Add raster overlay to glTF") { glm::dvec2 targetScreenPixels = RasterOverlayUtilities::computeDesiredScreenPixels( geometricError, - 16.0, + 16.0, // the Max SSE used to render the geometry details->rasterOverlayProjections[0], details->rasterOverlayRectangles[0]); From 4dc4a7b2931dfbde3d8eb2b1df571d960ba91014 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Mon, 20 Nov 2023 14:58:47 +1100 Subject: [PATCH 392/421] Ship unnecessary write to disk. --- .../test/TestAddRasterOverlayToGltf.cpp | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/CesiumRasterOverlays/test/TestAddRasterOverlayToGltf.cpp b/CesiumRasterOverlays/test/TestAddRasterOverlayToGltf.cpp index 319b41e5c..e6d74cc9b 100644 --- a/CesiumRasterOverlays/test/TestAddRasterOverlayToGltf.cpp +++ b/CesiumRasterOverlays/test/TestAddRasterOverlayToGltf.cpp @@ -257,21 +257,8 @@ TEST_CASE("Add raster overlay to glTF") { GltfWriterResult writeResult = writer.writeGlb(gltf, gltf.buffers[0].cesium.data, options); - std::filesystem::path outputFilename = - std::filesystem::temp_directory_path() / "Shadow_Tester_overlays.glb"; - - std::ofstream outputFile(outputFilename, std::ios::binary | std::ios::ate); - REQUIRE(outputFile); - - outputFile.write( - reinterpret_cast(writeResult.gltfBytes.data()), - int64_t(writeResult.gltfBytes.size())); - - outputFile.close(); - // Read it back and verify everything still looks good. - std::vector bytesBack = readFile(outputFilename); - GltfReaderResult resultBack = reader.readGltf(bytesBack); + GltfReaderResult resultBack = reader.readGltf(writeResult.gltfBytes); REQUIRE(resultBack.model); const Model& gltfBack = *resultBack.model; From d1a602ea82bddc7f0b2c82d29748325c0e197e5b Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Mon, 20 Nov 2023 16:41:20 +1100 Subject: [PATCH 393/421] Fix VS 2002 17.8 warnings/errors by defining _SILENCE_STDEXT_ARR_ITERS_DEPRECATION_WARNING --- CHANGES.md | 1 + extern/CMakeLists.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 54dade1a5..7bfdbf5b0 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -23,6 +23,7 @@ - Fixed a bug in `OrientedBoundingBox::contains` where it didn't account for the bounding box's center. - Fixed compiler error when calling `PropertyAttributeView::forEachProperty`. +- Fixed WD4996 warnings-as-errors when compiling with Visual Studio 2002 v17.8. ### v0.29.0 - 2023-11-01 diff --git a/extern/CMakeLists.txt b/extern/CMakeLists.txt index 432a34679..1d6f21c44 100644 --- a/extern/CMakeLists.txt +++ b/extern/CMakeLists.txt @@ -68,6 +68,7 @@ add_subdirectory(asyncplusplus) set(SPDLOG_BUILD_TESTING OFF CACHE INTERNAL "Disable SPDLOG Testing") add_subdirectory(spdlog) +target_compile_definitions(spdlog PUBLIC _SILENCE_STDEXT_ARR_ITERS_DEPRECATION_WARNING) if (NOT TARGET sqlite3) add_subdirectory(sqlite3) From d3f74aaf0a7e757b21354c69f8aa3df018512be6 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Mon, 20 Nov 2023 17:46:44 +1100 Subject: [PATCH 394/421] Include header for std::strcmp. --- CesiumUtility/src/Uri.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/CesiumUtility/src/Uri.cpp b/CesiumUtility/src/Uri.cpp index 5bff85396..c7e3679d4 100644 --- a/CesiumUtility/src/Uri.cpp +++ b/CesiumUtility/src/Uri.cpp @@ -2,6 +2,7 @@ #include +#include #include namespace CesiumUtility { From 7aba4a6fa3191f55499633febbda878f85243ced Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Mon, 20 Nov 2023 11:00:10 -0500 Subject: [PATCH 395/421] Added test --- CesiumGltfReader/test/TestGltfReader.cpp | 19 ++ CesiumGltfReader/test/data/BoxTextured.gltf | 181 ++++++++++++++++++++ 2 files changed, 200 insertions(+) create mode 100644 CesiumGltfReader/test/data/BoxTextured.gltf diff --git a/CesiumGltfReader/test/TestGltfReader.cpp b/CesiumGltfReader/test/TestGltfReader.cpp index 679b9403e..24453ae10 100644 --- a/CesiumGltfReader/test/TestGltfReader.cpp +++ b/CesiumGltfReader/test/TestGltfReader.cpp @@ -662,3 +662,22 @@ TEST_CASE("Ignores unknown properties if requested") { CHECK(result.model->unknownProperties.empty()); CHECK(result.model->asset.unknownProperties.empty()); } + +TEST_CASE("Decodes images with data uris") { + GltfReader reader; + GltfReaderResult result = reader.readGltf(readFile( + CesiumGltfReader_TEST_DATA_DIR + std::string("/BoxTextured.gltf"))); + + REQUIRE(result.warnings.empty()); + REQUIRE(result.errors.empty()); + + const Model& model = result.model.value(); + + REQUIRE(model.images.size() == 1); + + const ImageCesium& image = model.images.front().cesium; + + CHECK(image.width == 256); + CHECK(image.height == 256); + CHECK(!image.pixelData.empty()); +} diff --git a/CesiumGltfReader/test/data/BoxTextured.gltf b/CesiumGltfReader/test/data/BoxTextured.gltf new file mode 100644 index 000000000..70fb0a7f2 --- /dev/null +++ b/CesiumGltfReader/test/data/BoxTextured.gltf @@ -0,0 +1,181 @@ +{ + "asset": { + "generator": "COLLADA2GLTF", + "version": "2.0" + }, + "scene": 0, + "scenes": [ + { + "nodes": [ + 0 + ] + } + ], + "nodes": [ + { + "children": [ + 1 + ], + "matrix": [ + 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 + ] + }, + { + "mesh": 0 + } + ], + "meshes": [ + { + "primitives": [ + { + "attributes": { + "NORMAL": 1, + "POSITION": 2, + "TEXCOORD_0": 3 + }, + "indices": 0, + "mode": 4, + "material": 0 + } + ], + "name": "Mesh" + } + ], + "accessors": [ + { + "bufferView": 0, + "byteOffset": 0, + "componentType": 5123, + "count": 36, + "max": [ + 23 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 1, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 1.0, + 1.0, + 1.0 + ], + "min": [ + -1.0, + -1.0, + -1.0 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 288, + "componentType": 5126, + "count": 24, + "max": [ + 0.5, + 0.5, + 0.5 + ], + "min": [ + -0.5, + -0.5, + -0.5 + ], + "type": "VEC3" + }, + { + "bufferView": 2, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 6.0, + 1.0 + ], + "min": [ + 0.0, + 0.0 + ], + "type": "VEC2" + } + ], + "materials": [ + { + "pbrMetallicRoughness": { + "baseColorTexture": { + "index": 0 + }, + "metallicFactor": 0.0 + }, + "name": "Texture" + } + ], + "textures": [ + { + "sampler": 0, + "source": 0 + } + ], + "images": [ + { + "uri": "data:image/png;base64,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" + } + ], + "samplers": [ + { + "magFilter": 9729, + "minFilter": 9986, + "wrapS": 10497, + "wrapT": 10497 + } + ], + "bufferViews": [ + { + "buffer": 0, + "byteOffset": 768, + "byteLength": 72, + "target": 34963 + }, + { + "buffer": 0, + "byteOffset": 0, + "byteLength": 576, + "byteStride": 12, + "target": 34962 + }, + { + "buffer": 0, + "byteOffset": 576, + "byteLength": 192, + "byteStride": 8, + "target": 34962 + } + ], + "buffers": [ + { + "byteLength": 840, + "uri": "data:application/octet-stream;base64,AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAvwAAAL8AAAA/AAAAPwAAAL8AAAA/AAAAvwAAAD8AAAA/AAAAPwAAAD8AAAA/AAAAPwAAAD8AAAA/AAAAPwAAAL8AAAA/AAAAPwAAAD8AAAC/AAAAPwAAAL8AAAC/AAAAvwAAAD8AAAA/AAAAPwAAAD8AAAA/AAAAvwAAAD8AAAC/AAAAPwAAAD8AAAC/AAAAPwAAAL8AAAA/AAAAvwAAAL8AAAA/AAAAPwAAAL8AAAC/AAAAvwAAAL8AAAC/AAAAvwAAAL8AAAA/AAAAvwAAAD8AAAA/AAAAvwAAAL8AAAC/AAAAvwAAAD8AAAC/AAAAvwAAAL8AAAC/AAAAvwAAAD8AAAC/AAAAPwAAAL8AAAC/AAAAPwAAAD8AAAC/AADAQAAAAAAAAKBAAAAAAAAAwED+/38/AACgQP7/fz8AAIBAAAAAAAAAoEAAAAAAAACAQAAAgD8AAKBAAACAPwAAAEAAAAAAAACAPwAAAAAAAABAAACAPwAAgD8AAIA/AABAQAAAAAAAAIBAAAAAAAAAQEAAAIA/AACAQAAAgD8AAEBAAAAAAAAAAEAAAAAAAABAQAAAgD8AAABAAACAPwAAAAAAAAAAAAAAAP7/fz8AAIA/AAAAAAAAgD/+/38/AAABAAIAAwACAAEABAAFAAYABwAGAAUACAAJAAoACwAKAAkADAANAA4ADwAOAA0AEAARABIAEwASABEAFAAVABYAFwAWABUA" + } + ] +} From f0f009c87e237eb6515e0d9897159e1b1e2fa657 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Mon, 20 Nov 2023 11:00:33 -0500 Subject: [PATCH 396/421] Decapitalize function names --- .../include/CesiumGltf/AccessorUtility.h | 6 +-- CesiumGltf/src/AccessorUtility.cpp | 6 +-- CesiumGltf/test/TestAccessorUtility.cpp | 46 +++++++++---------- 3 files changed, 29 insertions(+), 29 deletions(-) diff --git a/CesiumGltf/include/CesiumGltf/AccessorUtility.h b/CesiumGltf/include/CesiumGltf/AccessorUtility.h index 1e5d597d4..5dfd88d83 100644 --- a/CesiumGltf/include/CesiumGltf/AccessorUtility.h +++ b/CesiumGltf/include/CesiumGltf/AccessorUtility.h @@ -51,7 +51,7 @@ typedef std::variant< * given glTF primitive and model. This verifies that the accessor is of a valid * type. If not, the returned accessor view will be invalid. */ -FeatureIdAccessorType GetFeatureIdAccessorView( +FeatureIdAccessorType getFeatureIdAccessorView( const Model& model, const MeshPrimitive& primitive, int32_t featureIdAttributeIndex); @@ -100,7 +100,7 @@ typedef std::variant< * is returned. */ IndexAccessorType -GetIndexAccessorView(const Model& model, const MeshPrimitive& primitive); +getIndexAccessorView(const Model& model, const MeshPrimitive& primitive); /** * Visitor that retrieves the vertex indices from the given accessor type @@ -165,7 +165,7 @@ typedef std::variant< * given glTF primitive and model. This verifies that the accessor is of a valid * type. If not, the returned accessor view will be invalid., */ -TexCoordAccessorType GetTexCoordAccessorView( +TexCoordAccessorType getTexCoordAccessorView( const Model& model, const MeshPrimitive& primitive, int32_t textureCoordinateSetIndex); diff --git a/CesiumGltf/src/AccessorUtility.cpp b/CesiumGltf/src/AccessorUtility.cpp index 8489fb65d..fc6786ab7 100644 --- a/CesiumGltf/src/AccessorUtility.cpp +++ b/CesiumGltf/src/AccessorUtility.cpp @@ -3,7 +3,7 @@ #include "CesiumGltf/Model.h" namespace CesiumGltf { -FeatureIdAccessorType GetFeatureIdAccessorView( +FeatureIdAccessorType getFeatureIdAccessorView( const Model& model, const MeshPrimitive& primitive, int32_t featureIdAttributeIndex) { @@ -38,7 +38,7 @@ FeatureIdAccessorType GetFeatureIdAccessorView( } IndexAccessorType -GetIndexAccessorView(const Model& model, const MeshPrimitive& primitive) { +getIndexAccessorView(const Model& model, const MeshPrimitive& primitive) { if (primitive.indices < 0) { return IndexAccessorType(); } @@ -62,7 +62,7 @@ GetIndexAccessorView(const Model& model, const MeshPrimitive& primitive) { } } -TexCoordAccessorType GetTexCoordAccessorView( +TexCoordAccessorType getTexCoordAccessorView( const Model& model, const MeshPrimitive& primitive, int32_t textureCoordinateSetIndex) { diff --git a/CesiumGltf/test/TestAccessorUtility.cpp b/CesiumGltf/test/TestAccessorUtility.cpp index b52110e8d..80cd5367d 100644 --- a/CesiumGltf/test/TestAccessorUtility.cpp +++ b/CesiumGltf/test/TestAccessorUtility.cpp @@ -57,7 +57,7 @@ TEST_CASE("Test CountFromAccessor") { } } -TEST_CASE("Test GetFeatureIdAccessorView") { +TEST_CASE("Test getFeatureIdAccessorView") { Model model; std::vector featureIds0{1, 2, 3, 4}; @@ -114,7 +114,7 @@ TEST_CASE("Test GetFeatureIdAccessorView") { SECTION("Handles invalid feature ID set index") { FeatureIdAccessorType featureIDAccessor = - GetFeatureIdAccessorView(model, primitive, 2); + getFeatureIdAccessorView(model, primitive, 2); REQUIRE( std::visit(StatusFromAccessor{}, featureIDAccessor) != AccessorViewStatus::Valid); @@ -125,7 +125,7 @@ TEST_CASE("Test GetFeatureIdAccessorView") { model.accessors[0].type = Accessor::Type::VEC2; FeatureIdAccessorType featureIDAccessor = - GetFeatureIdAccessorView(model, primitive, 0); + getFeatureIdAccessorView(model, primitive, 0); REQUIRE( std::visit(StatusFromAccessor{}, featureIDAccessor) != AccessorViewStatus::Valid); @@ -138,7 +138,7 @@ TEST_CASE("Test GetFeatureIdAccessorView") { model.accessors[1].normalized = true; FeatureIdAccessorType featureIDAccessor = - GetFeatureIdAccessorView(model, primitive, 1); + getFeatureIdAccessorView(model, primitive, 1); REQUIRE( std::visit(StatusFromAccessor{}, featureIDAccessor) != AccessorViewStatus::Valid); @@ -149,7 +149,7 @@ TEST_CASE("Test GetFeatureIdAccessorView") { SECTION("Creates from valid feature ID sets") { FeatureIdAccessorType featureIDAccessor = - GetFeatureIdAccessorView(model, primitive, 0); + getFeatureIdAccessorView(model, primitive, 0); REQUIRE( std::visit(StatusFromAccessor{}, featureIDAccessor) == AccessorViewStatus::Valid); @@ -157,7 +157,7 @@ TEST_CASE("Test GetFeatureIdAccessorView") { std::visit(CountFromAccessor{}, featureIDAccessor) == static_cast(featureIds0.size())); - featureIDAccessor = GetFeatureIdAccessorView(model, primitive, 1); + featureIDAccessor = getFeatureIdAccessorView(model, primitive, 1); REQUIRE( std::visit(StatusFromAccessor{}, featureIDAccessor) == AccessorViewStatus::Valid); @@ -208,7 +208,7 @@ TEST_CASE("FeatureIdFromAccessor") { } } -TEST_CASE("Test GetIndexAccessorView") { +TEST_CASE("Test getIndexAccessorView") { Model model; std::vector indices{0, 1, 2, 0, 2, 3}; @@ -239,7 +239,7 @@ TEST_CASE("Test GetIndexAccessorView") { SECTION("Handles invalid accessor type") { model.accessors[0].type = Accessor::Type::VEC2; - IndexAccessorType indexAccessor = GetIndexAccessorView(model, primitive); + IndexAccessorType indexAccessor = getIndexAccessorView(model, primitive); REQUIRE( std::visit(StatusFromAccessor{}, indexAccessor) != AccessorViewStatus::Valid); @@ -251,7 +251,7 @@ TEST_CASE("Test GetIndexAccessorView") { SECTION("Handles unsupported accessor component type") { model.accessors[0].componentType = Accessor::ComponentType::BYTE; - IndexAccessorType indexAccessor = GetIndexAccessorView(model, primitive); + IndexAccessorType indexAccessor = getIndexAccessorView(model, primitive); REQUIRE( std::visit(StatusFromAccessor{}, indexAccessor) != AccessorViewStatus::Valid); @@ -263,7 +263,7 @@ TEST_CASE("Test GetIndexAccessorView") { SECTION("Handles invalid normalized accessor") { model.accessors[0].normalized = true; - IndexAccessorType indexAccessor = GetIndexAccessorView(model, primitive); + IndexAccessorType indexAccessor = getIndexAccessorView(model, primitive); REQUIRE( std::visit(StatusFromAccessor{}, indexAccessor) != AccessorViewStatus::Valid); @@ -273,7 +273,7 @@ TEST_CASE("Test GetIndexAccessorView") { } SECTION("Creates from valid accessor") { - IndexAccessorType indexAccessor = GetIndexAccessorView(model, primitive); + IndexAccessorType indexAccessor = getIndexAccessorView(model, primitive); REQUIRE( std::visit(StatusFromAccessor{}, indexAccessor) == AccessorViewStatus::Valid); @@ -285,7 +285,7 @@ TEST_CASE("Test GetIndexAccessorView") { SECTION("Creates from nonexistent accessor") { primitive.indices = -1; - IndexAccessorType indexAccessor = GetIndexAccessorView(model, primitive); + IndexAccessorType indexAccessor = getIndexAccessorView(model, primitive); REQUIRE(std::get_if(&indexAccessor)); } } @@ -385,7 +385,7 @@ TEST_CASE("Test FaceVertexIndicesFromAccessor") { } } -TEST_CASE("Test GetTexCoordAccessorView") { +TEST_CASE("Test getTexCoordAccessorView") { Model model; std::vector texCoords0{ glm::vec2(0, 0), @@ -452,7 +452,7 @@ TEST_CASE("Test GetTexCoordAccessorView") { SECTION("Handles invalid texture coordinate set index") { TexCoordAccessorType texCoordAccessor = - GetTexCoordAccessorView(model, primitive, 2); + getTexCoordAccessorView(model, primitive, 2); REQUIRE( std::visit(StatusFromAccessor{}, texCoordAccessor) != AccessorViewStatus::Valid); @@ -463,7 +463,7 @@ TEST_CASE("Test GetTexCoordAccessorView") { model.accessors[0].type = Accessor::Type::SCALAR; TexCoordAccessorType texCoordAccessor = - GetTexCoordAccessorView(model, primitive, 0); + getTexCoordAccessorView(model, primitive, 0); REQUIRE( std::visit(StatusFromAccessor{}, texCoordAccessor) != AccessorViewStatus::Valid); @@ -476,7 +476,7 @@ TEST_CASE("Test GetTexCoordAccessorView") { model.accessors[0].componentType = Accessor::ComponentType::BYTE; TexCoordAccessorType texCoordAccessor = - GetTexCoordAccessorView(model, primitive, 0); + getTexCoordAccessorView(model, primitive, 0); REQUIRE( std::visit(StatusFromAccessor{}, texCoordAccessor) != AccessorViewStatus::Valid); @@ -489,7 +489,7 @@ TEST_CASE("Test GetTexCoordAccessorView") { model.accessors[1].normalized = false; TexCoordAccessorType texCoordAccessor = - GetTexCoordAccessorView(model, primitive, 2); + getTexCoordAccessorView(model, primitive, 2); REQUIRE( std::visit(StatusFromAccessor{}, texCoordAccessor) != AccessorViewStatus::Valid); @@ -500,7 +500,7 @@ TEST_CASE("Test GetTexCoordAccessorView") { SECTION("Creates from valid texture coordinate sets") { TexCoordAccessorType texCoordAccessor = - GetTexCoordAccessorView(model, primitive, 0); + getTexCoordAccessorView(model, primitive, 0); REQUIRE( std::visit(StatusFromAccessor{}, texCoordAccessor) == AccessorViewStatus::Valid); @@ -508,7 +508,7 @@ TEST_CASE("Test GetTexCoordAccessorView") { std::visit(CountFromAccessor{}, texCoordAccessor) == static_cast(texCoords0.size())); - texCoordAccessor = GetTexCoordAccessorView(model, primitive, 1); + texCoordAccessor = getTexCoordAccessorView(model, primitive, 1); REQUIRE( std::visit(StatusFromAccessor{}, texCoordAccessor) == AccessorViewStatus::Valid); @@ -585,20 +585,20 @@ TEST_CASE("Test TexCoordFromAccessor") { SECTION("Handles invalid accessor") { TexCoordAccessorType texCoordAccessor = - GetTexCoordAccessorView(model, primitive, 2); + getTexCoordAccessorView(model, primitive, 2); REQUIRE(!std::visit(TexCoordFromAccessor{0}, texCoordAccessor)); } SECTION("Handles invalid index") { TexCoordAccessorType texCoordAccessor = - GetTexCoordAccessorView(model, primitive, 0); + getTexCoordAccessorView(model, primitive, 0); REQUIRE(!std::visit(TexCoordFromAccessor{-1}, texCoordAccessor)); REQUIRE(!std::visit(TexCoordFromAccessor{10}, texCoordAccessor)); } SECTION("Retrieves from valid accessor and index") { TexCoordAccessorType texCoordAccessor = - GetTexCoordAccessorView(model, primitive, 0); + getTexCoordAccessorView(model, primitive, 0); for (size_t i = 0; i < texCoords0.size(); i++) { auto maybeTexCoord = std::visit( TexCoordFromAccessor{static_cast(i)}, @@ -611,7 +611,7 @@ TEST_CASE("Test TexCoordFromAccessor") { } SECTION("Retrieves from valid normalized accessor and index") { TexCoordAccessorType texCoordAccessor = - GetTexCoordAccessorView(model, primitive, 1); + getTexCoordAccessorView(model, primitive, 1); for (size_t i = 0; i < texCoords1.size(); i++) { auto maybeTexCoord = std::visit( TexCoordFromAccessor{static_cast(i)}, From b3769a783aba1f9dd97f18e8a80f203f2578691c Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Mon, 20 Nov 2023 11:28:25 -0500 Subject: [PATCH 397/421] Fix changelog entry --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 480ea3098..9dc503e37 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -14,7 +14,7 @@ - Added `Cesium3DTilesContent` library and namespace. It has classes for loading, converting, and manipulating 3D Tiles tile content. - Added `MetadataConversions`, which enables metadata values to be converted to different types for better usability in runtime engines. - Added various `typedef`s to catch all possible types of `AccessorView`s for an attribute, including `FeatureIdAccessorType` for feature ID attribute accessors, `IndexAccessorType` for index accessors, and `TexCoordAccessorType` for texture coordinate attribute accessors. -- Added `GetFeatureIdAccessorView`, `GetIndexAccessorView`, and `GetTexCoordAccessorView` to retrieve the `AccessorView` as a `FeatureIdAccessorType`, `IndexAccessorType`, or `TexCoordAccessorType` respectively. +- Added `getFeatureIdAccessorView`, `getIndexAccessorView`, and `getTexCoordAccessorView` to retrieve the `AccessorView` as a `FeatureIdAccessorType`, `IndexAccessorType`, or `TexCoordAccessorType` respectively. - Added `StatusFromAccessor` and `CountFromAccessor` visitors to retrieve the accessor status and size respectively. This can be used with `FeatureIdAccessorType`, `IndexAccessorType`, or `TexCoordAccessorType`. - Added `FeatureIdFromAccessor` to retrieve feature IDs from a `FeatureIdAccessorType`. - Added `IndicesForFaceFromAccessor` to retrieve the indices of the vertices that make up a face, as supplied by `IndexAccessorType`. From ebbc9c61c47d64bf2ad255b8632c8af4f731506a Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Mon, 20 Nov 2023 14:40:11 -0500 Subject: [PATCH 398/421] Fix typos and add missing param docs --- .../SubtreeAvailability.h | 48 +++++++++++-------- .../test/TestSubtreeAvailability.cpp | 2 +- 2 files changed, 28 insertions(+), 22 deletions(-) diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/SubtreeAvailability.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/SubtreeAvailability.h index 2dcffe141..e11599c26 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/SubtreeAvailability.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/SubtreeAvailability.h @@ -77,7 +77,7 @@ class SubtreeAvailability { * @param asyncSystem The async system with which to do background work. * @param pAssetAccessor The asset accessor to use to retrieve the subtree * resource from the URL. - * @param pLogger The logger to which to load errors and warnings and occur + * @param pLogger The logger to which to load errors and warnings that occur * during subtree load. * @param subtreeUrl The URL from which to retrieve the subtree file. * @param requestHeaders HTTP headers to include in the request for the @@ -95,19 +95,19 @@ class SubtreeAvailability { const std::vector& requestHeaders); /** - * @brief An AvailibilityView that indicates all tiles are either available or - * all tiles are unavailable. + * @brief An AvailibilityView that indicates that either all tiles are + * available or all tiles are unavailable. */ struct SubtreeConstantAvailability { /** - * @brief True if all tiles are availability, false if all tiles are + * @brief True if all tiles are availabile, false if all tiles are * unavailable. */ bool constant; }; /** - * @brief An AvailabilityView that access availability information from a + * @brief An AvailabilityView that accesses availability information from a * bitstream. */ struct SubtreeBufferViewAvailability { @@ -148,7 +148,7 @@ class SubtreeAvailability { Cesium3DTiles::Subtree&& subtree); /** - * @brief Determines if a given tile in the subtree is available. + * @brief Determines if a given tile in the quadtree is available. * * @param subtreeID The ID of the root tile of the subtree. * @param tileID The ID of the tile to query. @@ -159,7 +159,7 @@ class SubtreeAvailability { const CesiumGeometry::QuadtreeTileID& tileID) const noexcept; /** - * @brief Determines if a given tile in the subtree is available. + * @brief Determines if a given tile in the octree is available. * * @param subtreeID The ID of the root tile of the subtree. * @param tileID The ID of the tile to query. @@ -183,7 +183,7 @@ class SubtreeAvailability { uint64_t relativeTileMortonId) const noexcept; /** - * @brief Sets the availability state of a given tile in the subtree. + * @brief Sets the availability state of a given tile in the quadtree. * * @param subtreeID The ID of the root tile of the subtree. * @param tileID The ID of the tile for which to set availability. @@ -195,7 +195,7 @@ class SubtreeAvailability { bool isAvailable) noexcept; /** - * @brief Sets the availability state of a given tile in the subtree. + * @brief Sets the availability state of a given tile in the octree. * * @param subtreeID The ID of the root tile of the subtree. * @param tileID The ID of the tile for which to set availability. @@ -222,10 +222,11 @@ class SubtreeAvailability { bool isAvailable) noexcept; /** - * @brief Determines if content for a given tile in the subtree is available. + * @brief Determines if content for a given tile in the quadtree is available. * * @param subtreeID The ID of the root tile of the subtree. * @param tileID The ID of the tile to query. + * @param contentId The ID of the content to query. * @return True if the tile's content is available; otherwise, false. */ bool isContentAvailable( @@ -234,10 +235,11 @@ class SubtreeAvailability { uint64_t contentId) const noexcept; /** - * @brief Determines if content for a given tile in the subtree is available. + * @brief Determines if content for a given tile in the octree is available. * * @param subtreeID The ID of the root tile of the subtree. * @param tileID The ID of the tile to query. + * @param contentId The ID of the content to query. * @return True if the tile's content is available; otherwise, false. */ bool isContentAvailable( @@ -252,6 +254,7 @@ class SubtreeAvailability { * root of the subtree. * @param relativeTileMortonId The Morton ID of the tile to query. See * {@link ImplicitTilingUtilities::computeRelativeMortonIndex}. + * @param contentId The ID of the content to query. * @return True if the tile's content is available; otherwise, false. */ bool isContentAvailable( @@ -261,10 +264,11 @@ class SubtreeAvailability { /** * @brief Sets the availability state of the content for a given tile in the - * subtree. + * quadtree. * * @param subtreeID The ID of the root tile of the subtree. * @param tileID The ID of the tile for which to set content availability. + * @param contentId The ID of the content to query. * @param isAvailable The new availability state for the tile's content. */ void setContentAvailable( @@ -275,10 +279,11 @@ class SubtreeAvailability { /** * @brief Sets the availability state of the content for a given tile in the - * subtree. + * octree. * * @param subtreeID The ID of the root tile of the subtree. * @param tileID The ID of the tile for which to set content availability. + * @param contentId The ID of the content to query. * @param isAvailable The new availability state for the tile's content. */ void setContentAvailable( @@ -296,6 +301,7 @@ class SubtreeAvailability { * @param relativeTileMortonId The Morton ID of the tile for which to set * content availability. See * {@link ImplicitTilingUtilities::computeRelativeMortonIndex}. + * @param contentId The ID of the content to query. * @param isAvailable The new availability state for the tile's content. */ void setContentAvailable( @@ -313,7 +319,7 @@ class SubtreeAvailability { * @param thisSubtreeID The ID of the root tile of this subtree. * @param checkSubtreeID The ID of the tile to query to see if its subtree is * available. - * @return True if the tile's content is available; otherwise, false. + * @return True if the subtree is available; otherwise, false. */ bool isSubtreeAvailable( const CesiumGeometry::QuadtreeTileID& thisSubtreeID, @@ -328,7 +334,7 @@ class SubtreeAvailability { * @param thisSubtreeID The ID of the root tile of this subtree. * @param checkSubtreeID The ID of the tile to query to see if its subtree is * available. - * @return True if the tile's content is available; otherwise, false. + * @return True if the subtree is available; otherwise, false. */ bool isSubtreeAvailable( const CesiumGeometry::OctreeTileID& thisSubtreeID, @@ -343,13 +349,13 @@ class SubtreeAvailability { * @param relativeTileMortonId The Morton ID of the tile for which to check * subtree availability. See * {@link ImplicitTilingUtilities::computeRelativeMortonIndex}. - * @return True if the tile's subtree is available; otherwise, false. + * @return True if the subtree is available; otherwise, false. */ bool isSubtreeAvailable(uint64_t relativeSubtreeMortonId) const noexcept; /** - * @brief Sets the availability state of the child subtree rooted at the given - * tile. + * @brief Sets the availability state of the child quadtree rooted at the + * given tile. * * The provided `setSubtreeID` must be a child of the leaves of this * subtree. @@ -357,7 +363,7 @@ class SubtreeAvailability { * @param thisSubtreeID The ID of the root tile of this subtree. * @param setSubtreeID The ID of the tile to query to see if its subtree is * available. - * @return True if the tile's subtree is available; otherwise, false. + * @param isAvailable The new availability state for the subtree. */ void setSubtreeAvailable( const CesiumGeometry::QuadtreeTileID& thisSubtreeID, @@ -365,7 +371,7 @@ class SubtreeAvailability { bool isAvailable) noexcept; /** - * @brief Sets the availability state of the child subtree rooted at the given + * @brief Sets the availability state of the child octree rooted at the given * tile. * * The provided `setSubtreeID` must be a child of the leaves of this @@ -374,7 +380,7 @@ class SubtreeAvailability { * @param thisSubtreeID The ID of the root tile of this subtree. * @param setSubtreeID The ID of the tile to query to see if its subtree is * available. - * @return True if the tile's subtree is available; otherwise, false. + * @param isAvailable The new availability state for the subtree. */ void setSubtreeAvailable( const CesiumGeometry::OctreeTileID& thisSubtreeID, diff --git a/Cesium3DTilesContent/test/TestSubtreeAvailability.cpp b/Cesium3DTilesContent/test/TestSubtreeAvailability.cpp index dc55d1747..fe8a599e0 100644 --- a/Cesium3DTilesContent/test/TestSubtreeAvailability.cpp +++ b/Cesium3DTilesContent/test/TestSubtreeAvailability.cpp @@ -718,7 +718,7 @@ TEST_CASE("SubtreeAvailability modifications") { SubtreeAvailability& availability = *maybeAvailability; - SECTION("initially has all tiles available, and no content or subtress " + SECTION("initially has all tiles available, and no content or subtrees " "available") { CHECK(availability.isTileAvailable( QuadtreeTileID(0, 0, 0), From bed08f690c5faafa9d3bc57fffbff7cf8338ef51 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Mon, 20 Nov 2023 14:43:51 -0500 Subject: [PATCH 399/421] ID -> Id --- .../SubtreeAvailability.h | 64 +++++++++---------- .../src/SubtreeAvailability.cpp | 64 +++++++++---------- 2 files changed, 64 insertions(+), 64 deletions(-) diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/SubtreeAvailability.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/SubtreeAvailability.h index e11599c26..71b1dc475 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/SubtreeAvailability.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/SubtreeAvailability.h @@ -150,24 +150,24 @@ class SubtreeAvailability { /** * @brief Determines if a given tile in the quadtree is available. * - * @param subtreeID The ID of the root tile of the subtree. - * @param tileID The ID of the tile to query. + * @param subtreeId The ID of the root tile of the subtree. + * @param tileId The ID of the tile to query. * @return True if the tile is available; otherwise, false. */ bool isTileAvailable( - const CesiumGeometry::QuadtreeTileID& subtreeID, - const CesiumGeometry::QuadtreeTileID& tileID) const noexcept; + const CesiumGeometry::QuadtreeTileID& subtreeId, + const CesiumGeometry::QuadtreeTileID& tileId) const noexcept; /** * @brief Determines if a given tile in the octree is available. * - * @param subtreeID The ID of the root tile of the subtree. - * @param tileID The ID of the tile to query. + * @param subtreeId The ID of the root tile of the subtree. + * @param tileId The ID of the tile to query. * @return True if the tile is available; otherwise, false. */ bool isTileAvailable( - const CesiumGeometry::OctreeTileID& subtreeID, - const CesiumGeometry::OctreeTileID& tileID) const noexcept; + const CesiumGeometry::OctreeTileID& subtreeId, + const CesiumGeometry::OctreeTileID& tileId) const noexcept; /** * @brief Determines if a given tile in the subtree is available. @@ -185,25 +185,25 @@ class SubtreeAvailability { /** * @brief Sets the availability state of a given tile in the quadtree. * - * @param subtreeID The ID of the root tile of the subtree. - * @param tileID The ID of the tile for which to set availability. + * @param subtreeId The ID of the root tile of the subtree. + * @param tileId The ID of the tile for which to set availability. * @param isAvailable The new availability state for the tile. */ void setTileAvailable( - const CesiumGeometry::QuadtreeTileID& subtreeID, - const CesiumGeometry::QuadtreeTileID& tileID, + const CesiumGeometry::QuadtreeTileID& subtreeId, + const CesiumGeometry::QuadtreeTileID& tileId, bool isAvailable) noexcept; /** * @brief Sets the availability state of a given tile in the octree. * - * @param subtreeID The ID of the root tile of the subtree. - * @param tileID The ID of the tile for which to set availability. + * @param subtreeId The ID of the root tile of the subtree. + * @param tileId The ID of the tile for which to set availability. * @param isAvailable The new availability state for the tile. */ void setTileAvailable( - const CesiumGeometry::OctreeTileID& subtreeID, - const CesiumGeometry::OctreeTileID& tileID, + const CesiumGeometry::OctreeTileID& subtreeId, + const CesiumGeometry::OctreeTileID& tileId, bool isAvailable) noexcept; /** @@ -224,27 +224,27 @@ class SubtreeAvailability { /** * @brief Determines if content for a given tile in the quadtree is available. * - * @param subtreeID The ID of the root tile of the subtree. - * @param tileID The ID of the tile to query. + * @param subtreeId The ID of the root tile of the subtree. + * @param tileId The ID of the tile to query. * @param contentId The ID of the content to query. * @return True if the tile's content is available; otherwise, false. */ bool isContentAvailable( - const CesiumGeometry::QuadtreeTileID& subtreeID, - const CesiumGeometry::QuadtreeTileID& tileID, + const CesiumGeometry::QuadtreeTileID& subtreeId, + const CesiumGeometry::QuadtreeTileID& tileId, uint64_t contentId) const noexcept; /** * @brief Determines if content for a given tile in the octree is available. * - * @param subtreeID The ID of the root tile of the subtree. - * @param tileID The ID of the tile to query. + * @param subtreeId The ID of the root tile of the subtree. + * @param tileId The ID of the tile to query. * @param contentId The ID of the content to query. * @return True if the tile's content is available; otherwise, false. */ bool isContentAvailable( - const CesiumGeometry::OctreeTileID& subtreeID, - const CesiumGeometry::OctreeTileID& tileID, + const CesiumGeometry::OctreeTileID& subtreeId, + const CesiumGeometry::OctreeTileID& tileId, uint64_t contentId) const noexcept; /** @@ -266,14 +266,14 @@ class SubtreeAvailability { * @brief Sets the availability state of the content for a given tile in the * quadtree. * - * @param subtreeID The ID of the root tile of the subtree. - * @param tileID The ID of the tile for which to set content availability. + * @param subtreeId The ID of the root tile of the subtree. + * @param tileId The ID of the tile for which to set content availability. * @param contentId The ID of the content to query. * @param isAvailable The new availability state for the tile's content. */ void setContentAvailable( - const CesiumGeometry::QuadtreeTileID& subtreeID, - const CesiumGeometry::QuadtreeTileID& tileID, + const CesiumGeometry::QuadtreeTileID& subtreeId, + const CesiumGeometry::QuadtreeTileID& tileId, uint64_t contentId, bool isAvailable) noexcept; @@ -281,14 +281,14 @@ class SubtreeAvailability { * @brief Sets the availability state of the content for a given tile in the * octree. * - * @param subtreeID The ID of the root tile of the subtree. - * @param tileID The ID of the tile for which to set content availability. + * @param subtreeId The ID of the root tile of the subtree. + * @param tileId The ID of the tile for which to set content availability. * @param contentId The ID of the content to query. * @param isAvailable The new availability state for the tile's content. */ void setContentAvailable( - const CesiumGeometry::OctreeTileID& subtreeID, - const CesiumGeometry::OctreeTileID& tileID, + const CesiumGeometry::OctreeTileID& subtreeId, + const CesiumGeometry::OctreeTileID& tileId, uint64_t contentId, bool isAvailable) noexcept; diff --git a/Cesium3DTilesContent/src/SubtreeAvailability.cpp b/Cesium3DTilesContent/src/SubtreeAvailability.cpp index 8f2bc3d04..cdc65d93f 100644 --- a/Cesium3DTilesContent/src/SubtreeAvailability.cpp +++ b/Cesium3DTilesContent/src/SubtreeAvailability.cpp @@ -195,22 +195,22 @@ SubtreeAvailability::SubtreeAvailability( } bool SubtreeAvailability::isTileAvailable( - const CesiumGeometry::QuadtreeTileID& subtreeID, - const CesiumGeometry::QuadtreeTileID& tileID) const noexcept { + const CesiumGeometry::QuadtreeTileID& subtreeId, + const CesiumGeometry::QuadtreeTileID& tileId) const noexcept { uint64_t relativeTileMortonIdx = - ImplicitTilingUtilities::computeRelativeMortonIndex(subtreeID, tileID); + ImplicitTilingUtilities::computeRelativeMortonIndex(subtreeId, tileId); return this->isTileAvailable( - tileID.level - subtreeID.level, + tileId.level - subtreeId.level, relativeTileMortonIdx); } bool SubtreeAvailability::isTileAvailable( - const CesiumGeometry::OctreeTileID& subtreeID, - const CesiumGeometry::OctreeTileID& tileID) const noexcept { + const CesiumGeometry::OctreeTileID& subtreeId, + const CesiumGeometry::OctreeTileID& tileId) const noexcept { uint64_t relativeTileMortonIdx = - ImplicitTilingUtilities::computeRelativeMortonIndex(subtreeID, tileID); + ImplicitTilingUtilities::computeRelativeMortonIndex(subtreeId, tileId); return this->isTileAvailable( - tileID.level - subtreeID.level, + tileId.level - subtreeId.level, relativeTileMortonIdx); } @@ -224,25 +224,25 @@ bool SubtreeAvailability::isTileAvailable( } void SubtreeAvailability::setTileAvailable( - const CesiumGeometry::QuadtreeTileID& subtreeID, - const CesiumGeometry::QuadtreeTileID& tileID, + const CesiumGeometry::QuadtreeTileID& subtreeId, + const CesiumGeometry::QuadtreeTileID& tileId, bool isAvailable) noexcept { uint64_t relativeTileMortonIdx = - ImplicitTilingUtilities::computeRelativeMortonIndex(subtreeID, tileID); + ImplicitTilingUtilities::computeRelativeMortonIndex(subtreeId, tileId); this->setTileAvailable( - tileID.level - subtreeID.level, + tileId.level - subtreeId.level, relativeTileMortonIdx, isAvailable); } void SubtreeAvailability::setTileAvailable( - const CesiumGeometry::OctreeTileID& subtreeID, - const CesiumGeometry::OctreeTileID& tileID, + const CesiumGeometry::OctreeTileID& subtreeId, + const CesiumGeometry::OctreeTileID& tileId, bool isAvailable) noexcept { uint64_t relativeTileMortonIdx = - ImplicitTilingUtilities::computeRelativeMortonIndex(subtreeID, tileID); + ImplicitTilingUtilities::computeRelativeMortonIndex(subtreeId, tileId); this->setTileAvailable( - tileID.level - subtreeID.level, + tileId.level - subtreeId.level, relativeTileMortonIdx, isAvailable); } @@ -259,25 +259,25 @@ void SubtreeAvailability::setTileAvailable( } bool SubtreeAvailability::isContentAvailable( - const CesiumGeometry::QuadtreeTileID& subtreeID, - const CesiumGeometry::QuadtreeTileID& tileID, + const CesiumGeometry::QuadtreeTileID& subtreeId, + const CesiumGeometry::QuadtreeTileID& tileId, uint64_t contentId) const noexcept { uint64_t relativeTileMortonIdx = - ImplicitTilingUtilities::computeRelativeMortonIndex(subtreeID, tileID); + ImplicitTilingUtilities::computeRelativeMortonIndex(subtreeId, tileId); return this->isContentAvailable( - tileID.level - subtreeID.level, + tileId.level - subtreeId.level, relativeTileMortonIdx, contentId); } bool SubtreeAvailability::isContentAvailable( - const CesiumGeometry::OctreeTileID& subtreeID, - const CesiumGeometry::OctreeTileID& tileID, + const CesiumGeometry::OctreeTileID& subtreeId, + const CesiumGeometry::OctreeTileID& tileId, uint64_t contentId) const noexcept { uint64_t relativeTileMortonIdx = - ImplicitTilingUtilities::computeRelativeMortonIndex(subtreeID, tileID); + ImplicitTilingUtilities::computeRelativeMortonIndex(subtreeId, tileId); return this->isContentAvailable( - tileID.level - subtreeID.level, + tileId.level - subtreeId.level, relativeTileMortonIdx, contentId); } @@ -293,28 +293,28 @@ bool SubtreeAvailability::isContentAvailable( } void SubtreeAvailability::setContentAvailable( - const CesiumGeometry::QuadtreeTileID& subtreeID, - const CesiumGeometry::QuadtreeTileID& tileID, + const CesiumGeometry::QuadtreeTileID& subtreeId, + const CesiumGeometry::QuadtreeTileID& tileId, uint64_t contentId, bool isAvailable) noexcept { uint64_t relativeTileMortonIdx = - ImplicitTilingUtilities::computeRelativeMortonIndex(subtreeID, tileID); + ImplicitTilingUtilities::computeRelativeMortonIndex(subtreeId, tileId); this->setContentAvailable( - tileID.level - subtreeID.level, + tileId.level - subtreeId.level, relativeTileMortonIdx, contentId, isAvailable); } void SubtreeAvailability::setContentAvailable( - const CesiumGeometry::OctreeTileID& subtreeID, - const CesiumGeometry::OctreeTileID& tileID, + const CesiumGeometry::OctreeTileID& subtreeId, + const CesiumGeometry::OctreeTileID& tileId, uint64_t contentId, bool isAvailable) noexcept { uint64_t relativeTileMortonIdx = - ImplicitTilingUtilities::computeRelativeMortonIndex(subtreeID, tileID); + ImplicitTilingUtilities::computeRelativeMortonIndex(subtreeId, tileId); this->setContentAvailable( - tileID.level - subtreeID.level, + tileId.level - subtreeId.level, relativeTileMortonIdx, contentId, isAvailable); From 065ad184025845cadc58dc44dd86efb296f3781f Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Wed, 22 Nov 2023 19:48:23 -0500 Subject: [PATCH 400/421] Remove uint32_t support from PropertyAttribute --- .../CesiumGltf/PropertyAttributeView.h | 30 ++----------------- 1 file changed, 3 insertions(+), 27 deletions(-) diff --git a/CesiumGltf/include/CesiumGltf/PropertyAttributeView.h b/CesiumGltf/include/CesiumGltf/PropertyAttributeView.h index 089f0c0ea..59baada1c 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyAttributeView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyAttributeView.h @@ -105,7 +105,7 @@ class PropertyAttributeView { * This method will validate the EXT_structural_metadata format to ensure * {@link PropertyAttributePropertyView} retrieves the correct data. T must * be a scalar with a supported component type (int8_t, uint8_t, int16_t, - * uint16_t, uint32_t, float), a glm vecN composed of one of the scalar types, + * uint16_t, float), a glm vecN composed of one of the scalar types, * or a glm matN containing one of the scalar types. * * If T does not match the type specified by the class property, this returns @@ -157,7 +157,7 @@ class PropertyAttributeView { * This method will validate the EXT_structural_metadata format to ensure * {@link PropertyAttributePropertyView} retrieves the correct data. T must * be a scalar with a supported component type (int8_t, uint8_t, int16_t, - * uint16_t, uint32_t, float), a glm vecN composed of one of the scalar types, + * uint16_t, float), a glm vecN composed of one of the scalar types, * or a glm matN containing one of the scalar types. * * If the property is somehow invalid, an empty {@link PropertyAttributePropertyView} @@ -292,7 +292,7 @@ class PropertyAttributeView { * This method will validate the EXT_structural_metadata format to ensure * {@link PropertyAttributePropertyView} retrieves the correct data. T must be * a scalar with a supported component type (int8_t, uint8_t, int16_t, - * uint16_t, uint32_t, float), a glm vecN composed of one of the scalar types, + * uint16_t, float), a glm vecN composed of one of the scalar types, * or a PropertyArrayView containing one of the scalar types. * * If the property is invalid, an empty {@link PropertyAttributePropertyView} with an @@ -412,14 +412,6 @@ class PropertyAttributeView { propertyId, classProperty)); break; - case PropertyComponentType::Uint32: - callback( - propertyId, - getPropertyViewImpl( - primitive, - propertyId, - classProperty)); - break; case PropertyComponentType::Float32: callback( propertyId, @@ -477,14 +469,6 @@ class PropertyAttributeView { propertyId, classProperty)); break; - case PropertyComponentType::Uint32: - callback( - propertyId, - getPropertyViewImpl, Normalized>( - primitive, - propertyId, - classProperty)); - break; case PropertyComponentType::Float32: callback( propertyId, @@ -585,14 +569,6 @@ class PropertyAttributeView { propertyId, classProperty)); break; - case PropertyComponentType::Uint32: - callback( - propertyId, - getPropertyViewImpl, Normalized>( - primitive, - propertyId, - classProperty)); - break; case PropertyComponentType::Float32: callback( propertyId, From 278f936cce3d1dca69799a86e4b8543382af076b Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Mon, 27 Nov 2023 11:03:02 -0500 Subject: [PATCH 401/421] Update tests --- CesiumGltf/src/PropertyAttributeView.cpp | 2 - .../TestPropertyAttributePropertyView.cpp | 40 ---- CesiumGltf/test/TestPropertyAttributeView.cpp | 175 +++++++++--------- 3 files changed, 88 insertions(+), 129 deletions(-) diff --git a/CesiumGltf/src/PropertyAttributeView.cpp b/CesiumGltf/src/PropertyAttributeView.cpp index c21f42f1a..34d9a441b 100644 --- a/CesiumGltf/src/PropertyAttributeView.cpp +++ b/CesiumGltf/src/PropertyAttributeView.cpp @@ -38,8 +38,6 @@ getAccessorComponentTypeAsPropertyComponentType(const Accessor& accessor) { return PropertyComponentType::Int16; case Accessor::ComponentType::UNSIGNED_SHORT: return PropertyComponentType::Uint16; - case Accessor::ComponentType::UNSIGNED_INT: - return PropertyComponentType::Uint32; case Accessor::ComponentType::FLOAT: return PropertyComponentType::Float32; default: diff --git a/CesiumGltf/test/TestPropertyAttributePropertyView.cpp b/CesiumGltf/test/TestPropertyAttributePropertyView.cpp index 150dc5393..eebdcb327 100644 --- a/CesiumGltf/test/TestPropertyAttributePropertyView.cpp +++ b/CesiumGltf/test/TestPropertyAttributePropertyView.cpp @@ -73,9 +73,6 @@ const Accessor& addValuesToModel(Model& model, const std::vector& values) { case PropertyComponentType::Uint16: accessor.componentType = Accessor::ComponentType::UNSIGNED_SHORT; break; - case PropertyComponentType::Uint32: - accessor.componentType = Accessor::ComponentType::UNSIGNED_INT; - break; case PropertyComponentType::Float32: accessor.componentType = Accessor::ComponentType::FLOAT; break; @@ -207,10 +204,6 @@ TEST_CASE("Check scalar PropertyAttributePropertyView") { checkAttributeValues(data); } - SECTION("uint32_t") { - std::vector data{16777216, 65545, 131604, 16777480}; - checkAttributeValues(data); - } SECTION("float") { std::vector data{12.3333f, -12.44555f, -5.6111f, 6.7421f}; @@ -284,16 +277,6 @@ TEST_CASE("Check scalar PropertyAttributePropertyView (normalized)") { checkNormalizedAttributeValues(data, expected); } - SECTION("Uint32") { - std::vector data{16777216, 65545, 131604, 16777480}; - std::vector> expected{ - normalize(data[0]), - normalize(data[1]), - normalize(data[2]), - normalize(data[3])}; - checkNormalizedAttributeValues(data, expected); - } - SECTION("Uint8 with offset / scale") { std::vector data{12, 33, 56, 67}; const double offset = 1.0; @@ -349,15 +332,6 @@ TEST_CASE("Check vecN PropertyAttributePropertyView") { checkAttributeValues(data); } - SECTION("glm::uvec4") { - std::vector data{ - glm::uvec4(0, 12, 324, 256), - glm::uvec4(9234, 12, 7, 1), - glm::uvec4(532, 2, 88, 16), - glm::uvec4(264, 256, 22, 101)}; - checkAttributeValues(data); - } - SECTION("glm::vec3 with offset / scale") { std::vector data{ glm::vec3(1.0f, 2.0f, 3.0f), @@ -452,20 +426,6 @@ TEST_CASE("Check vecN PropertyAttributePropertyView (normalized)") { checkNormalizedAttributeValues(data, expected); } - SECTION("glm::uvec4") { - std::vector data{ - glm::uvec4(123, 20, 13, 0), - glm::uvec4(43, 5, 86, 11), - glm::uvec4(7345, 448, 3219, 83), - glm::uvec4(0, 775, 12, 27)}; - std::vector> expected{ - normalize(data[0]), - normalize(data[1]), - normalize(data[2]), - normalize(data[3])}; - checkNormalizedAttributeValues(data, expected); - } - SECTION("glm::i8vec2 with offset / scale") { std::vector data{ glm::i8vec2(28, -1), diff --git a/CesiumGltf/test/TestPropertyAttributeView.cpp b/CesiumGltf/test/TestPropertyAttributeView.cpp index e5b16e460..3b8f99a2c 100644 --- a/CesiumGltf/test/TestPropertyAttributeView.cpp +++ b/CesiumGltf/test/TestPropertyAttributeView.cpp @@ -75,9 +75,6 @@ void addAttributeToModel( case PropertyComponentType::Uint16: accessor.componentType = Accessor::ComponentType::UNSIGNED_SHORT; break; - case PropertyComponentType::Uint32: - accessor.componentType = Accessor::ComponentType::UNSIGNED_INT; - break; case PropertyComponentType::Float32: accessor.componentType = Accessor::ComponentType::FLOAT; break; @@ -729,17 +726,17 @@ TEST_CASE("Test matN PropertyAttributeProperty") { const std::string attributeName = "_ATTRIBUTE"; // clang-format off - std::vector data = { - glm::umat2x2( + std::vector data = { + glm::u16mat2x2( 12, 34, 30, 1), - glm::umat2x2( + glm::u16mat2x2( 11, 8, 73, 102), - glm::umat2x2( + glm::u16mat2x2( 1, 0, 63, 2), - glm::umat2x2( + glm::u16mat2x2( 4, 8, 3, 23)}; // clang-format on @@ -756,7 +753,7 @@ TEST_CASE("Test matN PropertyAttributeProperty") { Class& testClass = schema.classes["TestClass"]; ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; testClassProperty.type = ClassProperty::Type::MAT2; - testClassProperty.componentType = ClassProperty::ComponentType::UINT32; + testClassProperty.componentType = ClassProperty::ComponentType::UINT16; PropertyAttribute& propertyAttribute = metadata.propertyAttributes.emplace_back(); @@ -773,39 +770,40 @@ TEST_CASE("Test matN PropertyAttributeProperty") { view.getClassProperty("TestClassProperty"); REQUIRE(classProperty); REQUIRE(classProperty->type == ClassProperty::Type::MAT2); - REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT32); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT16); REQUIRE(classProperty->count == std::nullopt); REQUIRE(!classProperty->array); REQUIRE(!classProperty->normalized); SECTION("Access correct type") { - PropertyAttributePropertyView umat2x2Property = - view.getPropertyView(primitive, "TestClassProperty"); + PropertyAttributePropertyView u16mat2x2Property = + view.getPropertyView(primitive, "TestClassProperty"); REQUIRE( - umat2x2Property.status() == PropertyAttributePropertyViewStatus::Valid); + u16mat2x2Property.status() == + PropertyAttributePropertyViewStatus::Valid); for (size_t i = 0; i < data.size(); ++i) { - REQUIRE(umat2x2Property.getRaw(static_cast(i)) == data[i]); - REQUIRE(umat2x2Property.get(static_cast(i)) == data[i]); + REQUIRE(u16mat2x2Property.getRaw(static_cast(i)) == data[i]); + REQUIRE(u16mat2x2Property.get(static_cast(i)) == data[i]); } } SECTION("Access wrong type") { - PropertyAttributePropertyView uint32Invalid = - view.getPropertyView(primitive, "TestClassProperty"); + PropertyAttributePropertyView uint16Invalid = + view.getPropertyView(primitive, "TestClassProperty"); REQUIRE( - uint32Invalid.status() == + uint16Invalid.status() == PropertyAttributePropertyViewStatus::ErrorTypeMismatch); - PropertyAttributePropertyView uvec2Invalid = - view.getPropertyView(primitive, "TestClassProperty"); + PropertyAttributePropertyView u16vec2Invalid = + view.getPropertyView(primitive, "TestClassProperty"); REQUIRE( - uvec2Invalid.status() == + u16vec2Invalid.status() == PropertyAttributePropertyViewStatus::ErrorTypeMismatch); - PropertyAttributePropertyView umat4x4Invalid = - view.getPropertyView(primitive, "TestClassProperty"); + PropertyAttributePropertyView u16mat4x4Invalid = + view.getPropertyView(primitive, "TestClassProperty"); REQUIRE( - umat4x4Invalid.status() == + u16mat4x4Invalid.status() == PropertyAttributePropertyViewStatus::ErrorTypeMismatch); } @@ -818,8 +816,8 @@ TEST_CASE("Test matN PropertyAttributeProperty") { } SECTION("Access incorrectly as normalized") { - PropertyAttributePropertyView normalizedInvalid = - view.getPropertyView( + PropertyAttributePropertyView normalizedInvalid = + view.getPropertyView( primitive, "TestClassProperty"); REQUIRE( @@ -829,8 +827,8 @@ TEST_CASE("Test matN PropertyAttributeProperty") { SECTION("Buffer view points outside of the real buffer length") { model.buffers[bufferIndex].cesium.data.resize(4); - PropertyAttributePropertyView property = - view.getPropertyView(primitive, "TestClassProperty"); + PropertyAttributePropertyView property = + view.getPropertyView(primitive, "TestClassProperty"); REQUIRE( property.status() == PropertyAttributePropertyViewStatus::ErrorBufferViewOutOfBounds); @@ -838,8 +836,8 @@ TEST_CASE("Test matN PropertyAttributeProperty") { SECTION("Wrong buffer index") { model.bufferViews[bufferViewIndex].buffer = 2; - PropertyAttributePropertyView property = - view.getPropertyView(primitive, "TestClassProperty"); + PropertyAttributePropertyView property = + view.getPropertyView(primitive, "TestClassProperty"); REQUIRE( property.status() == PropertyAttributePropertyViewStatus::ErrorInvalidBuffer); @@ -847,8 +845,8 @@ TEST_CASE("Test matN PropertyAttributeProperty") { SECTION("Accessor view points outside of buffer viwe length") { model.accessors[accessorIndex].count = 10; - PropertyAttributePropertyView property = - view.getPropertyView(primitive, "TestClassProperty"); + PropertyAttributePropertyView property = + view.getPropertyView(primitive, "TestClassProperty"); REQUIRE( property.status() == PropertyAttributePropertyViewStatus::ErrorAccessorOutOfBounds); @@ -856,8 +854,8 @@ TEST_CASE("Test matN PropertyAttributeProperty") { SECTION("Wrong buffer view index") { model.accessors[accessorIndex].bufferView = -1; - PropertyAttributePropertyView property = - view.getPropertyView(primitive, "TestClassProperty"); + PropertyAttributePropertyView property = + view.getPropertyView(primitive, "TestClassProperty"); REQUIRE( property.status() == PropertyAttributePropertyViewStatus::ErrorInvalidBufferView); @@ -865,8 +863,8 @@ TEST_CASE("Test matN PropertyAttributeProperty") { SECTION("Wrong accessor normalization") { model.accessors[accessorIndex].normalized = true; - PropertyAttributePropertyView property = - view.getPropertyView(primitive, "TestClassProperty"); + PropertyAttributePropertyView property = + view.getPropertyView(primitive, "TestClassProperty"); REQUIRE( property.status() == PropertyAttributePropertyViewStatus:: ErrorAccessorNormalizationMismatch); @@ -875,8 +873,8 @@ TEST_CASE("Test matN PropertyAttributeProperty") { SECTION("Wrong accessor component type") { model.accessors[accessorIndex].componentType = Accessor::ComponentType::BYTE; - PropertyAttributePropertyView property = - view.getPropertyView(primitive, "TestClassProperty"); + PropertyAttributePropertyView property = + view.getPropertyView(primitive, "TestClassProperty"); REQUIRE( property.status() == PropertyAttributePropertyViewStatus:: ErrorAccessorComponentTypeMismatch); @@ -884,8 +882,8 @@ TEST_CASE("Test matN PropertyAttributeProperty") { SECTION("Wrong accessor type") { model.accessors[accessorIndex].type = Accessor::Type::SCALAR; - PropertyAttributePropertyView property = - view.getPropertyView(primitive, "TestClassProperty"); + PropertyAttributePropertyView property = + view.getPropertyView(primitive, "TestClassProperty"); REQUIRE( property.status() == PropertyAttributePropertyViewStatus::ErrorAccessorTypeMismatch); @@ -893,8 +891,8 @@ TEST_CASE("Test matN PropertyAttributeProperty") { SECTION("Wrong accessor index") { primitive.attributes[attributeName] = -1; - PropertyAttributePropertyView property = - view.getPropertyView(primitive, "TestClassProperty"); + PropertyAttributePropertyView property = + view.getPropertyView(primitive, "TestClassProperty"); REQUIRE( property.status() == PropertyAttributePropertyViewStatus::ErrorInvalidAccessor); @@ -902,8 +900,8 @@ TEST_CASE("Test matN PropertyAttributeProperty") { SECTION("Missing attribute") { primitive.attributes.clear(); - PropertyAttributePropertyView property = - view.getPropertyView(primitive, "TestClassProperty"); + PropertyAttributePropertyView property = + view.getPropertyView(primitive, "TestClassProperty"); REQUIRE( property.status() == PropertyAttributePropertyViewStatus::ErrorMissingAttribute); @@ -917,22 +915,22 @@ TEST_CASE("Test matN PropertyAttributeProperty (normalized)") { const std::string attributeName = "_ATTRIBUTE"; // clang-format off - std::vector data = { - glm::umat2x2( + std::vector data = { + glm::u16mat2x2( 12, 34, 30, 1), - glm::umat2x2( + glm::u16mat2x2( 11, 8, 73, 102), - glm::umat2x2( + glm::u16mat2x2( 1, 0, 63, 2), - glm::umat2x2( + glm::u16mat2x2( 4, 8, 3, 23)}; // clang-format on - addAttributeToModel( + addAttributeToModel( model, primitive, attributeName, @@ -946,7 +944,7 @@ TEST_CASE("Test matN PropertyAttributeProperty (normalized)") { Class& testClass = schema.classes["TestClass"]; ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; testClassProperty.type = ClassProperty::Type::MAT2; - testClassProperty.componentType = ClassProperty::ComponentType::UINT32; + testClassProperty.componentType = ClassProperty::ComponentType::UINT16; testClassProperty.normalized = true; PropertyAttribute& propertyAttribute = @@ -964,44 +962,47 @@ TEST_CASE("Test matN PropertyAttributeProperty (normalized)") { view.getClassProperty("TestClassProperty"); REQUIRE(classProperty); REQUIRE(classProperty->type == ClassProperty::Type::MAT2); - REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT32); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT16); REQUIRE(classProperty->count == std::nullopt); REQUIRE(!classProperty->array); REQUIRE(classProperty->normalized); SECTION("Access correct type") { - PropertyAttributePropertyView umat2x2Property = - view.getPropertyView( + PropertyAttributePropertyView u16mat2x2Property = + view.getPropertyView( primitive, "TestClassProperty"); REQUIRE( - umat2x2Property.status() == PropertyAttributePropertyViewStatus::Valid); + u16mat2x2Property.status() == + PropertyAttributePropertyViewStatus::Valid); for (size_t i = 0; i < data.size(); ++i) { - REQUIRE(umat2x2Property.getRaw(static_cast(i)) == data[i]); + REQUIRE(u16mat2x2Property.getRaw(static_cast(i)) == data[i]); REQUIRE( - umat2x2Property.get(static_cast(i)) == normalize(data[i])); + u16mat2x2Property.get(static_cast(i)) == normalize(data[i])); } } SECTION("Access wrong type") { - PropertyAttributePropertyView uint32Invalid = - view.getPropertyView(primitive, "TestClassProperty"); + PropertyAttributePropertyView uint16Invalid = + view.getPropertyView(primitive, "TestClassProperty"); REQUIRE( - uint32Invalid.status() == + uint16Invalid.status() == PropertyAttributePropertyViewStatus::ErrorTypeMismatch); - PropertyAttributePropertyView uvec2Invalid = - view.getPropertyView(primitive, "TestClassProperty"); + PropertyAttributePropertyView u16vec2Invalid = + view.getPropertyView( + primitive, + "TestClassProperty"); REQUIRE( - uvec2Invalid.status() == + u16vec2Invalid.status() == PropertyAttributePropertyViewStatus::ErrorTypeMismatch); - PropertyAttributePropertyView umat4x4Invalid = - view.getPropertyView( + PropertyAttributePropertyView u16mat4x4Invalid = + view.getPropertyView( primitive, "TestClassProperty"); REQUIRE( - umat4x4Invalid.status() == + u16mat4x4Invalid.status() == PropertyAttributePropertyViewStatus::ErrorTypeMismatch); } @@ -1016,8 +1017,8 @@ TEST_CASE("Test matN PropertyAttributeProperty (normalized)") { } SECTION("Access incorrectly as non-normalized") { - PropertyAttributePropertyView nonNormalizedInvalid = - view.getPropertyView(primitive, "TestClassProperty"); + PropertyAttributePropertyView nonNormalizedInvalid = + view.getPropertyView(primitive, "TestClassProperty"); REQUIRE( nonNormalizedInvalid.status() == PropertyAttributePropertyViewStatus::ErrorNormalizationMismatch); @@ -1033,8 +1034,8 @@ TEST_CASE("Test matN PropertyAttributeProperty (normalized)") { SECTION("Wrong accessor normalization") { model.accessors[accessorIndex].normalized = false; - PropertyAttributePropertyView property = - view.getPropertyView( + PropertyAttributePropertyView property = + view.getPropertyView( primitive, "TestClassProperty"); REQUIRE( @@ -1924,17 +1925,17 @@ TEST_CASE("Test callback for matN PropertyAttributeProperty") { MeshPrimitive& primitive = mesh.primitives.emplace_back(); // clang-format off - std::vector data = { - glm::umat2x2( + std::vector data = { + glm::u16mat2x2( 12, 34, 30, 1), - glm::umat2x2( + glm::u16mat2x2( 11, 8, 73, 102), - glm::umat2x2( + glm::u16mat2x2( 1, 0, 63, 2), - glm::umat2x2( + glm::u16mat2x2( 4, 8, 3, 23)}; // clang-format on @@ -1949,7 +1950,7 @@ TEST_CASE("Test callback for matN PropertyAttributeProperty") { Class& testClass = schema.classes["TestClass"]; ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; testClassProperty.type = ClassProperty::Type::MAT2; - testClassProperty.componentType = ClassProperty::ComponentType::UINT32; + testClassProperty.componentType = ClassProperty::ComponentType::UINT16; PropertyAttribute& propertyAttribute = metadata.propertyAttributes.emplace_back(); @@ -1966,7 +1967,7 @@ TEST_CASE("Test callback for matN PropertyAttributeProperty") { view.getClassProperty("TestClassProperty"); REQUIRE(classProperty); REQUIRE(classProperty->type == ClassProperty::Type::MAT2); - REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT32); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT16); REQUIRE(classProperty->count == std::nullopt); REQUIRE(!classProperty->array); REQUIRE(!classProperty->normalized); @@ -1980,7 +1981,7 @@ TEST_CASE("Test callback for matN PropertyAttributeProperty") { auto propertyValue) mutable { invokedCallbackCount++; if constexpr (std::is_same_v< - PropertyAttributePropertyView, + PropertyAttributePropertyView, decltype(propertyValue)>) { REQUIRE( propertyValue.status() == @@ -2005,23 +2006,23 @@ TEST_CASE("Test callback for matN PropertyAttributeProperty (normalized)") { MeshPrimitive& primitive = mesh.primitives.emplace_back(); // clang-format off - std::vector data = { - glm::umat2x2( + std::vector data = { + glm::u16mat2x2( 12, 34, 30, 1), - glm::umat2x2( + glm::u16mat2x2( 11, 8, 73, 102), - glm::umat2x2( + glm::u16mat2x2( 1, 0, 63, 2), - glm::umat2x2( + glm::u16mat2x2( 4, 8, 3, 23)}; // clang-format on const std::string attributeName = "_ATTRIBUTE"; - addAttributeToModel( + addAttributeToModel( model, primitive, attributeName, @@ -2034,7 +2035,7 @@ TEST_CASE("Test callback for matN PropertyAttributeProperty (normalized)") { Class& testClass = schema.classes["TestClass"]; ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; testClassProperty.type = ClassProperty::Type::MAT2; - testClassProperty.componentType = ClassProperty::ComponentType::UINT32; + testClassProperty.componentType = ClassProperty::ComponentType::UINT16; testClassProperty.normalized = true; PropertyAttribute& propertyAttribute = @@ -2052,7 +2053,7 @@ TEST_CASE("Test callback for matN PropertyAttributeProperty (normalized)") { view.getClassProperty("TestClassProperty"); REQUIRE(classProperty); REQUIRE(classProperty->type == ClassProperty::Type::MAT2); - REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT32); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT16); REQUIRE(classProperty->count == std::nullopt); REQUIRE(!classProperty->array); REQUIRE(classProperty->normalized); @@ -2066,7 +2067,7 @@ TEST_CASE("Test callback for matN PropertyAttributeProperty (normalized)") { auto propertyValue) mutable { invokedCallbackCount++; if constexpr (std::is_same_v< - PropertyAttributePropertyView, + PropertyAttributePropertyView, decltype(propertyValue)>) { REQUIRE( propertyValue.status() == From dcec8624326fdb90b5cbbaf2a32122dd4674d5f5 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Mon, 27 Nov 2023 13:38:33 -0500 Subject: [PATCH 402/421] Fix formatting --- CesiumGltf/test/TestPropertyAttributePropertyView.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/CesiumGltf/test/TestPropertyAttributePropertyView.cpp b/CesiumGltf/test/TestPropertyAttributePropertyView.cpp index eebdcb327..2067e6cf5 100644 --- a/CesiumGltf/test/TestPropertyAttributePropertyView.cpp +++ b/CesiumGltf/test/TestPropertyAttributePropertyView.cpp @@ -204,7 +204,6 @@ TEST_CASE("Check scalar PropertyAttributePropertyView") { checkAttributeValues(data); } - SECTION("float") { std::vector data{12.3333f, -12.44555f, -5.6111f, 6.7421f}; checkAttributeValues(data); From 0a535f44a235fc84373eecc50befbdf48fdbba5f Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 29 Nov 2023 08:55:30 +1100 Subject: [PATCH 403/421] Use availability constants instead of 1/0. --- Cesium3DTilesContent/src/SubtreeAvailability.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Cesium3DTilesContent/src/SubtreeAvailability.cpp b/Cesium3DTilesContent/src/SubtreeAvailability.cpp index cdc65d93f..a3f2bdd54 100644 --- a/Cesium3DTilesContent/src/SubtreeAvailability.cpp +++ b/Cesium3DTilesContent/src/SubtreeAvailability.cpp @@ -30,7 +30,8 @@ std::optional parseAvailabilityView( std::vector& bufferViews) { if (availability.constant) { return SubtreeAvailability::SubtreeConstantAvailability{ - *availability.constant == 1}; + *availability.constant == + Cesium3DTiles::Availability::Constant::AVAILABLE}; } int64_t bufferViewIndex = -1; @@ -123,9 +124,12 @@ std::optional parseAvailabilityView( ImplicitTileSubdivisionScheme subdivisionScheme, uint32_t levelsInSubtree) noexcept { Subtree subtree; - subtree.tileAvailability.constant = true; - subtree.contentAvailability.emplace_back().constant = false; - subtree.childSubtreeAvailability.constant = false; + subtree.tileAvailability.constant = + Cesium3DTiles::Availability::Constant::AVAILABLE; + subtree.contentAvailability.emplace_back().constant = + Cesium3DTiles::Availability::Constant::UNAVAILABLE; + subtree.childSubtreeAvailability.constant = + Cesium3DTiles::Availability::Constant::UNAVAILABLE; return SubtreeAvailability::fromSubtree( subdivisionScheme, From 8ebee290254bee7c3669437e15ab4924a0656a26 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 29 Nov 2023 09:11:15 +1100 Subject: [PATCH 404/421] Remove unused struct. --- .../include/Cesium3DTilesReader/SubtreeFileReader.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/Cesium3DTilesReader/include/Cesium3DTilesReader/SubtreeFileReader.h b/Cesium3DTilesReader/include/Cesium3DTilesReader/SubtreeFileReader.h index 9e7b0b678..fbaa5eb94 100644 --- a/Cesium3DTilesReader/include/Cesium3DTilesReader/SubtreeFileReader.h +++ b/Cesium3DTilesReader/include/Cesium3DTilesReader/SubtreeFileReader.h @@ -8,8 +8,6 @@ namespace Cesium3DTilesReader { -struct SubtreeReadOptions {}; - /** * @brief Reads 3D Tiles subtrees from a binary or JSON subtree file. * From 1a82401d1a8bb36a4e90e53c5564f8cfaf47aab3 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 29 Nov 2023 09:11:42 +1100 Subject: [PATCH 405/421] Add contentId bounds check. --- Cesium3DTilesContent/src/SubtreeAvailability.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/Cesium3DTilesContent/src/SubtreeAvailability.cpp b/Cesium3DTilesContent/src/SubtreeAvailability.cpp index a3f2bdd54..da2504b00 100644 --- a/Cesium3DTilesContent/src/SubtreeAvailability.cpp +++ b/Cesium3DTilesContent/src/SubtreeAvailability.cpp @@ -290,6 +290,8 @@ bool SubtreeAvailability::isContentAvailable( uint32_t relativeTileLevel, uint64_t relativeTileMortonId, uint64_t contentId) const noexcept { + if (contentId >= this->_contentAvailability.size()) + return false; return isAvailable( relativeTileLevel, relativeTileMortonId, @@ -329,11 +331,13 @@ void SubtreeAvailability::setContentAvailable( uint64_t relativeTileMortonId, uint64_t contentId, bool isAvailable) noexcept { - this->setAvailable( - relativeTileLevel, - relativeTileMortonId, - this->_contentAvailability[contentId], - isAvailable); + if (contentId < this->_contentAvailability.size()) { + this->setAvailable( + relativeTileLevel, + relativeTileMortonId, + this->_contentAvailability[contentId], + isAvailable); + } } bool SubtreeAvailability::isSubtreeAvailable( From fc633541ede7491a64e7568755485b63c4a549a6 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 29 Nov 2023 09:26:39 +1100 Subject: [PATCH 406/421] Add RasterOverlayLoadFailure to changelog. --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 520139ab4..d35078062 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -10,7 +10,7 @@ - `GltfUtilities::parseGltfCopyright` now returns the credits as a vector of string_views. Previously it took a `CreditSystem` and created credits directly. - The `SubtreeAvailability` constructor and `loadSubtree` static method now take an `ImplicitTileSubdivisionScheme` enumeration parameter instead of a `powerOf2` parameter. They also now require a `levelsInSubtree` parameter, which is needed when switching from constant to bitstream availability. Lastly, the constructor now takes a `Subtree` parameter instead of a `std::vector>` representing the buffers. - `SubtreeConstantAvailability`, `SubtreeBufferViewAvailability`, and `AvailabilityView` are now members of `SubtreeAvailability`. -- Moved `RasterOverlay`, `RasterOverlayTileProvider`, `RasterOverlayTile`, `QuadtreeRasterOverlayTileProvider`, and all of the `RasterOverlay`-derived types to a new `CesiumRasterOverlays` library and namespace. +- Moved `RasterOverlay`, `RasterOverlayTileProvider`, `RasterOverlayTile`, `QuadtreeRasterOverlayTileProvider`, `RasterOverlayLoadFailure`, and all of the `RasterOverlay`-derived types to a new `CesiumRasterOverlays` library and namespace. ##### Additions :tada: From ec266b19c76d88c05769e1284599b82404c61fb8 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 29 Nov 2023 09:29:07 +1100 Subject: [PATCH 407/421] Fix doc. --- .../IPrepareRasterOverlayRendererResources.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CesiumRasterOverlays/include/CesiumRasterOverlays/IPrepareRasterOverlayRendererResources.h b/CesiumRasterOverlays/include/CesiumRasterOverlays/IPrepareRasterOverlayRendererResources.h index 7f5c40c52..d9d88878f 100644 --- a/CesiumRasterOverlays/include/CesiumRasterOverlays/IPrepareRasterOverlayRendererResources.h +++ b/CesiumRasterOverlays/include/CesiumRasterOverlays/IPrepareRasterOverlayRendererResources.h @@ -54,8 +54,10 @@ class CESIUMRASTEROVERLAYS_API IPrepareRasterOverlayRendererResources { /** * @brief Frees previously-prepared renderer resources for a raster tile. * - * This method is always called from the thread that called - * {@link Tileset::updateView} or deleted the tileset. + * This method is always called from the thread that destroyed the + * {@link RasterOverlayTile}. When raster overlays are used with tilesets, + * this is the thread that called {@link Tileset::updateView} or deleted the + * tileset. * * @param rasterTile The tile for which to free renderer resources. * @param pLoadThreadResult The result returned by From 611c72b66db6e42565750270185c68f7d48c0da7 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 29 Nov 2023 09:34:07 +1100 Subject: [PATCH 408/421] Fix duplicate changelog entry. --- CHANGES.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 12fcdb317..0d680e14e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,14 +6,13 @@ - Moved `ErrorList`, `CreditSystem`, and `Credit` from `Cesium3DTilesSelection` to `CesiumUtility`. - Moved `GltfUtilities` from `Cesium3DTilesSelection` to `Cesium3DTilesContent`. -- Moved `RasterOverlay`, `RasterOverlayTileProvider`, `RasterOverlayTile`, `QuadtreeRasterOverlayTileProvider`, `RasterOverlayDetails`, and all of the `RasterOverlay`-derived types to a new `CesiumRasterOverlays` library and namespace. +- Moved `RasterOverlay`, `RasterOverlayTileProvider`, `RasterOverlayTile`, `QuadtreeRasterOverlayTileProvider`, `RasterOverlayLoadFailure`, `RasterOverlayDetails`, and all of the `RasterOverlay`-derived types to a new `CesiumRasterOverlays` library and namespace. - Moved `createRasterOverlayTextureCoordinates` method from `GltfUtilities` to a new `RasterOverlayUtilities` class in the `CesiumRasterOverlays` library. - `GltfUtilities::parseGltfCopyright` now returns the credits as a vector of string_views. Previously it took a `CreditSystem` and created credits directly. - The `SubtreeAvailability` constructor and `loadSubtree` static method now take an `ImplicitTileSubdivisionScheme` enumeration parameter instead of a `powerOf2` parameter. They also now require a `levelsInSubtree` parameter, which is needed when switching from constant to bitstream availability. Lastly, the constructor now takes a `Subtree` parameter instead of a `std::vector>` representing the buffers. - `SubtreeConstantAvailability`, `SubtreeBufferViewAvailability`, and `AvailabilityView` are now members of `SubtreeAvailability`. - Moved `ImageManipulation` from `CesiumGltfReader` to `CesiumGltfContent`. - Added some new parameters to `RasterOverlayUtilities::createRasterOverlayTextureCoordinates` and changed the order of some existing parameters. -- Moved `RasterOverlay`, `RasterOverlayTileProvider`, `RasterOverlayTile`, `QuadtreeRasterOverlayTileProvider`, `RasterOverlayLoadFailure`, and all of the `RasterOverlay`-derived types to a new `CesiumRasterOverlays` library and namespace. ##### Additions :tada: From a900fa64896858feeeb3810e3aac770dffdbc528 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 29 Nov 2023 09:40:36 +1100 Subject: [PATCH 409/421] Move TestImageManipulation.cpp to the right library. --- .../test/TestImageManipulation.cpp | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {CesiumGltfReader => CesiumGltfContent}/test/TestImageManipulation.cpp (100%) diff --git a/CesiumGltfReader/test/TestImageManipulation.cpp b/CesiumGltfContent/test/TestImageManipulation.cpp similarity index 100% rename from CesiumGltfReader/test/TestImageManipulation.cpp rename to CesiumGltfContent/test/TestImageManipulation.cpp From 2373586abccab0c83e3b3f6c62813438f0878022 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 29 Nov 2023 09:42:25 +1100 Subject: [PATCH 410/421] Fix typo. --- CesiumRasterOverlays/src/RasterOverlayUtilities.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CesiumRasterOverlays/src/RasterOverlayUtilities.cpp b/CesiumRasterOverlays/src/RasterOverlayUtilities.cpp index 3d36bea98..14177bbe8 100644 --- a/CesiumRasterOverlays/src/RasterOverlayUtilities.cpp +++ b/CesiumRasterOverlays/src/RasterOverlayUtilities.cpp @@ -284,7 +284,7 @@ RasterOverlayUtilities::createRasterOverlayTextureCoordinates( const CesiumGeometry::Rectangle& rectangle, const CesiumGeospatial::Ellipsoid& ellipsoid) { // We're aiming to estimate the maximum number of pixels (in each projected - // direction) the tile will occupy on the screen. The will be determined by + // direction) the tile will occupy on the screen. They will be determined by // the tile's geometric error, because when less error is needed (i.e. the // viewer moved closer), the LOD will switch to show the tile's children // instead of this tile. From 6317c9c5248d33443ee343d106887e02356c742d Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 29 Nov 2023 09:42:38 +1100 Subject: [PATCH 411/421] Change TODO to a regular comment. --- CesiumRasterOverlays/test/TestAddRasterOverlayToGltf.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CesiumRasterOverlays/test/TestAddRasterOverlayToGltf.cpp b/CesiumRasterOverlays/test/TestAddRasterOverlayToGltf.cpp index e6d74cc9b..8a231d049 100644 --- a/CesiumRasterOverlays/test/TestAddRasterOverlayToGltf.cpp +++ b/CesiumRasterOverlays/test/TestAddRasterOverlayToGltf.cpp @@ -198,11 +198,12 @@ TEST_CASE("Add raster overlay to glTF") { image.bufferView = int32_t(gltf.bufferViews.size() - 1); - // TODO: the below will replace any existing color texture with + // The below will replace any existing color texture with // the raster overlay, because glTF only allows one color // texture. However, it doesn't clean up previous textures or // texture coordinates, leaving the model bigger than it needs - // to be. + // to be. If this were production code (not just a test/demo), we + // would want to address this. int32_t newMaterialIndex = -1; From b44274abc1ce36547810a23507fedd0b1c08dfbb Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 29 Nov 2023 09:44:11 +1100 Subject: [PATCH 412/421] Add return doc. --- .../include/CesiumRasterOverlays/RasterOverlayTileProvider.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CesiumRasterOverlays/include/CesiumRasterOverlays/RasterOverlayTileProvider.h b/CesiumRasterOverlays/include/CesiumRasterOverlays/RasterOverlayTileProvider.h index d42fb8cae..9f74e5b39 100644 --- a/CesiumRasterOverlays/include/CesiumRasterOverlays/RasterOverlayTileProvider.h +++ b/CesiumRasterOverlays/include/CesiumRasterOverlays/RasterOverlayTileProvider.h @@ -326,6 +326,8 @@ class CESIUMRASTEROVERLAYS_API RasterOverlayTileProvider * performance. Consider using {@link loadTileThrottled} instead. * * @param tile The tile to load. + * @return A future that, when the tile is loaded, resolves to the loaded tile + * and the tile provider that loaded it. */ CesiumAsync::Future loadTile(RasterOverlayTile& tile); From d54ee1f49664f73ecd09471ba868877957cae060 Mon Sep 17 00:00:00 2001 From: Joseph Kaile Date: Thu, 30 Nov 2023 14:11:01 -0500 Subject: [PATCH 413/421] return generated api url when Get fails --- CesiumIonClient/src/Connection.cpp | 38 +++++++++++++++--------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/CesiumIonClient/src/Connection.cpp b/CesiumIonClient/src/Connection.cpp index 5fdc3cce8..c5f2d9f2e 100644 --- a/CesiumIonClient/src/Connection.cpp +++ b/CesiumIonClient/src/Connection.cpp @@ -372,6 +372,22 @@ Asset jsonToAsset(const rapidjson::Value& item) { return result; } +std::optional generateApiUrl(const std::string& ionUrl) { + UriUriA newUri; + if (uriParseSingleUriA(&newUri, ionUrl.c_str(), nullptr) != URI_SUCCESS) { + return std::optional(); + } + + std::string hostName = + std::string(newUri.hostText.first, newUri.hostText.afterLast); + std::string scheme = + std::string(newUri.scheme.first, newUri.scheme.afterLast); + + uriFreeUriMembersA(&newUri); + + return std::make_optional(scheme + "://api." + hostName + '/'); +} + } // namespace CesiumAsync::Future> @@ -397,26 +413,10 @@ CesiumIonClient::Connection::getApiUrl( } } } - - UriUriA newUri; - if (uriParseSingleUriA(&newUri, ionUrl.c_str(), nullptr) != - URI_SUCCESS) { - return std::optional(); - } - - std::string hostName = - std::string(newUri.hostText.first, newUri.hostText.afterLast); - std::string scheme = - std::string(newUri.scheme.first, newUri.scheme.afterLast); - - uriFreeUriMembersA(&newUri); - - return std::make_optional( - scheme + "://api." + hostName + '/'); + return generateApiUrl(ionUrl); }) - .catchImmediately([](std::exception&&) { - return std::optional(std::nullopt); - }); + .catchImmediately( + [ionUrl](std::exception&&) { return generateApiUrl(ionUrl); }); } CesiumAsync::Future> Connection::assets() const { From 7d5a93d4b524163cc5e04a4fe6c9072be4190fc6 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Fri, 1 Dec 2023 11:08:02 +1100 Subject: [PATCH 414/421] Bump to v0.30.0, update CHANGES.md. --- CHANGES.md | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index a53d6e0e6..f3b04055e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,6 @@ # Change Log -### ? - ? +### v0.30.0 - 2023-12-01 ##### Breaking Changes :mega: @@ -8,7 +8,7 @@ - Moved `GltfUtilities` from `Cesium3DTilesSelection` to `Cesium3DTilesContent`. - Moved `RasterOverlay`, `RasterOverlayTileProvider`, `RasterOverlayTile`, `QuadtreeRasterOverlayTileProvider`, `RasterOverlayLoadFailure`, `RasterOverlayDetails`, and all of the `RasterOverlay`-derived types to a new `CesiumRasterOverlays` library and namespace. - Moved `createRasterOverlayTextureCoordinates` method from `GltfUtilities` to a new `RasterOverlayUtilities` class in the `CesiumRasterOverlays` library. -- `GltfUtilities::parseGltfCopyright` now returns the credits as a vector of string_views. Previously it took a `CreditSystem` and created credits directly. +- `GltfUtilities::parseGltfCopyright` now returns the credits as a vector of `std::string_view` instances. Previously it took a `CreditSystem` and created credits directly. - The `SubtreeAvailability` constructor and `loadSubtree` static method now take an `ImplicitTileSubdivisionScheme` enumeration parameter instead of a `powerOf2` parameter. They also now require a `levelsInSubtree` parameter, which is needed when switching from constant to bitstream availability. Lastly, the constructor now takes a `Subtree` parameter instead of a `std::vector>` representing the buffers. - `SubtreeConstantAvailability`, `SubtreeBufferViewAvailability`, and `AvailabilityView` are now members of `SubtreeAvailability`. - Moved `ImageManipulation` from `CesiumGltfReader` to `CesiumGltfContent`. diff --git a/package.json b/package.json index e64481a6b..9ee6ed488 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cesium-native", - "version": "0.29.0", + "version": "0.30.0", "description": "Cesium 3D Geospatial for C++", "main": "index.js", "directories": { From db02367993cb2db6d71a3facf26ef660325351dc Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Mon, 4 Dec 2023 16:18:59 +1100 Subject: [PATCH 415/421] Update CHANGES.md. --- CHANGES.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index f3b04055e..01406e495 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,11 @@ # Change Log +### ? - ? + +##### Fixes :wrench: + +- Fixed a bug where the `getApiUrl` method of `CesiumIonClient::Connection` would not return the default API URL if the attempt to access `config.json` failed in a more serious way, such as because of an invalid hostname. + ### v0.30.0 - 2023-12-01 ##### Breaking Changes :mega: From 606200135fcb85de0e2cb8df3f88892a94cd7890 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Mon, 4 Dec 2023 18:00:32 +1100 Subject: [PATCH 416/421] Add support for new defaults service to CesiumIonClient. --- CHANGES.md | 4 + CesiumIonClient/CMakeLists.txt | 1 + .../include/CesiumIonClient/Connection.h | 12 ++ .../include/CesiumIonClient/Defaults.h | 95 ++++++++++++++++ CesiumIonClient/src/Connection.cpp | 107 +++++++++++++++++- CesiumIonClient/src/Response.cpp | 2 + CesiumIonClient/test/TestConnection.cpp | 74 ++++++++++++ CesiumIonClient/test/data/defaults.json | 90 +++++++++++++++ 8 files changed, 384 insertions(+), 1 deletion(-) create mode 100644 CesiumIonClient/include/CesiumIonClient/Defaults.h create mode 100644 CesiumIonClient/test/TestConnection.cpp create mode 100644 CesiumIonClient/test/data/defaults.json diff --git a/CHANGES.md b/CHANGES.md index 01406e495..bc52b31d9 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,6 +2,10 @@ ### ? - ? +##### Additions :tada: + +- Add `defaults` method to `CesiumIonClient::Connection`. + ##### Fixes :wrench: - Fixed a bug where the `getApiUrl` method of `CesiumIonClient::Connection` would not return the default API URL if the attempt to access `config.json` failed in a more serious way, such as because of an invalid hostname. diff --git a/CesiumIonClient/CMakeLists.txt b/CesiumIonClient/CMakeLists.txt index 524f1a392..41fdf9e21 100644 --- a/CesiumIonClient/CMakeLists.txt +++ b/CesiumIonClient/CMakeLists.txt @@ -12,6 +12,7 @@ set_target_properties(CesiumIonClient PROPERTIES TEST_SOURCES "${CESIUM_ION_CLIENT_TEST_SOURCES}" TEST_HEADERS "${CESIUM_ION_CLIENT_TEST_HEADERS}" + TEST_DATA_DIR ${CMAKE_CURRENT_LIST_DIR}/test/data ) set_target_properties(CesiumIonClient diff --git a/CesiumIonClient/include/CesiumIonClient/Connection.h b/CesiumIonClient/include/CesiumIonClient/Connection.h index 4f3f7d46a..b57840f42 100644 --- a/CesiumIonClient/include/CesiumIonClient/Connection.h +++ b/CesiumIonClient/include/CesiumIonClient/Connection.h @@ -1,6 +1,7 @@ #pragma once #include "Assets.h" +#include "Defaults.h" #include "Profile.h" #include "Response.h" #include "Token.h" @@ -164,6 +165,17 @@ class CESIUMASYNC_API Connection { */ CesiumAsync::Future> me() const; + /** + * @brief Retrieves default imagery, terrain and building assets along with + * quick add assets that can be useful to use within other applications. + * + * This route will always return data, but will return user specific + * information with any valid token. + * + * @return A future that resolves to the default information. + */ + CesiumAsync::Future> defaults() const; + /** * @brief Gets the list of available assets. * diff --git a/CesiumIonClient/include/CesiumIonClient/Defaults.h b/CesiumIonClient/include/CesiumIonClient/Defaults.h new file mode 100644 index 000000000..cce05a72e --- /dev/null +++ b/CesiumIonClient/include/CesiumIonClient/Defaults.h @@ -0,0 +1,95 @@ +#pragma once + +namespace CesiumIonClient { + +/** + * @brief The default assets. + */ +struct DefaultAssets { + /** + * @brief The asset id of the default imagery asset. + */ + int64_t imagery = -1; + + /** + * @brief The asset id of the default terrain asset. + */ + int64_t terrain = -1; + + /** + * @brief The asset id of the default buildings asset. + */ + int64_t buildings = -1; +}; + +/** + * @brief A raster overlay available for use with a quick add asset. + */ +struct QuickAddRasterOverlay { + /** + * @brief The name of this asset. + */ + std::string name{}; + + /** + * @brief The unique identifier for this asset. + */ + int64_t assetId = -1; + + /** + * @brief `true` if the user is subscribed to the asset, `false` otherwise. + */ + bool subscribed = false; +}; + +/** + * @brief A quick add asset. + */ +struct QuickAddAsset { + /** + * @brief The name of this asset. + */ + std::string name{}; + + /** + * @brief The name of the main asset. In most cases this will be the same as + * `name`, but in the cases of assets with raster overlays, this will be the + * non-imagery asset. + */ + std::string objectName{}; + + /** + * @brief A Markdown compatible string describing this asset. + */ + std::string description{}; + + /** + * @brief The unique identifier for this asset. + */ + int64_t assetId = -1; + + /** + * @brief This asset's type. + * + * Valid values are: `3DTILES`, `GLTF`, `IMAGERY`, `TERRAIN`, `KML`, `CZML`, + * `GEOJSON`. + */ + std::string type{}; + + /** + * @brief `true` if the user is subscribed to the asset, `false` otherwise. + */ + bool subscribed = false; + + /** + * @brief The raster overlays available for this asset + */ + std::vector rasterOverlays{}; +}; + +struct Defaults { + DefaultAssets defaultAssets{}; + std::vector quickAddAssets{}; +}; + +} // namespace CesiumIonClient diff --git a/CesiumIonClient/src/Connection.cpp b/CesiumIonClient/src/Connection.cpp index c5f2d9f2e..422d179bd 100644 --- a/CesiumIonClient/src/Connection.cpp +++ b/CesiumIonClient/src/Connection.cpp @@ -419,6 +419,111 @@ CesiumIonClient::Connection::getApiUrl( [ionUrl](std::exception&&) { return generateApiUrl(ionUrl); }); } +namespace { + +QuickAddRasterOverlay +jsonToQuickAddRasterOverlay(const rapidjson::Value& json) { + QuickAddRasterOverlay result; + + result.name = JsonHelpers::getStringOrDefault(json, "name", std::string()); + result.assetId = JsonHelpers::getInt64OrDefault(json, "assetId", -1); + result.subscribed = JsonHelpers::getBoolOrDefault(json, "subscribed", false); + + return result; +} + +QuickAddAsset jsonToQuickAddAsset(const rapidjson::Value& json) { + QuickAddAsset result; + + result.name = JsonHelpers::getStringOrDefault(json, "name", std::string()); + result.objectName = + JsonHelpers::getStringOrDefault(json, "objectName", std::string()); + result.description = + JsonHelpers::getStringOrDefault(json, "description", std::string()); + result.assetId = JsonHelpers::getInt64OrDefault(json, "assetId", -1); + result.type = JsonHelpers::getStringOrDefault(json, "type", std::string()); + result.subscribed = JsonHelpers::getBoolOrDefault(json, "subscribed", false); + + auto overlaysIt = json.FindMember("rasterOverlays"); + if (overlaysIt != json.MemberEnd() && overlaysIt->value.IsArray()) { + const rapidjson::Value& items = overlaysIt->value; + result.rasterOverlays.resize(items.Size()); + std::transform( + items.Begin(), + items.End(), + result.rasterOverlays.begin(), + jsonToQuickAddRasterOverlay); + } + + return result; +} + +Defaults defaultsFromJson(const rapidjson::Document& json) { + Defaults defaults; + + auto defaultAssetsIt = json.FindMember("defaultAssets"); + if (defaultAssetsIt != json.MemberEnd() && + defaultAssetsIt->value.IsObject()) { + defaults.defaultAssets.imagery = + JsonHelpers::getInt64OrDefault(defaultAssetsIt->value, "imagery", -1); + defaults.defaultAssets.terrain = + JsonHelpers::getInt64OrDefault(defaultAssetsIt->value, "terrain", -1); + defaults.defaultAssets.buildings = + JsonHelpers::getInt64OrDefault(defaultAssetsIt->value, "buildings", -1); + } + + auto quickAddAssetsIt = json.FindMember("quickAddAssets"); + if (quickAddAssetsIt != json.MemberEnd() && + quickAddAssetsIt->value.IsArray()) { + const rapidjson::Value& items = quickAddAssetsIt->value; + defaults.quickAddAssets.resize(items.Size()); + std::transform( + items.Begin(), + items.End(), + defaults.quickAddAssets.begin(), + jsonToQuickAddAsset); + } + + return defaults; +} + +} // namespace + +Future> Connection::defaults() const { + return this->_pAssetAccessor + ->get( + this->_asyncSystem, + CesiumUtility::Uri::resolve(this->_apiUrl, "v1/defaults"), + {{"Accept", "application/json"}, + {"Authorization", "Bearer " + this->_accessToken}}) + .thenInMainThread( + [](std::shared_ptr&& pRequest) { + const IAssetResponse* pResponse = pRequest->response(); + if (!pResponse) { + return createEmptyResponse(); + } + + if (pResponse->statusCode() < 200 || + pResponse->statusCode() >= 300) { + return createErrorResponse(pResponse); + } + + rapidjson::Document d; + if (!parseJsonObject(pResponse, d)) { + return createJsonErrorResponse(pResponse, d); + } + if (!d.IsObject()) { + return createJsonTypeResponse(pResponse, "object"); + } + + return Response( + defaultsFromJson(d), + pResponse->statusCode(), + std::string(), + std::string()); + }); +} + CesiumAsync::Future> Connection::assets() const { return this->_pAssetAccessor ->get( @@ -507,7 +612,7 @@ TokenList tokenListFromJson(const rapidjson::Value& json) { TokenList result; const auto itemsIt = json.FindMember("items"); - if (itemsIt != json.MemberEnd() || !itemsIt->value.IsArray()) { + if (itemsIt != json.MemberEnd() && itemsIt->value.IsArray()) { const rapidjson::Value& items = itemsIt->value; for (auto it = items.Begin(); it != items.End(); ++it) { diff --git a/CesiumIonClient/src/Response.cpp b/CesiumIonClient/src/Response.cpp index 0143063af..fc621a515 100644 --- a/CesiumIonClient/src/Response.cpp +++ b/CesiumIonClient/src/Response.cpp @@ -1,6 +1,7 @@ #include "CesiumIonClient/Response.h" #include "CesiumIonClient/Assets.h" +#include "CesiumIonClient/Defaults.h" #include "CesiumIonClient/Profile.h" #include "CesiumIonClient/TokenList.h" #include "parseLinkHeader.h" @@ -73,6 +74,7 @@ Response::Response( // Explicit instantiations template struct Response; template struct Response; +template struct Response; template struct Response; template struct Response; template struct Response; diff --git a/CesiumIonClient/test/TestConnection.cpp b/CesiumIonClient/test/TestConnection.cpp new file mode 100644 index 000000000..19a2b02af --- /dev/null +++ b/CesiumIonClient/test/TestConnection.cpp @@ -0,0 +1,74 @@ +#include +#include +#include +#include +#include +#include + +#include + +using namespace CesiumAsync; +using namespace CesiumIonClient; +using namespace CesiumNativeTests; + +TEST_CASE("CesiumIonClient::Connection") { + std::shared_ptr pAssetAccessor = + std::make_shared( + std::map>()); + + std::unique_ptr pResponse = + std::make_unique( + static_cast(200), + "doesn't matter", + CesiumAsync::HttpHeaders{}, + readFile( + std::filesystem::path(CesiumIonClient_TEST_DATA_DIR) / + "defaults.json")); + + std::shared_ptr pRequest = + std::make_shared( + "GET", + "doesn't matter", + CesiumAsync::HttpHeaders{}, + std::move(pResponse)); + + pAssetAccessor->mockCompletedRequests.insert( + {"https://example.com/v1/defaults", std::move(pRequest)}); + + AsyncSystem asyncSystem(std::make_shared()); + Connection connection( + asyncSystem, + pAssetAccessor, + "my access token", + "https://example.com/"); + + Future> futureDefaults = connection.defaults(); + Response defaults = + waitForFuture(asyncSystem, std::move(futureDefaults)); + + REQUIRE(defaults.value); + + CHECK(defaults.value->defaultAssets.imagery == 2); + CHECK(defaults.value->defaultAssets.terrain == 1); + CHECK(defaults.value->defaultAssets.buildings == 624); + + REQUIRE(defaults.value->quickAddAssets.size() == 9); + const QuickAddAsset& cwtAndBing = defaults.value->quickAddAssets[6]; + CHECK(cwtAndBing.name == "Cesium World Terrain + Bing Maps Aerial"); + CHECK(cwtAndBing.objectName == "Cesium World Terrain"); + CHECK( + cwtAndBing.description == + "High-resolution global terrain tileset curated from several data " + "sources. See the official [Cesium World " + "Terrain](https://cesium.com/content/cesium-world-terrain/) page for " + "details. textured with Aerial imagery."); + CHECK(cwtAndBing.assetId == 1); + CHECK(cwtAndBing.type == "TERRAIN"); + CHECK(cwtAndBing.subscribed == true); + + REQUIRE(cwtAndBing.rasterOverlays.size() == 1); + const QuickAddRasterOverlay& bing = cwtAndBing.rasterOverlays[0]; + CHECK(bing.name == "Bing Maps Aerial"); + CHECK(bing.assetId == 2); + CHECK(bing.subscribed == true); +} diff --git a/CesiumIonClient/test/data/defaults.json b/CesiumIonClient/test/data/defaults.json new file mode 100644 index 000000000..55ff14e88 --- /dev/null +++ b/CesiumIonClient/test/data/defaults.json @@ -0,0 +1,90 @@ +{ + "defaultAssets": { "imagery": 2, "terrain": 1, "buildings": 624 }, + "quickAddAssets": [ + { + "name": "Cesium World Terrain", + "objectName": "Cesium World Terrain", + "description": "High-resolution global terrain tileset curated from several data sources. See the official [Cesium World Terrain](https://cesium.com/content/cesium-world-terrain/) page for details.", + "assetId": 1, + "type": "TERRAIN", + "subscribed": true + }, + { + "name": "Bing Maps Aerial", + "objectName": "Bing Maps Aerial", + "description": "Aerial imagery.", + "assetId": 2, + "type": "IMAGERY", + "subscribed": true + }, + { + "name": "Bing Maps Aerial with Labels", + "objectName": "Bing Maps Aerial with Labels", + "description": "Bing Maps Aerial with Labels", + "assetId": 23, + "type": "IMAGERY", + "subscribed": false + }, + { + "name": "Sentinel-2", + "objectName": "Sentinel-2", + "description": "Sentinel-2 cloudless imagery of the entire world down to zoom level 13.", + "assetId": 623, + "type": "IMAGERY", + "subscribed": false + }, + { + "name": "Cesium OSM Buildings", + "objectName": "Cesium OSM Buildings", + "description": "A 3D buildings layer derived from OpenStreetMap covering the entire world. It contains over 350 million buildings with per-feature data like name, address, whether a building is commercial or residential, and more.\n\nSee the [Cesium OSM Buildings page](https://cesium.com/content/cesium-osm-buildings/) to learn about the available properties and what you can do with this asset.\n\nThis tileset is updated monthly. For the latest news, visit our [community forum thread](https://community.cesium.com/t/weve-just-updated-cesium-osm-buildings/10764/last).", + "assetId": 624, + "type": "3DTILES", + "subscribed": true + }, + { + "name": "Google Photorealistic 3D Tiles", + "objectName": "Google Photorealistic 3D Tiles", + "description": "Photorealistic 3D Tiles from Google Maps Platform. \n\nYour use of this asset is subject to the current versions of these documents:\n\n- Google Maps Platform Service Specific Terms at https://cloud.google.com/maps-platform/terms/maps-service-terms \n- The Google Maps/Google Earth Additional Terms of Service at https://maps.google.com/help/terms_maps.html \n- The Map Tiles API Policy at https://developers.google.com/maps/documentation/tile/policies \n- The Google Privacy Policy at https://www.google.com/policies/privacy/ ", + "assetId": 823, + "type": "3DTILES", + "subscribed": true + }, + { + "name": "Cesium World Terrain + Bing Maps Aerial", + "objectName": "Cesium World Terrain", + "description": "High-resolution global terrain tileset curated from several data sources. See the official [Cesium World Terrain](https://cesium.com/content/cesium-world-terrain/) page for details. textured with Aerial imagery.", + "assetId": 1, + "type": "TERRAIN", + "subscribed": true, + "rasterOverlays": [ + { "name": "Bing Maps Aerial", "assetId": 2, "subscribed": true } + ] + }, + { + "name": "Cesium World Terrain + Bing Maps Aerial with Labels", + "objectName": "Cesium World Terrain", + "description": "High-resolution global terrain tileset curated from several data sources. See the official [Cesium World Terrain](https://cesium.com/content/cesium-world-terrain/) page for details. textured with Bing Maps Aerial with Labels", + "assetId": 1, + "type": "TERRAIN", + "subscribed": true, + "rasterOverlays": [ + { + "name": "Bing Maps Aerial with Labels", + "assetId": 23, + "subscribed": false + } + ] + }, + { + "name": "Cesium World Terrain + Sentinel-2", + "objectName": "Cesium World Terrain", + "description": "High-resolution global terrain tileset curated from several data sources. See the official [Cesium World Terrain](https://cesium.com/content/cesium-world-terrain/) page for details. textured with Sentinel-2 cloudless imagery of the entire world down to zoom level 13.", + "assetId": 1, + "type": "TERRAIN", + "subscribed": true, + "rasterOverlays": [ + { "name": "Sentinel-2", "assetId": 623, "subscribed": false } + ] + } + ] +} From eaa33bbfb571468d9d2e439b4af018ccac8cb982 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Tue, 5 Dec 2023 11:25:25 +1100 Subject: [PATCH 417/421] Add doc for CesiumIonClient::Defaults. --- CesiumIonClient/include/CesiumIonClient/Defaults.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CesiumIonClient/include/CesiumIonClient/Defaults.h b/CesiumIonClient/include/CesiumIonClient/Defaults.h index cce05a72e..eec76493d 100644 --- a/CesiumIonClient/include/CesiumIonClient/Defaults.h +++ b/CesiumIonClient/include/CesiumIonClient/Defaults.h @@ -87,8 +87,20 @@ struct QuickAddAsset { std::vector rasterOverlays{}; }; +/** + * @brief The data returned by Cesium ion's `v1/defaults` service. It includes + * information about default imagery, terrain and building assets along with + * quick add assets that can be useful to use within other applications. + */ struct Defaults { + /** + * @brief The default assets + */ DefaultAssets defaultAssets{}; + + /** + * @brief The quick add assets + */ std::vector quickAddAssets{}; }; From 0aa888d548f32ddb4c6bd9b25365b11adf4516c8 Mon Sep 17 00:00:00 2001 From: Hannes Janetzek Date: Tue, 5 Dec 2023 12:53:33 +0100 Subject: [PATCH 418/421] fix crash from accessing deleted SubtreeFileReader keep SubtreeFileReader instance in the loaders future context --- Cesium3DTilesContent/src/SubtreeAvailability.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cesium3DTilesContent/src/SubtreeAvailability.cpp b/Cesium3DTilesContent/src/SubtreeAvailability.cpp index da2504b00..fe0f3aa47 100644 --- a/Cesium3DTilesContent/src/SubtreeAvailability.cpp +++ b/Cesium3DTilesContent/src/SubtreeAvailability.cpp @@ -146,10 +146,10 @@ SubtreeAvailability::loadSubtree( const std::shared_ptr& pLogger, const std::string& subtreeUrl, const std::vector& requestHeaders) { - SubtreeFileReader reader; - return reader.load(asyncSystem, pAssetAccessor, subtreeUrl, requestHeaders) + auto reader = std::make_shared(); + return reader->load(asyncSystem, pAssetAccessor, subtreeUrl, requestHeaders) .thenInMainThread( - [pLogger, subtreeUrl, subdivisionScheme, levelsInSubtree]( + [pLogger, subtreeUrl, subdivisionScheme, levelsInSubtree, reader]( ReadJsonResult&& subtree) -> std::optional { if (!subtree.value) { From 262846e69b2c9e944bd65879954bf00617704c05 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Thu, 7 Dec 2023 13:13:00 +1100 Subject: [PATCH 419/421] Rename, doc, test, CHANGES.md. --- CHANGES.md | 6 ++++++ Cesium3DTilesContent/src/SubtreeAvailability.cpp | 6 +++--- .../test/TestSubtreeAvailability.cpp | 7 ++++--- .../Cesium3DTilesReader/SubtreeFileReader.h | 6 ++++++ .../CesiumNativeTests/ThreadTaskProcessor.h | 14 ++++++++++++++ 5 files changed, 33 insertions(+), 6 deletions(-) create mode 100644 CesiumNativeTests/include/CesiumNativeTests/ThreadTaskProcessor.h diff --git a/CHANGES.md b/CHANGES.md index f3b04055e..c5a17ed77 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,11 @@ # Change Log +### ? - ? + +##### Fixes :wrench: + +- Fixed a crash in `SubtreeAvailability::loadSubtree`. + ### v0.30.0 - 2023-12-01 ##### Breaking Changes :mega: diff --git a/Cesium3DTilesContent/src/SubtreeAvailability.cpp b/Cesium3DTilesContent/src/SubtreeAvailability.cpp index fe0f3aa47..b885021df 100644 --- a/Cesium3DTilesContent/src/SubtreeAvailability.cpp +++ b/Cesium3DTilesContent/src/SubtreeAvailability.cpp @@ -146,10 +146,10 @@ SubtreeAvailability::loadSubtree( const std::shared_ptr& pLogger, const std::string& subtreeUrl, const std::vector& requestHeaders) { - auto reader = std::make_shared(); - return reader->load(asyncSystem, pAssetAccessor, subtreeUrl, requestHeaders) + auto pReader = std::make_shared(); + return pReader->load(asyncSystem, pAssetAccessor, subtreeUrl, requestHeaders) .thenInMainThread( - [pLogger, subtreeUrl, subdivisionScheme, levelsInSubtree, reader]( + [pLogger, subtreeUrl, subdivisionScheme, levelsInSubtree, pReader]( ReadJsonResult&& subtree) -> std::optional { if (!subtree.value) { diff --git a/Cesium3DTilesContent/test/TestSubtreeAvailability.cpp b/Cesium3DTilesContent/test/TestSubtreeAvailability.cpp index fe8a599e0..7685398d8 100644 --- a/Cesium3DTilesContent/test/TestSubtreeAvailability.cpp +++ b/Cesium3DTilesContent/test/TestSubtreeAvailability.cpp @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include #include @@ -284,7 +286,7 @@ std::optional mockLoadSubtreeJson( std::make_shared(std::move(mapUrlToRequest)); // mock async system - auto pMockTaskProcessor = std::make_shared(); + auto pMockTaskProcessor = std::make_shared(); CesiumAsync::AsyncSystem asyncSystem{pMockTaskProcessor}; auto subtreeFuture = SubtreeAvailability::loadSubtree( @@ -296,8 +298,7 @@ std::optional mockLoadSubtreeJson( "test", {}); - asyncSystem.dispatchMainThreadTasks(); - return subtreeFuture.wait(); + return waitForFuture(asyncSystem, std::move(subtreeFuture)); } } // namespace diff --git a/Cesium3DTilesReader/include/Cesium3DTilesReader/SubtreeFileReader.h b/Cesium3DTilesReader/include/Cesium3DTilesReader/SubtreeFileReader.h index fbaa5eb94..dc9658019 100644 --- a/Cesium3DTilesReader/include/Cesium3DTilesReader/SubtreeFileReader.h +++ b/Cesium3DTilesReader/include/Cesium3DTilesReader/SubtreeFileReader.h @@ -40,6 +40,12 @@ class CESIUM3DTILESREADER_API SubtreeFileReader { /** * @brief Asynchronously loads a subtree from a URL. * + * Please note that the `SubtreeFileReader` instance must remain valid until + * the returned future resolves or rejects. Destroying it earlier will result + * in undefined behavior. One easy way to achieve this is to construct the + * reader with `std::make_shared` and capture the `std::shared_ptr` in the + * continuation lambda. + * * @param asyncSystem The AsyncSystem used to do asynchronous work. * @param pAssetAccessor The accessor used to retrieve the URL and any other * required resources. diff --git a/CesiumNativeTests/include/CesiumNativeTests/ThreadTaskProcessor.h b/CesiumNativeTests/include/CesiumNativeTests/ThreadTaskProcessor.h new file mode 100644 index 000000000..b97cb79ab --- /dev/null +++ b/CesiumNativeTests/include/CesiumNativeTests/ThreadTaskProcessor.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +#include + +namespace CesiumNativeTests { +class ThreadTaskProcessor : public CesiumAsync::ITaskProcessor { +public: + virtual void startTask(std::function f) override { + std::thread(f).detach(); + } +}; +} // namespace CesiumNativeTests From 2aaa97269ade3e80014c8b13031f2dd9f3d7a8e7 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Mon, 11 Dec 2023 17:22:59 +1100 Subject: [PATCH 420/421] Make api hostname into a complete URL. --- CesiumIonClient/src/Connection.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CesiumIonClient/src/Connection.cpp b/CesiumIonClient/src/Connection.cpp index c5f2d9f2e..df9005e69 100644 --- a/CesiumIonClient/src/Connection.cpp +++ b/CesiumIonClient/src/Connection.cpp @@ -409,7 +409,8 @@ CesiumIonClient::Connection::getApiUrl( if (parseJsonObject(pResponse, d) && d.IsObject()) { const auto itr = d.FindMember("apiHostname"); if (itr != d.MemberEnd() && itr->value.IsString()) { - return std::make_optional(itr->value.GetString()); + return std::make_optional( + std::string("https://") + itr->value.GetString() + "/"); } } } From 41f018647ae7e465b0fb359cdd6f22b9583e5c73 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Thu, 14 Dec 2023 11:20:49 +1100 Subject: [PATCH 421/421] Bump to v0.31.0. --- CHANGES.md | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index b1fe72027..5d7df4641 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,6 @@ # Change Log -### ? - ? +### v0.31.0 - 2023-12-14 ##### Additions :tada: diff --git a/package.json b/package.json index 9ee6ed488..0aa00c9c7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cesium-native", - "version": "0.30.0", + "version": "0.31.0", "description": "Cesium 3D Geospatial for C++", "main": "index.js", "directories": {

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 GU;hJp{hgNp literal 0 HcmV?d00001 diff --git a/CesiumGltfReader/test/data/DucksMeshopt/Duck-vp-3-vt-3-vn-3.glb b/CesiumGltfReader/test/data/DucksMeshopt/Duck-vp-3-vt-3-vn-3.glb new file mode 100644 index 0000000000000000000000000000000000000000..d7200c50f564cf5abe17e7cddad3ecff7ec9a885 GIT binary patch literal 28748 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 S{fhO+x3=uKvx``X;P~Hb&Dur) literal 0 HcmV?d00001 diff --git a/CesiumGltfReader/test/data/DucksMeshopt/Duck-vp-9-vt-9-vn-9.glb b/CesiumGltfReader/test/data/DucksMeshopt/Duck-vp-9-vt-9-vn-9.glb new file mode 100644 index 0000000000000000000000000000000000000000..7e9bc151010a1e334b38b034c8040c1318fb72f4 GIT binary patch literal 42956 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 Date: Sat, 22 Jul 2023 10:06:35 -0600 Subject: [PATCH 121/421] Add unit tests similar to Google 3D Tiles response headers --- CesiumAsync/test/TestCacheAssetAccessor.cpp | 139 +++++++++++--------- 1 file changed, 78 insertions(+), 61 deletions(-) diff --git a/CesiumAsync/test/TestCacheAssetAccessor.cpp b/CesiumAsync/test/TestCacheAssetAccessor.cpp index e45ea6b61..008952edb 100644 --- a/CesiumAsync/test/TestCacheAssetAccessor.cpp +++ b/CesiumAsync/test/TestCacheAssetAccessor.cpp @@ -86,81 +86,98 @@ class MockStoreCacheDatabase : public ICacheDatabase { } // namespace +bool runResponseCacheTest( + int statusCode, + const std::string& methodStr, + const HttpHeaders& httpHeaders) { + std::unique_ptr mockResponse = + std::make_unique( + static_cast(statusCode), + "app/json", + httpHeaders, + std::vector()); + + std::shared_ptr mockRequest = + std::make_shared( + methodStr, + "test.com", + HttpHeaders{}, + std::move(mockResponse)); + + std::unique_ptr ownedMockCacheDatabase = + std::make_unique(); + MockStoreCacheDatabase* mockCacheDatabase = ownedMockCacheDatabase.get(); + + std::shared_ptr cacheAssetAccessor = + std::make_shared( + spdlog::default_logger(), + std::make_unique(mockRequest), + std::move(ownedMockCacheDatabase)); + + std::shared_ptr mockTaskProcessor = + std::make_shared(); + + AsyncSystem asyncSystem(mockTaskProcessor); + cacheAssetAccessor + ->get(asyncSystem, "test.com", std::vector{}) + .wait(); + + return mockCacheDatabase->storeResponseCall; +} + TEST_CASE("Test the condition of caching the request") { SECTION("Cache request") { - SECTION("Request with GET method, has max-age, cacheable status code") { + SECTION("GET request, has max-age, cacheable status code") { int statusCode = GENERATE(200, 202, 203, 204, 205, 304); - std::unique_ptr mockResponse = - std::make_unique( - static_cast(statusCode), - "app/json", - HttpHeaders{ - {"Content-Type", "app/json"}, - {"Cache-Control", "must-revalidate, max-age=100"}}, - std::vector()); + HttpHeaders headers = { + {"Content-Type", "app/json"}, + {"Cache-Control", "must-revalidate, max-age=100"}}; - std::shared_ptr mockRequest = - std::make_shared( - "GET", - "test.com", - HttpHeaders{}, - std::move(mockResponse)); + bool responseCached = runResponseCacheTest(statusCode, "GET", headers); - std::unique_ptr ownedMockCacheDatabase = - std::make_unique(); - MockStoreCacheDatabase* mockCacheDatabase = ownedMockCacheDatabase.get(); - std::shared_ptr cacheAssetAccessor = - std::make_shared( - spdlog::default_logger(), - std::make_unique(mockRequest), - std::move(ownedMockCacheDatabase)); - std::shared_ptr mockTaskProcessor = - std::make_shared(); + REQUIRE(responseCached == true); + } - AsyncSystem asyncSystem(mockTaskProcessor); - cacheAssetAccessor - ->get(asyncSystem, "test.com", std::vector{}) - .wait(); - REQUIRE(mockCacheDatabase->storeResponseCall == true); + SECTION("GET Request, has Expires header, cacheable status code") { + int statusCode = GENERATE(200, 202, 203, 204, 205, 304); + + HttpHeaders headers = { + {"Content-Type", "app/json"}, + {"Expires", "Wed, 21 Oct 5020 07:28:00 GMT"}}; + + bool responseCached = runResponseCacheTest(statusCode, "GET", headers); + + REQUIRE(responseCached == true); } - SECTION( - "Request with GET method, has Expires header, cacheable status code") { + SECTION("GET Request, max-age 0, old Expires header") { int statusCode = GENERATE(200, 202, 203, 204, 205, 304); - std::unique_ptr mockResponse = - std::make_unique( - static_cast(statusCode), - "app/json", - HttpHeaders{ - {"Content-Type", "app/json"}, - {"Expires", "Wed, 21 Oct 5020 07:28:00 GMT"}}, - std::vector()); + // Similar to Google Photorealistic 3D Tiles, root request + HttpHeaders headers = { + {"Content-Type", "application/json"}, + {"Cache-Control", "private, max-age=0, must-revalidate"}, + {"ETag", "deadbeef"}, + {"Expires", "Mon, 01 Jan 1990 00:00:00 GMT"}}; - std::shared_ptr mockRequest = - std::make_shared( - "GET", - "test.com", - HttpHeaders{}, - std::move(mockResponse)); + bool responseCached = runResponseCacheTest(statusCode, "GET", headers); - std::unique_ptr ownedMockCacheDatabase = - std::make_unique(); - MockStoreCacheDatabase* mockCacheDatabase = ownedMockCacheDatabase.get(); - std::shared_ptr cacheAssetAccessor = - std::make_shared( - spdlog::default_logger(), - std::make_unique(mockRequest), - std::move(ownedMockCacheDatabase)); - std::shared_ptr mockTaskProcessor = - std::make_shared(); + REQUIRE(responseCached == true); + } - AsyncSystem asyncSystem(mockTaskProcessor); - cacheAssetAccessor - ->get(asyncSystem, "test.com", std::vector{}) - .wait(); - REQUIRE(mockCacheDatabase->storeResponseCall == true); + SECTION("GET Request, max-age 0, stale-while-revalidate") { + int statusCode = GENERATE(200, 202, 203, 204, 205, 304); + + // Similar to Google Photorealistic 3D Tiles, tile request + HttpHeaders headers = { + {"Content-Type", "application/json"}, + {"Cache-Control", "private, max-age=0, stale-while-revalidate=86400"}, + {"ETag", "deadbeef"}}; + + bool responseCached = runResponseCacheTest(statusCode, "GET", headers); + + REQUIRE(responseCached == true); } } From 643b2f1831764eafc0b1f72d2c745487e65af360 Mon Sep 17 00:00:00 2001 From: Joseph Kaile Date: Mon, 24 Jul 2023 11:34:16 -0400 Subject: [PATCH 122/421] fix testing failure and add some performance benefits --- .../include/CesiumGltfReader/GltfReader.h | 4 +- CesiumGltfReader/src/GltfReader.cpp | 4 +- ...izeMeshData.cpp => dequantizeMeshData.cpp} | 84 +++++++++++-------- ...uantizeMeshData.h => dequantizeMeshData.h} | 2 +- .../src/transformTextureCoords.cpp | 56 +++++++++---- 5 files changed, 93 insertions(+), 57 deletions(-) rename CesiumGltfReader/src/{unquantizeMeshData.cpp => dequantizeMeshData.cpp} (57%) rename CesiumGltfReader/src/{unquantizeMeshData.h => dequantizeMeshData.h} (69%) diff --git a/CesiumGltfReader/include/CesiumGltfReader/GltfReader.h b/CesiumGltfReader/include/CesiumGltfReader/GltfReader.h index feba7d80b..a82e54758 100644 --- a/CesiumGltfReader/include/CesiumGltfReader/GltfReader.h +++ b/CesiumGltfReader/include/CesiumGltfReader/GltfReader.h @@ -102,10 +102,10 @@ struct CESIUMGLTFREADER_API GltfReaderOptions { bool decodeDraco = true; /** - * @brief Whether the quantized and compressed mesh data are unquantized on + * @brief Whether the quantized and compressed mesh data are dequantized on * the CPU or not, according to the KHR_mesh_quantization extension */ - bool unquantizeMeshData = true; + bool dequantizeMeshData = true; /** * @brief Whether the texture coordinates of a texture are transformed or diff --git a/CesiumGltfReader/src/GltfReader.cpp b/CesiumGltfReader/src/GltfReader.cpp index 94a757b84..9bbf95042 100644 --- a/CesiumGltfReader/src/GltfReader.cpp +++ b/CesiumGltfReader/src/GltfReader.cpp @@ -4,9 +4,9 @@ #include "decodeDataUrls.h" #include "decodeDraco.h" #include "decodeMeshOpt.h" +#include "dequantizeMeshData.h" #include "registerExtensions.h" #include "transformTextureCoords.h" -#include "unquantizeMeshData.h" #include #include @@ -311,7 +311,7 @@ void postprocess( decodeMeshOpt(model, readGltf); } - if (options.unquantizeMeshData && + if (options.dequantizeMeshData && std::find( model.extensionsUsed.begin(), model.extensionsUsed.end(), diff --git a/CesiumGltfReader/src/unquantizeMeshData.cpp b/CesiumGltfReader/src/dequantizeMeshData.cpp similarity index 57% rename from CesiumGltfReader/src/unquantizeMeshData.cpp rename to CesiumGltfReader/src/dequantizeMeshData.cpp index 03b60979c..601244e79 100644 --- a/CesiumGltfReader/src/unquantizeMeshData.cpp +++ b/CesiumGltfReader/src/dequantizeMeshData.cpp @@ -1,4 +1,4 @@ -#include "unquantizeMeshData.h" +#include "dequantizeMeshData.h" #include "CesiumGltfReader/GltfReader.h" @@ -11,8 +11,6 @@ using namespace CesiumGltf; namespace CesiumGltfReader { namespace { -template -using xVec = AccessorView>; template float intToFloat(T t) { return intToFloat(t); } @@ -29,28 +27,20 @@ template <> float intToFloat(std::int16_t c) { template <> float intToFloat(std::uint16_t c) { return c / 65535.0f; } template -void unquantizeFloat(float* fPtr, const xVec& quantizedView) { - for (int i = 0; i < quantizedView.size(); i++) { - const auto& q = quantizedView[i]; +void dequantizeFloat( + float* fPtr, + int64_t count, + std::byte* bPtr, + int64_t stride) { + for (int i = 0; i < count; i++, bPtr += stride) { for (unsigned int j = 0; j < N; j++) { - *fPtr++ = intToFloat(q[j]); + *fPtr++ = intToFloat(reinterpret_cast(bPtr)[j]); } } } template -void unquantizeAccessor(Model& model, Accessor& accessor) { - xVec quantizedView(model, accessor); - if (quantizedView.status() != AccessorViewStatus::Valid) { - return; - } - if (quantizedView.size() != accessor.count) { - return; - } - Buffer& buffer = model.buffers.emplace_back(); - int64_t byteLength = accessor.count * N * sizeof(float); - buffer.byteLength = byteLength; - buffer.cesium.data.resize(byteLength); +void dequantizeAccessor(Model& model, Accessor& accessor) { accessor.componentType = AccessorSpec::ComponentType::FLOAT; accessor.byteOffset = 0; @@ -63,50 +53,76 @@ void unquantizeAccessor(Model& model, Accessor& accessor) { BufferView* pBufferView = Model::getSafe(&model.bufferViews, accessor.bufferView); - pBufferView->buffer = static_cast(model.buffers.size() - 1); + + if (!pBufferView) { + return; + } + + Buffer* pBuffer = Model::getSafe(&model.buffers, pBufferView->buffer); + if (!pBuffer) { + return; + } + + int64_t byteStride; + if (pBufferView->byteStride) { + byteStride = *pBufferView->byteStride; + } else { + byteStride = accessor.computeByteStride(model); + } + + int64_t byteLength = accessor.count * N * sizeof(float); + std::vector buffer; + buffer.resize(byteLength); + + dequantizeFloat( + reinterpret_cast(buffer.data()), + accessor.count, + pBuffer->cesium.data.data() + pBufferView->byteOffset + + accessor.byteOffset, + byteStride); + pBufferView->byteOffset = 0; pBufferView->byteStride = N * sizeof(float); pBufferView->byteLength = byteLength; - unquantizeFloat( - reinterpret_cast(buffer.cesium.data.data()), - quantizedView); + pBuffer->cesium.data = std::move(buffer); + pBuffer->byteLength = byteLength; } -template void unquantizeAccessor(Model& model, Accessor& accessor) { +template void dequantizeAccessor(Model& model, Accessor& accessor) { switch (accessor.componentType) { case Accessor::ComponentType::BYTE: - unquantizeAccessor(model, accessor); + dequantizeAccessor(model, accessor); break; case Accessor::ComponentType::UNSIGNED_BYTE: - unquantizeAccessor(model, accessor); + dequantizeAccessor(model, accessor); break; case Accessor::ComponentType::SHORT: - unquantizeAccessor(model, accessor); + dequantizeAccessor(model, accessor); break; case Accessor::ComponentType::UNSIGNED_SHORT: - unquantizeAccessor(model, accessor); + dequantizeAccessor(model, accessor); break; } } -void unquantizeAccessor(Model& model, Accessor& accessor) { +void dequantizeAccessor(Model& model, Accessor& accessor) { int8_t numberOfComponents = accessor.computeNumberOfComponents(); switch (numberOfComponents) { case 2: - unquantizeAccessor<2>(model, accessor); + dequantizeAccessor<2>(model, accessor); break; case 3: - unquantizeAccessor<3>(model, accessor); + dequantizeAccessor<3>(model, accessor); break; case 4: - unquantizeAccessor<4>(model, accessor); + dequantizeAccessor<4>(model, accessor); break; } } } // namespace -void unquantizeMeshData(Model& model) { +void dequantizeMeshData(Model& model) { for (Mesh& mesh : model.meshes) { for (MeshPrimitive& primitive : mesh.primitives) { for (std::pair attribute : @@ -114,7 +130,7 @@ void unquantizeMeshData(Model& model) { Accessor* pAccessor = Model::getSafe(&model.accessors, attribute.second); if (pAccessor) { - unquantizeAccessor(model, *pAccessor); + dequantizeAccessor(model, *pAccessor); } } } diff --git a/CesiumGltfReader/src/unquantizeMeshData.h b/CesiumGltfReader/src/dequantizeMeshData.h similarity index 69% rename from CesiumGltfReader/src/unquantizeMeshData.h rename to CesiumGltfReader/src/dequantizeMeshData.h index 14a20f855..c36f7b23f 100644 --- a/CesiumGltfReader/src/unquantizeMeshData.h +++ b/CesiumGltfReader/src/dequantizeMeshData.h @@ -6,5 +6,5 @@ struct Model; namespace CesiumGltfReader { -void unquantizeMeshData(CesiumGltf::Model& model); +void dequantizeMeshData(CesiumGltf::Model& model); } // namespace CesiumGltfReader diff --git a/CesiumGltfReader/src/transformTextureCoords.cpp b/CesiumGltfReader/src/transformTextureCoords.cpp index a56bc94a9..880824ccd 100644 --- a/CesiumGltfReader/src/transformTextureCoords.cpp +++ b/CesiumGltfReader/src/transformTextureCoords.cpp @@ -12,27 +12,47 @@ void transformBufferView( Buffer& buffer, ExtensionKhrTextureTransform& textureTransform) { - glm::vec2 Offset(textureTransform.offset[0], textureTransform.offset[1]); - glm::vec2 Scale(textureTransform.scale[0], textureTransform.scale[1]); + if (textureTransform.offset.size() < 2 || textureTransform.scale.size() < 2) { + return; + } + float Rotation = static_cast(textureTransform.rotation); - glm::mat3 translation = glm::mat3(1, 0, 0, 0, 1, 0, Offset.x, Offset.y, 1); - glm::mat3 rotation = glm::mat3( - cos(Rotation), - sin(Rotation), - 0, - -sin(Rotation), - cos(Rotation), - 0, - 0, - 0, - 1); - glm::mat3 scale = glm::mat3(Scale.x, 0, 0, 0, Scale.y, 0, 0, 0, 1); - glm::mat3 matrix = translation * rotation * scale; - glm::vec2* uvs = reinterpret_cast(buffer.cesium.data.data()); + if (Rotation == 0.0f) { + float OffsetX = static_cast(textureTransform.offset[0]); + float OffsetY = static_cast(textureTransform.offset[1]); + float ScaleX = static_cast(textureTransform.scale[0]); + float ScaleY = static_cast(textureTransform.scale[1]); - for (int i = 0; i < accessorView.size(); i++) { - *uvs++ = glm::vec2((matrix * glm::vec3(accessorView[i], 1))); + glm::vec2* uvs = reinterpret_cast(buffer.cesium.data.data()); + for (int i = 0; i < accessorView.size(); i++) { + glm::vec2 uv = accessorView[i]; + uv.x = uv.x * ScaleX + OffsetX; + uv.y = uv.y * ScaleY + OffsetY; + *uvs++ = uv; + } + } else { + glm::vec2 Offset(textureTransform.offset[0], textureTransform.offset[1]); + glm::vec2 Scale(textureTransform.scale[0], textureTransform.scale[1]); + glm::mat3 translation = glm::mat3(1, 0, 0, 0, 1, 0, Offset.x, Offset.y, 1); + glm::mat3 rotation = glm::mat3( + cos(Rotation), + sin(Rotation), + 0, + -sin(Rotation), + cos(Rotation), + 0, + 0, + 0, + 1); + glm::mat3 scale = glm::mat3(Scale.x, 0, 0, 0, Scale.y, 0, 0, 0, 1); + glm::mat3 matrix = translation * rotation * scale; + + glm::vec2* uvs = reinterpret_cast(buffer.cesium.data.data()); + + for (int i = 0; i < accessorView.size(); i++) { + *uvs++ = glm::vec2((matrix * glm::vec3(accessorView[i], 1))); + } } } } // namespace From 053846489b0ee06b039f8d83723fa8c1d6e9062f Mon Sep 17 00:00:00 2001 From: Joseph Kaile Date: Mon, 24 Jul 2023 11:52:37 -0400 Subject: [PATCH 123/421] fix rename error --- CesiumGltfReader/src/GltfReader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CesiumGltfReader/src/GltfReader.cpp b/CesiumGltfReader/src/GltfReader.cpp index 9bbf95042..4d85839e5 100644 --- a/CesiumGltfReader/src/GltfReader.cpp +++ b/CesiumGltfReader/src/GltfReader.cpp @@ -316,7 +316,7 @@ void postprocess( model.extensionsUsed.begin(), model.extensionsUsed.end(), "KHR_mesh_quantization") != model.extensionsUsed.end()) { - unquantizeMeshData(model); + dequantizeMeshData(model); } if (options.applyTextureTransform && From 3664ad888495f00fca427224e42c99d2b4cf9a12 Mon Sep 17 00:00:00 2001 From: Brian L <130494071+csciguy8@users.noreply.github.com> Date: Mon, 24 Jul 2023 10:42:50 -0600 Subject: [PATCH 124/421] Remove logs when registering magic headers of file extensions --- Cesium3DTilesSelection/src/GltfConverters.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/Cesium3DTilesSelection/src/GltfConverters.cpp b/Cesium3DTilesSelection/src/GltfConverters.cpp index 1119cddda..a1bf24b5a 100644 --- a/Cesium3DTilesSelection/src/GltfConverters.cpp +++ b/Cesium3DTilesSelection/src/GltfConverters.cpp @@ -12,14 +12,12 @@ std::unordered_map void GltfConverters::registerMagic( const std::string& magic, ConverterFunction converter) { - SPDLOG_INFO("Registering magic header {}", magic); _loadersByMagic[magic] = converter; } void GltfConverters::registerFileExtension( const std::string& fileExtension, ConverterFunction converter) { - SPDLOG_INFO("Registering file extension {}", fileExtension); std::string lowerCaseFileExtension = toLowerCase(fileExtension); _loadersByFileExtension[lowerCaseFileExtension] = converter; From e0642b6f6b5ba234f4d7090463106c0737728dde Mon Sep 17 00:00:00 2001 From: Joseph Kaile Date: Mon, 24 Jul 2023 13:36:29 -0400 Subject: [PATCH 125/421] fix linux compile errors --- CesiumGltfReader/src/decodeMeshOpt.cpp | 7 +++++++ CesiumGltfReader/src/dequantizeMeshData.cpp | 6 ++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/CesiumGltfReader/src/decodeMeshOpt.cpp b/CesiumGltfReader/src/decodeMeshOpt.cpp index 4a6c4ba09..85f967185 100644 --- a/CesiumGltfReader/src/decodeMeshOpt.cpp +++ b/CesiumGltfReader/src/decodeMeshOpt.cpp @@ -5,7 +5,14 @@ #include #include +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wsign-conversion" +#endif #include +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif using namespace CesiumGltf; diff --git a/CesiumGltfReader/src/dequantizeMeshData.cpp b/CesiumGltfReader/src/dequantizeMeshData.cpp index 601244e79..32f4ca7f0 100644 --- a/CesiumGltfReader/src/dequantizeMeshData.cpp +++ b/CesiumGltfReader/src/dequantizeMeshData.cpp @@ -4,8 +4,6 @@ #include -#include - using namespace CesiumGltf; namespace CesiumGltfReader { @@ -70,9 +68,9 @@ void dequantizeAccessor(Model& model, Accessor& accessor) { byteStride = accessor.computeByteStride(model); } - int64_t byteLength = accessor.count * N * sizeof(float); + int64_t byteLength = accessor.count * static_cast(N * sizeof(float)); std::vector buffer; - buffer.resize(byteLength); + buffer.resize(static_cast(byteLength)); dequantizeFloat( reinterpret_cast(buffer.data()), From 7965f5d50bc444dab5e150cc7f407e6e706ddaaa Mon Sep 17 00:00:00 2001 From: Joseph Kaile Date: Mon, 24 Jul 2023 14:42:06 -0400 Subject: [PATCH 126/421] add more error checking --- .../include/CesiumGltfReader/GltfReader.h | 6 ++++ CesiumGltfReader/src/GltfReader.cpp | 33 ++++++++++--------- CesiumGltfReader/src/decodeMeshOpt.cpp | 16 ++++++++- CesiumGltfReader/src/dequantizeMeshData.cpp | 29 +++++++++++----- 4 files changed, 58 insertions(+), 26 deletions(-) diff --git a/CesiumGltfReader/include/CesiumGltfReader/GltfReader.h b/CesiumGltfReader/include/CesiumGltfReader/GltfReader.h index a82e54758..c44d63000 100644 --- a/CesiumGltfReader/include/CesiumGltfReader/GltfReader.h +++ b/CesiumGltfReader/include/CesiumGltfReader/GltfReader.h @@ -101,6 +101,12 @@ struct CESIUMGLTFREADER_API GltfReaderOptions { */ bool decodeDraco = true; + /** + * @brief Whether the mesh data are decompressed on the CPU or not, according + * to the EXT_meshopt_compression extension + */ + bool decompressMeshData = true; + /** * @brief Whether the quantized and compressed mesh data are dequantized on * the CPU or not, according to the KHR_mesh_quantization extension diff --git a/CesiumGltfReader/src/GltfReader.cpp b/CesiumGltfReader/src/GltfReader.cpp index 4d85839e5..bd634a1f6 100644 --- a/CesiumGltfReader/src/GltfReader.cpp +++ b/CesiumGltfReader/src/GltfReader.cpp @@ -304,27 +304,28 @@ void postprocess( decodeDraco(readGltf); } - if (std::find( + if (options.decompressMeshData && + std::find( model.extensionsUsed.begin(), model.extensionsUsed.end(), "EXT_meshopt_compression") != model.extensionsUsed.end()) { decodeMeshOpt(model, readGltf); - } - - if (options.dequantizeMeshData && - std::find( - model.extensionsUsed.begin(), - model.extensionsUsed.end(), - "KHR_mesh_quantization") != model.extensionsUsed.end()) { - dequantizeMeshData(model); - } - if (options.applyTextureTransform && - std::find( - model.extensionsUsed.begin(), - model.extensionsUsed.end(), - "KHR_texture_transform") != model.extensionsUsed.end()) { - transformTexture(model); + if (options.dequantizeMeshData && + std::find( + model.extensionsUsed.begin(), + model.extensionsUsed.end(), + "KHR_mesh_quantization") != model.extensionsUsed.end()) { + dequantizeMeshData(model); + + if (options.applyTextureTransform && + std::find( + model.extensionsUsed.begin(), + model.extensionsUsed.end(), + "KHR_texture_transform") != model.extensionsUsed.end()) { + transformTexture(model); + } + } } } diff --git a/CesiumGltfReader/src/decodeMeshOpt.cpp b/CesiumGltfReader/src/decodeMeshOpt.cpp index 85f967185..46f857cbf 100644 --- a/CesiumGltfReader/src/decodeMeshOpt.cpp +++ b/CesiumGltfReader/src/decodeMeshOpt.cpp @@ -90,6 +90,15 @@ void decodeAccessor( if (!pMeshOpt) { return; } + + // check the filter value + if (pMeshOpt->filter != + ExtensionBufferViewExtMeshoptCompression::Filter::NONE) { + // add a warning message + readGltf.warnings.emplace_back("The ext_meshopt_compression extension has " + "a filter that is not supported."); + return; + } Buffer* pBuffer = model.getSafe(&model.buffers, pMeshOpt->buffer); if (!pBuffer) { readGltf.warnings.emplace_back( @@ -108,8 +117,13 @@ void decodeAccessor( Buffer* pDest = &model.buffers.emplace_back(); pBuffer = model.getSafe(&model.buffers, pMeshOpt->buffer); int64_t byteLength = pMeshOpt->byteStride * pMeshOpt->count; - pDest->byteLength = byteLength; + if (byteLength < 0) { + readGltf.warnings.emplace_back( + "The ext_meshopt_compression extension has a negative byte length."); + return; + } pDest->cesium.data.resize(static_cast(byteLength)); + pDest->byteLength = byteLength; pBufferView->byteLength = byteLength; pBufferView->byteStride = pMeshOpt->byteStride; pBufferView->byteOffset = 0; diff --git a/CesiumGltfReader/src/dequantizeMeshData.cpp b/CesiumGltfReader/src/dequantizeMeshData.cpp index 32f4ca7f0..7c1431b3d 100644 --- a/CesiumGltfReader/src/dequantizeMeshData.cpp +++ b/CesiumGltfReader/src/dequantizeMeshData.cpp @@ -40,15 +40,6 @@ void dequantizeFloat( template void dequantizeAccessor(Model& model, Accessor& accessor) { - accessor.componentType = AccessorSpec::ComponentType::FLOAT; - accessor.byteOffset = 0; - for (double& d : accessor.min) { - d = intToFloat(static_cast(d)); - } - for (double& d : accessor.max) { - d = intToFloat(static_cast(d)); - } - BufferView* pBufferView = Model::getSafe(&model.bufferViews, accessor.bufferView); @@ -68,7 +59,18 @@ void dequantizeAccessor(Model& model, Accessor& accessor) { byteStride = accessor.computeByteStride(model); } + if (pBufferView->byteOffset + accessor.byteOffset + + static_cast(accessor.count * byteStride) > + pBuffer->cesium.data.size() || + sizeof(T) * N > byteStride) { + return; + } + int64_t byteLength = accessor.count * static_cast(N * sizeof(float)); + if (byteLength < 0) { + return; + } + std::vector buffer; buffer.resize(static_cast(byteLength)); @@ -79,6 +81,15 @@ void dequantizeAccessor(Model& model, Accessor& accessor) { accessor.byteOffset, byteStride); + accessor.componentType = AccessorSpec::ComponentType::FLOAT; + accessor.byteOffset = 0; + for (double& d : accessor.min) { + d = intToFloat(static_cast(d)); + } + for (double& d : accessor.max) { + d = intToFloat(static_cast(d)); + } + pBufferView->byteOffset = 0; pBufferView->byteStride = N * sizeof(float); pBufferView->byteLength = byteLength; From 87980fdb042d50ff1c1edc33fd6c68893d008c24 Mon Sep 17 00:00:00 2001 From: Joseph Kaile Date: Mon, 24 Jul 2023 17:37:56 -0400 Subject: [PATCH 127/421] fix compile warnings --- CesiumGltfReader/src/decodeMeshOpt.cpp | 4 ++-- CesiumGltfReader/src/dequantizeMeshData.cpp | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CesiumGltfReader/src/decodeMeshOpt.cpp b/CesiumGltfReader/src/decodeMeshOpt.cpp index 46f857cbf..b1897035f 100644 --- a/CesiumGltfReader/src/decodeMeshOpt.cpp +++ b/CesiumGltfReader/src/decodeMeshOpt.cpp @@ -107,8 +107,8 @@ void decodeAccessor( return; } if (pMeshOpt->byteOffset < 0 || pMeshOpt->byteLength < 0 || - pMeshOpt->byteOffset + pMeshOpt->byteLength > - static_cast(pBuffer->cesium.data.size())) { + static_cast(pMeshOpt->byteOffset + pMeshOpt->byteLength) > + pBuffer->cesium.data.size()) { readGltf.warnings.emplace_back( "The ext_meshopt_compression extension has a bufferView that " "extends beyond its buffer."); diff --git a/CesiumGltfReader/src/dequantizeMeshData.cpp b/CesiumGltfReader/src/dequantizeMeshData.cpp index 7c1431b3d..7b035b54b 100644 --- a/CesiumGltfReader/src/dequantizeMeshData.cpp +++ b/CesiumGltfReader/src/dequantizeMeshData.cpp @@ -59,10 +59,10 @@ void dequantizeAccessor(Model& model, Accessor& accessor) { byteStride = accessor.computeByteStride(model); } - if (pBufferView->byteOffset + accessor.byteOffset + - static_cast(accessor.count * byteStride) > - pBuffer->cesium.data.size() || - sizeof(T) * N > byteStride) { + if (static_cast( + pBufferView->byteOffset + accessor.byteOffset + + accessor.count * byteStride) > pBuffer->cesium.data.size() || + static_cast(sizeof(T) * N) > byteStride) { return; } From 018a7380b8d970ec9f78bd753afb95c81a1deb45 Mon Sep 17 00:00:00 2001 From: Brian L <130494071+csciguy8@users.noreply.github.com> Date: Tue, 25 Jul 2023 11:11:23 -0600 Subject: [PATCH 128/421] Refactor using std::optional. Fixup unit tests --- CesiumAsync/src/CachingAssetAccessor.cpp | 4 +- CesiumAsync/src/ResponseCacheControl.cpp | 43 ++++++++----------- CesiumAsync/src/ResponseCacheControl.h | 32 ++++++-------- CesiumAsync/test/TestCacheAssetAccessor.cpp | 6 ++- CesiumAsync/test/TestDiskCache.cpp | 4 +- CesiumAsync/test/TestResponseCacheControl.cpp | 4 ++ 6 files changed, 43 insertions(+), 50 deletions(-) diff --git a/CesiumAsync/src/CachingAssetAccessor.cpp b/CesiumAsync/src/CachingAssetAccessor.cpp index 944ee6cfd..594ea6d95 100644 --- a/CesiumAsync/src/CachingAssetAccessor.cpp +++ b/CesiumAsync/src/CachingAssetAccessor.cpp @@ -354,7 +354,9 @@ std::time_t calculateExpiryTime( (cacheControl->maxAgeExists() || cacheControl->sharedMaxAgeExists()); if (preferCacheControl) { - return std::time(nullptr) + cacheControl->maxAgeValue(); + int maxAgeValue = + cacheControl->maxAgeExists() ? cacheControl->maxAgeValue() : 0; + return std::time(nullptr) + maxAgeValue; } else { const IAssetResponse* pResponse = request.response(); const HttpHeaders& responseHeaders = pResponse->headers(); diff --git a/CesiumAsync/src/ResponseCacheControl.cpp b/CesiumAsync/src/ResponseCacheControl.cpp index 84084a02c..50d1dca42 100644 --- a/CesiumAsync/src/ResponseCacheControl.cpp +++ b/CesiumAsync/src/ResponseCacheControl.cpp @@ -14,12 +14,9 @@ ResponseCacheControl::ResponseCacheControl( bool accessControlPublic, bool accessControlPrivate, bool proxyRevalidate, - bool maxAgeExists, - int maxAgeValue, - bool sharedMaxAgeExists, - int sharedMaxAgeValue, - bool staleWhileRevalidateExists, - int staleWhileRevalidateValue) + std::optional maxAge, + std::optional sharedMaxAge, + std::optional staleWhileRevalidate) : _mustRevalidate{mustRevalidate}, _noCache{noCache}, _noStore{noStore}, @@ -27,12 +24,9 @@ ResponseCacheControl::ResponseCacheControl( _accessControlPublic{accessControlPublic}, _accessControlPrivate{accessControlPrivate}, _proxyRevalidate{proxyRevalidate}, - _maxAgeExists{maxAgeExists}, - _maxAgeValue{maxAgeValue}, - _sharedMaxAgeExists{sharedMaxAgeExists}, - _sharedMaxAgeValue{sharedMaxAgeValue}, - _staleWhileRevalidateExists{staleWhileRevalidateExists}, - _staleWhileRevalidateValue{staleWhileRevalidateValue} {} + _maxAge{maxAge}, + _sharedMaxAge{sharedMaxAge}, + _staleWhileRevalidate{staleWhileRevalidate} {} /*static*/ std::optional ResponseCacheControl::parseFromResponseHeaders(const HttpHeaders& headers) { @@ -84,18 +78,20 @@ ResponseCacheControl::parseFromResponseHeaders(const HttpHeaders& headers) { std::map::const_iterator mapIter; + std::optional maxAge; mapIter = parameterizedDirectives.find("max-age"); - bool maxAgeExists = mapIter != parameterizedDirectives.end(); - int maxAgeValue = maxAgeExists ? std::stoi(mapIter->second) : 0; + if (mapIter != parameterizedDirectives.end()) + maxAge = std::stoi(mapIter->second); + std::optional sharedMaxAge; mapIter = parameterizedDirectives.find("s-maxage"); - bool sharedMaxAgeExists = mapIter != parameterizedDirectives.end(); - int sharedMaxAgeValue = sharedMaxAgeExists ? std::stoi(mapIter->second) : 0; + if (mapIter != parameterizedDirectives.end()) + sharedMaxAge = std::stoi(mapIter->second); + std::optional staleWhileRevalidate; mapIter = parameterizedDirectives.find("stale-while-revalidate"); - bool staleWhileRevalidateExists = mapIter != parameterizedDirectives.end(); - int staleWhileRevalidateValue = - staleWhileRevalidateExists ? std::stoi(mapIter->second) : 0; + if (mapIter != parameterizedDirectives.end()) + staleWhileRevalidate = std::stoi(mapIter->second); return ResponseCacheControl( mustRevalidate, @@ -105,12 +101,9 @@ ResponseCacheControl::parseFromResponseHeaders(const HttpHeaders& headers) { accessControlPublic, accessControlPrivate, proxyRevalidate, - maxAgeExists, - maxAgeValue, - sharedMaxAgeExists, - sharedMaxAgeValue, - staleWhileRevalidateExists, - staleWhileRevalidateValue); + maxAge, + sharedMaxAge, + staleWhileRevalidate); } std::string trimSpace(const std::string& str) { diff --git a/CesiumAsync/src/ResponseCacheControl.h b/CesiumAsync/src/ResponseCacheControl.h index 474143fc5..bad12963d 100644 --- a/CesiumAsync/src/ResponseCacheControl.h +++ b/CesiumAsync/src/ResponseCacheControl.h @@ -39,12 +39,9 @@ class ResponseCacheControl { bool accessControlPublic, bool accessControlPrivate, bool proxyRevalidate, - bool maxAgeExists, - int maxAgeValue, - bool sharedMaxAgeExists, - int sharedMaxAgeValue, - bool staleWhileRevalidateExists, - int staleWhileRevalidateValue); + std::optional maxAge, + std::optional sharedMaxAge, + std::optional staleWhileRevalidate); /** * @brief Must-Revalidate directive that appears in the Cache-Control header. @@ -89,31 +86,31 @@ class ResponseCacheControl { * @brief Existence of Max-Age directive that appears in the Cache-Control * header. */ - bool maxAgeExists() const noexcept { return _maxAgeExists; } + bool maxAgeExists() const noexcept { return _maxAge.has_value(); } /** * @brief Value of Max-Age directive that appears in the Cache-Control header. */ - int maxAgeValue() const noexcept { return _maxAgeValue; } + int maxAgeValue() const noexcept { return *_maxAge; } /** * @brief Existence of S-Maxage directive that appears in the Cache-Control * header. */ - bool sharedMaxAgeExists() const noexcept { return _sharedMaxAgeExists; } + bool sharedMaxAgeExists() const noexcept { return _sharedMaxAge.has_value(); } /** * @brief Value of S-Maxage directive that appears in the Cache-Control * header. */ - int sharedMaxAgeValue() const noexcept { return _sharedMaxAgeValue; } + int sharedMaxAgeValue() const noexcept { return *_sharedMaxAge; } /** * @brief Existence of Stale-While-Revalidate directive that appears in the * Cache-Control header. */ bool staleWhileRevalidateExists() const noexcept { - return _staleWhileRevalidateExists; + return _staleWhileRevalidate.has_value(); } /** @@ -121,7 +118,7 @@ class ResponseCacheControl { * Cache-Control header. */ int staleWhileRevalidateValue() const noexcept { - return _staleWhileRevalidateValue; + return *_staleWhileRevalidate; } /** @@ -139,13 +136,8 @@ class ResponseCacheControl { bool _accessControlPrivate; bool _proxyRevalidate; - bool _maxAgeExists; - int _maxAgeValue; - - bool _sharedMaxAgeExists; - int _sharedMaxAgeValue; - - bool _staleWhileRevalidateExists; - int _staleWhileRevalidateValue; + std::optional _maxAge; + std::optional _sharedMaxAge; + std::optional _staleWhileRevalidate; }; } // namespace CesiumAsync diff --git a/CesiumAsync/test/TestCacheAssetAccessor.cpp b/CesiumAsync/test/TestCacheAssetAccessor.cpp index 008952edb..42e331c52 100644 --- a/CesiumAsync/test/TestCacheAssetAccessor.cpp +++ b/CesiumAsync/test/TestCacheAssetAccessor.cpp @@ -610,8 +610,9 @@ TEST_CASE("Test serving cache item") { REQUIRE(cacheControl->accessControlPublic() == false); REQUIRE(cacheControl->accessControlPrivate() == true); REQUIRE(cacheControl->proxyRevalidate() == false); + REQUIRE(cacheControl->maxAgeExists() == true); REQUIRE(cacheControl->maxAgeValue() == 100); - REQUIRE(cacheControl->sharedMaxAgeValue() == 0); + REQUIRE(cacheControl->sharedMaxAgeExists() == false); }) .wait(); } @@ -704,8 +705,9 @@ TEST_CASE("Test serving cache item") { REQUIRE(cacheControl->accessControlPublic() == false); REQUIRE(cacheControl->accessControlPrivate() == true); REQUIRE(cacheControl->proxyRevalidate() == false); + REQUIRE(cacheControl->maxAgeExists() == true); REQUIRE(cacheControl->maxAgeValue() == 300); - REQUIRE(cacheControl->sharedMaxAgeValue() == 0); + REQUIRE(cacheControl->sharedMaxAgeExists() == false); }) .wait(); } diff --git a/CesiumAsync/test/TestDiskCache.cpp b/CesiumAsync/test/TestDiskCache.cpp index 4481ba857..d42661970 100644 --- a/CesiumAsync/test/TestDiskCache.cpp +++ b/CesiumAsync/test/TestDiskCache.cpp @@ -166,8 +166,8 @@ TEST_CASE("Test disk cache with Sqlite") { REQUIRE(cacheControl->accessControlPublic() == true); REQUIRE(cacheControl->accessControlPrivate() == false); REQUIRE(cacheControl->proxyRevalidate() == true); - REQUIRE(cacheControl->maxAgeValue() == 0); - REQUIRE(cacheControl->sharedMaxAgeValue() == 0); + REQUIRE(cacheControl->maxAgeExists() == false); + REQUIRE(cacheControl->sharedMaxAgeExists() == false); } } diff --git a/CesiumAsync/test/TestResponseCacheControl.cpp b/CesiumAsync/test/TestResponseCacheControl.cpp index 75bb240b4..7bd0e9d96 100644 --- a/CesiumAsync/test/TestResponseCacheControl.cpp +++ b/CesiumAsync/test/TestResponseCacheControl.cpp @@ -32,7 +32,9 @@ TEST_CASE("Test parsing cache-control header") { REQUIRE(cacheControl->accessControlPublic()); REQUIRE(cacheControl->accessControlPrivate()); REQUIRE(cacheControl->proxyRevalidate()); + REQUIRE(cacheControl->maxAgeExists() == true); REQUIRE(cacheControl->maxAgeValue() == 1000); + REQUIRE(cacheControl->sharedMaxAgeExists() == true); REQUIRE(cacheControl->sharedMaxAgeValue() == 10); } @@ -53,7 +55,9 @@ TEST_CASE("Test parsing cache-control header") { REQUIRE(cacheControl->accessControlPublic()); REQUIRE(cacheControl->accessControlPrivate()); REQUIRE(cacheControl->proxyRevalidate() == false); + REQUIRE(cacheControl->maxAgeExists() == true); REQUIRE(cacheControl->maxAgeValue() == 1000); + REQUIRE(cacheControl->sharedMaxAgeExists() == true); REQUIRE(cacheControl->sharedMaxAgeValue() == 10); } } From a72438bec0464e49f277852ecc9d151f89d68413 Mon Sep 17 00:00:00 2001 From: Brian L <130494071+csciguy8@users.noreply.github.com> Date: Tue, 25 Jul 2023 11:33:45 -0600 Subject: [PATCH 129/421] Remove redundant code from shouldRevalidateCache() --- CesiumAsync/src/CachingAssetAccessor.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/CesiumAsync/src/CachingAssetAccessor.cpp b/CesiumAsync/src/CachingAssetAccessor.cpp index 594ea6d95..d9cbbcd5f 100644 --- a/CesiumAsync/src/CachingAssetAccessor.cpp +++ b/CesiumAsync/src/CachingAssetAccessor.cpp @@ -272,11 +272,9 @@ bool shouldRevalidateCache(const CacheItem& cacheItem) { if (cacheControl && cacheControl->noCache()) return true; - bool cacheIsStale = isCacheStale(cacheItem); - if (cacheControl && cacheIsStale && cacheControl->mustRevalidate()) - return true; - - return cacheIsStale; + // Always revalidate if cache is stale. We always assume online scenarios. + // A must-revalidate directive doesn't change this logic. + return isCacheStale(cacheItem); } bool isCacheStale(const CacheItem& cacheItem) noexcept { From 53689f1123782758be32003fe8139c0b8df1cc38 Mon Sep 17 00:00:00 2001 From: Brian L <130494071+csciguy8@users.noreply.github.com> Date: Tue, 25 Jul 2023 12:21:13 -0600 Subject: [PATCH 130/421] Modify shouldCacheRequest for additional no-cache cases. Add more unit tests --- CesiumAsync/src/CachingAssetAccessor.cpp | 46 +++++++++++++-------- CesiumAsync/test/TestCacheAssetAccessor.cpp | 39 ++++++++++++++--- 2 files changed, 62 insertions(+), 23 deletions(-) diff --git a/CesiumAsync/src/CachingAssetAccessor.cpp b/CesiumAsync/src/CachingAssetAccessor.cpp index d9cbbcd5f..d233e1d1b 100644 --- a/CesiumAsync/src/CachingAssetAccessor.cpp +++ b/CesiumAsync/src/CachingAssetAccessor.cpp @@ -309,29 +309,36 @@ bool shouldCacheRequest( return false; } - // Check cache control header if it exists - bool ignoreExpiresHeader = false; - if (cacheControl) { - if (cacheControl->noStore()) - return false; - - // If there is a Cache-Control header with the max-age or s-maxage directive - // in the response, the Expires header is ignored - ignoreExpiresHeader = - cacheControl->maxAgeExists() || cacheControl->sharedMaxAgeExists(); - } + const HttpHeaders& headers = pResponse->headers(); + HttpHeaders::const_iterator expiresHeader = headers.find("Expires"); + bool expiresExists = expiresHeader != headers.end(); - // Check older Expires header - if (!ignoreExpiresHeader) { - const HttpHeaders& headers = pResponse->headers(); - HttpHeaders::const_iterator expiresHeader = headers.find("Expires"); - if (expiresHeader == headers.end()) - return false; + // If no cache control or expires, don't cache + if (!cacheControl && !expiresExists) + return false; + + // + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control + // + // The no-store response directive indicates that any caches of any kind + // (private or shared) should not store this response. + if (cacheControl && cacheControl->noStore()) + return false; + + // + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Expires + // + // If there is a Cache-Control header with the max-age or s-maxage directive + // in the response, the Expires header is ignored + bool ignoreExpiresHeader = + cacheControl && + (cacheControl->maxAgeExists() || cacheControl->sharedMaxAgeExists()); + // Check older Expires header + if (expiresExists && !ignoreExpiresHeader) return std::difftime( convertHttpDateToTime(expiresHeader->second), std::time(nullptr)) > 0.0; - } return true; } @@ -345,6 +352,9 @@ std::time_t calculateExpiryTime( const IAssetRequest& request, const std::optional& cacheControl) { + // + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Expires + // // If there is a Cache-Control header with the max-age or s-maxage directive // in the response, the Expires header is ignored bool preferCacheControl = diff --git a/CesiumAsync/test/TestCacheAssetAccessor.cpp b/CesiumAsync/test/TestCacheAssetAccessor.cpp index 42e331c52..1f951e5cc 100644 --- a/CesiumAsync/test/TestCacheAssetAccessor.cpp +++ b/CesiumAsync/test/TestCacheAssetAccessor.cpp @@ -135,7 +135,6 @@ TEST_CASE("Test the condition of caching the request") { {"Cache-Control", "must-revalidate, max-age=100"}}; bool responseCached = runResponseCacheTest(statusCode, "GET", headers); - REQUIRE(responseCached == true); } @@ -147,7 +146,6 @@ TEST_CASE("Test the condition of caching the request") { {"Expires", "Wed, 21 Oct 5020 07:28:00 GMT"}}; bool responseCached = runResponseCacheTest(statusCode, "GET", headers); - REQUIRE(responseCached == true); } @@ -162,7 +160,6 @@ TEST_CASE("Test the condition of caching the request") { {"Expires", "Mon, 01 Jan 1990 00:00:00 GMT"}}; bool responseCached = runResponseCacheTest(statusCode, "GET", headers); - REQUIRE(responseCached == true); } @@ -176,7 +173,30 @@ TEST_CASE("Test the condition of caching the request") { {"ETag", "deadbeef"}}; bool responseCached = runResponseCacheTest(statusCode, "GET", headers); + REQUIRE(responseCached == true); + } + + SECTION("GET Request, no-cache with Etag") { + int statusCode = GENERATE(200, 202, 203, 204, 205, 304); + + HttpHeaders headers = { + {"Content-Type", "application/json"}, + {"Cache-Control", "no-cache"}, + {"ETag", "deadbeef"}}; + + bool responseCached = runResponseCacheTest(statusCode, "GET", headers); + REQUIRE(responseCached == true); + } + + SECTION("GET Request, no-cache with Last-Modified") { + int statusCode = GENERATE(200, 202, 203, 204, 205, 304); + + HttpHeaders headers = { + {"Content-Type", "application/json"}, + {"Cache-Control", "no-cache"}, + {"Last-Modified", "Mon, 01 Jan 1990 00:00:00 GMT"}}; + bool responseCached = runResponseCacheTest(statusCode, "GET", headers); REQUIRE(responseCached == true); } } @@ -291,7 +311,7 @@ TEST_CASE("Test the condition of caching the request") { } SECTION( - "No store for response that has No-Cache in the cache-control header") { + "Store for response that has No-Cache in the cache-control header") { std::unique_ptr mockResponse = std::make_unique( static_cast(200), @@ -323,7 +343,16 @@ TEST_CASE("Test the condition of caching the request") { cacheAssetAccessor ->get(asyncSystem, "test.com", std::vector{}) .wait(); - REQUIRE(mockCacheDatabase->storeResponseCall == false); + + // + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control + // + // The no-cache response directive indicates that the response can be + // stored in caches, but the response must be validated with the origin + // server before each reuse, even when the cache is disconnected from the + // origin server. + + REQUIRE(mockCacheDatabase->storeResponseCall == true); } SECTION( From 6049f72a3fa40d45a967612a3fde9ae29474f970 Mon Sep 17 00:00:00 2001 From: Joseph Kaile Date: Tue, 25 Jul 2023 14:26:19 -0400 Subject: [PATCH 131/421] apply pull request review code changes --- CesiumGltfReader/src/GltfReader.cpp | 30 +++++------ CesiumGltfReader/src/decodeMeshOpt.cpp | 4 +- CesiumGltfReader/src/dequantizeMeshData.cpp | 52 +++++++++++++++---- .../src/transformTextureCoords.cpp | 29 ++++++----- 4 files changed, 76 insertions(+), 39 deletions(-) diff --git a/CesiumGltfReader/src/GltfReader.cpp b/CesiumGltfReader/src/GltfReader.cpp index bd634a1f6..5f76970d1 100644 --- a/CesiumGltfReader/src/GltfReader.cpp +++ b/CesiumGltfReader/src/GltfReader.cpp @@ -310,22 +310,22 @@ void postprocess( model.extensionsUsed.end(), "EXT_meshopt_compression") != model.extensionsUsed.end()) { decodeMeshOpt(model, readGltf); + } - if (options.dequantizeMeshData && - std::find( - model.extensionsUsed.begin(), - model.extensionsUsed.end(), - "KHR_mesh_quantization") != model.extensionsUsed.end()) { - dequantizeMeshData(model); - - if (options.applyTextureTransform && - std::find( - model.extensionsUsed.begin(), - model.extensionsUsed.end(), - "KHR_texture_transform") != model.extensionsUsed.end()) { - transformTexture(model); - } - } + if (options.dequantizeMeshData && + std::find( + model.extensionsUsed.begin(), + model.extensionsUsed.end(), + "KHR_mesh_quantization") != model.extensionsUsed.end()) { + dequantizeMeshData(model); + } + + if (options.applyTextureTransform && + std::find( + model.extensionsUsed.begin(), + model.extensionsUsed.end(), + "KHR_texture_transform") != model.extensionsUsed.end()) { + transformTexture(model); } } diff --git a/CesiumGltfReader/src/decodeMeshOpt.cpp b/CesiumGltfReader/src/decodeMeshOpt.cpp index b1897035f..d59be937a 100644 --- a/CesiumGltfReader/src/decodeMeshOpt.cpp +++ b/CesiumGltfReader/src/decodeMeshOpt.cpp @@ -57,12 +57,12 @@ int decodeBufferView( buffer.size()); } else { if (meshOpt.byteStride == sizeof(std::uint16_t)) { - return decodeIndices( + return decodeIndices( reinterpret_cast(pDest), buffer, meshOpt); } else if (meshOpt.byteStride == sizeof(std::uint32_t)) { - return decodeIndices( + return decodeIndices( reinterpret_cast(pDest), buffer, meshOpt); diff --git a/CesiumGltfReader/src/dequantizeMeshData.cpp b/CesiumGltfReader/src/dequantizeMeshData.cpp index 7b035b54b..2e9843061 100644 --- a/CesiumGltfReader/src/dequantizeMeshData.cpp +++ b/CesiumGltfReader/src/dequantizeMeshData.cpp @@ -10,7 +10,7 @@ namespace CesiumGltfReader { namespace { -template float intToFloat(T t) { return intToFloat(t); } +template float intToFloat(T t) = delete; template <> float intToFloat(std::int8_t c) { return std::max(c / 127.0f, -1.0f); @@ -28,11 +28,24 @@ template void dequantizeFloat( float* fPtr, int64_t count, - std::byte* bPtr, + const std::byte* bPtr, int64_t stride) { for (int i = 0; i < count; i++, bPtr += stride) { for (unsigned int j = 0; j < N; j++) { - *fPtr++ = intToFloat(reinterpret_cast(bPtr)[j]); + *fPtr++ = intToFloat(reinterpret_cast(bPtr)[j]); + } + } +} + +template +void castToFloat( + float* fPtr, + int64_t count, + const std::byte* bPtr, + int64_t stride) { + for (int i = 0; i < count; i++, bPtr += stride) { + for (unsigned int j = 0; j < N; j++) { + *fPtr++ = static_cast(reinterpret_cast(bPtr)[j]); } } } @@ -74,13 +87,23 @@ void dequantizeAccessor(Model& model, Accessor& accessor) { std::vector buffer; buffer.resize(static_cast(byteLength)); - dequantizeFloat( - reinterpret_cast(buffer.data()), - accessor.count, + const std::byte* bPtr = reinterpret_cast( pBuffer->cesium.data.data() + pBufferView->byteOffset + - accessor.byteOffset, - byteStride); - + accessor.byteOffset); + + if (accessor.normalized) { + dequantizeFloat( + reinterpret_cast(buffer.data()), + accessor.count, + bPtr, + byteStride); + } else { + castToFloat( + reinterpret_cast(buffer.data()), + accessor.count, + bPtr, + byteStride); + } accessor.componentType = AccessorSpec::ComponentType::FLOAT; accessor.byteOffset = 0; for (double& d : accessor.min) { @@ -138,7 +161,16 @@ void dequantizeMeshData(Model& model) { primitive.attributes) { Accessor* pAccessor = Model::getSafe(&model.accessors, attribute.second); - if (pAccessor) { + if (!pAccessor) { + continue; + } + if (pAccessor->componentType == Accessor::ComponentType::FLOAT) { + continue; + } + const std::string& attributeName = attribute.first; + if (attributeName == "POSITION" || attributeName == "NORMAL" || + attributeName == "TANGENT" || + attributeName.find("TEXCOORD") != std::string::npos) { dequantizeAccessor(model, *pAccessor); } } diff --git a/CesiumGltfReader/src/transformTextureCoords.cpp b/CesiumGltfReader/src/transformTextureCoords.cpp index 880824ccd..cd12a5d88 100644 --- a/CesiumGltfReader/src/transformTextureCoords.cpp +++ b/CesiumGltfReader/src/transformTextureCoords.cpp @@ -57,10 +57,11 @@ void transformBufferView( } } // namespace +template void processTextureInfo( Model& model, MeshPrimitive& primitive, - std::optional& textureInfo) { + T& textureInfo) { if (!textureInfo) { return; } @@ -87,10 +88,10 @@ void processTextureInfo( return; } Accessor& accessor = model.accessors.emplace_back(*pAccessor); - Buffer& buffer = model.buffers.emplace_back(); - buffer.cesium.data.resize(static_cast(pBufferView->byteLength)); const AccessorView accessorView(model, accessor); if (accessorView.status() == AccessorViewStatus::Valid) { + Buffer& buffer = model.buffers.emplace_back(); + buffer.cesium.data.resize(static_cast(pBufferView->byteLength)); transformBufferView(accessorView, buffer, *pTextureTransform); accessor.bufferView = static_cast(model.bufferViews.size()); BufferView& bufferView = model.bufferViews.emplace_back(*pBufferView); @@ -103,15 +104,19 @@ void processTextureInfo( void transformTexture(Model& model) { for (Mesh& mesh : model.meshes) { for (MeshPrimitive& primitive : mesh.primitives) { - Material* pMaterial = - Model::getSafe(&model.materials, primitive.material); - if (pMaterial) { - if (pMaterial->pbrMetallicRoughness) { - processTextureInfo( - model, - primitive, - pMaterial->pbrMetallicRoughness->baseColorTexture); - } + Material* material = Model::getSafe(&model.materials, primitive.material); + if (material) { + processTextureInfo( + model, + primitive, + material->pbrMetallicRoughness->baseColorTexture); + processTextureInfo( + model, + primitive, + material->pbrMetallicRoughness->metallicRoughnessTexture); + processTextureInfo(model, primitive, material->normalTexture); + processTextureInfo(model, primitive, material->occlusionTexture); + processTextureInfo(model, primitive, material->emissiveTexture); } } } From 5cc8dff015ee06fecf477b2d9a98d5dc9fadb131 Mon Sep 17 00:00:00 2001 From: Joseph Kaile Date: Tue, 25 Jul 2023 20:32:31 -0400 Subject: [PATCH 132/421] apply pull request comments --- .../include/CesiumGltfReader/GltfReader.h | 2 +- CesiumGltfReader/src/GltfReader.cpp | 6 +++--- ...oords.cpp => applyKHRTextureTransform.cpp} | 4 ++-- .../src/applyKHRTextureTransform.h | 19 +++++++++++++++++++ CesiumGltfReader/src/decodeMeshOpt.cpp | 10 +++++----- CesiumGltfReader/src/decodeMeshOpt.h | 8 ++++++++ CesiumGltfReader/src/dequantizeMeshData.cpp | 5 ++--- CesiumGltfReader/src/dequantizeMeshData.h | 5 +++++ CesiumGltfReader/src/transformTextureCoords.h | 10 ---------- .../test/data/DucksMeshopt/README.md | 6 +++++- 10 files changed, 50 insertions(+), 25 deletions(-) rename CesiumGltfReader/src/{transformTextureCoords.cpp => applyKHRTextureTransform.cpp} (98%) create mode 100644 CesiumGltfReader/src/applyKHRTextureTransform.h delete mode 100644 CesiumGltfReader/src/transformTextureCoords.h diff --git a/CesiumGltfReader/include/CesiumGltfReader/GltfReader.h b/CesiumGltfReader/include/CesiumGltfReader/GltfReader.h index c44d63000..85e59855a 100644 --- a/CesiumGltfReader/include/CesiumGltfReader/GltfReader.h +++ b/CesiumGltfReader/include/CesiumGltfReader/GltfReader.h @@ -105,7 +105,7 @@ struct CESIUMGLTFREADER_API GltfReaderOptions { * @brief Whether the mesh data are decompressed on the CPU or not, according * to the EXT_meshopt_compression extension */ - bool decompressMeshData = true; + bool decodeMeshOptData = true; /** * @brief Whether the quantized and compressed mesh data are dequantized on diff --git a/CesiumGltfReader/src/GltfReader.cpp b/CesiumGltfReader/src/GltfReader.cpp index 5f76970d1..9c336df95 100644 --- a/CesiumGltfReader/src/GltfReader.cpp +++ b/CesiumGltfReader/src/GltfReader.cpp @@ -1,12 +1,12 @@ #include "CesiumGltfReader/GltfReader.h" #include "ModelJsonHandler.h" +#include "applyKHRTextureTransform.h" #include "decodeDataUrls.h" #include "decodeDraco.h" #include "decodeMeshOpt.h" #include "dequantizeMeshData.h" #include "registerExtensions.h" -#include "transformTextureCoords.h" #include #include @@ -304,7 +304,7 @@ void postprocess( decodeDraco(readGltf); } - if (options.decompressMeshData && + if (options.decodeMeshOptData && std::find( model.extensionsUsed.begin(), model.extensionsUsed.end(), @@ -325,7 +325,7 @@ void postprocess( model.extensionsUsed.begin(), model.extensionsUsed.end(), "KHR_texture_transform") != model.extensionsUsed.end()) { - transformTexture(model); + applyKHRTextureTransform(model); } } diff --git a/CesiumGltfReader/src/transformTextureCoords.cpp b/CesiumGltfReader/src/applyKHRTextureTransform.cpp similarity index 98% rename from CesiumGltfReader/src/transformTextureCoords.cpp rename to CesiumGltfReader/src/applyKHRTextureTransform.cpp index cd12a5d88..24010d5f4 100644 --- a/CesiumGltfReader/src/transformTextureCoords.cpp +++ b/CesiumGltfReader/src/applyKHRTextureTransform.cpp @@ -1,4 +1,4 @@ -#include "transformTextureCoords.h" +#include "applyKHRTextureTransform.h" #include "CesiumGltf/ExtensionKhrTextureTransform.h" #include "CesiumGltfReader/GltfReader.h" @@ -101,7 +101,7 @@ void processTextureInfo( } } // namespace -void transformTexture(Model& model) { +void applyKHRTextureTransform(Model& model) { for (Mesh& mesh : model.meshes) { for (MeshPrimitive& primitive : mesh.primitives) { Material* material = Model::getSafe(&model.materials, primitive.material); diff --git a/CesiumGltfReader/src/applyKHRTextureTransform.h b/CesiumGltfReader/src/applyKHRTextureTransform.h new file mode 100644 index 000000000..14fdcc742 --- /dev/null +++ b/CesiumGltfReader/src/applyKHRTextureTransform.h @@ -0,0 +1,19 @@ +#pragma once + +namespace CesiumGltf { +struct Model; +} + +namespace CesiumGltfReader { + +/** + * @brief Applies the KHR_texture_transform extension to the texture coordinates + * of a model. This function modifies the model by transforming the UV + * coordinates of each texture according to the offset, rotation, and scale + * properties specified by the extension. + * @post The function will create a copy of the original UV buffer with updated + * coordinates that reflect the applied transformations and store it in a new + * buffer view and accessor. + */ +void applyKHRTextureTransform(CesiumGltf::Model& model); +} // namespace CesiumGltfReader diff --git a/CesiumGltfReader/src/decodeMeshOpt.cpp b/CesiumGltfReader/src/decodeMeshOpt.cpp index d59be937a..0a602ec8e 100644 --- a/CesiumGltfReader/src/decodeMeshOpt.cpp +++ b/CesiumGltfReader/src/decodeMeshOpt.cpp @@ -95,14 +95,14 @@ void decodeAccessor( if (pMeshOpt->filter != ExtensionBufferViewExtMeshoptCompression::Filter::NONE) { // add a warning message - readGltf.warnings.emplace_back("The ext_meshopt_compression extension has " + readGltf.warnings.emplace_back("The EXT_meshopt_compression extension has " "a filter that is not supported."); return; } Buffer* pBuffer = model.getSafe(&model.buffers, pMeshOpt->buffer); if (!pBuffer) { readGltf.warnings.emplace_back( - "The ext_meshopt_compression extension has an invalid buffer " + "The EXT_meshopt_compression extension has an invalid buffer " "index."); return; } @@ -110,7 +110,7 @@ void decodeAccessor( static_cast(pMeshOpt->byteOffset + pMeshOpt->byteLength) > pBuffer->cesium.data.size()) { readGltf.warnings.emplace_back( - "The ext_meshopt_compression extension has a bufferView that " + "The EXT_meshopt_compression extension has a bufferView that " "extends beyond its buffer."); return; } @@ -119,7 +119,7 @@ void decodeAccessor( int64_t byteLength = pMeshOpt->byteStride * pMeshOpt->count; if (byteLength < 0) { readGltf.warnings.emplace_back( - "The ext_meshopt_compression extension has a negative byte length."); + "The EXT_meshopt_compression extension has a negative byte length."); return; } pDest->cesium.data.resize(static_cast(byteLength)); @@ -134,7 +134,7 @@ void decodeAccessor( static_cast(pMeshOpt->byteLength)), *pMeshOpt) != 0) { readGltf.warnings.emplace_back( - "The ext_meshopt_compression extension has a corrupted or " + "The EXT_meshopt_compression extension has a corrupted or " "incompatible meshopt compression buffer."); return; } diff --git a/CesiumGltfReader/src/decodeMeshOpt.h b/CesiumGltfReader/src/decodeMeshOpt.h index e5a9fb8aa..093190209 100644 --- a/CesiumGltfReader/src/decodeMeshOpt.h +++ b/CesiumGltfReader/src/decodeMeshOpt.h @@ -10,6 +10,14 @@ struct GltfReaderResult; namespace CesiumGltfReader { +/** + * @brief Decodes the mesh data in the model according to the + * EXT_meshopt_compression extension + * + * The decompressed buffer may be in a quantized format as specified by the + * KHR_mesh_quantization extension, in which case the data will have to be + * dequantized to get the original values. + **/ void decodeMeshOpt( CesiumGltf::Model& model, CesiumGltfReader::GltfReaderResult& readGltf); diff --git a/CesiumGltfReader/src/dequantizeMeshData.cpp b/CesiumGltfReader/src/dequantizeMeshData.cpp index 2e9843061..eb5d1fd6e 100644 --- a/CesiumGltfReader/src/dequantizeMeshData.cpp +++ b/CesiumGltfReader/src/dequantizeMeshData.cpp @@ -87,9 +87,8 @@ void dequantizeAccessor(Model& model, Accessor& accessor) { std::vector buffer; buffer.resize(static_cast(byteLength)); - const std::byte* bPtr = reinterpret_cast( - pBuffer->cesium.data.data() + pBufferView->byteOffset + - accessor.byteOffset); + const std::byte* bPtr = pBuffer->cesium.data.data() + + pBufferView->byteOffset + accessor.byteOffset; if (accessor.normalized) { dequantizeFloat( diff --git a/CesiumGltfReader/src/dequantizeMeshData.h b/CesiumGltfReader/src/dequantizeMeshData.h index c36f7b23f..8a4b17901 100644 --- a/CesiumGltfReader/src/dequantizeMeshData.h +++ b/CesiumGltfReader/src/dequantizeMeshData.h @@ -6,5 +6,10 @@ struct Model; namespace CesiumGltfReader { +/** + * @brief Dequantizes any quantized data in the accessors of the glTF model and + * converts them to floating-point data as specified in the + * KHR_quantization extension. + */ void dequantizeMeshData(CesiumGltf::Model& model); } // namespace CesiumGltfReader diff --git a/CesiumGltfReader/src/transformTextureCoords.h b/CesiumGltfReader/src/transformTextureCoords.h deleted file mode 100644 index 6485782fe..000000000 --- a/CesiumGltfReader/src/transformTextureCoords.h +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -namespace CesiumGltf { -struct Model; -} - -namespace CesiumGltfReader { - -void transformTexture(CesiumGltf::Model& model); -} // namespace CesiumGltfReader diff --git a/CesiumGltfReader/test/data/DucksMeshopt/README.md b/CesiumGltfReader/test/data/DucksMeshopt/README.md index 1bbf357d4..d7c4d2c8b 100644 --- a/CesiumGltfReader/test/data/DucksMeshopt/README.md +++ b/CesiumGltfReader/test/data/DucksMeshopt/README.md @@ -4,4 +4,8 @@ This repository contains the Duck GLTF model that is used for testing various fe ## License Information -The Duck model is in the public domain and can be used for any purpose. The source is [here](https://github.com/KhronosGroup/glTF-Sample-Assets/tree/main/Models/Duck). \ No newline at end of file +The Duck model is licensed under the SCEA Shared Source License, Version 1.0 (the "License"). You may obtain a copy of the License at: + +https://web.archive.org/web/20160320123355/http://research.scea.com/scea_shared_source_license.html + +The source is [here](https://github.com/KhronosGroup/glTF-Sample-Assets/tree/main/Models/Duck). From 03c1806485ee73eed258d5d9879a3b1d842dcb09 Mon Sep 17 00:00:00 2001 From: Serein Pfeiffer Date: Wed, 26 Jul 2023 15:11:10 +0200 Subject: [PATCH 133/421] deps: Add Catch2 dep only if not already present and tests activated. --- extern/CMakeLists.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/extern/CMakeLists.txt b/extern/CMakeLists.txt index c7b6220b9..3a19dfe00 100644 --- a/extern/CMakeLists.txt +++ b/extern/CMakeLists.txt @@ -1,6 +1,9 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_LIST_DIR}/Catch2/contrib") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} PARENT_SCOPE) -add_subdirectory(Catch2) + +if (NOT TARGET Catch2 AND CESIUM_TESTS_ENABLED) + add_subdirectory(Catch2) +endif() add_subdirectory(GSL) From 62fb7ded3f65aa9ddd1bc9874765c72666dd09e3 Mon Sep 17 00:00:00 2001 From: Joseph Birkner Date: Wed, 26 Jul 2023 16:17:11 +0200 Subject: [PATCH 134/421] deps: Make GLM strict behavior optional. --- CMakeLists.txt | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c868a75a7..b20022bd8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,6 +10,7 @@ option(PRIVATE_CESIUM_SQLITE "ON to rename SQLite symbols to cesium_sqlite3_* so option(CESIUM_TRACING_ENABLED "Whether to enable the Cesium performance tracing framework (CESIUM_TRACE_* macros)." OFF) option(CESIUM_COVERAGE_ENABLED "Whether to enable code coverage" OFF) option(CESIUM_TESTS_ENABLED "Whether to enable tests" ON) +option(CESIUM_GLM_STRICT_ENABLED "Whether to force strict GLM compile definitions." ON) if (CESIUM_TRACING_ENABLED) add_compile_definitions(CESIUM_TRACING_ENABLED=1) @@ -93,13 +94,15 @@ function(configure_cesium_library targetName) CXX_EXTENSIONS NO ) - target_compile_definitions( - ${targetName} - PUBLIC - GLM_FORCE_XYZW_ONLY # Disable .rgba and .stpq to make it easier to view values from debugger - GLM_FORCE_EXPLICIT_CTOR # Disallow implicit conversions between dvec3 <-> dvec4, dvec3 <-> fvec3, etc - GLM_FORCE_SIZE_T_LENGTH # Make vec.length() and vec[idx] use size_t instead of int - ) + if (CESIUM_GLM_STRICT_ENABLED) + target_compile_definitions( + ${targetName} + PUBLIC + GLM_FORCE_XYZW_ONLY # Disable .rgba and .stpq to make it easier to view values from debugger + GLM_FORCE_EXPLICIT_CTOR # Disallow implicit conversions between dvec3 <-> dvec4, dvec3 <-> fvec3, etc + GLM_FORCE_SIZE_T_LENGTH # Make vec.length() and vec[idx] use size_t instead of int + ) + endif() if (BUILD_SHARED_LIBS) target_compile_definitions( From 0638ccb3e6c8a38d76b030af04ceb581b3cfd469 Mon Sep 17 00:00:00 2001 From: Joseph Birkner Date: Wed, 26 Jul 2023 17:27:38 +0200 Subject: [PATCH 135/421] Fix CESIUM_NATIVE_DRACO_LIBRARY for wasm build. --- extern/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extern/CMakeLists.txt b/extern/CMakeLists.txt index 3a19dfe00..0c8a1f94b 100644 --- a/extern/CMakeLists.txt +++ b/extern/CMakeLists.txt @@ -40,9 +40,9 @@ add_subdirectory(uriparser) if ((NOT TARGET draco) AND (NOT TARGET draco_static)) add_subdirectory(draco) - if (MSVC) + if (MSVC OR EMSCRIPTEN) set(CESIUM_NATIVE_DRACO_LIBRARY draco) - else() + elseif() set(CESIUM_NATIVE_DRACO_LIBRARY draco_static) endif() endif() From 8abe88353aea7275755319b603331ec087d956bb Mon Sep 17 00:00:00 2001 From: Brian L <130494071+csciguy8@users.noreply.github.com> Date: Wed, 26 Jul 2023 09:52:51 -0600 Subject: [PATCH 136/421] Add support for cases where we only need Etag or Last-Modified to cache --- CesiumAsync/src/CachingAssetAccessor.cpp | 23 +++++++++++------- CesiumAsync/test/TestCacheAssetAccessor.cpp | 26 +++++++++++++++++++-- 2 files changed, 39 insertions(+), 10 deletions(-) diff --git a/CesiumAsync/src/CachingAssetAccessor.cpp b/CesiumAsync/src/CachingAssetAccessor.cpp index d233e1d1b..750644c3f 100644 --- a/CesiumAsync/src/CachingAssetAccessor.cpp +++ b/CesiumAsync/src/CachingAssetAccessor.cpp @@ -313,10 +313,6 @@ bool shouldCacheRequest( HttpHeaders::const_iterator expiresHeader = headers.find("Expires"); bool expiresExists = expiresHeader != headers.end(); - // If no cache control or expires, don't cache - if (!cacheControl && !expiresExists) - return false; - // // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control // @@ -330,17 +326,28 @@ bool shouldCacheRequest( // // If there is a Cache-Control header with the max-age or s-maxage directive // in the response, the Expires header is ignored - bool ignoreExpiresHeader = + bool preferCacheControl = cacheControl && (cacheControl->maxAgeExists() || cacheControl->sharedMaxAgeExists()); - // Check older Expires header - if (expiresExists && !ignoreExpiresHeader) + // Cache control defines expiration in the future, we should cache + if (preferCacheControl) + return true; + + // Check older Expires header, if no cache control is overriding it + if (expiresExists && !preferCacheControl) return std::difftime( convertHttpDateToTime(expiresHeader->second), std::time(nullptr)) > 0.0; - return true; + // If we have a way to revalidate, we can store + bool hasEtag = headers.find("ETag") != headers.end(); + bool hasLastModifiedEtag = headers.find("Last-Modified") != headers.end(); + if (hasEtag || hasLastModifiedEtag) + return true; + + // Else don't store it + return false; } std::string calculateCacheKey(const IAssetRequest& request) { diff --git a/CesiumAsync/test/TestCacheAssetAccessor.cpp b/CesiumAsync/test/TestCacheAssetAccessor.cpp index 1f951e5cc..698050754 100644 --- a/CesiumAsync/test/TestCacheAssetAccessor.cpp +++ b/CesiumAsync/test/TestCacheAssetAccessor.cpp @@ -199,6 +199,28 @@ TEST_CASE("Test the condition of caching the request") { bool responseCached = runResponseCacheTest(statusCode, "GET", headers); REQUIRE(responseCached == true); } + + SECTION("GET Request, just Last-Modified") { + int statusCode = GENERATE(200, 202, 203, 204, 205, 304); + + HttpHeaders headers = { + {"Content-Type", "application/json"}, + {"Last-Modified", "Mon, 01 Jan 1990 00:00:00 GMT"}}; + + bool responseCached = runResponseCacheTest(statusCode, "GET", headers); + REQUIRE(responseCached == true); + } + + SECTION("GET Request, just Etag") { + int statusCode = GENERATE(200, 202, 203, 204, 205, 304); + + HttpHeaders headers = { + {"Content-Type", "application/json"}, + {"ETag", "deadbeef"}}; + + bool responseCached = runResponseCacheTest(statusCode, "GET", headers); + REQUIRE(responseCached == true); + } } SECTION("No cache condition") { @@ -311,7 +333,7 @@ TEST_CASE("Test the condition of caching the request") { } SECTION( - "Store for response that has No-Cache in the cache-control header") { + "No store for response that has No-Cache in the cache-control header") { std::unique_ptr mockResponse = std::make_unique( static_cast(200), @@ -352,7 +374,7 @@ TEST_CASE("Test the condition of caching the request") { // server before each reuse, even when the cache is disconnected from the // origin server. - REQUIRE(mockCacheDatabase->storeResponseCall == true); + REQUIRE(mockCacheDatabase->storeResponseCall == false); } SECTION( From 5cbda54b7f44230a76234aaa1e7ec2f094205f60 Mon Sep 17 00:00:00 2001 From: Brian Langevin <130494071+csciguy8@users.noreply.github.com> Date: Wed, 26 Jul 2023 10:04:20 -0600 Subject: [PATCH 137/421] Update CHANGES.md --- CHANGES.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 586b3c9de..508c3f675 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,12 @@ # Change Log + +### v?.?.? - 2023-?-? + +##### Additions :tada: + +- Add caching support for Google 3d Photorealistic Tiles. Fixes cases where the origin server is using combinations of HTTP header directives that would cause tiles to not go to disk cache (`max-age-0`, `stale-while-revalidate`, and `Expires`). + ### v0.25.1 - 2023-07-03 ##### Additions :tada: From d9857303e004537aefd4ec1c4871b8d6a53decb3 Mon Sep 17 00:00:00 2001 From: Joseph Kaile Date: Wed, 26 Jul 2023 12:32:13 -0400 Subject: [PATCH 138/421] fix linux compile error --- CesiumGltfReader/src/applyKHRTextureTransform.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/CesiumGltfReader/src/applyKHRTextureTransform.cpp b/CesiumGltfReader/src/applyKHRTextureTransform.cpp index 24010d5f4..dcea4ad12 100644 --- a/CesiumGltfReader/src/applyKHRTextureTransform.cpp +++ b/CesiumGltfReader/src/applyKHRTextureTransform.cpp @@ -10,7 +10,7 @@ namespace { void transformBufferView( const AccessorView& accessorView, Buffer& buffer, - ExtensionKhrTextureTransform& textureTransform) { + const ExtensionKhrTextureTransform& textureTransform) { if (textureTransform.offset.size() < 2 || textureTransform.scale.size() < 2) { return; @@ -61,12 +61,14 @@ template void processTextureInfo( Model& model, MeshPrimitive& primitive, - T& textureInfo) { + std::optional& textureInfo) { + static_assert(std::is_base_of::value); if (!textureInfo) { return; } - ExtensionKhrTextureTransform* pTextureTransform = - textureInfo->getExtension(); + const TextureInfo& textureInfoValue = static_cast(*textureInfo); + const ExtensionKhrTextureTransform* pTextureTransform = + textureInfoValue.getExtension(); if (!pTextureTransform) { return; } From e00a12b37d4bc69b8c44150853875cc1a502b225 Mon Sep 17 00:00:00 2001 From: Brian Langevin <130494071+csciguy8@users.noreply.github.com> Date: Wed, 26 Jul 2023 11:11:39 -0600 Subject: [PATCH 139/421] Update CHANGES.md --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index a874f9fde..15b4fb701 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -9,7 +9,7 @@ ##### Fixes :wrench: - Fixed a bug in the 3D Tiles selection algorithm that could cause missing detail if a tileset had a leaf tile that was considered "unconditionally refined" due to having a geometric error larger than its parent's. -- Fixed a bug where `GltfReader::readImage` would always populate `mipPositions` when reading KTX2 images, even when the KTX2 file indicated that it had no mip levels and that they should be created, if necessary, from the base image. As a result, `generateMipMaps` wouldn't generate any mipmaps for the image.>>>>>>> main +- Fixed a bug where `GltfReader::readImage` would always populate `mipPositions` when reading KTX2 images, even when the KTX2 file indicated that it had no mip levels and that they should be created, if necessary, from the base image. As a result, `generateMipMaps` wouldn't generate any mipmaps for the image. ### v0.25.1 - 2023-07-03 From 65090a7327db65862bc00b1a61234cc8b94afa22 Mon Sep 17 00:00:00 2001 From: Joseph Kaile Date: Wed, 26 Jul 2023 13:50:08 -0400 Subject: [PATCH 140/421] decode filter --- CesiumGltfReader/src/decodeMeshOpt.cpp | 39 ++++++++++++++++++++------ 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/CesiumGltfReader/src/decodeMeshOpt.cpp b/CesiumGltfReader/src/decodeMeshOpt.cpp index 0a602ec8e..9a9e26841 100644 --- a/CesiumGltfReader/src/decodeMeshOpt.cpp +++ b/CesiumGltfReader/src/decodeMeshOpt.cpp @@ -20,6 +20,36 @@ namespace CesiumGltfReader { namespace { +void decodeFilter( + std::byte* buffer, + ExtensionBufferViewExtMeshoptCompression& meshOpt) { + if (meshOpt.filter == + ExtensionBufferViewExtMeshoptCompression::Filter::NONE) { + return; + } + if (meshOpt.filter == + ExtensionBufferViewExtMeshoptCompression::Filter::OCTAHEDRAL) { + meshopt_decodeFilterOct( + buffer, + static_cast(meshOpt.count), + static_cast(meshOpt.byteStride)); + } else if ( + meshOpt.filter == + ExtensionBufferViewExtMeshoptCompression::Filter::QUATERNION) { + meshopt_decodeFilterQuat( + buffer, + static_cast(meshOpt.count), + static_cast(meshOpt.byteStride)); + } else if ( + meshOpt.filter == + ExtensionBufferViewExtMeshoptCompression::Filter::EXPONENTIAL) { + meshopt_decodeFilterExp( + buffer, + static_cast(meshOpt.count), + static_cast(meshOpt.byteStride)); + } +} + template int decodeIndices( T* data, @@ -91,14 +121,6 @@ void decodeAccessor( return; } - // check the filter value - if (pMeshOpt->filter != - ExtensionBufferViewExtMeshoptCompression::Filter::NONE) { - // add a warning message - readGltf.warnings.emplace_back("The EXT_meshopt_compression extension has " - "a filter that is not supported."); - return; - } Buffer* pBuffer = model.getSafe(&model.buffers, pMeshOpt->buffer); if (!pBuffer) { readGltf.warnings.emplace_back( @@ -138,6 +160,7 @@ void decodeAccessor( "incompatible meshopt compression buffer."); return; } + decodeFilter(pDest->cesium.data.data(), *pMeshOpt); pBufferView->buffer = static_cast(model.buffers.size() - 1); pBufferView->extensions.erase( ExtensionBufferViewExtMeshoptCompression::ExtensionName); From fe00f13d68e596768c02f612ef5c2d1609d24ad1 Mon Sep 17 00:00:00 2001 From: Joseph Birkner Date: Thu, 27 Jul 2023 11:26:40 +0200 Subject: [PATCH 141/421] Undo last change. --- extern/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extern/CMakeLists.txt b/extern/CMakeLists.txt index 0c8a1f94b..3a19dfe00 100644 --- a/extern/CMakeLists.txt +++ b/extern/CMakeLists.txt @@ -40,9 +40,9 @@ add_subdirectory(uriparser) if ((NOT TARGET draco) AND (NOT TARGET draco_static)) add_subdirectory(draco) - if (MSVC OR EMSCRIPTEN) + if (MSVC) set(CESIUM_NATIVE_DRACO_LIBRARY draco) - elseif() + else() set(CESIUM_NATIVE_DRACO_LIBRARY draco_static) endif() endif() From a59ffac3efb4d537b59d0c06c0e4f18fdc964694 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Thu, 27 Jul 2023 22:00:58 +1000 Subject: [PATCH 142/421] A few more test cases, and tweak the logic so they pass. --- CesiumAsync/src/CachingAssetAccessor.cpp | 36 ++++++++---- CesiumAsync/test/TestCacheAssetAccessor.cpp | 61 +++++++++++++++++++++ 2 files changed, 85 insertions(+), 12 deletions(-) diff --git a/CesiumAsync/src/CachingAssetAccessor.cpp b/CesiumAsync/src/CachingAssetAccessor.cpp index 750644c3f..964234a8e 100644 --- a/CesiumAsync/src/CachingAssetAccessor.cpp +++ b/CesiumAsync/src/CachingAssetAccessor.cpp @@ -326,19 +326,31 @@ bool shouldCacheRequest( // // If there is a Cache-Control header with the max-age or s-maxage directive // in the response, the Expires header is ignored - bool preferCacheControl = - cacheControl && - (cacheControl->maxAgeExists() || cacheControl->sharedMaxAgeExists()); - - // Cache control defines expiration in the future, we should cache - if (preferCacheControl) - return true; + bool preferCacheControl = false; + + // If Cache-Control expiration is in the future, definitely cache. But we + // might be able to cache even if it's not. + if (cacheControl) { + if (cacheControl->maxAgeExists()) { + preferCacheControl = true; + if (cacheControl->maxAgeValue() > 0) + return true; + } else if (cacheControl->sharedMaxAgeExists()) { + preferCacheControl = true; + if (cacheControl->sharedMaxAgeValue() > 0) + return true; + } + } - // Check older Expires header, if no cache control is overriding it - if (expiresExists && !preferCacheControl) - return std::difftime( - convertHttpDateToTime(expiresHeader->second), - std::time(nullptr)) > 0.0; + // If Expires is in the future, definitely cache. But we might be able to + // cache even if it's not. + if (!preferCacheControl && expiresExists) { + bool alreadyExpired = std::difftime( + convertHttpDateToTime(expiresHeader->second), + std::time(nullptr)) <= 0.0; + if (!alreadyExpired) + return true; + } // If we have a way to revalidate, we can store bool hasEtag = headers.find("ETag") != headers.end(); diff --git a/CesiumAsync/test/TestCacheAssetAccessor.cpp b/CesiumAsync/test/TestCacheAssetAccessor.cpp index 698050754..0ecbc349f 100644 --- a/CesiumAsync/test/TestCacheAssetAccessor.cpp +++ b/CesiumAsync/test/TestCacheAssetAccessor.cpp @@ -221,6 +221,56 @@ TEST_CASE("Test the condition of caching the request") { bool responseCached = runResponseCacheTest(statusCode, "GET", headers); REQUIRE(responseCached == true); } + + SECTION( + "GET Request, Expires header is less than current, but has an ETag") { + int statusCode = GENERATE(200, 202, 203, 204, 205, 304); + + HttpHeaders headers = { + {"Content-Type", "application/json"}, + {"ETag", "deadbeef"}, + {"Expires", "Wed, 21 Oct 2010 07:28:00 GMT"}}; + + bool responseCached = runResponseCacheTest(statusCode, "GET", headers); + REQUIRE(responseCached == true); + } + + SECTION("GET Request, Expires header is less than current, but has a " + "Last-Modified") { + int statusCode = GENERATE(200, 202, 203, 204, 205, 304); + + HttpHeaders headers = { + {"Content-Type", "application/json"}, + {"Last-Modified", "Mon, 01 Jan 1990 00:00:00 GMT"}, + {"Expires", "Wed, 21 Oct 2010 07:28:00 GMT"}}; + + bool responseCached = runResponseCacheTest(statusCode, "GET", headers); + REQUIRE(responseCached == true); + } + + SECTION("GET Request, max-age is zero, but has an ETag") { + int statusCode = GENERATE(200, 202, 203, 204, 205, 304); + + HttpHeaders headers = { + {"Content-Type", "application/json"}, + {"ETag", "deadbeef"}, + {"Cache-Control", "max-age=0"}}; + + bool responseCached = runResponseCacheTest(statusCode, "GET", headers); + REQUIRE(responseCached == true); + } + + SECTION("GET Request, max-age is zero, but has a Last-Modified") { + int statusCode = GENERATE(200, 202, 203, 204, 205, 304); + + HttpHeaders headers = { + {"Content-Type", "application/json"}, + {"Last-Modified", "Mon, 01 Jan 1990 00:00:00 GMT"}, + {"Cache-Control", "max-age=0"}}; + + bool responseCached = runResponseCacheTest(statusCode, "GET", headers); + REQUIRE(responseCached == true); + } } SECTION("No cache condition") { @@ -445,6 +495,17 @@ TEST_CASE("Test the condition of caching the request") { .wait(); REQUIRE(mockCacheDatabase->storeResponseCall == false); } + + SECTION("No store if max-age=0 and response has no ETag or Last-Modified") { + int statusCode = GENERATE(200, 202, 203, 204, 205, 304); + + HttpHeaders headers = { + {"Content-Type", "application/json"}, + {"Cache-Control", "max-age=0"}}; + + bool responseCached = runResponseCacheTest(statusCode, "GET", headers); + REQUIRE(responseCached == false); + } } } From 6e1c17c9a5e73ea488ba28160d414413550ab702 Mon Sep 17 00:00:00 2001 From: Joseph Kaile Date: Thu, 27 Jul 2023 15:04:26 -0400 Subject: [PATCH 143/421] reorg meshopt decode --- .../src/applyKHRTextureTransform.cpp | 26 ++-- CesiumGltfReader/src/decodeMeshOpt.cpp | 126 ++++++++---------- CesiumGltfReader/src/dequantizeMeshData.cpp | 42 +++--- 3 files changed, 91 insertions(+), 103 deletions(-) diff --git a/CesiumGltfReader/src/applyKHRTextureTransform.cpp b/CesiumGltfReader/src/applyKHRTextureTransform.cpp index dcea4ad12..74c810621 100644 --- a/CesiumGltfReader/src/applyKHRTextureTransform.cpp +++ b/CesiumGltfReader/src/applyKHRTextureTransform.cpp @@ -9,7 +9,7 @@ namespace CesiumGltfReader { namespace { void transformBufferView( const AccessorView& accessorView, - Buffer& buffer, + std::vector& data, const ExtensionKhrTextureTransform& textureTransform) { if (textureTransform.offset.size() < 2 || textureTransform.scale.size() < 2) { @@ -24,7 +24,7 @@ void transformBufferView( float ScaleX = static_cast(textureTransform.scale[0]); float ScaleY = static_cast(textureTransform.scale[1]); - glm::vec2* uvs = reinterpret_cast(buffer.cesium.data.data()); + glm::vec2* uvs = reinterpret_cast(data.data()); for (int i = 0; i < accessorView.size(); i++) { glm::vec2 uv = accessorView[i]; uv.x = uv.x * ScaleX + OffsetX; @@ -48,7 +48,7 @@ void transformBufferView( glm::mat3 scale = glm::mat3(Scale.x, 0, 0, 0, Scale.y, 0, 0, 0, 1); glm::mat3 matrix = translation * rotation * scale; - glm::vec2* uvs = reinterpret_cast(buffer.cesium.data.data()); + glm::vec2* uvs = reinterpret_cast(data.data()); for (int i = 0; i < accessorView.size(); i++) { *uvs++ = glm::vec2((matrix * glm::vec3(accessorView[i], 1))); @@ -89,17 +89,21 @@ void processTextureInfo( if (!pBufferView) { return; } - Accessor& accessor = model.accessors.emplace_back(*pAccessor); - const AccessorView accessorView(model, accessor); + const AccessorView accessorView(model, *pAccessor); if (accessorView.status() == AccessorViewStatus::Valid) { - Buffer& buffer = model.buffers.emplace_back(); - buffer.cesium.data.resize(static_cast(pBufferView->byteLength)); - transformBufferView(accessorView, buffer, *pTextureTransform); + std::vector data; + data.resize(static_cast(pBufferView->byteLength)); + transformBufferView(accessorView, data, *pTextureTransform); + textureInfo->extensions.erase(ExtensionKhrTextureTransform::ExtensionName); + find->second = static_cast(model.accessors.size()); + Accessor& accessor = model.accessors.emplace_back(*pAccessor); + pBufferView = Model::getSafe(&model.bufferViews, accessor.bufferView); accessor.bufferView = static_cast(model.bufferViews.size()); BufferView& bufferView = model.bufferViews.emplace_back(*pBufferView); - bufferView.buffer = static_cast(model.buffers.size() - 1); - find->second = static_cast(model.accessors.size() - 1); - textureInfo->extensions.erase(ExtensionKhrTextureTransform::ExtensionName); + bufferView.buffer = static_cast(model.buffers.size()); + Buffer& buffer = model.buffers.emplace_back(); + buffer.byteLength = bufferView.byteLength; + buffer.cesium.data = std::move(data); } } // namespace diff --git a/CesiumGltfReader/src/decodeMeshOpt.cpp b/CesiumGltfReader/src/decodeMeshOpt.cpp index 9a9e26841..d1310b0a0 100644 --- a/CesiumGltfReader/src/decodeMeshOpt.cpp +++ b/CesiumGltfReader/src/decodeMeshOpt.cpp @@ -22,7 +22,7 @@ namespace { void decodeFilter( std::byte* buffer, - ExtensionBufferViewExtMeshoptCompression& meshOpt) { + const ExtensionBufferViewExtMeshoptCompression& meshOpt) { if (meshOpt.filter == ExtensionBufferViewExtMeshoptCompression::Filter::NONE) { return; @@ -54,7 +54,7 @@ template int decodeIndices( T* data, const gsl::span& buffer, - ExtensionBufferViewExtMeshoptCompression& meshOpt) { + const ExtensionBufferViewExtMeshoptCompression& meshOpt) { if (meshOpt.mode == ExtensionBufferViewExtMeshoptCompression::Mode::TRIANGLES) { return meshopt_decodeIndexBuffer( @@ -76,7 +76,7 @@ int decodeIndices( int decodeBufferView( void* pDest, const gsl::span& buffer, - ExtensionBufferViewExtMeshoptCompression& meshOpt) { + const ExtensionBufferViewExtMeshoptCompression& meshOpt) { if (meshOpt.mode == ExtensionBufferViewExtMeshoptCompression::Mode::ATTRIBUTES) { return meshopt_decodeVertexBuffer( @@ -101,80 +101,60 @@ int decodeBufferView( } } } - -void decodeAccessor( - int32_t accessor, - Model& model, - CesiumGltfReader::GltfReaderResult& readGltf) { - Accessor* pAccessor = Model::getSafe(&model.accessors, accessor); - if (!pAccessor) { - return; - } - BufferView* pBufferView = - Model::getSafe(&model.bufferViews, pAccessor->bufferView); - if (!pBufferView) { - return; - } - ExtensionBufferViewExtMeshoptCompression* pMeshOpt = - pBufferView->getExtension(); - if (!pMeshOpt) { - return; - } - - Buffer* pBuffer = model.getSafe(&model.buffers, pMeshOpt->buffer); - if (!pBuffer) { - readGltf.warnings.emplace_back( - "The EXT_meshopt_compression extension has an invalid buffer " - "index."); - return; - } - if (pMeshOpt->byteOffset < 0 || pMeshOpt->byteLength < 0 || - static_cast(pMeshOpt->byteOffset + pMeshOpt->byteLength) > - pBuffer->cesium.data.size()) { - readGltf.warnings.emplace_back( - "The EXT_meshopt_compression extension has a bufferView that " - "extends beyond its buffer."); - return; - } - Buffer* pDest = &model.buffers.emplace_back(); - pBuffer = model.getSafe(&model.buffers, pMeshOpt->buffer); - int64_t byteLength = pMeshOpt->byteStride * pMeshOpt->count; - if (byteLength < 0) { - readGltf.warnings.emplace_back( - "The EXT_meshopt_compression extension has a negative byte length."); - return; - } - pDest->cesium.data.resize(static_cast(byteLength)); - pDest->byteLength = byteLength; - pBufferView->byteLength = byteLength; - pBufferView->byteStride = pMeshOpt->byteStride; - pBufferView->byteOffset = 0; - if (decodeBufferView( - pDest->cesium.data.data(), - gsl::span( - pBuffer->cesium.data.data() + pMeshOpt->byteOffset, - static_cast(pMeshOpt->byteLength)), - *pMeshOpt) != 0) { - readGltf.warnings.emplace_back( - "The EXT_meshopt_compression extension has a corrupted or " - "incompatible meshopt compression buffer."); - return; - } - decodeFilter(pDest->cesium.data.data(), *pMeshOpt); - pBufferView->buffer = static_cast(model.buffers.size() - 1); - pBufferView->extensions.erase( - ExtensionBufferViewExtMeshoptCompression::ExtensionName); -} } // namespace void decodeMeshOpt(Model& model, CesiumGltfReader::GltfReaderResult& readGltf) { - for (Mesh& mesh : model.meshes) { - for (MeshPrimitive& primitive : mesh.primitives) { - decodeAccessor(primitive.indices, model, readGltf); - for (std::pair attribute : - primitive.attributes) { - decodeAccessor(attribute.second, model, readGltf); + for (size_t i = 0; i < model.bufferViews.size(); ++i) { + BufferView& bufferView = model.bufferViews[i]; + const ExtensionBufferViewExtMeshoptCompression* pMeshOpt = + bufferView.getExtension(); + if (pMeshOpt) { + const Buffer* pBuffer = model.getSafe(&model.buffers, pMeshOpt->buffer); + if (!pBuffer) { + readGltf.warnings.emplace_back( + "The EXT_meshopt_compression extension has an invalid buffer " + "index."); + continue; } + + if (pMeshOpt->byteOffset < 0 || pMeshOpt->byteLength < 0 || + static_cast(pMeshOpt->byteOffset + pMeshOpt->byteLength) > + pBuffer->cesium.data.size()) { + readGltf.warnings.emplace_back( + "The EXT_meshopt_compression extension has a bufferView that " + "extends beyond its buffer."); + continue; + } + int64_t byteLength = pMeshOpt->byteStride * pMeshOpt->count; + if (byteLength < 0) { + readGltf.warnings.emplace_back("The EXT_meshopt_compression extension " + "has a negative byte length."); + continue; + } + + std::vector data; + data.resize(static_cast(byteLength)); + if (decodeBufferView( + data.data(), + gsl::span( + pBuffer->cesium.data.data() + pMeshOpt->byteOffset, + static_cast(pMeshOpt->byteLength)), + *pMeshOpt) != 0) { + readGltf.warnings.emplace_back( + "The EXT_meshopt_compression extension has a corrupted or " + "incompatible meshopt compression buffer."); + continue; + } + decodeFilter(data.data(), *pMeshOpt); + Buffer& buffer = model.buffers.emplace_back(); + buffer.byteLength = byteLength; + buffer.cesium.data = std::move(data); + + bufferView.buffer = static_cast(model.buffers.size() - 1); + bufferView.byteOffset = 0; + bufferView.byteLength = byteLength; + bufferView.extensions.erase( + ExtensionBufferViewExtMeshoptCompression::ExtensionName); } } } diff --git a/CesiumGltfReader/src/dequantizeMeshData.cpp b/CesiumGltfReader/src/dequantizeMeshData.cpp index eb5d1fd6e..785cca55a 100644 --- a/CesiumGltfReader/src/dequantizeMeshData.cpp +++ b/CesiumGltfReader/src/dequantizeMeshData.cpp @@ -25,7 +25,7 @@ template <> float intToFloat(std::int16_t c) { template <> float intToFloat(std::uint16_t c) { return c / 65535.0f; } template -void dequantizeFloat( +void normalizeQuantized( float* fPtr, int64_t count, const std::byte* bPtr, @@ -38,7 +38,7 @@ void dequantizeFloat( } template -void castToFloat( +void castQuantizedToFloat( float* fPtr, int64_t count, const std::byte* bPtr, @@ -84,40 +84,44 @@ void dequantizeAccessor(Model& model, Accessor& accessor) { return; } - std::vector buffer; - buffer.resize(static_cast(byteLength)); + std::vector data; + data.resize(static_cast(byteLength)); const std::byte* bPtr = pBuffer->cesium.data.data() + pBufferView->byteOffset + accessor.byteOffset; if (accessor.normalized) { - dequantizeFloat( - reinterpret_cast(buffer.data()), + normalizeQuantized( + reinterpret_cast(data.data()), accessor.count, bPtr, byteStride); + for (double& d : accessor.min) { + d = intToFloat(static_cast(d)); + } + for (double& d : accessor.max) { + d = intToFloat(static_cast(d)); + } } else { - castToFloat( - reinterpret_cast(buffer.data()), + castQuantizedToFloat( + reinterpret_cast(data.data()), accessor.count, bPtr, byteStride); } accessor.componentType = AccessorSpec::ComponentType::FLOAT; accessor.byteOffset = 0; - for (double& d : accessor.min) { - d = intToFloat(static_cast(d)); - } - for (double& d : accessor.max) { - d = intToFloat(static_cast(d)); - } + accessor.bufferView = static_cast(model.bufferViews.size()); - pBufferView->byteOffset = 0; - pBufferView->byteStride = N * sizeof(float); - pBufferView->byteLength = byteLength; + BufferView& bufferView = model.bufferViews.emplace_back(*pBufferView); + bufferView.byteOffset = 0; + bufferView.byteStride = N * sizeof(float); + bufferView.byteLength = byteLength; + bufferView.buffer = static_cast(model.buffers.size()); - pBuffer->cesium.data = std::move(buffer); - pBuffer->byteLength = byteLength; + Buffer& buffer = model.buffers.emplace_back(); + buffer.cesium.data = std::move(data); + buffer.byteLength = byteLength; } template void dequantizeAccessor(Model& model, Accessor& accessor) { From ef58d31fa0741ba24de745067051c475a1ada828 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Fri, 28 Jul 2023 22:59:58 +1000 Subject: [PATCH 144/421] Add LocalHorizontalCoordinateSystem constructors taking matrices. --- CHANGES.md | 1 + .../LocalHorizontalCoordinateSystem.h | 24 +++++++++++++++++++ .../src/LocalHorizontalCoordinateSystem.cpp | 11 +++++++++ 3 files changed, 36 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 15b4fb701..f78158098 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,6 +5,7 @@ ##### Additions :tada: - Add caching support for Google 3d Photorealistic Tiles. Fixes cases where the origin server is using combinations of HTTP header directives that would cause tiles to not go to disk cache (`max-age-0`, `stale-while-revalidate`, and `Expires`). +- Added new constructors to `LocalHorizontalCoordinateSystem` taking ECEF<->Local transformation matrices directly. ##### Fixes :wrench: diff --git a/CesiumGeospatial/include/CesiumGeospatial/LocalHorizontalCoordinateSystem.h b/CesiumGeospatial/include/CesiumGeospatial/LocalHorizontalCoordinateSystem.h index 1f608e8a3..8b6f378cd 100644 --- a/CesiumGeospatial/include/CesiumGeospatial/LocalHorizontalCoordinateSystem.h +++ b/CesiumGeospatial/include/CesiumGeospatial/LocalHorizontalCoordinateSystem.h @@ -72,6 +72,30 @@ class CESIUMGEOSPATIAL_API LocalHorizontalCoordinateSystem { double scaleToMeters = 1.0, const Ellipsoid& ellipsoid = Ellipsoid::WGS84); + /** + * @brief Create a new coordinate system with a specified transformation to + * the Earth-Centered, Earth-Fixed frame. This is an advanced constructor and + * should be avoided in most cases. + * + * @param localToEcef The transformation matrix from this coordinate system to + * the ECEF frame. + */ + explicit LocalHorizontalCoordinateSystem(const glm::dmat4& localToEcef); + + /** + * @brief Create a new coordinate system with the specified transformations + * between the local frame and the Earth-Centered, Earth-Fixed frame. This is + * an advanced constructor and should be avoided in most cases. + * + * @param localToEcef The transformation matrix from this coordinate system to + * the ECEF frame. This must be the inverse of `ecefToLocal`. + * @param ecefToLocal The transformation matrix from the ECEF frame to this + * coordinate system. This must be the inverse of `localToEcef`. + */ + LocalHorizontalCoordinateSystem( + const glm::dmat4& localToEcef, + const glm::dmat4& ecefToLocal); + /** * @brief Gets the transformation matrix from the local horizontal coordinate * system managed by this instance to the Earth-Centered, Earth-fixed diff --git a/CesiumGeospatial/src/LocalHorizontalCoordinateSystem.cpp b/CesiumGeospatial/src/LocalHorizontalCoordinateSystem.cpp index cb553c1ba..b1375313b 100644 --- a/CesiumGeospatial/src/LocalHorizontalCoordinateSystem.cpp +++ b/CesiumGeospatial/src/LocalHorizontalCoordinateSystem.cpp @@ -86,6 +86,17 @@ LocalHorizontalCoordinateSystem::LocalHorizontalCoordinateSystem( this->_ecefToLocal = glm::affineInverse(this->_localToEcef); } +CesiumGeospatial::LocalHorizontalCoordinateSystem:: + LocalHorizontalCoordinateSystem(const glm::dmat4& localToEcef) + : _localToEcef(localToEcef), + _ecefToLocal(glm::affineInverse(localToEcef)) {} + +CesiumGeospatial::LocalHorizontalCoordinateSystem:: + LocalHorizontalCoordinateSystem( + const glm::dmat4& localToEcef, + const glm::dmat4& ecefToLocal) + : _localToEcef(localToEcef), _ecefToLocal(ecefToLocal) {} + glm::dvec3 LocalHorizontalCoordinateSystem::localPositionToEcef( const glm::dvec3& localPosition) const noexcept { return glm::dvec3(this->_localToEcef * glm::dvec4(localPosition, 1.0)); From 40d22b1b14d6434184a18588a49bde048db07ea1 Mon Sep 17 00:00:00 2001 From: Joseph Kaile Date: Fri, 28 Jul 2023 12:45:18 -0400 Subject: [PATCH 145/421] fix doc comments --- .../include/CesiumGltfReader/GltfReader.h | 10 ++++++---- CesiumGltfReader/src/applyKHRTextureTransform.cpp | 3 +-- CesiumGltfReader/src/decodeMeshOpt.cpp | 12 +++++------- CesiumGltfReader/src/dequantizeMeshData.cpp | 2 -- 4 files changed, 12 insertions(+), 15 deletions(-) diff --git a/CesiumGltfReader/include/CesiumGltfReader/GltfReader.h b/CesiumGltfReader/include/CesiumGltfReader/GltfReader.h index 85e59855a..e2ef4282b 100644 --- a/CesiumGltfReader/include/CesiumGltfReader/GltfReader.h +++ b/CesiumGltfReader/include/CesiumGltfReader/GltfReader.h @@ -102,14 +102,16 @@ struct CESIUMGLTFREADER_API GltfReaderOptions { bool decodeDraco = true; /** - * @brief Whether the mesh data are decompressed on the CPU or not, according - * to the EXT_meshopt_compression extension + * @brief Whether the mesh data are decompressed as part of the load process, + * or left in the compressed format according to the EXT_meshopt_compression + * extension */ bool decodeMeshOptData = true; /** - * @brief Whether the quantized and compressed mesh data are dequantized on - * the CPU or not, according to the KHR_mesh_quantization extension + * @brief Whether the quantized mesh data are dequantized and converted to + * floating-point values when loading, according to the KHR_mesh_quantization + * extension. */ bool dequantizeMeshData = true; diff --git a/CesiumGltfReader/src/applyKHRTextureTransform.cpp b/CesiumGltfReader/src/applyKHRTextureTransform.cpp index 74c810621..494008963 100644 --- a/CesiumGltfReader/src/applyKHRTextureTransform.cpp +++ b/CesiumGltfReader/src/applyKHRTextureTransform.cpp @@ -72,7 +72,7 @@ void processTextureInfo( if (!pTextureTransform) { return; } - int64_t texCoord = 0; + int64_t texCoord = textureInfoValue.texCoord; if (pTextureTransform->texCoord) { texCoord = *pTextureTransform->texCoord; } @@ -97,7 +97,6 @@ void processTextureInfo( textureInfo->extensions.erase(ExtensionKhrTextureTransform::ExtensionName); find->second = static_cast(model.accessors.size()); Accessor& accessor = model.accessors.emplace_back(*pAccessor); - pBufferView = Model::getSafe(&model.bufferViews, accessor.bufferView); accessor.bufferView = static_cast(model.bufferViews.size()); BufferView& bufferView = model.bufferViews.emplace_back(*pBufferView); bufferView.buffer = static_cast(model.buffers.size()); diff --git a/CesiumGltfReader/src/decodeMeshOpt.cpp b/CesiumGltfReader/src/decodeMeshOpt.cpp index d1310b0a0..af8a4fe92 100644 --- a/CesiumGltfReader/src/decodeMeshOpt.cpp +++ b/CesiumGltfReader/src/decodeMeshOpt.cpp @@ -2,7 +2,6 @@ #include "CesiumGltfReader/GltfReader.h" -#include #include #ifdef __GNUC__ @@ -74,13 +73,13 @@ int decodeIndices( } int decodeBufferView( - void* pDest, + void* data, const gsl::span& buffer, const ExtensionBufferViewExtMeshoptCompression& meshOpt) { if (meshOpt.mode == ExtensionBufferViewExtMeshoptCompression::Mode::ATTRIBUTES) { return meshopt_decodeVertexBuffer( - pDest, + data, static_cast(meshOpt.count), static_cast(meshOpt.byteStride), reinterpret_cast(buffer.data()), @@ -88,12 +87,12 @@ int decodeBufferView( } else { if (meshOpt.byteStride == sizeof(std::uint16_t)) { return decodeIndices( - reinterpret_cast(pDest), + reinterpret_cast(data), buffer, meshOpt); } else if (meshOpt.byteStride == sizeof(std::uint32_t)) { return decodeIndices( - reinterpret_cast(pDest), + reinterpret_cast(data), buffer, meshOpt); } else { @@ -104,8 +103,7 @@ int decodeBufferView( } // namespace void decodeMeshOpt(Model& model, CesiumGltfReader::GltfReaderResult& readGltf) { - for (size_t i = 0; i < model.bufferViews.size(); ++i) { - BufferView& bufferView = model.bufferViews[i]; + for (BufferView& bufferView : model.bufferViews) { const ExtensionBufferViewExtMeshoptCompression* pMeshOpt = bufferView.getExtension(); if (pMeshOpt) { diff --git a/CesiumGltfReader/src/dequantizeMeshData.cpp b/CesiumGltfReader/src/dequantizeMeshData.cpp index 785cca55a..579a7f635 100644 --- a/CesiumGltfReader/src/dequantizeMeshData.cpp +++ b/CesiumGltfReader/src/dequantizeMeshData.cpp @@ -2,8 +2,6 @@ #include "CesiumGltfReader/GltfReader.h" -#include - using namespace CesiumGltf; namespace CesiumGltfReader { From 5c89b3c7d307dcacc446910a0dec8ed85c2cfcf7 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Mon, 31 Jul 2023 19:08:08 +1000 Subject: [PATCH 146/421] Tiny tweaks, mostly to formatting. --- CesiumGltfReader/src/applyKHRTextureTransform.cpp | 11 ++++++----- CesiumGltfReader/src/decodeMeshOpt.cpp | 3 +-- CesiumGltfReader/src/dequantizeMeshData.cpp | 5 ++--- CesiumGltfReader/test/data/DucksMeshopt/README.md | 8 +++++--- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/CesiumGltfReader/src/applyKHRTextureTransform.cpp b/CesiumGltfReader/src/applyKHRTextureTransform.cpp index 494008963..705b6a7b1 100644 --- a/CesiumGltfReader/src/applyKHRTextureTransform.cpp +++ b/CesiumGltfReader/src/applyKHRTextureTransform.cpp @@ -1,10 +1,11 @@ #include "applyKHRTextureTransform.h" -#include "CesiumGltf/ExtensionKhrTextureTransform.h" -#include "CesiumGltfReader/GltfReader.h" - #include +#include +#include + using namespace CesiumGltf; + namespace CesiumGltfReader { namespace { void transformBufferView( @@ -66,7 +67,7 @@ void processTextureInfo( if (!textureInfo) { return; } - const TextureInfo& textureInfoValue = static_cast(*textureInfo); + const TextureInfo& textureInfoValue = *textureInfo; const ExtensionKhrTextureTransform* pTextureTransform = textureInfoValue.getExtension(); if (!pTextureTransform) { @@ -104,7 +105,7 @@ void processTextureInfo( buffer.byteLength = bufferView.byteLength; buffer.cesium.data = std::move(data); } -} // namespace +} void applyKHRTextureTransform(Model& model) { for (Mesh& mesh : model.meshes) { diff --git a/CesiumGltfReader/src/decodeMeshOpt.cpp b/CesiumGltfReader/src/decodeMeshOpt.cpp index af8a4fe92..49b05ed97 100644 --- a/CesiumGltfReader/src/decodeMeshOpt.cpp +++ b/CesiumGltfReader/src/decodeMeshOpt.cpp @@ -1,8 +1,7 @@ #include "decodeMeshOpt.h" -#include "CesiumGltfReader/GltfReader.h" - #include +#include #ifdef __GNUC__ #pragma GCC diagnostic push diff --git a/CesiumGltfReader/src/dequantizeMeshData.cpp b/CesiumGltfReader/src/dequantizeMeshData.cpp index 579a7f635..8370356ad 100644 --- a/CesiumGltfReader/src/dequantizeMeshData.cpp +++ b/CesiumGltfReader/src/dequantizeMeshData.cpp @@ -1,6 +1,6 @@ #include "dequantizeMeshData.h" -#include "CesiumGltfReader/GltfReader.h" +#include using namespace CesiumGltf; @@ -170,8 +170,7 @@ void dequantizeMeshData(Model& model) { } const std::string& attributeName = attribute.first; if (attributeName == "POSITION" || attributeName == "NORMAL" || - attributeName == "TANGENT" || - attributeName.find("TEXCOORD") != std::string::npos) { + attributeName == "TANGENT" || attributeName.find("TEXCOORD") == 0) { dequantizeAccessor(model, *pAccessor); } } diff --git a/CesiumGltfReader/test/data/DucksMeshopt/README.md b/CesiumGltfReader/test/data/DucksMeshopt/README.md index d7c4d2c8b..b1ef6c204 100644 --- a/CesiumGltfReader/test/data/DucksMeshopt/README.md +++ b/CesiumGltfReader/test/data/DucksMeshopt/README.md @@ -1,11 +1,13 @@ # GLTF Model for Testing -This repository contains the Duck GLTF model that is used for testing various features and scenarios. The model is taken from the [glTF-Sample-Assets](https://github.com/KhronosGroup/glTF-Sample-Assets) repository, which provides a set of sample models for testing and demonstrating the glTF format. +This repository contains the Duck GLTF model that is used for testing various features and scenarios. The model is taken from the [glTF-Sample-Assets](https://github.com/KhronosGroup/glTF-Sample-Assets) repository, which provides a set of sample models for testing and demonstrating the glTF format. The specific model is [here](https://github.com/KhronosGroup/glTF-Sample-Assets/tree/main/Models/Duck). ## License Information -The Duck model is licensed under the SCEA Shared Source License, Version 1.0 (the "License"). You may obtain a copy of the License at: +Copyright 2006 Sony Computer Entertainment Inc. + +Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at: https://web.archive.org/web/20160320123355/http://research.scea.com/scea_shared_source_license.html -The source is [here](https://github.com/KhronosGroup/glTF-Sample-Assets/tree/main/Models/Duck). +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. From a2cebe04f1bfedc54e8498e7ed13cda087e0212f Mon Sep 17 00:00:00 2001 From: Joseph Kaile Date: Mon, 31 Jul 2023 17:41:45 -0400 Subject: [PATCH 147/421] add test to verify compressed vertex attributes are within range --- CesiumGltfReader/test/TestGltfReader.cpp | 109 ++++++++++++++++-- .../test/data/DucksMeshopt/Duck.glb | Bin 0 -> 120484 bytes 2 files changed, 99 insertions(+), 10 deletions(-) create mode 100644 CesiumGltfReader/test/data/DucksMeshopt/Duck.glb diff --git a/CesiumGltfReader/test/TestGltfReader.cpp b/CesiumGltfReader/test/TestGltfReader.cpp index 0635cb6bd..eac6e3e21 100644 --- a/CesiumGltfReader/test/TestGltfReader.cpp +++ b/CesiumGltfReader/test/TestGltfReader.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -11,6 +12,7 @@ #include #include +#include #include using namespace CesiumGltf; @@ -108,25 +110,112 @@ TEST_CASE("CesiumGltfReader::GltfReader") { CHECK(model.meshes[0].primitives[0].targets[0]["NORMAL"] == 11); } +namespace { +struct VertexAttributeRange { + glm::vec3 positionRange; + glm::vec3 normalRange; + glm::vec2 texCoordRange; +}; + +template +T getRange(const CesiumGltf::AccessorView& accessorView) { + T min{std::numeric_limits::max()}; + T max{std::numeric_limits::lowest()}; + for (int32_t i = 0; i < accessorView.size(); ++i) { + const T& value = accessorView[i]; + for (uint32_t j = 0; j < static_cast(value.length()); ++j) { + min[j] = glm::min(min[j], value[j]); + max[j] = glm::max(max[j], value[j]); + } + } + return max - min; +} + +template T getRange(const Model& model, int32_t accessor) { + CesiumGltf::AccessorView accessorView(model, accessor); + REQUIRE(accessorView.status() == CesiumGltf::AccessorViewStatus::Valid); + return getRange(accessorView); +} + +VertexAttributeRange getVertexAttributeRange(const Model& model) { + VertexAttributeRange var; + model.forEachPrimitiveInScene( + -1, + [&var]( + const Model& model, + const Node&, + const Mesh&, + const MeshPrimitive& primitive, + const glm::dmat4& transform) { + for (std::pair attribute : + primitive.attributes) { + const std::string& attributeName = attribute.first; + if (attributeName == "POSITION") { + var.positionRange = glm::vec3( + transform * + glm::dvec4(getRange(model, attribute.second), 0)); + } else if (attributeName == "NORMAL") { + var.normalRange = + glm::normalize(getRange(model, attribute.second)); + } else if (attributeName.find("TEXCOORD") == 0) { + var.texCoordRange = getRange(model, attribute.second); + } + } + }); + return var; +} + +template +bool epsilonCompare(const T& v1, const T& v2, double epsilon) { + for (uint32_t i = 0; i < static_cast(v1.length()); ++i) { + if (!CesiumUtility::Math::equalsEpsilon(v1[i], v2[i], epsilon)) { + return false; + } + } + return true; +} +} // namespace + TEST_CASE("Can decompress meshes using EXT_meshopt_compression") { - std::vector filenames; + + VertexAttributeRange originalVar; + { + GltfReader reader; + GltfReaderResult result = reader.readGltf(readFile( + CesiumGltfReader_TEST_DATA_DIR + + std::string("/DucksMeshopt/Duck.glb"))); + const Model& model = result.model.value(); + originalVar = getVertexAttributeRange(model); + } + for (int n = 3; n <= 15; n += 3) { std::string filename = CesiumGltfReader_TEST_DATA_DIR + std::string("/DucksMeshopt/Duck") + "-vp-" + std::to_string(n) + "-vt-" + std::to_string(n) + "-vn-" + std::to_string(n) + ".glb"; if (std::filesystem::exists(filename)) { - filenames.push_back(filename); + std::vector data = readFile(filename); + GltfReader reader; + GltfReaderResult result = reader.readGltf(data); + REQUIRE(result.model); + REQUIRE(result.warnings.empty()); + const Model& model = result.model.value(); + VertexAttributeRange compressedVar = getVertexAttributeRange(model); + double error = 1.0f / (pow(2, n - 1)); + REQUIRE(epsilonCompare( + originalVar.positionRange, + compressedVar.positionRange, + error)); + REQUIRE(epsilonCompare( + originalVar.normalRange, + compressedVar.normalRange, + error)); + REQUIRE(epsilonCompare( + originalVar.texCoordRange, + compressedVar.texCoordRange, + error)); } } - - for (const std::string& filename : filenames) { - std::vector data = readFile(filename); - GltfReader reader; - GltfReaderResult result = reader.readGltf(data); - REQUIRE(result.model); - REQUIRE(result.warnings.empty()); - } } TEST_CASE("Read TriangleWithoutIndices") { diff --git a/CesiumGltfReader/test/data/DucksMeshopt/Duck.glb b/CesiumGltfReader/test/data/DucksMeshopt/Duck.glb new file mode 100644 index 0000000000000000000000000000000000000000..217170d2bd67051270be974292dc3b834eefe206 GIT binary patch literal 120484 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 RAbBlY?7Qf;2Rj+ye*ggh(RTm< literal 0 HcmV?d00001 From f935ed55daa55d804fe1fad13d570aaf0feba27f Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Tue, 1 Aug 2023 09:30:14 +1000 Subject: [PATCH 148/421] Install meshoptimizer targets. --- CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index c868a75a7..74e9a1d77 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -206,3 +206,5 @@ install(TARGETS s2geometry) install(TARGETS expected-lite) install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/extern/expected-lite/include/nonstd TYPE INCLUDE) + +install(TARGETS meshoptimizer) From 172ac5ddcce602c8b268ad342639554dea2f6004 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Tue, 1 Aug 2023 12:49:59 +1000 Subject: [PATCH 149/421] Bump to v0.26.0. Update CHANGES.md. --- CHANGES.md | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 8d8ba38a7..ea79c2349 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,10 +1,10 @@ # Change Log -### ? - ? +### v0.26.0 - 2023-08-01 ##### Additions :tada: -- Add caching support for Google 3d Photorealistic Tiles. Fixes cases where the origin server is using combinations of HTTP header directives that would cause tiles to not go to disk cache (`max-age-0`, `stale-while-revalidate`, and `Expires`). +- Added caching support for Google Maps Photorealistic 3D Tiles. Or other cases where the origin server is using combinations of HTTP header directives that previously caused tiles not to go to disk cache (such as `max-age-0`, `stale-while-revalidate`, and `Expires`). - Added support for the `EXT_meshopt_compression` extension, which allows decompressing mesh data using the meshoptimizer library. Also added support for the `KHR_mesh_quantization` and `KHR_texture_transform` extensions, which are often used together with the `EXT_meshopt_compression` extension to optimize the size and performance of glTF files. ##### Fixes :wrench: diff --git a/package.json b/package.json index f52ea79d5..95e6f5f37 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cesium-native", - "version": "0.25.1", + "version": "0.26.0", "description": "Cesium 3D Geospatial for C++", "main": "index.js", "directories": { From 24f46f63a46843d9fb88cb66e172f25da8bb8b1f Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 2 Aug 2023 10:10:05 +1000 Subject: [PATCH 150/421] Update doc for Tileset destructor. --- .../include/Cesium3DTilesSelection/Tileset.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/Tileset.h b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/Tileset.h index 569bf1de8..c0de9c35d 100644 --- a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/Tileset.h +++ b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/Tileset.h @@ -84,9 +84,12 @@ class CESIUM3DTILESSELECTION_API Tileset final { /** * @brief Destroys this tileset. * - * This may block the calling thread while waiting for pending asynchronous - * tile loads to terminate. Use {@link canBeDestroyedWithoutBlocking} to - * determine if it will block. + * Destroying the tileset will immediately (before the destructor returns) + * unload as much tile content as possible. However, tiles that are currently + * in the process of being loaded cannot be unloaded immediately. These tiles + * will be unloaded asynchronously some time after this destructor returns. To + * be notified of completion of the async portion of the tileset destruction, + * subscribe to {@link getAsyncDestructionCompleteEvent}. */ ~Tileset() noexcept; From ada43f09505061067f5ebf7aa445f7133122b3f4 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 2 Aug 2023 10:57:04 +1000 Subject: [PATCH 151/421] Fix clang warnings. --- CesiumGeospatial/src/LocalHorizontalCoordinateSystem.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CesiumGeospatial/src/LocalHorizontalCoordinateSystem.cpp b/CesiumGeospatial/src/LocalHorizontalCoordinateSystem.cpp index b1375313b..02f934b3f 100644 --- a/CesiumGeospatial/src/LocalHorizontalCoordinateSystem.cpp +++ b/CesiumGeospatial/src/LocalHorizontalCoordinateSystem.cpp @@ -88,14 +88,14 @@ LocalHorizontalCoordinateSystem::LocalHorizontalCoordinateSystem( CesiumGeospatial::LocalHorizontalCoordinateSystem:: LocalHorizontalCoordinateSystem(const glm::dmat4& localToEcef) - : _localToEcef(localToEcef), - _ecefToLocal(glm::affineInverse(localToEcef)) {} + : _ecefToLocal(glm::affineInverse(localToEcef)), + _localToEcef(localToEcef) {} CesiumGeospatial::LocalHorizontalCoordinateSystem:: LocalHorizontalCoordinateSystem( const glm::dmat4& localToEcef, const glm::dmat4& ecefToLocal) - : _localToEcef(localToEcef), _ecefToLocal(ecefToLocal) {} + : _ecefToLocal(ecefToLocal), _localToEcef(localToEcef) {} glm::dvec3 LocalHorizontalCoordinateSystem::localPositionToEcef( const glm::dvec3& localPosition) const noexcept { From e4ef04b566b1db2c58f22acc449c2afb67d20d51 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Wed, 2 Aug 2023 11:05:58 -0400 Subject: [PATCH 152/421] Prevent empty error message from getting propagated to a tileset's loadErrorCallback --- CHANGES.md | 6 ++++++ .../src/TilesetContentManager.cpp | 14 ++++++++------ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index ea79c2349..cb27b4d7f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,11 @@ # Change Log +### v0.27.0 - 2023-09-01 + +##### Fixes :wrench: + +- Fixed a bug where an empty error message would get propagated to a tileset's `loadErrorCallback`. + ### v0.26.0 - 2023-08-01 ##### Additions :tada: diff --git a/Cesium3DTilesSelection/src/TilesetContentManager.cpp b/Cesium3DTilesSelection/src/TilesetContentManager.cpp index 79ceb1130..bf7b7e0c2 100644 --- a/Cesium3DTilesSelection/src/TilesetContentManager.cpp +++ b/Cesium3DTilesSelection/src/TilesetContentManager.cpp @@ -1425,12 +1425,14 @@ void TilesetContentManager::propagateTilesetContentLoaderResult( this->_externals.pLogger, "Warnings when loading tileset"); - if (loadErrorCallback) { - loadErrorCallback(TilesetLoadFailureDetails{ - nullptr, - type, - result.statusCode, - CesiumUtility::joinToString(result.errors.errors, "\n- ")}); + if (result.errors) { + if (loadErrorCallback) { + loadErrorCallback(TilesetLoadFailureDetails{ + nullptr, + type, + result.statusCode, + CesiumUtility::joinToString(result.errors.errors, "\n- ")}); + } } if (!result.errors) { From 335ea90678287534f1afeda8878e45ec87f68dea Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Fri, 4 Aug 2023 08:53:12 +1000 Subject: [PATCH 153/421] Hackily get cesium-native compiling on UWP. --- CMakeLists.txt | 2 +- CesiumIonClient/CMakeLists.txt | 2 +- CesiumIonClient/src/Connection.cpp | 29 +++++++++++++++++++++++++---- extern/CMakeLists.txt | 16 ++++++++-------- extern/sqlite3/CMakeLists.txt | 4 ++++ 5 files changed, 39 insertions(+), 14 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 74e9a1d77..de66fcc38 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -183,7 +183,7 @@ install(TARGETS modp_b64) install(TARGETS httplib) -install(TARGETS csprng) +# install(TARGETS csprng) install(TARGETS GSL) install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/extern/GSL/include/gsl TYPE INCLUDE) diff --git a/CesiumIonClient/CMakeLists.txt b/CesiumIonClient/CMakeLists.txt index 65e250816..6e4a3d037 100644 --- a/CesiumIonClient/CMakeLists.txt +++ b/CesiumIonClient/CMakeLists.txt @@ -39,7 +39,7 @@ target_link_libraries(CesiumIonClient CesiumAsync CesiumUtility PRIVATE - csprng + # csprng modp_b64 PicoSHA2 ) diff --git a/CesiumIonClient/src/Connection.cpp b/CesiumIonClient/src/Connection.cpp index 51995a278..a6d50c504 100644 --- a/CesiumIonClient/src/Connection.cpp +++ b/CesiumIonClient/src/Connection.cpp @@ -1,13 +1,15 @@ +#define _CRT_RAND_S #include "CesiumIonClient/Connection.h" #include "parseLinkHeader.h" #include #include +#include #include #include -#include +// #include #include #include #include @@ -15,6 +17,7 @@ #include #include +#include #include #ifdef _MSC_VER @@ -114,14 +117,32 @@ std::string createAuthorizationErrorHtml( std::string redirectUrl = Uri::resolve("http://127.0.0.1:" + std::to_string(port), redirectPath); - duthomhas::csprng rng; + // duthomhas::csprng rng; std::vector stateBytes(32, 0); - rng(stateBytes); + // rng(stateBytes); + + gsl::span span = stateBytes; + gsl::span intSpan = + CesiumUtility::reintepretCastSpan(span); + for (size_t i = 0; i < intSpan.size(); ++i) { + if (rand_s(&intSpan[i]) != 0) { + throw std::exception("Failed to generate random numbers for \"state\"."); + } + } std::string state = encodeBase64(stateBytes); std::vector codeVerifierBytes(32, 0); - rng(codeVerifierBytes); + // rng(codeVerifierBytes); + + span = codeVerifierBytes; + intSpan = CesiumUtility::reintepretCastSpan(span); + for (size_t i = 0; i < intSpan.size(); ++i) { + if (rand_s(&intSpan[i]) != 0) { + throw std::exception( + "Failed to generate random numbers for \"codeVerifier\"."); + } + } std::string codeVerifier = encodeBase64(codeVerifierBytes); diff --git a/extern/CMakeLists.txt b/extern/CMakeLists.txt index 0a6545035..247ebd551 100644 --- a/extern/CMakeLists.txt +++ b/extern/CMakeLists.txt @@ -112,13 +112,13 @@ add_subdirectory(cpp-httplib) # CSPRNG's CMake setup is old school, and it really only has one source # file, so set up a library manually here. -add_library(csprng CSPRNG/source/csprng.cpp) -target_include_directories( - csprng - PUBLIC - CSPRNG/source -) -set(CSPRNG_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/CSPRNG/source/duthomhas/csprng.h ${CMAKE_CURRENT_SOURCE_DIR}/CSPRNG/source/duthomhas/csprng.hpp ${CMAKE_CURRENT_SOURCE_DIR}/CSPRNG/source/duthomhas/is_iterable.hpp) +# add_library(csprng CSPRNG/source/csprng.cpp) +# target_include_directories( +# csprng +# PUBLIC +# CSPRNG/source +# ) +# set(CSPRNG_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/CSPRNG/source/duthomhas/csprng.h ${CMAKE_CURRENT_SOURCE_DIR}/CSPRNG/source/duthomhas/csprng.hpp ${CMAKE_CURRENT_SOURCE_DIR}/CSPRNG/source/duthomhas/is_iterable.hpp) # PicoSHA2 doesn't have CMakeLists.txt at all add_library(PicoSHA2 INTERFACE) @@ -183,7 +183,7 @@ include(ExternalProject) ExternalProject_Add(libjpeg-turbo SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/libjpeg-turbo" PREFIX "libjpeg-turbo" - CONFIGURE_COMMAND ${CMAKE_COMMAND} -B ${CMAKE_CURRENT_BINARY_DIR}/libjpeg-turbo -S ${CMAKE_CURRENT_LIST_DIR}/libjpeg-turbo -DCMAKE_INSTALL_PREFIX=${TJ_INSTALL_PREFIX} -DENABLE_SHARED=0 -DWITH_CRT_DLL=1 -G ${CMAKE_GENERATOR} -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME} -DCMAKE_SYSTEM_PROCESSOR=$,${CMAKE_SYSTEM_PROCESSOR},unknown> -DCMAKE_BUILD_TYPE=$ -DCMAKE_POSITION_INDEPENDENT_CODE=${CMAKE_POSITION_INDEPENDENT_CODE} -DCMAKE_ANDROID_ARCH_ABI=${CMAKE_ANDROID_ARCH_ABI} -DCMAKE_GENERATOR_PLATFORM=${CMAKE_GENERATOR_PLATFORM} -DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM} -DCMAKE_OSX_ARCHITECTURES=${CMAKE_OSX_ARCHITECTURES} -DCMAKE_OSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET} -DCMAKE_DEBUG_POSTFIX=${CMAKE_DEBUG_POSTFIX} -DCMAKE_TOOLCHAIN_FILE=${TJ_TOOLCHAIN_FILE} -DCMAKE_POSITION_INDEPENDENT_CODE=${CMAKE_POSITION_INDEPENDENT_CODE} + CONFIGURE_COMMAND ${CMAKE_COMMAND} -B ${CMAKE_CURRENT_BINARY_DIR}/libjpeg-turbo -S ${CMAKE_CURRENT_LIST_DIR}/libjpeg-turbo -DCMAKE_INSTALL_PREFIX=${TJ_INSTALL_PREFIX} -DENABLE_SHARED=0 -DWITH_CRT_DLL=1 -G ${CMAKE_GENERATOR} -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME} "-DCMAKE_SYSTEM_VERSION=${CMAKE_SYSTEM_VERSION}" -DCMAKE_SYSTEM_PROCESSOR=$,${CMAKE_SYSTEM_PROCESSOR},unknown> -DCMAKE_BUILD_TYPE=$ -DCMAKE_POSITION_INDEPENDENT_CODE=${CMAKE_POSITION_INDEPENDENT_CODE} -DCMAKE_ANDROID_ARCH_ABI=${CMAKE_ANDROID_ARCH_ABI} -DCMAKE_GENERATOR_PLATFORM=${CMAKE_GENERATOR_PLATFORM} -DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM} -DCMAKE_OSX_ARCHITECTURES=${CMAKE_OSX_ARCHITECTURES} -DCMAKE_OSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET} -DCMAKE_DEBUG_POSTFIX=${CMAKE_DEBUG_POSTFIX} -DCMAKE_TOOLCHAIN_FILE=${TJ_TOOLCHAIN_FILE} -DCMAKE_POSITION_INDEPENDENT_CODE=${CMAKE_POSITION_INDEPENDENT_CODE} BUILD_COMMAND ${CMAKE_COMMAND} --build ${CMAKE_CURRENT_BINARY_DIR}/libjpeg-turbo --config $ --target install INSTALL_COMMAND "" BUILD_BYPRODUCTS ${TJ_INSTALL_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}turbojpeg${CMAKE_STATIC_LIBRARY_SUFFIX} diff --git a/extern/sqlite3/CMakeLists.txt b/extern/sqlite3/CMakeLists.txt index 58fc2b5f0..4be0bde6a 100644 --- a/extern/sqlite3/CMakeLists.txt +++ b/extern/sqlite3/CMakeLists.txt @@ -16,6 +16,10 @@ if (PRIVATE_CESIUM_SQLITE) target_compile_definitions(sqlite3 PUBLIC PRIVATE_CESIUM_SQLITE) endif() +if (${CMAKE_SYSTEM_NAME} MATCHES "WindowsStore") + target_compile_definitions(sqlite3 PUBLIC SQLITE_OS_WINRT) +endif() + file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/sqlite3/sqlite3.h CONTENT "${SQLITE3_HEADER_CONTENTS}") file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/sqlite3/sqlite3.c CONTENT "${SQLITE3_SOURCE_CONTENTS}") file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/sqlite3/cesium-sqlite3.h INPUT ${CMAKE_CURRENT_SOURCE_DIR}/cesium-sqlite3.h) From f54556704e9c15ac256c3d4844676952e43083a5 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Fri, 4 Aug 2023 18:26:18 +1000 Subject: [PATCH 154/421] Restore compatibility with non-Windows platforms. --- CesiumIonClient/CMakeLists.txt | 5 +- CesiumIonClient/src/Connection.cpp | 27 +-------- CesiumIonClient/src/fillWithRandomBytes.cpp | 61 +++++++++++++++++++++ CesiumIonClient/src/fillWithRandomBytes.h | 9 +++ extern/CMakeLists.txt | 16 +++--- 5 files changed, 86 insertions(+), 32 deletions(-) create mode 100644 CesiumIonClient/src/fillWithRandomBytes.cpp create mode 100644 CesiumIonClient/src/fillWithRandomBytes.h diff --git a/CesiumIonClient/CMakeLists.txt b/CesiumIonClient/CMakeLists.txt index 6e4a3d037..524f1a392 100644 --- a/CesiumIonClient/CMakeLists.txt +++ b/CesiumIonClient/CMakeLists.txt @@ -39,11 +39,14 @@ target_link_libraries(CesiumIonClient CesiumAsync CesiumUtility PRIVATE - # csprng modp_b64 PicoSHA2 ) +if (NOT ${CMAKE_SYSTEM_NAME} MATCHES "WindowsStore") + target_link_libraries(CesiumIonClient PRIVATE csprng) +endif() + # httplib erroneously does not declare its include a `SYSTEM` include, so # we must extract its include_directories using this function and manually # reinclude it as 'SYSTEM'. diff --git a/CesiumIonClient/src/Connection.cpp b/CesiumIonClient/src/Connection.cpp index a6d50c504..ac2b2169e 100644 --- a/CesiumIonClient/src/Connection.cpp +++ b/CesiumIonClient/src/Connection.cpp @@ -1,6 +1,6 @@ -#define _CRT_RAND_S #include "CesiumIonClient/Connection.h" +#include "fillWithRandomBytes.h" #include "parseLinkHeader.h" #include @@ -9,7 +9,6 @@ #include #include -// #include #include #include #include @@ -17,7 +16,6 @@ #include #include -#include #include #ifdef _MSC_VER @@ -117,32 +115,13 @@ std::string createAuthorizationErrorHtml( std::string redirectUrl = Uri::resolve("http://127.0.0.1:" + std::to_string(port), redirectPath); - // duthomhas::csprng rng; std::vector stateBytes(32, 0); - // rng(stateBytes); - - gsl::span span = stateBytes; - gsl::span intSpan = - CesiumUtility::reintepretCastSpan(span); - for (size_t i = 0; i < intSpan.size(); ++i) { - if (rand_s(&intSpan[i]) != 0) { - throw std::exception("Failed to generate random numbers for \"state\"."); - } - } + fillWithRandomBytes(stateBytes); std::string state = encodeBase64(stateBytes); std::vector codeVerifierBytes(32, 0); - // rng(codeVerifierBytes); - - span = codeVerifierBytes; - intSpan = CesiumUtility::reintepretCastSpan(span); - for (size_t i = 0; i < intSpan.size(); ++i) { - if (rand_s(&intSpan[i]) != 0) { - throw std::exception( - "Failed to generate random numbers for \"codeVerifier\"."); - } - } + fillWithRandomBytes(codeVerifierBytes); std::string codeVerifier = encodeBase64(codeVerifierBytes); diff --git a/CesiumIonClient/src/fillWithRandomBytes.cpp b/CesiumIonClient/src/fillWithRandomBytes.cpp new file mode 100644 index 000000000..571c2e169 --- /dev/null +++ b/CesiumIonClient/src/fillWithRandomBytes.cpp @@ -0,0 +1,61 @@ +#define _CRT_RAND_S +#include "fillWithRandomBytes.h" + +#include + +// When WINAPI_FAMILY_PARTITION && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) +// is true, this is a Univeral Windows Platform build. csprng doesn't work on +// UWP. So we use the Windows-only rand_s function instead. + +#ifdef WINAPI_FAMILY +#if WINAPI_FAMILY == WINAPI_FAMILY_PC_APP || \ + WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP +#define IS_UWP 1 +#else +#define IS_UWP 0 +#endif +#else +#define IS_UWP 0 +#endif + +#if IS_UWP +#include +#else +#include +#endif + +namespace CesiumIonClient { + +void fillWithRandomBytes(const gsl::span& buffer) { +#if IS_UWP + size_t i = 0; + for (; i <= buffer.size() - sizeof(std::uint32_t); + i += sizeof(std::uint32_t)) { + std::uint32_t r; + if (rand_s(&r) != 0) { + throw std::exception("Failed to generate random numbers."); + } + std::memcpy(&buffer[i], &r, sizeof(std::uint32_t)); + } + + if (i < buffer.size()) { + assert(buffer.size() - i < 4); + + std::uint32_t extra; + if (rand_s(&extra) != 0) { + throw std::exception("Failed to generate random numbers."); + } + + std::uint8_t* pSource = reinterpret_cast(&extra); + for (; i < buffer.size(); ++i) { + buffer[i] = *pSource; + ++pSource; + } + } +#else + duthomhas::csprng rng; + rng(buffer); +#endif +} + +} // namespace CesiumIonClient diff --git a/CesiumIonClient/src/fillWithRandomBytes.h b/CesiumIonClient/src/fillWithRandomBytes.h new file mode 100644 index 000000000..55765c720 --- /dev/null +++ b/CesiumIonClient/src/fillWithRandomBytes.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +namespace CesiumIonClient { + +void fillWithRandomBytes(const gsl::span& buffer); + +} diff --git a/extern/CMakeLists.txt b/extern/CMakeLists.txt index 247ebd551..488dffbed 100644 --- a/extern/CMakeLists.txt +++ b/extern/CMakeLists.txt @@ -112,13 +112,15 @@ add_subdirectory(cpp-httplib) # CSPRNG's CMake setup is old school, and it really only has one source # file, so set up a library manually here. -# add_library(csprng CSPRNG/source/csprng.cpp) -# target_include_directories( -# csprng -# PUBLIC -# CSPRNG/source -# ) -# set(CSPRNG_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/CSPRNG/source/duthomhas/csprng.h ${CMAKE_CURRENT_SOURCE_DIR}/CSPRNG/source/duthomhas/csprng.hpp ${CMAKE_CURRENT_SOURCE_DIR}/CSPRNG/source/duthomhas/is_iterable.hpp) +if (NOT ${CMAKE_SYSTEM_NAME} MATCHES "WindowsStore") + add_library(csprng CSPRNG/source/csprng.cpp) + target_include_directories( + csprng + PUBLIC + CSPRNG/source + ) + set(CSPRNG_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/CSPRNG/source/duthomhas/csprng.h ${CMAKE_CURRENT_SOURCE_DIR}/CSPRNG/source/duthomhas/csprng.hpp ${CMAKE_CURRENT_SOURCE_DIR}/CSPRNG/source/duthomhas/is_iterable.hpp) +endif() # PicoSHA2 doesn't have CMakeLists.txt at all add_library(PicoSHA2 INTERFACE) From 53ed812fe334f40395e04a09e0a7393c809b03c4 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Fri, 4 Aug 2023 19:28:53 +1000 Subject: [PATCH 155/421] Add test for fillWithRandomBytes. --- CesiumIonClient/src/fillWithRandomBytes.cpp | 14 ++++---- .../test/TestFillWithRandomBytes.cpp | 32 +++++++++++++++++++ 2 files changed, 40 insertions(+), 6 deletions(-) create mode 100644 CesiumIonClient/test/TestFillWithRandomBytes.cpp diff --git a/CesiumIonClient/src/fillWithRandomBytes.cpp b/CesiumIonClient/src/fillWithRandomBytes.cpp index 571c2e169..f3c090e57 100644 --- a/CesiumIonClient/src/fillWithRandomBytes.cpp +++ b/CesiumIonClient/src/fillWithRandomBytes.cpp @@ -29,13 +29,15 @@ namespace CesiumIonClient { void fillWithRandomBytes(const gsl::span& buffer) { #if IS_UWP size_t i = 0; - for (; i <= buffer.size() - sizeof(std::uint32_t); - i += sizeof(std::uint32_t)) { - std::uint32_t r; - if (rand_s(&r) != 0) { - throw std::exception("Failed to generate random numbers."); + if (buffer.size() >= sizeof(uint32_t)) { + for (; i <= buffer.size() - sizeof(std::uint32_t); + i += sizeof(std::uint32_t)) { + std::uint32_t r; + if (rand_s(&r) != 0) { + throw std::exception("Failed to generate random numbers."); + } + std::memcpy(&buffer[i], &r, sizeof(std::uint32_t)); } - std::memcpy(&buffer[i], &r, sizeof(std::uint32_t)); } if (i < buffer.size()) { diff --git a/CesiumIonClient/test/TestFillWithRandomBytes.cpp b/CesiumIonClient/test/TestFillWithRandomBytes.cpp new file mode 100644 index 000000000..e3e8fe46a --- /dev/null +++ b/CesiumIonClient/test/TestFillWithRandomBytes.cpp @@ -0,0 +1,32 @@ +#include "../src/fillWithRandomBytes.h" + +#include + +using namespace CesiumIonClient; + +TEST_CASE("fillWithRandomBytes") { + std::vector sizes = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + for (size_t i = 0; i < sizes.size(); ++i) { + size_t size = sizes[i]; + + // Allocate an extra byte to make sure we don't overflow the buffer + std::vector buffer(size + 1); + gsl::span bufferSpan(buffer.data(), size); + + fillWithRandomBytes(bufferSpan); + + // Make sure the rest are non-zeros + for (size_t j = 0; j < size; ++j) { + // In the unlikely event the value is zero, generate some new random + // values. Repeat this up to 10 times. The chances that the random value + // at a particular position is zero ten times in a row is vanishingly + // small. + for (int k = 0; buffer[j] == 0 && k < 10; ++k) + fillWithRandomBytes(bufferSpan); + CHECK(buffer[j] != 0); + } + + // Make sure the last byte was not overwritten + CHECK(buffer[size] == 0); + } +} From 4e6c3cd11302176194b0f2854b35fae5f07d19a1 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Fri, 4 Aug 2023 19:35:05 +1000 Subject: [PATCH 156/421] Mark csprng headers as SYSTEM PRIVATE. --- extern/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/CMakeLists.txt b/extern/CMakeLists.txt index 488dffbed..699b124cb 100644 --- a/extern/CMakeLists.txt +++ b/extern/CMakeLists.txt @@ -116,7 +116,7 @@ if (NOT ${CMAKE_SYSTEM_NAME} MATCHES "WindowsStore") add_library(csprng CSPRNG/source/csprng.cpp) target_include_directories( csprng - PUBLIC + SYSTEM PRIVATE CSPRNG/source ) set(CSPRNG_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/CSPRNG/source/duthomhas/csprng.h ${CMAKE_CURRENT_SOURCE_DIR}/CSPRNG/source/duthomhas/csprng.hpp ${CMAKE_CURRENT_SOURCE_DIR}/CSPRNG/source/duthomhas/is_iterable.hpp) From 9b44b7d5ae304175bcb7e224ea2d22d18cc66a00 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Fri, 4 Aug 2023 19:41:08 +1000 Subject: [PATCH 157/421] Install csprng, except on UWP. --- CMakeLists.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index de66fcc38..0380348b0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -183,7 +183,10 @@ install(TARGETS modp_b64) install(TARGETS httplib) -# install(TARGETS csprng) +# Don't install CSPRNG when building for Universal Windows Platform +if (NOT ${CMAKE_SYSTEM_NAME} MATCHES "WindowsStore") + install(TARGETS csprng) +endif() install(TARGETS GSL) install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/extern/GSL/include/gsl TYPE INCLUDE) From 60ad1066da6f078279deec9e6163166f8208187d Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Fri, 4 Aug 2023 19:48:07 +1000 Subject: [PATCH 158/421] Header dir needs to be public. --- extern/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/CMakeLists.txt b/extern/CMakeLists.txt index 699b124cb..8d3af65d9 100644 --- a/extern/CMakeLists.txt +++ b/extern/CMakeLists.txt @@ -116,7 +116,7 @@ if (NOT ${CMAKE_SYSTEM_NAME} MATCHES "WindowsStore") add_library(csprng CSPRNG/source/csprng.cpp) target_include_directories( csprng - SYSTEM PRIVATE + SYSTEM PUBLIC CSPRNG/source ) set(CSPRNG_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/CSPRNG/source/duthomhas/csprng.h ${CMAKE_CURRENT_SOURCE_DIR}/CSPRNG/source/duthomhas/csprng.hpp ${CMAKE_CURRENT_SOURCE_DIR}/CSPRNG/source/duthomhas/is_iterable.hpp) From a495c7e88d54a433146ae5bea4dfd199e953ad22 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Fri, 4 Aug 2023 19:49:44 +1000 Subject: [PATCH 159/421] Formatting. --- CesiumIonClient/src/fillWithRandomBytes.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CesiumIonClient/src/fillWithRandomBytes.cpp b/CesiumIonClient/src/fillWithRandomBytes.cpp index f3c090e57..51a69115a 100644 --- a/CesiumIonClient/src/fillWithRandomBytes.cpp +++ b/CesiumIonClient/src/fillWithRandomBytes.cpp @@ -31,7 +31,7 @@ void fillWithRandomBytes(const gsl::span& buffer) { size_t i = 0; if (buffer.size() >= sizeof(uint32_t)) { for (; i <= buffer.size() - sizeof(std::uint32_t); - i += sizeof(std::uint32_t)) { + i += sizeof(std::uint32_t)) { std::uint32_t r; if (rand_s(&r) != 0) { throw std::exception("Failed to generate random numbers."); From 729357ea0e0fb39d5b10cfb0b458028b8a923363 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Fri, 4 Aug 2023 21:27:39 +1000 Subject: [PATCH 160/421] Update CHANGES.md. --- CHANGES.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index cb27b4d7f..c0263ec68 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,10 +1,11 @@ # Change Log -### v0.27.0 - 2023-09-01 +### ? - ? ##### Fixes :wrench: - Fixed a bug where an empty error message would get propagated to a tileset's `loadErrorCallback`. +- Fixed several small build script issues to allow cesium-native to be used in Univeral Windows Platform (UWP) applications, such as those that run on Holo Lens 2. ### v0.26.0 - 2023-08-01 From 6dcadfe38ebe59649ad3f70c715af14dba241404 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Tue, 15 Aug 2023 12:43:10 -0400 Subject: [PATCH 161/421] Create PropertyView --- .../include/CesiumGltf/PropertyArrayView.h | 57 +- .../CesiumGltf/PropertyTablePropertyView.h | 83 +- .../include/CesiumGltf/PropertyTableView.h | 21 +- CesiumGltf/include/CesiumGltf/PropertyView.h | 1593 ++++ CesiumGltf/src/PropertyTableView.cpp | 24 +- .../test/TestPropertyTablePropertyView.cpp | 146 +- CesiumGltf/test/TestPropertyTableView.cpp | 6494 ++++++++--------- CesiumGltf/test/TestPropertyView.cpp | 359 + .../include/CesiumUtility/JsonValue.h | 2 +- 9 files changed, 5422 insertions(+), 3357 deletions(-) create mode 100644 CesiumGltf/include/CesiumGltf/PropertyView.h create mode 100644 CesiumGltf/test/TestPropertyView.cpp diff --git a/CesiumGltf/include/CesiumGltf/PropertyArrayView.h b/CesiumGltf/include/CesiumGltf/PropertyArrayView.h index b921c13f3..f7978107c 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyArrayView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyArrayView.h @@ -37,9 +37,7 @@ template class PropertyArrayView { /** * @brief Constructs an array view from a vector of values. This is mainly - * used by PropertyTextureProperty -- since the values are swizzled from the - * texture, the values cannot be viewed in place, and must passed in through a - * correctly ordered vector. + * used when the values cannot be viewed in place. * * @param values The vector containing the values. */ @@ -85,18 +83,39 @@ template <> class PropertyArrayView { int64_t size) noexcept : _values{buffer}, _bitOffset{bitOffset}, _size{size} {} + /** + * @brief Constructs an array view from a vector of values. This is mainly + * used when the values cannot be viewed in place. + * + * @param values The vector containing the values. + */ + PropertyArrayView(const std::vector&& values) + : _values{std::move(values)}, _bitOffset{0}, _size{0} { + const auto vectorValues = std::get>(_values); + _size = static_cast(vectorValues.size()); + } + bool operator[](int64_t index) const noexcept { + // There's no way to access the bitstream data in std::vector, so this + // implementation is very "either or". + if (std::holds_alternative>(_values)) { + const auto vectorValues = std::get>(_values); + return vectorValues[static_cast(index)]; + } + + const auto values = std::get>(_values); index += _bitOffset; const int64_t byteIndex = index / 8; const int64_t bitIndex = index % 8; - const int bitValue = static_cast(_values[byteIndex] >> bitIndex) & 1; + const int bitValue = static_cast(values[byteIndex] >> bitIndex) & 1; return bitValue == 1; } int64_t size() const noexcept { return _size; } private: - gsl::span _values; + using ArrayType = std::variant, std::vector>; + ArrayType _values; int64_t _bitOffset; int64_t _size; }; @@ -127,7 +146,28 @@ template <> class PropertyArrayView { _stringOffsetType{stringOffsetType}, _size{size} {} + /** + * @brief Constructs an array view from a vector of values. This is mainly + * used when the values cannot be viewed in place. + * + * @param values The vector containing the values. + */ + PropertyArrayView(const std::vector&& values) + : _values{std::move(values)}, + _stringOffsets{}, + _stringOffsetType{PropertyComponentType::None}, + _size{0} { + const auto vectorValues = std::get>(_values); + _size = static_cast(vectorValues.size()); + } + std::string_view operator[](int64_t index) const noexcept { + if (std::holds_alternative>(_values)) { + const auto vectorValues = std::get>(_values); + return vectorValues[static_cast(index)]; + } + + const auto values = std::get>(_values); const size_t currentOffset = getOffsetFromOffsetsBuffer(index, _stringOffsets, _stringOffsetType); const size_t nextOffset = getOffsetFromOffsetsBuffer( @@ -135,15 +175,18 @@ template <> class PropertyArrayView { _stringOffsets, _stringOffsetType); return std::string_view( - reinterpret_cast(_values.data() + currentOffset), + reinterpret_cast(values.data() + currentOffset), (nextOffset - currentOffset)); } int64_t size() const noexcept { return _size; } private: - gsl::span _values; + using ArrayType = + std::variant, std::vector>; + ArrayType _values; gsl::span _stringOffsets; + PropertyComponentType _stringOffsetType; int64_t _size; }; diff --git a/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h index 08a7659e4..3edc7b0a8 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h @@ -2,6 +2,7 @@ #include "CesiumGltf/PropertyArrayView.h" #include "CesiumGltf/PropertyTypeTraits.h" +#include "CesiumGltf/PropertyView.h" #include @@ -168,17 +169,17 @@ enum class PropertyTablePropertyViewStatus { * the scalar types, bool, std::string_view, or PropertyArrayView with T as * one of the aforementioned types. */ -template class PropertyTablePropertyView { +template +class PropertyTablePropertyView : public PropertyView { public: /** * @brief Constructs an invalid instance for a non-existent property. */ PropertyTablePropertyView() - : _status{PropertyTablePropertyViewStatus::ErrorNonexistentProperty}, - _values{}, - _arrayCount{}, - _size{}, - _normalized{} {} + : PropertyView(), + _status{PropertyTablePropertyViewStatus::ErrorNonexistentProperty}, + _values, + _size{} {} /** * @brief Constructs an invalid instance for an erroneous property. @@ -186,7 +187,7 @@ template class PropertyTablePropertyView { * @param status The {@link PropertyTablePropertyViewStatus} indicating the error with the property. */ PropertyTablePropertyView(PropertyTablePropertyViewStatus status) - : _status{status}, _values{}, _arrayCount{}, _size{}, _normalized{} { + : PropertyView(), _status{status}, _values{}, _size{} { assert( _status != PropertyTablePropertyViewStatus::Valid && "An empty property view should not be constructed with a valid status"); @@ -200,10 +201,12 @@ template class PropertyTablePropertyView { * @param normalized Whether this property has a normalized integer type. */ PropertyTablePropertyView( - gsl::span values, + const PropertyTableProperty& property, + const ClassProperty& classProperty, int64_t size, - bool normalized) noexcept - : _status{PropertyTablePropertyViewStatus::Valid}, + gsl::span values) noexcept + : PropertyView(classProperty, property), + _status{PropertyTablePropertyViewStatus::Valid}, _values{values}, _arrayOffsets{}, _arrayOffsetType{PropertyComponentType::None}, @@ -211,9 +214,7 @@ template class PropertyTablePropertyView { _stringOffsets{}, _stringOffsetType{PropertyComponentType::None}, _stringOffsetTypeSize{0}, - _arrayCount{0}, - _size{size}, - _normalized{normalized} {} + _size{size} {} /** * @brief Construct a valid instance pointing to the data specified by @@ -225,18 +226,20 @@ template class PropertyTablePropertyView { * @param offsetType The offset type of stringOffsets specified by {@link PropertyTableProperty::stringOffsetType} * @param arrayCount The number of elements in each array value specified by {@link ClassProperty::count} * @param size The number of elements in the property table specified by {@link PropertyTable::count} - * @param normalized Whether this property has a normalized integer type. + * @param normalized Whether this property has a normalized integer + * type. */ PropertyTablePropertyView( + const PropertyTableProperty& property, + const ClassProperty& classProperty, + int64_t size, gsl::span values, gsl::span arrayOffsets, gsl::span stringOffsets, PropertyComponentType arrayOffsetType, - PropertyComponentType stringOffsetType, - int64_t arrayCount, - int64_t size, - bool normalized) noexcept - : _status{PropertyTablePropertyViewStatus::Valid}, + PropertyComponentType stringOffsetType) noexcept + : PropertyView(classProperty, property), + _status{PropertyTablePropertyViewStatus::Valid}, _values{values}, _arrayOffsets{arrayOffsets}, _arrayOffsetType{arrayOffsetType}, @@ -244,9 +247,7 @@ template class PropertyTablePropertyView { _stringOffsets{stringOffsets}, _stringOffsetType{stringOffsetType}, _stringOffsetTypeSize{getOffsetTypeSize(stringOffsetType)}, - _arrayCount{arrayCount}, - _size{size}, - _normalized{normalized} {} + _size{size} {} /** * @brief Gets the status of this property table property view. @@ -310,21 +311,6 @@ template class PropertyTablePropertyView { return status() == PropertyTablePropertyViewStatus::Valid ? _size : 0; } - /** - * @brief Get the element count of the fixed-length arrays in this property. - * Only applicable when the property is an array type. - * - * @return The count of this property. - */ - int64_t getArrayCount() const noexcept { return _arrayCount; } - - /** - * @brief Whether this property has a normalized integer type. - * - * @return Whether this property has a normalized integer type. - */ - bool isNormalized() const noexcept { return _normalized; } - private: ElementType getNumericValue(int64_t index) const noexcept { return reinterpret_cast(_values.data())[index]; @@ -351,9 +337,10 @@ template class PropertyTablePropertyView { template PropertyArrayView getNumericArrayValues(int64_t index) const noexcept { + size_t count = static_cast(this->arrayCount()); // Handle fixed-length arrays - if (_arrayCount > 0) { - size_t arraySize = _arrayCount * sizeof(T); + if (count > 0) { + size_t arraySize = count * sizeof(T); const gsl::span values( _values.data() + index * arraySize, arraySize); @@ -373,10 +360,11 @@ template class PropertyTablePropertyView { PropertyArrayView getStringArrayValues(int64_t index) const noexcept { + size_t count = static_cast(this->arrayCount()); // Handle fixed-length arrays - if (_arrayCount > 0) { + if (count > 0) { // Copy the corresponding string offsets to pass to the PropertyArrayView. - const size_t arraySize = _arrayCount * _stringOffsetTypeSize; + const size_t arraySize = count * _stringOffsetTypeSize; const gsl::span stringOffsetValues( _stringOffsets.data() + index * arraySize, arraySize + _stringOffsetTypeSize); @@ -384,7 +372,7 @@ template class PropertyTablePropertyView { _values, stringOffsetValues, _stringOffsetType, - _arrayCount); + count); } // Handle variable-length arrays @@ -404,14 +392,15 @@ template class PropertyTablePropertyView { } PropertyArrayView getBooleanArrayValues(int64_t index) const noexcept { + size_t count = static_cast(this->arrayCount()); // Handle fixed-length arrays - if (_arrayCount > 0) { - const size_t offsetBits = _arrayCount * index; - const size_t nextOffsetBits = _arrayCount * (index + 1); + if (count > 0) { + const size_t offsetBits = count * index; + const size_t nextOffsetBits = count * (index + 1); const gsl::span buffer( _values.data() + offsetBits / 8, (nextOffsetBits / 8 - offsetBits / 8 + 1)); - return PropertyArrayView(buffer, offsetBits % 8, _arrayCount); + return PropertyArrayView(buffer, offsetBits % 8, count); } // Handle variable-length arrays @@ -452,8 +441,6 @@ template class PropertyTablePropertyView { PropertyComponentType _stringOffsetType; int64_t _stringOffsetTypeSize; - int64_t _arrayCount; int64_t _size; - bool _normalized; }; } // namespace CesiumGltf diff --git a/CesiumGltf/include/CesiumGltf/PropertyTableView.h b/CesiumGltf/include/CesiumGltf/PropertyTableView.h index 6af76ce7f..c43c26512 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTableView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTableView.h @@ -1025,9 +1025,10 @@ class PropertyTableView { } return PropertyTablePropertyView( - values, + propertyTableProperty, + classProperty, _pPropertyTable->count, - classProperty.normalized); + values); } PropertyTablePropertyView getStringPropertyValues( @@ -1103,14 +1104,14 @@ class PropertyTableView { } return PropertyTablePropertyView>( + propertyTableProperty, + classProperty, + static_cast(_pPropertyTable->count), values, gsl::span(), gsl::span(), PropertyComponentType::None, - PropertyComponentType::None, - static_cast(fixedLengthArrayCount), - static_cast(_pPropertyTable->count), - classProperty.normalized); + PropertyComponentType::None); } // Handle variable-length arrays @@ -1136,14 +1137,14 @@ class PropertyTableView { } return PropertyTablePropertyView>( + propertyTableProperty, + classProperty, + static_cast(_pPropertyTable->count), values, arrayOffsets, gsl::span(), arrayOffsetType, - PropertyComponentType::None, - 0, - static_cast(_pPropertyTable->count), - classProperty.normalized); + PropertyComponentType::None); } PropertyTablePropertyView> diff --git a/CesiumGltf/include/CesiumGltf/PropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyView.h new file mode 100644 index 000000000..26bec92ea --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/PropertyView.h @@ -0,0 +1,1593 @@ +#include "CesiumGltf/ClassProperty.h" +#include "CesiumGltf/PropertyTableProperty.h" +#include "CesiumGltf/PropertyTextureProperty.h" +#include "CesiumGltf/PropertyTypeTraits.h" + +#include +#include + +namespace CesiumGltf { +/** + * @brief Indicates the status of a property view. + * + * The {@link PropertyView} constructor always completes successfully. + * However, it may find fundamental errors within the property definition + * itself. This enumeration provides the reason. + */ +enum class PropertyViewStatus { + /** + * @brief This property view is valid and ready to use. + */ + Valid, + + /** + * @brief This property view is trying to view a property that does not exist. + */ + ErrorNonexistentProperty, + + /** + * @brief This property view's type does not match what is + * specified in {@link ClassProperty::type}. + */ + ErrorTypeMismatch, + + /** + * @brief This property view's component type does not match what + * is specified in {@link ClassProperty::componentType}. + */ + ErrorComponentTypeMismatch, + + /** + * @brief This property view differs from what is specified in + * {@link ClassProperty::array}. + */ + ErrorArrayTypeMismatch, +}; + +/** + * @brief An interface for generic metadata property in EXT_structural_metadata. + * + * Whether they belong to property tables, property textures, or property + * attributes, properties have their own sub-properties affecting the actual + * property values. Although they are typically defined via class property, they + * may be overriden by individual instances of the property. The constructor is + * responsible for resolving those differences. + */ +template class IPropertyView { +public: + /** + * @brief Get the element count of the fixed-length arrays in this property. + * Only applicable when the property is an array type. + * + * @return The count of this property. + */ + virtual int64_t arrayCount() const noexcept = 0; + + /** + * @brief Whether this property has a normalized integer type. + * + * @return Whether this property has a normalized integer type. + */ + virtual bool normalized() const noexcept = 0; + + /** + * @brief Gets the offset to apply to property values. Only applicable to + * SCALAR, VECN, and MATN types when the component type is FLOAT32 or + * FLOAT64, or when the property is normalized. + * + * @returns The property's offset, or std::nullopt if it was not specified. + */ + virtual std::optional offset() const noexcept = 0; + /** + * @brief Gets the scale to apply to property values. Only applicable to + * SCALAR, VECN, and MATN types when the component type is FLOAT32 or + * FLOAT64, or when the property is normalized. + * + * @returns The property's scale, or std::nullopt if it was not specified. + */ + virtual std::optional scale() const noexcept = 0; + + /** + * @brief Gets the maximum allowed value for the property. Only applicable to + * SCALAR, VECN, and MATN types. This is the maximum of all property + * values, after the transforms based on the normalized, offset, and + * scale properties have been applied. + * + * @returns The property's maximum value, or std::nullopt if it was not + * specified. + */ + virtual std::optional max() const noexcept = 0; + + /** + * @brief Gets the minimum allowed value for the property. Only applicable to + * SCALAR, VECN, and MATN types. This is the minimum of all property + * values, after the transforms based on the normalized, offset, and + * scale properties have been applied. + * + * @returns The property's minimum value, or std::nullopt if it was not + * specified. + */ + virtual std::optional min() const noexcept = 0; + + /** + * @brief Whether the property must be present in every entity conforming to + * the class. If not required, instances of the property may include "no data" + * values, or the entire property may be omitted. + */ + virtual bool required() const noexcept = 0; + + /** + * @brief Gets the "no data" value, i.e., the value representing missing data + * in the property wherever it appears. Also known as a sentinel value. This + * is given as the plain property value, without the transforms from the + * normalized, offset, and scale properties. + */ + virtual std::optional noData() const noexcept = 0; + + /** + * @brief Gets the default value to use when encountering a "no data" value or + * an omitted property. The value is given in its final form, taking the + * effect of normalized, offset, and scale properties into account. + */ + virtual std::optional defaultValue() const noexcept = 0; + +protected: + /** + * @brief Gets the status of the property view. Derived classes will often + * have their own status enumerations, so this is only used internally. + */ + virtual PropertyViewStatus propertyViewStatus() const noexcept = 0; +}; + +/** + * @brief Represents a generic metadata property in EXT_structural_metadata. + * Implements the {@link IPropertyView} interface. + * + * Child classes should validate that the class property type matches the + * "ElementType" before constructing a property view. + */ +template +class PropertyView : IPropertyView { +public: + /** + * @brief Constructs an empty property instance. + */ + PropertyView() + : _propertyViewStatus(PropertyViewStatus::ErrorNonexistentProperty), + _normalized(false), + _offset(std::nullopt), + _scale(std::nullopt), + _max(std::nullopt), + _min(std::nullopt), + _required(false), + _noData(std::nullopt), + _defaultValue(std::nullopt) {} + + /** + * @brief Constructs a property instance from a class definition only. + */ + PropertyView(const ClassProperty& classProperty) + : _propertyViewStatus(PropertyViewStatus::Valid), + _normalized(classProperty.normalized), + _offset(std::nullopt), + _scale(std::nullopt), + _max(std::nullopt), + _min(std::nullopt), + _required(classProperty.required), + _noData(std::nullopt), + _defaultValue(std::nullopt) { + if (convertStringToPropertyType(classProperty.type) != + TypeToPropertyType::value) { + _propertyViewStatus = PropertyViewStatus::ErrorTypeMismatch; + return; + } + + if (!classProperty.componentType && + TypeToPropertyType::component != + PropertyComponentType::None) { + _propertyViewStatus = PropertyViewStatus::ErrorComponentTypeMismatch; + return; + } + + if (classProperty.componentType && + convertStringToPropertyComponentType(*classProperty.componentType) != + TypeToPropertyType::component) { + _propertyViewStatus = PropertyViewStatus::ErrorComponentTypeMismatch; + return; + } + + if (classProperty.array) { + _propertyViewStatus = PropertyViewStatus::ErrorArrayTypeMismatch; + return; + } + + _offset = classProperty.offset + ? getValue(*classProperty.offset) + : std::nullopt; + _scale = classProperty.scale ? getValue(*classProperty.scale) + : std::nullopt; + _max = classProperty.max ? getValue(*classProperty.max) + : std::nullopt; + _min = classProperty.min ? getValue(*classProperty.min) + : std::nullopt; + + if (!_required) { + _noData = classProperty.noData + ? getValue(*classProperty.noData) + : std::nullopt; + _defaultValue = + classProperty.defaultProperty + ? getValue(*classProperty.defaultProperty) + : std::nullopt; + } + } + +protected: + /** + * @brief Constructs a property instance from a property table property and + * its class definition. + */ + PropertyView( + const ClassProperty& classProperty, + const PropertyTableProperty& property) + : PropertyView(classProperty) { + if (_propertyViewStatus != PropertyViewStatus::Valid) { + return; + } + + // If the property has its own values, override the class-provided values. + if (property.offset) { + _offset = getValue(*property.offset); + } + + if (property.scale) { + _scale = getValue(*property.scale); + } + + if (property.max) { + _max = getValue(*property.max); + } + + if (property.min) { + _min = getValue(*property.min); + } + } + + /** + * @brief Constructs a property instance from a property texture property and + * its class definition. + */ + PropertyView( + const ClassProperty& classProperty, + const PropertyTextureProperty& property) + : PropertyView(classProperty) { + if (_propertyViewStatus != PropertyViewStatus::Valid) { + return; + } + + // If the property has its own values, override the class-provided values. + if (property.offset) { + _offset = getValue(*property.offset); + } + + if (property.scale) { + _scale = getValue(*property.scale); + } + + if (property.max) { + _max = getValue(*property.max); + } + + if (property.min) { + _min = getValue(*property.min); + } + } + +public: + /** + * @copydoc IPropertyView::arrayCount + */ + virtual int64_t arrayCount() const noexcept override { return 0; } + + /** + * @copydoc IPropertyView::normalized + */ + virtual bool normalized() const noexcept override { return _normalized; } + + /** + * @copydoc IPropertyView::offset + */ + virtual std::optional offset() const noexcept override { + return _offset; + } + + /** + * @copydoc IPropertyView::scale + */ + virtual std::optional scale() const noexcept override { + return _scale; + } + + /** + * @copydoc IPropertyView::max + */ + virtual std::optional max() const noexcept override { + return _max; + } + + /** + * @copydoc IPropertyView::min + */ + virtual std::optional min() const noexcept override { + return _min; + } + + /** + * @copydoc IPropertyView::required + */ + virtual bool required() const noexcept override { return _required; } + + /** + * @copydoc IPropertyView::arrayCount + */ + virtual std::optional noData() const noexcept override { + return _noData; + } + + /** + * @copydoc IPropertyView::arrayCount + */ + virtual std::optional defaultValue() const noexcept override { + return _defaultValue; + } + +protected: + /** + * @copydoc IPropertyView::propertyViewStatus + */ + virtual PropertyViewStatus propertyViewStatus() const noexcept override { + return _propertyViewStatus; + } + +private: + PropertyViewStatus _propertyViewStatus; + + bool _normalized; + + std::optional _offset; + std::optional _scale; + std::optional _max; + std::optional _min; + + bool _required; + std::optional _noData; + std::optional _defaultValue; + + /** + * @brief Attempts to parse from the given json value. + * + * If T is a type with multiple components, e.g. a VECN or MATN type, this + * will return std::nullopt if one or more components could not be parsed. + * + * If T is an array view, this will return std::nullopt if one or + * more entries could not be parsed. + * + * @return The value as an instance of T, or std::nullopt if it could not be + * parsed. + */ + template + static std::optional getValue(const CesiumUtility::JsonValue& jsonValue) { + if constexpr (IsMetadataScalar::value) { + return getScalar(jsonValue); + } else + + if constexpr (IsMetadataVecN::value) { + return getVecN(jsonValue); + } else + return std::nullopt; + + // if constexpr (IsMetadataMatN::value) { + //} + } + + /** + * @brief Attempts to parse from the given json value. If it could not be + * parsed as an ElementType, this returns the default value. + * + * If ElementType is a type with multiple components, e.g. a VECN or MATN + * type. + * + * @return The value as an ElementType, or std::nullopt if it could not be + * parsed. + */ + template + static T getValueOrDefault( + const CesiumUtility::JsonValue& jsonValue, + const T& defaultValue) { + if constexpr (IsMetadataScalar::value) { + return jsonValue.getSafeNumberOrDefault(defaultValue); + } + + if constexpr (IsMetadataVecN::value) { + return getVecNOrDefault(jsonValue, defaultValue); + } + + if constexpr (IsMetadataMatN::value) { + } + } + + /** + * @brief Attempts to parse from the given json value. If it could not be + * parsed as an ElementType, this returns the default value. + * + * If ElementType is a type with multiple components, e.g. a VECN or MATN + * type. + * + * @return The value as an ElementType, or std::nullopt if it could not be + * parsed. + */ + template + static std::optional getScalar(const CesiumUtility::JsonValue& jsonValue) { + try { + return jsonValue.getSafeNumber(); + } catch (const CesiumUtility::JsonValueNotRealValue& /*error*/) { + return std::nullopt; + } catch (const gsl::narrowing_error& /*error*/) { + // Continue below. + } + + // If the value is in range, but it loses precision, a narrowing_error will + // be returned. Try to round to a reasonable number anyways, even if it + // loses precision. + if (jsonValue.isInt64()) { + int64_t int64Value = jsonValue.getInt64(); + if (int64Value >= + static_cast(std::numeric_limits::lowest()) && + int64Value <= static_cast(std::numeric_limits::max())) { + return static_cast(int64Value); + } + } else if (jsonValue.isUint64()) { + uint64_t uint64Value = jsonValue.getUint64(); + if (uint64Value <= static_cast(std::numeric_limits::max())) { + return static_cast(uint64Value); + } + } else if (jsonValue.isDouble()) { + double doubleValue = jsonValue.getDouble(); + if (doubleValue >= + static_cast(std::numeric_limits::lowest()) && + doubleValue <= static_cast(std::numeric_limits::max())) { + return static_cast(doubleValue); + } + } + + return std::nullopt; + } + + template + static std::optional + getVecN(const CesiumUtility::JsonValue& jsonValue) { + if (!jsonValue.isArray()) { + return std::nullopt; + } + + return std::nullopt; + // const CesiumUtility::JsonValue::Array& array = value.getArray(); + // glm::length_t N = VecType::length(); + // if (array.size() != N) { + // return std::nullopt; + //} + + // using T = VecType::type; + + // glm::length result; + // for (glm::length_t i = 0; i < N; i++) { + // std::optional value = getValue(array[i]); + // if (!value) { + // return std::nullopt; + // } + + // result[i] = value; + //} + + // return result; + } + + template + static VecType getVecNOrDefault( + const CesiumUtility::JsonValue& value, + const VecType& defaultValue) { + if (!jsonValue.isArray()) { + return defaultValue; + } + + const CesiumUtility::JsonValue::Array& array = jsonValue.getArray(); + glm::length_t N = VecType::length(); + if (array.size() != N) { + return defaultValue; + } + + using T = VecType::type; + + VecType result; + for (glm::length_t i = 0; i < N; i++) { + std::optional value = getValue(array[i]); + if (!value) { + return std::nullopt; + } + + result[i] = value ? *value : defaultValue; + } + + return result; + } + + // template + // static std::optional + // getMatNFromJsonValue(const CesiumUtility::JsonValue& value) { + // if (!jsonValue.isArray()) { + // return std::nullopt; + // } + + // const CesiumUtility::JsonValue::Array& array = jsonValue.getArray(); + // if (array.size() != N) { + // return std::nullopt; + // } + + // glm::vec result; + // for (glm::length_t i = 0; i < N; i++) { + // std::optional value = getValue(array[i]); + // if (!value) { + // return std::nullopt; + // } + + // result[i] = value; + // } + + // return result; + //} +}; + +template <> class PropertyView : IPropertyView { +public: + /** + * @brief Constructs an empty property instance. + */ + PropertyView() + : _propertyViewStatus(PropertyViewStatus::ErrorNonexistentProperty), + _required(false), + _defaultValue(std::nullopt) {} + + /** + * @brief Constructs a property instance from a class definition only. + */ + PropertyView(const ClassProperty& classProperty) + : _propertyViewStatus(PropertyViewStatus::Valid), + _required(classProperty.required), + _defaultValue(std::nullopt) { + if (classProperty.type != ClassProperty::Type::BOOLEAN) { + _propertyViewStatus = PropertyViewStatus::ErrorTypeMismatch; + return; + } + + if (classProperty.array) { + _propertyViewStatus = PropertyViewStatus::ErrorArrayTypeMismatch; + return; + } + + if (!_required) { + _defaultValue = classProperty.defaultProperty + ? getBooleanValue(*classProperty.defaultProperty) + : std::nullopt; + } + } + + /** + * @brief Constructs a property instance from a property table property and + * its class definition. + */ + PropertyView( + const ClassProperty& classProperty, + const PropertyTableProperty& /*property*/) + : PropertyView(classProperty) {} + + /** + * @brief Constructs a property instance from a property texture property and + * its class definition. + */ + PropertyView( + const ClassProperty& classProperty, + const PropertyTextureProperty& /*property*/) + : PropertyView(classProperty) {} + + ~PropertyView() {} + + /** + * @copydoc IPropertyView::arrayCount + */ + virtual int64_t arrayCount() const noexcept override { return 0; } + + /** + * @copydoc IPropertyView::normalized + */ + virtual bool normalized() const noexcept override { return false; } + + /** + * @copydoc IPropertyView::offset + */ + virtual std::optional offset() const noexcept override { + return std::nullopt; + } + + /** + * @copydoc IPropertyView::scale + */ + virtual std::optional scale() const noexcept override { + return std::nullopt; + } + + /** + * @copydoc IPropertyView::max + */ + virtual std::optional max() const noexcept override { + return std::nullopt; + } + + /** + * @copydoc IPropertyView::min + */ + virtual std::optional min() const noexcept override { + return std::nullopt; + } + + /** + * @copydoc IPropertyView::required + */ + virtual bool required() const noexcept override { return _required; } + + /** + * @copydoc IPropertyView::noData + */ + virtual std::optional noData() const noexcept override { + return std::nullopt; + } + + /** + * @copydoc IPropertyView::defaultValue + */ + virtual std::optional defaultValue() const noexcept override { + return _defaultValue; + } + +protected: + /** + * @copydoc IPropertyView::propertyViewStatus + */ + virtual PropertyViewStatus propertyViewStatus() const noexcept override { + return _propertyViewStatus; + } + +private: + PropertyViewStatus _propertyViewStatus; + bool _required; + std::optional _defaultValue; + + static std::optional + getBooleanValue(const CesiumUtility::JsonValue& value) { + if (!value.isBool()) { + return std::nullopt; + } + + return value.getBool(); + } +}; + +template <> +class PropertyView : IPropertyView { +public: + /** + * @brief Constructs an empty property instance. + */ + PropertyView() + : _propertyViewStatus(PropertyViewStatus::ErrorNonexistentProperty), + _required(false), + _noData(std::nullopt), + _defaultValue(std::nullopt) {} + + /** + * @brief Constructs a property instance from a class definition only. + */ + PropertyView(const ClassProperty& classProperty) + : _propertyViewStatus(PropertyViewStatus::Valid), + _required(classProperty.required), + _noData(std::nullopt), + _defaultValue(std::nullopt) { + if (classProperty.type != ClassProperty::Type::STRING) { + _propertyViewStatus = PropertyViewStatus::ErrorTypeMismatch; + return; + } + + if (classProperty.array) { + _propertyViewStatus = PropertyViewStatus::ErrorArrayTypeMismatch; + return; + } + + if (!_required) { + _noData = classProperty.noData ? getStringValue(*classProperty.noData) + : std::nullopt; + _defaultValue = classProperty.defaultProperty + ? getStringValue(*classProperty.defaultProperty) + : std::nullopt; + } + } + + /** + * @brief Constructs a property instance from a property table property and + * its class definition. + */ + PropertyView( + const ClassProperty& classProperty, + const PropertyTableProperty& /*property*/) + : PropertyView(classProperty) {} + + /** + * @brief Constructs a property instance from a property texture property and + * its class definition. + */ + PropertyView( + const ClassProperty& classProperty, + const PropertyTextureProperty& /*property*/) + : PropertyView(classProperty) {} + + ~PropertyView() {} + + /** + * @copydoc IPropertyView::arrayCount + */ + virtual int64_t arrayCount() const noexcept override { return 0; } + + /** + * @copydoc IPropertyView::normalized + */ + virtual bool normalized() const noexcept override { return false; } + + /** + * @copydoc IPropertyView::offset + */ + virtual std::optional offset() const noexcept override { + return std::nullopt; + } + + /** + * @copydoc IPropertyView::scale + */ + virtual std::optional scale() const noexcept override { + return std::nullopt; + } + + /** + * @copydoc IPropertyView::max + */ + virtual std::optional max() const noexcept override { + return std::nullopt; + } + + /** + * @copydoc IPropertyView::min + */ + virtual std::optional min() const noexcept override { + return std::nullopt; + } + + /** + * @copydoc IPropertyView::required + */ + virtual bool required() const noexcept override { return _required; } + + /** + * @copydoc IPropertyView::noData + */ + virtual std::optional noData() const noexcept override { + if (_noData) + return std::string_view(*_noData); + + return std::nullopt; + } + + /** + * @copydoc IPropertyView::defaultValue + */ + virtual std::optional + defaultValue() const noexcept override { + if (_defaultValue) + return std::string_view(*_defaultValue); + + return std::nullopt; + } + +protected: + /** + * @copydoc IPropertyView::propertyViewStatus + */ + virtual PropertyViewStatus propertyViewStatus() const noexcept override { + return _propertyViewStatus; + } + +private: + PropertyViewStatus _propertyViewStatus; + bool _required; + std::optional _noData; + std::optional _defaultValue; + + static std::optional + getStringValue(const CesiumUtility::JsonValue& value) { + if (!value.isString()) { + return std::nullopt; + } + + return std::string(value.getString().c_str()); + } +}; + +template +class PropertyView> + : IPropertyView> { +public: + /** + * @brief Constructs an empty property instance. + */ + PropertyView() + : _propertyViewStatus(PropertyViewStatus::ErrorNonexistentProperty), + _count(0), + _normalized(false), + _offset(std::nullopt), + _scale(std::nullopt), + _max(std::nullopt), + _min(std::nullopt), + _required(false), + _noData(std::nullopt), + _defaultValue(std::nullopt) {} + + /** + * @brief Constructs a property instance from a class definition only. + */ + PropertyView(const ClassProperty& classProperty) + : _propertyViewStatus(PropertyViewStatus::Valid), + _count(0), + _normalized(classProperty.normalized), + _offset(std::nullopt), + _scale(std::nullopt), + _max(std::nullopt), + _min(std::nullopt), + _required(classProperty.required), + _noData(std::nullopt), + _defaultValue(std::nullopt) { + if (convertStringToPropertyType(classProperty.type) != + TypeToPropertyType::value) { + _propertyViewStatus = PropertyViewStatus::ErrorTypeMismatch; + return; + } + + if (!classProperty.componentType && + TypeToPropertyType::component != + PropertyComponentType::None) { + _propertyViewStatus = PropertyViewStatus::ErrorComponentTypeMismatch; + return; + } + + if (classProperty.componentType && + convertStringToPropertyComponentType(*classProperty.componentType) != + TypeToPropertyType::component) { + _propertyViewStatus = PropertyViewStatus::ErrorComponentTypeMismatch; + return; + } + + if (!classProperty.array) { + _propertyViewStatus = PropertyViewStatus::ErrorArrayTypeMismatch; + return; + } + + _count = classProperty.count ? *classProperty.count : 0; + + //_offset = classProperty.offset + // ? getValue(*classProperty.offset) + // : std::nullopt; + //_scale = classProperty.scale ? getValue(*classProperty.scale) + // : std::nullopt; + //_max = classProperty.max ? getValue(*classProperty.max) + // : std::nullopt; + //_min = classProperty.min ? getValue(*classProperty.min) + // : std::nullopt; + + //if (!_required) { + // _noData = classProperty.noData + // ? getValue(*classProperty.noData) + // : std::nullopt; + // _defaultValue = + // classProperty.defaultProperty + // ? getValue(*classProperty.defaultProperty) + // : std::nullopt; + //} + } + +protected: + /** + * @brief Constructs a property instance from a property table property and + * its class definition. + */ + PropertyView( + const ClassProperty& classProperty, + const PropertyTableProperty& /*property*/) + : PropertyView(classProperty) { + if (_propertyViewStatus != PropertyViewStatus::Valid) { + return; + } + + //// If the property has its own values, override the class-provided values. + //if (property.offset) { + // _offset = getValue(*property.offset); + //} + + //if (property.scale) { + // _scale = getValue(*property.scale); + //} + + //if (property.max) { + // _max = getValue(*property.max); + //} + + //if (property.min) { + // _min = getValue(*property.min); + //} + } + + /** + * @brief Constructs a property instance from a property texture property and + * its class definition. + */ + PropertyView( + const ClassProperty& classProperty, + const PropertyTextureProperty& /*property*/) + : PropertyView(classProperty) { + if (_propertyViewStatus != PropertyViewStatus::Valid) { + return; + } + + //// If the property has its own values, override the class-provided values. + //if (property.offset) { + // _offset = getValue(*property.offset); + //} + + //if (property.scale) { + // _scale = getValue(*property.scale); + //} + + //if (property.max) { + // _max = getValue(*property.max); + //} + + //if (property.min) { + // _min = getValue(*property.min); + //} + } + +public: + /** + * @copydoc IPropertyView::arrayCount + */ + virtual int64_t arrayCount() const noexcept override { return _count; } + + /** + * @copydoc IPropertyView::normalized + */ + virtual bool normalized() const noexcept override { return _normalized; } + + /** + * @copydoc IPropertyView::offset + */ + virtual std::optional> + offset() const noexcept override { + return _offset; + } + + /** + * @copydoc IPropertyView::scale + */ + virtual std::optional> + scale() const noexcept override { + return _scale; + } + + /** + * @copydoc IPropertyView::max + */ + virtual std::optional> + max() const noexcept override { + return _max; + } + + /** + * @copydoc IPropertyView::min + */ + virtual std::optional> + min() const noexcept override { + return _min; + } + + /** + * @copydoc IPropertyView::required + */ + virtual bool required() const noexcept override { return _required; } + + /** + * @copydoc IPropertyView::noData + */ + virtual std::optional> + noData() const noexcept override { + return _noData; + } + + /** + * @copydoc IPropertyView::defaultValue + */ + virtual std::optional> + defaultValue() const noexcept override { + return _defaultValue; + } + +protected: + /** + * @copydoc IPropertyView::propertyViewStatus + */ + virtual PropertyViewStatus propertyViewStatus() const noexcept override { + return _propertyViewStatus; + } + +private: + PropertyViewStatus _propertyViewStatus; + + int64_t _count; + bool _normalized; + + std::optional> _offset; + std::optional> _scale; + std::optional> _max; + std::optional> _min; + + bool _required; + std::optional> _noData; + std::optional> _defaultValue; + + /** + * @brief Attempts to parse from the given json value. + * + * If T is a type with multiple components, e.g. a VECN or MATN type, this + * will return std::nullopt if one or more components could not be parsed. + * + * If T is an array view, this will return std::nullopt if one or + * more entries could not be parsed. + * + * @return The value as an instance of T, or std::nullopt if it could not be + * parsed. + */ + template + static std::optional getValue(const CesiumUtility::JsonValue& jsonValue) { + if constexpr (IsMetadataScalar::value) { + return getScalar(jsonValue); + } else + + if constexpr (IsMetadataVecN::value) { + return getVecN(jsonValue); + } else + return std::nullopt; + + // if constexpr (IsMetadataMatN::value) { + //} + } + + /** + * @brief Attempts to parse from the given json value. If it could not be + * parsed as an ElementType, this returns the default value. + * + * If ElementType is a type with multiple components, e.g. a VECN or MATN + * type. + * + * @return The value as an ElementType, or std::nullopt if it could not be + * parsed. + */ + template + static T getValueOrDefault( + const CesiumUtility::JsonValue& jsonValue, + const T& defaultValue) { + if constexpr (IsMetadataScalar::value) { + return jsonValue.getSafeNumberOrDefault(defaultValue); + } + + if constexpr (IsMetadataVecN::value) { + return getVecNOrDefault(jsonValue, defaultValue); + } + + if constexpr (IsMetadataMatN::value) { + } + + if constexpr (IsMetadataArray::value) { + return defaultValue; + // if (!jsonValue.isArray()) { + // return PropertyArrayView(std::vector()); + // } + + // return jsonValue.isArray() + // ? getArrayValueOrDefault(jsonValue.getArray(), + // defaultValue) : defaultValue; + } + } + + /** + * @brief Attempts to parse from the given json value. If it could not be + * parsed as an ElementType, this returns the default value. + * + * If ElementType is a type with multiple components, e.g. a VECN or MATN + * type. + * + * @return The value as an ElementType, or std::nullopt if it could not be + * parsed. + */ + template + static std::optional getScalar(const CesiumUtility::JsonValue& jsonValue) { + try { + return jsonValue.getSafeNumber(); + } catch (const CesiumUtility::JsonValueNotRealValue& /*error*/) { + return std::nullopt; + } catch (const gsl::narrowing_error& /*error*/) { + // Continue below. + } + + // If the value is in range, but it loses precision, a narrowing_error will + // be returned. Try to round to a reasonable number anyways, even if it + // loses precision. + if (jsonValue.isInt64()) { + int64_t int64Value = jsonValue.getInt64(); + if (int64Value >= + static_cast(std::numeric_limits::lowest()) && + int64Value <= static_cast(std::numeric_limits::max())) { + return static_cast(int64Value); + } + } else if (jsonValue.isUint64()) { + uint64_t uint64Value = jsonValue.getUint64(); + if (uint64Value <= static_cast(std::numeric_limits::max())) { + return static_cast(uint64Value); + } + } else if (jsonValue.isDouble()) { + double doubleValue = jsonValue.getDouble(); + if (doubleValue >= + static_cast(std::numeric_limits::lowest()) && + doubleValue <= static_cast(std::numeric_limits::max())) { + return static_cast(doubleValue); + } + } + + return std::nullopt; + } + + template + static std::optional + getVecN(const CesiumUtility::JsonValue& jsonValue) { + if (!jsonValue.isArray()) { + return std::nullopt; + } + + return std::nullopt; + // const CesiumUtility::JsonValue::Array& array = value.getArray(); + // glm::length_t N = VecType::length(); + // if (array.size() != N) { + // return std::nullopt; + //} + + // using T = VecType::type; + + // glm::length result; + // for (glm::length_t i = 0; i < N; i++) { + // std::optional value = getValue(array[i]); + // if (!value) { + // return std::nullopt; + // } + + // result[i] = value; + //} + + // return result; + } + + template + static VecType getVecNOrDefault( + const CesiumUtility::JsonValue& value, + const VecType& defaultValue) { + if (!jsonValue.isArray()) { + return defaultValue; + } + + const CesiumUtility::JsonValue::Array& array = jsonValue.getArray(); + glm::length_t N = VecType::length(); + if (array.size() != N) { + return defaultValue; + } + + using T = VecType::type; + + VecType result; + for (glm::length_t i = 0; i < N; i++) { + std::optional value = getValue(array[i]); + if (!value) { + return std::nullopt; + } + + result[i] = value ? *value : defaultValue; + } + + return result; + } + + // template + // static std::optional + // getMatNFromJsonValue(const CesiumUtility::JsonValue& value) { + // if (!jsonValue.isArray()) { + // return std::nullopt; + // } + + // const CesiumUtility::JsonValue::Array& array = jsonValue.getArray(); + // if (array.size() != N) { + // return std::nullopt; + // } + + // glm::vec result; + // for (glm::length_t i = 0; i < N; i++) { + // std::optional value = getValue(array[i]); + // if (!value) { + // return std::nullopt; + // } + + // result[i] = value; + // } + + // return result; + //} + + template + static std::optional> + getArrayValue(const CesiumUtility::JsonValue::Array& /*arrayValue*/) { + return std::nullopt; + // std::vector values(arrayValue.size()); + // for (size_t i = 0; i < arrayValue.size(); i++) { + // const CesiumUtility::JsonValue& value = arrayValue[i]; + // std::optional arrayElement = getValue(value); + // values[i] = arrayElement; + //} + + // return PropertyArrayView(values); + } + + template + static PropertyArrayView getArrayValueOrDefault( + const CesiumUtility::JsonValue::Array& arrayValue, + const ArrayType& defaultElementValue) { + + std::vector values(arrayValue.size()); + for (size_t i = 0; i < arrayValue.size(); i++) { + const CesiumUtility::JsonValue& value = arrayValue[i]; + std::optional arrayElement = getValue(value); + values[i] = arrayElement; + } + + return PropertyArrayView(values); + } +}; + +template <> +class PropertyView> + : IPropertyView> { +public: + /** + * @brief Constructs an empty property instance. + */ + PropertyView() + : _propertyViewStatus(PropertyViewStatus::ErrorNonexistentProperty), + _count(0), + _required(false), + _defaultValue(std::nullopt) {} + + /** + * @brief Constructs a property instance from a class definition only. + */ + PropertyView(const ClassProperty& classProperty) + : _propertyViewStatus(PropertyViewStatus::Valid), + _count(0), + _required(classProperty.required), + _defaultValue(std::nullopt) { + if (classProperty.type != ClassProperty::Type::BOOLEAN) { + _propertyViewStatus = PropertyViewStatus::ErrorTypeMismatch; + return; + } + + if (!classProperty.array) { + _propertyViewStatus = PropertyViewStatus::ErrorArrayTypeMismatch; + return; + } + + _count = classProperty.count ? *classProperty.count : 0; + + if (!_required) { + _defaultValue = + classProperty.defaultProperty + ? getBooleanArrayValues(*classProperty.defaultProperty) + : std::nullopt; + } + } + + /** + * @brief Constructs a property instance from a property table property and + * its class definition. + */ + PropertyView( + const ClassProperty& classProperty, + const PropertyTableProperty& /*property*/) + : PropertyView(classProperty) {} + + /** + * @brief Constructs a property instance from a property texture property and + * its class definition. + */ + PropertyView( + const ClassProperty& classProperty, + const PropertyTextureProperty& /*property*/) + : PropertyView(classProperty) {} + + ~PropertyView() {} + + /** + * @copydoc IPropertyView::arrayCount + */ + virtual int64_t arrayCount() const noexcept override { return _count; } + + /** + * @copydoc IPropertyView::normalized + */ + virtual bool normalized() const noexcept override { return false; } + + /** + * @copydoc IPropertyView::offset + */ + virtual std::optional> + offset() const noexcept override { + return std::nullopt; + } + + /** + * @copydoc IPropertyView::scale + */ + virtual std::optional> + scale() const noexcept override { + return std::nullopt; + } + + /** + * @copydoc IPropertyView::max + */ + virtual std::optional> max() const noexcept override { + return std::nullopt; + } + + /** + * @copydoc IPropertyView::min + */ + virtual std::optional> min() const noexcept override { + return std::nullopt; + } + + /** + * @copydoc IPropertyView::required + */ + virtual bool required() const noexcept override { return _required; } + + /** + * @copydoc IPropertyView::noData + */ + virtual std::optional> + noData() const noexcept override { + return std::nullopt; + } + + /** + * @copydoc IPropertyView::defaultValue + */ + virtual std::optional> + defaultValue() const noexcept override { + return _defaultValue; + } + +protected: + /** + * @copydoc IPropertyView::propertyViewStatus + */ + virtual PropertyViewStatus propertyViewStatus() const noexcept override { + return _propertyViewStatus; + } + +private: + PropertyViewStatus _propertyViewStatus; + int64_t _count; + bool _required; + std::optional> _defaultValue; + + static std::optional> + getBooleanArrayValues(const CesiumUtility::JsonValue& value) { + if (!value.isArray()) { + return std::nullopt; + } + + const CesiumUtility::JsonValue::Array& array = value.getArray(); + std::vector values(array.size()); + for (size_t i = 0; i < values.size(); i++) { + if (!array[i].isBool()) { + return std::nullopt; + } + + values[i] = array[i].getBool(); + } + + return values; + } +}; + +template <> +class PropertyView> + : IPropertyView> { +public: + /** + * @brief Constructs an empty property instance. + */ + PropertyView() + : _propertyViewStatus(PropertyViewStatus::ErrorNonexistentProperty), + _count(0), + _required(false), + _noData(std::nullopt), + _defaultValue(std::nullopt) {} + + /** + * @brief Constructs a property instance from a class definition only. + */ + PropertyView(const ClassProperty& classProperty) + : _propertyViewStatus(PropertyViewStatus::Valid), + _count(0), + _required(classProperty.required), + _noData(std::nullopt), + _defaultValue(std::nullopt) { + if (classProperty.type != ClassProperty::Type::STRING) { + _propertyViewStatus = PropertyViewStatus::ErrorTypeMismatch; + return; + } + + if (!classProperty.array) { + _propertyViewStatus = PropertyViewStatus::ErrorArrayTypeMismatch; + return; + } + + _count = classProperty.count ? *classProperty.count : 0; + + // if (!_required) { + // _noData = classProperty.noData ? + // getStringArrayData(*classProperty.noData) + // : std::nullopt; + // _defaultValue = classProperty.defaultProperty + // ? getStringArrayData(*classProperty.defaultProperty) + // : std::nullopt; + //} + } + + /** + * @brief Constructs a property instance from a property table property and + * its class definition. + */ + PropertyView( + const ClassProperty& classProperty, + const PropertyTableProperty& /*property*/) + : PropertyView(classProperty) {} + + /** + * @brief Constructs a property instance from a property texture property and + * its class definition. + */ + PropertyView( + const ClassProperty& classProperty, + const PropertyTextureProperty& /*property*/) + : PropertyView(classProperty) {} + + ~PropertyView() {} + + /** + * @copydoc IPropertyView::arrayCount + */ + virtual int64_t arrayCount() const noexcept override { return _count; } + + /** + * @copydoc IPropertyView::normalized + */ + virtual bool normalized() const noexcept override { return false; } + + /** + * @copydoc IPropertyView::offset + */ + virtual std::optional> + offset() const noexcept override { + return std::nullopt; + } + + /** + * @copydoc IPropertyView::scale + */ + virtual std::optional> + scale() const noexcept override { + return std::nullopt; + } + + /** + * @copydoc IPropertyView::max + */ + virtual std::optional> + max() const noexcept override { + return std::nullopt; + } + + /** + * @copydoc IPropertyView::min + */ + virtual std::optional> + min() const noexcept override { + return std::nullopt; + } + + /** + * @copydoc IPropertyView::required + */ + virtual bool required() const noexcept override { return _required; } + + /** + * @copydoc IPropertyView::noData + */ + virtual std::optional> + noData() const noexcept override { + return _noData; + } + + /** + * @copydoc IPropertyView::defaultValue + */ + virtual std::optional> + defaultValue() const noexcept override { + return _defaultValue; + } + +protected: + /** + * @copydoc IPropertyView::propertyViewStatus + */ + virtual PropertyViewStatus propertyViewStatus() const noexcept override { + return _propertyViewStatus; + } + +private: + PropertyViewStatus _propertyViewStatus; + int64_t _count; + bool _required; + std::optional> _noData; + std::optional> _defaultValue; + + static std::optional> + getStringArrayData(const CesiumUtility::JsonValue& value) { + if (!value.isArray()) { + return std::nullopt; + } + + std::vector strings; + } +}; + +} // namespace CesiumGltf diff --git a/CesiumGltf/src/PropertyTableView.cpp b/CesiumGltf/src/PropertyTableView.cpp index 0e861151e..77d137233 100644 --- a/CesiumGltf/src/PropertyTableView.cpp +++ b/CesiumGltf/src/PropertyTableView.cpp @@ -316,14 +316,14 @@ PropertyTableView::getStringPropertyValues( } return PropertyTablePropertyView( + propertyTableProperty, + classProperty, + _pPropertyTable->count, values, gsl::span(), stringOffsets, PropertyComponentType::None, - offsetType, - 0, - _pPropertyTable->count, - classProperty.normalized); + offsetType); } PropertyTablePropertyView> @@ -389,14 +389,14 @@ PropertyTableView::getStringArrayPropertyValues( } return PropertyTablePropertyView>( + propertyTableProperty, + classProperty, + _pPropertyTable->count, values, gsl::span(), stringOffsets, PropertyComponentType::None, - stringOffsetType, - fixedLengthArrayCount, - _pPropertyTable->count, - classProperty.normalized); + stringOffsetType); } // Get array offset type @@ -472,13 +472,13 @@ PropertyTableView::getStringArrayPropertyValues( } return PropertyTablePropertyView>( + propertyTableProperty, + classProperty, + _pPropertyTable->count, values, arrayOffsets, stringOffsets, arrayOffsetType, - stringOffsetType, - 0, - _pPropertyTable->count, - classProperty.normalized); + stringOffsetType); } } // namespace CesiumGltf diff --git a/CesiumGltf/test/TestPropertyTablePropertyView.cpp b/CesiumGltf/test/TestPropertyTablePropertyView.cpp index 8fc0a28c1..8524cde90 100644 --- a/CesiumGltf/test/TestPropertyTablePropertyView.cpp +++ b/CesiumGltf/test/TestPropertyTablePropertyView.cpp @@ -16,10 +16,22 @@ template static void checkNumeric(const std::vector& expected) { data.resize(expected.size() * sizeof(T)); std::memcpy(data.data(), expected.data(), data.size()); + PropertyTableProperty propertyTableProperty; + ClassProperty classProperty; + classProperty.type = + convertPropertyTypeToString(TypeToPropertyType::value); + + PropertyComponentType componentType = TypeToPropertyType::component; + if (componentType != PropertyComponentType::None) { + classProperty.componentType = + convertPropertyComponentTypeToString(componentType); + } + PropertyTablePropertyView property( - gsl::span(data.data(), data.size()), + propertyTableProperty, + classProperty, static_cast(expected.size()), - false); + gsl::span(data.data(), data.size())); for (int64_t i = 0; i < property.size(); ++i) { REQUIRE(property.get(i) == expected[static_cast(i)]); @@ -45,15 +57,30 @@ static void checkVariableLengthArray( offsets.data(), offsets.size() * sizeof(OffsetType)); + PropertyTableProperty propertyTableProperty; + ClassProperty classProperty; + classProperty.type = + convertPropertyTypeToString(TypeToPropertyType::value); + + PropertyComponentType componentType = TypeToPropertyType::component; + if (componentType != PropertyComponentType::None) { + classProperty.componentType = + convertPropertyComponentTypeToString(componentType); + } + + classProperty.array = true; + PropertyTablePropertyView> property( + propertyTableProperty, + classProperty, + instanceCount, gsl::span(buffer.data(), buffer.size()), gsl::span(offsetBuffer.data(), offsetBuffer.size()), gsl::span(), offsetType, - PropertyComponentType::None, - 0, - instanceCount, - false); + PropertyComponentType::None); + + REQUIRE(property.arrayCount() == 0); size_t expectedIdx = 0; for (int64_t i = 0; i < property.size(); ++i) { @@ -76,15 +103,31 @@ static void checkFixedLengthArray( buffer.resize(data.size() * sizeof(T)); std::memcpy(buffer.data(), data.data(), data.size() * sizeof(T)); + PropertyTableProperty propertyTableProperty; + ClassProperty classProperty; + classProperty.type = + convertPropertyTypeToString(TypeToPropertyType::value); + + PropertyComponentType componentType = TypeToPropertyType::component; + if (componentType != PropertyComponentType::None) { + classProperty.componentType = + convertPropertyComponentTypeToString(componentType); + } + + classProperty.array = true; + classProperty.count = fixedLengthArrayCount; + PropertyTablePropertyView> property( + propertyTableProperty, + classProperty, + instanceCount, gsl::span(buffer.data(), buffer.size()), gsl::span(), gsl::span(), PropertyComponentType::None, - PropertyComponentType::None, - fixedLengthArrayCount, - instanceCount, - false); + PropertyComponentType::None); + + REQUIRE(property.arrayCount() == fixedLengthArrayCount); size_t expectedIdx = 0; for (int64_t i = 0; i < property.size(); ++i) { @@ -221,11 +264,17 @@ TEST_CASE("Check boolean PropertyTablePropertyView") { std::vector data(sizeof(val)); std::memcpy(data.data(), &val, sizeof(val)); + PropertyTableProperty propertyTableProperty; + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::BOOLEAN; + size_t instanceCount = sizeof(unsigned long) * CHAR_BIT; PropertyTablePropertyView property( - gsl::span(data.data(), data.size()), + propertyTableProperty, + classProperty, static_cast(instanceCount), - false); + gsl::span(data.data(), data.size())); + for (int64_t i = 0; i < property.size(); ++i) { REQUIRE(property.get(i) == bits[static_cast(i)]); } @@ -268,15 +317,20 @@ TEST_CASE("Check string PropertyTablePropertyView") { ¤tOffset, sizeof(uint32_t)); + PropertyTableProperty propertyTableProperty; + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::STRING; + PropertyTablePropertyView property( + propertyTableProperty, + classProperty, + static_cast(strings.size()), gsl::span(buffer.data(), buffer.size()), gsl::span(), gsl::span(offsetBuffer.data(), offsetBuffer.size()), PropertyComponentType::None, - PropertyComponentType::Uint32, - 0, - static_cast(strings.size()), - false); + PropertyComponentType::Uint32); + for (int64_t i = 0; i < property.size(); ++i) { REQUIRE(property.get(i) == strings[static_cast(i)]); } @@ -826,15 +880,23 @@ TEST_CASE("Check fixed-length array of string") { ¤tStringOffset, sizeof(uint32_t)); + PropertyTableProperty propertyTableProperty; + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::STRING; + classProperty.array = true; + classProperty.count = 3; + PropertyTablePropertyView> property( + propertyTableProperty, + classProperty, + static_cast(stringCount / 3), gsl::span(buffer.data(), buffer.size()), gsl::span(), gsl::span(stringOffsets.data(), stringOffsets.size()), PropertyComponentType::None, - PropertyComponentType::Uint32, - 3, - static_cast(stringCount / 3), - false); + PropertyComponentType::Uint32); + + REQUIRE(property.arrayCount() == classProperty.count); size_t expectedIdx = 0; for (int64_t i = 0; i < property.size(); ++i) { @@ -897,17 +959,24 @@ TEST_CASE("Check variable-length string array PropertyTablePropertyView") { ¤tOffset, sizeof(uint32_t)); + PropertyTableProperty propertyTableProperty; + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::STRING; + classProperty.array = true; + PropertyTablePropertyView> property( + propertyTableProperty, + classProperty, + 3, gsl::span(buffer.data(), buffer.size()), gsl::span( reinterpret_cast(arrayOffsets.data()), arrayOffsets.size() * sizeof(uint32_t)), gsl::span(stringOffsets.data(), stringOffsets.size()), PropertyComponentType::Uint32, - PropertyComponentType::Uint32, - 0, - 3, - false); + PropertyComponentType::Uint32); + + REQUIRE(property.arrayCount() == 0); size_t expectedIdx = 0; for (int64_t i = 0; i < property.size(); ++i) { @@ -928,17 +997,24 @@ TEST_CASE("Check fixed-length boolean array PropertyTablePropertyView") { static_cast(0b11111010), static_cast(0b11100111)}; + PropertyTableProperty propertyTableProperty; + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::BOOLEAN; + classProperty.array = true; + classProperty.count = 12; + PropertyTablePropertyView> property( + propertyTableProperty, + classProperty, + 2, gsl::span(buffer.data(), buffer.size()), gsl::span(), gsl::span(), PropertyComponentType::Uint32, - PropertyComponentType::None, - 12, - 2, - false); + PropertyComponentType::None); REQUIRE(property.size() == 2); + REQUIRE(property.arrayCount() == classProperty.count); PropertyArrayView val0 = property.get(0); REQUIRE(val0.size() == 12); @@ -979,19 +1055,25 @@ TEST_CASE("Check variable-length boolean array PropertyTablePropertyView") { std::vector offsetBuffer{0, 3, 12, 28}; + PropertyTableProperty propertyTableProperty; + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::BOOLEAN; + classProperty.array = true; + PropertyTablePropertyView> property( + propertyTableProperty, + classProperty, + 3, gsl::span(buffer.data(), buffer.size()), gsl::span( reinterpret_cast(offsetBuffer.data()), offsetBuffer.size() * sizeof(uint32_t)), gsl::span(), PropertyComponentType::Uint32, - PropertyComponentType::None, - 0, - 3, - false); + PropertyComponentType::None); REQUIRE(property.size() == 3); + REQUIRE(property.arrayCount() == 0); PropertyArrayView val0 = property.get(0); REQUIRE(val0.size() == 3); diff --git a/CesiumGltf/test/TestPropertyTableView.cpp b/CesiumGltf/test/TestPropertyTableView.cpp index 4a625a350..434e30124 100644 --- a/CesiumGltf/test/TestPropertyTableView.cpp +++ b/CesiumGltf/test/TestPropertyTableView.cpp @@ -1,3247 +1,3247 @@ -#include "CesiumGltf/PropertyTableView.h" - -#include - -#include - -using namespace CesiumGltf; - -template -void addBufferToModel(Model& model, const std::vector& values) { - Buffer& valueBuffer = model.buffers.emplace_back(); - valueBuffer.cesium.data.resize(values.size() * sizeof(T)); - valueBuffer.byteLength = static_cast(valueBuffer.cesium.data.size()); - std::memcpy( - valueBuffer.cesium.data.data(), - values.data(), - valueBuffer.cesium.data.size()); - - BufferView& valueBufferView = model.bufferViews.emplace_back(); - valueBufferView.buffer = static_cast(model.buffers.size() - 1); - valueBufferView.byteOffset = 0; - valueBufferView.byteLength = valueBuffer.byteLength; -} - -TEST_CASE("Test PropertyTableView on model without EXT_structural_metadata " - "extension") { - Model model; - - // Create an erroneously isolated property table. - PropertyTable propertyTable; - propertyTable.classProperty = "TestClass"; - propertyTable.count = static_cast(10); - - PropertyTableProperty& propertyTableProperty = - propertyTable.properties["TestClassProperty"]; - propertyTableProperty.values = static_cast(0); - - PropertyTableView view(model, propertyTable); - REQUIRE( - view.status() == PropertyTableViewStatus::ErrorMissingMetadataExtension); - REQUIRE(view.size() == 0); - - const ClassProperty* classProperty = - view.getClassProperty("TestClassProperty"); - REQUIRE(!classProperty); -} - -TEST_CASE("Test PropertyTableView on model without metadata schema") { - Model model; - - ExtensionModelExtStructuralMetadata& metadata = - model.addExtension(); - - PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); - propertyTable.classProperty = "TestClass"; - propertyTable.count = static_cast(10); - - PropertyTableProperty& propertyTableProperty = - propertyTable.properties["TestClassProperty"]; - propertyTableProperty.values = static_cast(0); - - PropertyTableView view(model, propertyTable); - REQUIRE(view.status() == PropertyTableViewStatus::ErrorMissingSchema); - REQUIRE(view.size() == 0); - - const ClassProperty* classProperty = - view.getClassProperty("TestClassProperty"); - REQUIRE(!classProperty); -} - -TEST_CASE("Test property table with nonexistent class") { - Model model; - - ExtensionModelExtStructuralMetadata& metadata = - model.addExtension(); - - Schema& schema = metadata.schema.emplace(); - Class& testClass = schema.classes["TestClass"]; - ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; - testClassProperty.type = ClassProperty::Type::SCALAR; - testClassProperty.componentType = ClassProperty::ComponentType::UINT32; - - PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); - propertyTable.classProperty = "I Don't Exist"; - propertyTable.count = static_cast(10); - - PropertyTableProperty& propertyTableProperty = - propertyTable.properties["TestClassProperty"]; - propertyTableProperty.values = static_cast(0); - - PropertyTableView view(model, propertyTable); - REQUIRE(view.status() == PropertyTableViewStatus::ErrorClassNotFound); - REQUIRE(view.size() == 0); - - const ClassProperty* classProperty = - view.getClassProperty("TestClassProperty"); - REQUIRE(!classProperty); -} - -TEST_CASE("Test scalar PropertyTableProperty") { - Model model; - std::vector values = {12, 34, 30, 11, 34, 34, 11, 33, 122, 33}; - - addBufferToModel(model, values); - size_t valueBufferIndex = model.buffers.size() - 1; - size_t valueBufferViewIndex = model.bufferViews.size() - 1; - - ExtensionModelExtStructuralMetadata& metadata = - model.addExtension(); - - Schema& schema = metadata.schema.emplace(); - Class& testClass = schema.classes["TestClass"]; - ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; - testClassProperty.type = ClassProperty::Type::SCALAR; - testClassProperty.componentType = ClassProperty::ComponentType::UINT32; - - PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); - propertyTable.classProperty = "TestClass"; - propertyTable.count = static_cast(values.size()); - - PropertyTableProperty& propertyTableProperty = - propertyTable.properties["TestClassProperty"]; - propertyTableProperty.values = static_cast(valueBufferViewIndex); - - PropertyTableView view(model, propertyTable); - REQUIRE(view.status() == PropertyTableViewStatus::Valid); - REQUIRE(view.size() == propertyTable.count); - - const ClassProperty* classProperty = - view.getClassProperty("TestClassProperty"); - REQUIRE(classProperty); - REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); - REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT32); - REQUIRE(classProperty->count == std::nullopt); - REQUIRE(!classProperty->array); - - SECTION("Access correct type") { - PropertyTablePropertyView uint32Property = - view.getPropertyView("TestClassProperty"); - REQUIRE(uint32Property.status() == PropertyTablePropertyViewStatus::Valid); - REQUIRE(uint32Property.size() > 0); - - for (int64_t i = 0; i < uint32Property.size(); ++i) { - REQUIRE(uint32Property.get(i) == values[static_cast(i)]); - } - } - - SECTION("Access wrong type") { - PropertyTablePropertyView uvec3Invalid = - view.getPropertyView("TestClassProperty"); - REQUIRE( - uvec3Invalid.status() == - PropertyTablePropertyViewStatus::ErrorTypeMismatch); - - PropertyTablePropertyView u32mat3x3Invalid = - view.getPropertyView("TestClassProperty"); - REQUIRE( - u32mat3x3Invalid.status() == - PropertyTablePropertyViewStatus::ErrorTypeMismatch); - - PropertyTablePropertyView boolInvalid = - view.getPropertyView("TestClassProperty"); - REQUIRE( - boolInvalid.status() == - PropertyTablePropertyViewStatus::ErrorTypeMismatch); - - PropertyTablePropertyView stringInvalid = - view.getPropertyView("TestClassProperty"); - REQUIRE( - stringInvalid.status() == - PropertyTablePropertyViewStatus::ErrorTypeMismatch); - } - - SECTION("Access wrong component type") { - PropertyTablePropertyView uint8Invalid = - view.getPropertyView("TestClassProperty"); - REQUIRE( - uint8Invalid.status() == - PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); - - PropertyTablePropertyView int32Invalid = - view.getPropertyView("TestClassProperty"); - REQUIRE( - int32Invalid.status() == - PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); - - PropertyTablePropertyView uint64Invalid = - view.getPropertyView("TestClassProperty"); - REQUIRE( - uint64Invalid.status() == - PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); - } - - SECTION("Access incorrectly as array") { - PropertyTablePropertyView> uint32ArrayInvalid = - view.getPropertyView>("TestClassProperty"); - REQUIRE( - uint32ArrayInvalid.status() == - PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); - } - - SECTION("Wrong buffer index") { - model.bufferViews[valueBufferViewIndex].buffer = 2; - PropertyTablePropertyView uint32Property = - view.getPropertyView("TestClassProperty"); - REQUIRE( - uint32Property.status() == - PropertyTablePropertyViewStatus::ErrorInvalidValueBuffer); - } - - SECTION("Wrong buffer view index") { - propertyTableProperty.values = -1; - PropertyTablePropertyView uint32Property = - view.getPropertyView("TestClassProperty"); - REQUIRE( - uint32Property.status() == - PropertyTablePropertyViewStatus::ErrorInvalidValueBufferView); - } - - SECTION("Buffer view points outside of the real buffer length") { - model.buffers[valueBufferIndex].cesium.data.resize(12); - PropertyTablePropertyView uint32Property = - view.getPropertyView("TestClassProperty"); - REQUIRE( - uint32Property.status() == - PropertyTablePropertyViewStatus::ErrorBufferViewOutOfBounds); - } - - SECTION("Buffer view length isn't multiple of sizeof(T)") { - model.bufferViews[valueBufferViewIndex].byteLength = 13; - PropertyTablePropertyView uint32Property = - view.getPropertyView("TestClassProperty"); - REQUIRE( - uint32Property.status() == - PropertyTablePropertyViewStatus:: - ErrorBufferViewSizeNotDivisibleByTypeSize); - } - - SECTION("Buffer view length doesn't match with propertyTableCount") { - model.bufferViews[valueBufferViewIndex].byteLength = 12; - PropertyTablePropertyView uint32Property = - view.getPropertyView("TestClassProperty"); - REQUIRE( - uint32Property.status() == - PropertyTablePropertyViewStatus:: - ErrorBufferViewSizeDoesNotMatchPropertyTableCount); - } -} - -TEST_CASE("Test vecN PropertyTableProperty") { - Model model; - std::vector values = { - glm::ivec3(-12, 34, 30), - glm::ivec3(11, 73, 0), - glm::ivec3(-2, 6, 12), - glm::ivec3(-4, 8, -13)}; - - addBufferToModel(model, values); - size_t valueBufferIndex = model.buffers.size() - 1; - size_t valueBufferViewIndex = model.bufferViews.size() - 1; - - ExtensionModelExtStructuralMetadata& metadata = - model.addExtension(); - - Schema& schema = metadata.schema.emplace(); - Class& testClass = schema.classes["TestClass"]; - ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; - testClassProperty.type = ClassProperty::Type::VEC3; - testClassProperty.componentType = ClassProperty::ComponentType::INT32; - - PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); - propertyTable.classProperty = "TestClass"; - propertyTable.count = static_cast(values.size()); - - PropertyTableProperty& propertyTableProperty = - propertyTable.properties["TestClassProperty"]; - propertyTableProperty.values = static_cast(valueBufferViewIndex); - - PropertyTableView view(model, propertyTable); - REQUIRE(view.status() == PropertyTableViewStatus::Valid); - REQUIRE(view.size() == propertyTable.count); - - const ClassProperty* classProperty = - view.getClassProperty("TestClassProperty"); - REQUIRE(classProperty); - REQUIRE(classProperty->type == ClassProperty::Type::VEC3); - REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT32); - REQUIRE(classProperty->count == std::nullopt); - REQUIRE(!classProperty->array); - - SECTION("Access correct type") { - PropertyTablePropertyView ivec3Property = - view.getPropertyView("TestClassProperty"); - REQUIRE(ivec3Property.status() == PropertyTablePropertyViewStatus::Valid); - REQUIRE(ivec3Property.size() > 0); - - for (int64_t i = 0; i < ivec3Property.size(); ++i) { - REQUIRE(ivec3Property.get(i) == values[static_cast(i)]); - } - } - - SECTION("Access wrong type") { - PropertyTablePropertyView int32Invalid = - view.getPropertyView("TestClassProperty"); - REQUIRE( - int32Invalid.status() == - PropertyTablePropertyViewStatus::ErrorTypeMismatch); - - PropertyTablePropertyView ivec2Invalid = - view.getPropertyView("TestClassProperty"); - REQUIRE( - ivec2Invalid.status() == - PropertyTablePropertyViewStatus::ErrorTypeMismatch); - - PropertyTablePropertyView i32mat3x3Invalid = - view.getPropertyView("TestClassProperty"); - REQUIRE( - i32mat3x3Invalid.status() == - PropertyTablePropertyViewStatus::ErrorTypeMismatch); - - PropertyTablePropertyView boolInvalid = - view.getPropertyView("TestClassProperty"); - REQUIRE( - boolInvalid.status() == - PropertyTablePropertyViewStatus::ErrorTypeMismatch); - - PropertyTablePropertyView stringInvalid = - view.getPropertyView("TestClassProperty"); - REQUIRE( - stringInvalid.status() == - PropertyTablePropertyViewStatus::ErrorTypeMismatch); - } - - SECTION("Access wrong component type") { - PropertyTablePropertyView u8vec3Invalid = - view.getPropertyView("TestClassProperty"); - REQUIRE( - u8vec3Invalid.status() == - PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); - - PropertyTablePropertyView i16vec3Invalid = - view.getPropertyView("TestClassProperty"); - REQUIRE( - i16vec3Invalid.status() == - PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); - - PropertyTablePropertyView vec3Invalid = - view.getPropertyView("TestClassProperty"); - REQUIRE( - vec3Invalid.status() == - PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); - } - - SECTION("Access incorrectly as array") { - PropertyTablePropertyView> ivec3ArrayInvalid = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - ivec3ArrayInvalid.status() == - PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); - } - - SECTION("Wrong buffer index") { - model.bufferViews[valueBufferViewIndex].buffer = 2; - PropertyTablePropertyView ivec3Property = - view.getPropertyView("TestClassProperty"); - REQUIRE( - ivec3Property.status() == - PropertyTablePropertyViewStatus::ErrorInvalidValueBuffer); - } - - SECTION("Wrong buffer view index") { - propertyTableProperty.values = -1; - PropertyTablePropertyView ivec3Property = - view.getPropertyView("TestClassProperty"); - REQUIRE( - ivec3Property.status() == - PropertyTablePropertyViewStatus::ErrorInvalidValueBufferView); - } - - SECTION("Buffer view points outside of the real buffer length") { - model.buffers[valueBufferIndex].cesium.data.resize(12); - PropertyTablePropertyView ivec3Property = - view.getPropertyView("TestClassProperty"); - REQUIRE( - ivec3Property.status() == - PropertyTablePropertyViewStatus::ErrorBufferViewOutOfBounds); - } - - SECTION("Buffer view length isn't multiple of sizeof(T)") { - model.bufferViews[valueBufferViewIndex].byteLength = 11; - PropertyTablePropertyView ivec3Property = - view.getPropertyView("TestClassProperty"); - REQUIRE( - ivec3Property.status() == - PropertyTablePropertyViewStatus:: - ErrorBufferViewSizeNotDivisibleByTypeSize); - } - - SECTION("Buffer view length doesn't match with propertyTableCount") { - model.bufferViews[valueBufferViewIndex].byteLength = 12; - PropertyTablePropertyView ivec3Property = - view.getPropertyView("TestClassProperty"); - REQUIRE( - ivec3Property.status() == - PropertyTablePropertyViewStatus:: - ErrorBufferViewSizeDoesNotMatchPropertyTableCount); - } -} - -TEST_CASE("Test matN PropertyTableProperty") { - Model model; - // clang-format off - std::vector values = { - glm::u32mat2x2( - 12, 34, - 30, 1), - glm::u32mat2x2( - 11, 8, - 73, 102), - glm::u32mat2x2( - 1, 0, - 63, 2), - glm::u32mat2x2( - 4, 8, - 3, 23)}; - // clang-format on - - addBufferToModel(model, values); - size_t valueBufferIndex = model.buffers.size() - 1; - size_t valueBufferViewIndex = model.bufferViews.size() - 1; - - ExtensionModelExtStructuralMetadata& metadata = - model.addExtension(); - - Schema& schema = metadata.schema.emplace(); - Class& testClass = schema.classes["TestClass"]; - ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; - testClassProperty.type = ClassProperty::Type::MAT2; - testClassProperty.componentType = ClassProperty::ComponentType::UINT32; - - PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); - propertyTable.classProperty = "TestClass"; - propertyTable.count = static_cast(values.size()); - - PropertyTableProperty& propertyTableProperty = - propertyTable.properties["TestClassProperty"]; - propertyTableProperty.values = static_cast(valueBufferViewIndex); - - PropertyTableView view(model, propertyTable); - REQUIRE(view.status() == PropertyTableViewStatus::Valid); - REQUIRE(view.size() == propertyTable.count); - - const ClassProperty* classProperty = - view.getClassProperty("TestClassProperty"); - REQUIRE(classProperty); - REQUIRE(classProperty->type == ClassProperty::Type::MAT2); - REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT32); - REQUIRE(classProperty->count == std::nullopt); - REQUIRE(!classProperty->array); - - SECTION("Access correct type") { - PropertyTablePropertyView u32mat2x2Property = - view.getPropertyView("TestClassProperty"); - REQUIRE( - u32mat2x2Property.status() == PropertyTablePropertyViewStatus::Valid); - REQUIRE(u32mat2x2Property.size() > 0); - - for (int64_t i = 0; i < u32mat2x2Property.size(); ++i) { - REQUIRE(u32mat2x2Property.get(i) == values[static_cast(i)]); - } - } - - SECTION("Access wrong type") { - PropertyTablePropertyView uint32Invalid = - view.getPropertyView("TestClassProperty"); - REQUIRE( - uint32Invalid.status() == - PropertyTablePropertyViewStatus::ErrorTypeMismatch); - - PropertyTablePropertyView uvec2Invalid = - view.getPropertyView("TestClassProperty"); - REQUIRE( - uvec2Invalid.status() == - PropertyTablePropertyViewStatus::ErrorTypeMismatch); - - PropertyTablePropertyView u32mat4x4Invalid = - view.getPropertyView("TestClassProperty"); - REQUIRE( - u32mat4x4Invalid.status() == - PropertyTablePropertyViewStatus::ErrorTypeMismatch); - - PropertyTablePropertyView boolInvalid = - view.getPropertyView("TestClassProperty"); - REQUIRE( - boolInvalid.status() == - PropertyTablePropertyViewStatus::ErrorTypeMismatch); - - PropertyTablePropertyView stringInvalid = - view.getPropertyView("TestClassProperty"); - REQUIRE( - stringInvalid.status() == - PropertyTablePropertyViewStatus::ErrorTypeMismatch); - } - - SECTION("Access wrong component type") { - PropertyTablePropertyView u8mat2x2Invalid = - view.getPropertyView("TestClassProperty"); - REQUIRE( - u8mat2x2Invalid.status() == - PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); - - PropertyTablePropertyView i32mat2x2Invalid = - view.getPropertyView("TestClassProperty"); - REQUIRE( - i32mat2x2Invalid.status() == - PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); - - PropertyTablePropertyView mat2Invalid = - view.getPropertyView("TestClassProperty"); - REQUIRE( - mat2Invalid.status() == - PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); - } - - SECTION("Access incorrectly as array") { - PropertyTablePropertyView> - u32mat2x2ArrayInvalid = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - u32mat2x2ArrayInvalid.status() == - PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); - } - - SECTION("Wrong buffer index") { - model.bufferViews[valueBufferViewIndex].buffer = 2; - PropertyTablePropertyView u32mat2x2Property = - view.getPropertyView("TestClassProperty"); - REQUIRE( - u32mat2x2Property.status() == - PropertyTablePropertyViewStatus::ErrorInvalidValueBuffer); - } - - SECTION("Wrong buffer view index") { - propertyTableProperty.values = -1; - PropertyTablePropertyView u32mat2x2Property = - view.getPropertyView("TestClassProperty"); - REQUIRE( - u32mat2x2Property.status() == - PropertyTablePropertyViewStatus::ErrorInvalidValueBufferView); - } - - SECTION("Buffer view points outside of the real buffer length") { - model.buffers[valueBufferIndex].cesium.data.resize(sizeof(glm::u32mat2x2)); - PropertyTablePropertyView u32mat2x2Property = - view.getPropertyView("TestClassProperty"); - REQUIRE( - u32mat2x2Property.status() == - PropertyTablePropertyViewStatus::ErrorBufferViewOutOfBounds); - } - - SECTION("Buffer view length isn't multiple of sizeof(T)") { - model.bufferViews[valueBufferViewIndex].byteLength = - sizeof(glm::u32mat2x2) * 4 - 1; - PropertyTablePropertyView u32mat2x2Property = - view.getPropertyView("TestClassProperty"); - REQUIRE( - u32mat2x2Property.status() == - PropertyTablePropertyViewStatus:: - ErrorBufferViewSizeNotDivisibleByTypeSize); - } - - SECTION("Buffer view length doesn't match with propertyTableCount") { - model.bufferViews[valueBufferViewIndex].byteLength = sizeof(glm::u32mat2x2); - - PropertyTablePropertyView u32mat2x2Property = - view.getPropertyView("TestClassProperty"); - REQUIRE( - u32mat2x2Property.status() == - PropertyTablePropertyViewStatus:: - ErrorBufferViewSizeDoesNotMatchPropertyTableCount); - } -} - -TEST_CASE("Test boolean PropertyTableProperty") { - Model model; - - int64_t instanceCount = 21; - std::vector expected; - std::vector values; - values.resize(3); - for (int64_t i = 0; i < instanceCount; ++i) { - if (i % 2 == 0) { - expected.emplace_back(true); - } else { - expected.emplace_back(false); - } - - uint8_t expectedValue = expected.back(); - int64_t byteIndex = i / 8; - int64_t bitIndex = i % 8; - values[static_cast(byteIndex)] = static_cast( - (expectedValue << bitIndex) | values[static_cast(byteIndex)]); - } - addBufferToModel(model, values); - - ExtensionModelExtStructuralMetadata& metadata = - model.addExtension(); - - Schema& schema = metadata.schema.emplace(); - Class& testClass = schema.classes["TestClass"]; - ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; - testClassProperty.type = ClassProperty::Type::BOOLEAN; - - PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); - propertyTable.classProperty = "TestClass"; - propertyTable.count = static_cast(instanceCount); - - PropertyTableProperty& propertyTableProperty = - propertyTable.properties["TestClassProperty"]; - propertyTableProperty.values = - static_cast(model.bufferViews.size() - 1); - - PropertyTableView view(model, propertyTable); - REQUIRE(view.status() == PropertyTableViewStatus::Valid); - REQUIRE(view.size() == propertyTable.count); - - const ClassProperty* classProperty = - view.getClassProperty("TestClassProperty"); - REQUIRE(classProperty); - REQUIRE(classProperty->type == ClassProperty::Type::BOOLEAN); - REQUIRE(classProperty->componentType == std::nullopt); - REQUIRE(classProperty->count == std::nullopt); - REQUIRE(!classProperty->array); - - SECTION("Access correct type") { - PropertyTablePropertyView boolProperty = - view.getPropertyView("TestClassProperty"); - REQUIRE(boolProperty.status() == PropertyTablePropertyViewStatus::Valid); - REQUIRE(boolProperty.size() == instanceCount); - for (int64_t i = 0; i < boolProperty.size(); ++i) { - bool expectedValue = expected[static_cast(i)]; - REQUIRE(boolProperty.get(i) == expectedValue); - } - } - - SECTION("Buffer size doesn't match with propertyTableCount") { - propertyTable.count = 66; - PropertyTablePropertyView boolProperty = - view.getPropertyView("TestClassProperty"); - REQUIRE( - boolProperty.status() == - PropertyTablePropertyViewStatus:: - ErrorBufferViewSizeDoesNotMatchPropertyTableCount); - } -} - -TEST_CASE("Test string PropertyTableProperty") { - Model model; - - std::vector expected{"What's up", "Test_0", "Test_1", "", "Hi"}; - size_t totalBytes = 0; - for (const std::string& expectedValue : expected) { - totalBytes += expectedValue.size(); - } - - std::vector stringOffsets( - (expected.size() + 1) * sizeof(uint32_t)); - std::vector values(totalBytes); - uint32_t* offsetValue = reinterpret_cast(stringOffsets.data()); - for (size_t i = 0; i < expected.size(); ++i) { - const std::string& expectedValue = expected[i]; - std::memcpy( - values.data() + offsetValue[i], - expectedValue.c_str(), - expectedValue.size()); - offsetValue[i + 1] = - offsetValue[i] + static_cast(expectedValue.size()); - } - - addBufferToModel(model, values); - size_t valueBufferIndex = model.buffers.size() - 1; - size_t valueBufferViewIndex = model.bufferViews.size() - 1; - - addBufferToModel(model, stringOffsets); - size_t offsetBufferIndex = model.buffers.size() - 1; - size_t offsetBufferViewIndex = model.bufferViews.size() - 1; - - ExtensionModelExtStructuralMetadata& metadata = - model.addExtension(); - - Schema& schema = metadata.schema.emplace(); - Class& testClass = schema.classes["TestClass"]; - ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; - testClassProperty.type = ClassProperty::Type::STRING; - - PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); - propertyTable.classProperty = "TestClass"; - propertyTable.count = static_cast(expected.size()); - - PropertyTableProperty& propertyTableProperty = - propertyTable.properties["TestClassProperty"]; - propertyTableProperty.stringOffsetType = - PropertyTableProperty::StringOffsetType::UINT32; - propertyTableProperty.values = static_cast(valueBufferViewIndex); - propertyTableProperty.stringOffsets = - static_cast(offsetBufferViewIndex); - - PropertyTableView view(model, propertyTable); - REQUIRE(view.status() == PropertyTableViewStatus::Valid); - REQUIRE(view.size() == propertyTable.count); - - const ClassProperty* classProperty = - view.getClassProperty("TestClassProperty"); - REQUIRE(classProperty); - REQUIRE(classProperty->type == ClassProperty::Type::STRING); - REQUIRE(classProperty->componentType == std::nullopt); - REQUIRE(classProperty->count == std::nullopt); - REQUIRE(!classProperty->array); - - SECTION("Access correct type") { - PropertyTablePropertyView stringProperty = - view.getPropertyView("TestClassProperty"); - REQUIRE(stringProperty.status() == PropertyTablePropertyViewStatus::Valid); - for (size_t i = 0; i < expected.size(); ++i) { - REQUIRE(stringProperty.get(static_cast(i)) == expected[i]); - } - } - - SECTION("Wrong array type") { - PropertyTablePropertyView> - stringArrayInvalid = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - stringArrayInvalid.status() == - PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); - } - - SECTION("Wrong offset type") { - propertyTableProperty.stringOffsetType = - PropertyTableProperty::StringOffsetType::UINT8; - PropertyTablePropertyView stringProperty = - view.getPropertyView("TestClassProperty"); - REQUIRE( - stringProperty.status() == - PropertyTablePropertyViewStatus:: - ErrorBufferViewSizeDoesNotMatchPropertyTableCount); - - propertyTableProperty.stringOffsetType = - PropertyTableProperty::StringOffsetType::UINT64; - stringProperty = - view.getPropertyView("TestClassProperty"); - REQUIRE( - stringProperty.status() == - PropertyTablePropertyViewStatus:: - ErrorBufferViewSizeDoesNotMatchPropertyTableCount); - - propertyTableProperty.stringOffsetType = "NONSENSE"; - stringProperty = - view.getPropertyView("TestClassProperty"); - REQUIRE( - stringProperty.status() == - PropertyTablePropertyViewStatus::ErrorInvalidStringOffsetType); - - propertyTableProperty.stringOffsetType = ""; - propertyTableProperty.arrayOffsetType = - PropertyTableProperty::StringOffsetType::UINT32; - stringProperty = - view.getPropertyView("TestClassProperty"); - REQUIRE( - stringProperty.status() == - PropertyTablePropertyViewStatus::ErrorInvalidStringOffsetType); - } - - SECTION("Offset values are not sorted ascending") { - uint32_t* offset = reinterpret_cast( - model.buffers[offsetBufferIndex].cesium.data.data()); - offset[2] = - static_cast(model.buffers[valueBufferIndex].byteLength + 4); - PropertyTablePropertyView stringProperty = - view.getPropertyView("TestClassProperty"); - REQUIRE( - stringProperty.status() == - PropertyTablePropertyViewStatus::ErrorStringOffsetsNotSorted); - } - - SECTION("Offset value points outside of value buffer") { - uint32_t* offset = reinterpret_cast( - model.buffers[offsetBufferIndex].cesium.data.data()); - offset[propertyTable.count] = - static_cast(model.buffers[valueBufferIndex].byteLength + 4); - PropertyTablePropertyView stringProperty = - view.getPropertyView("TestClassProperty"); - REQUIRE( - stringProperty.status() == - PropertyTablePropertyViewStatus::ErrorStringOffsetOutOfBounds); - } -} - -TEST_CASE("Test fixed-length scalar array") { - Model model; - std::vector values = - {12, 34, 30, 11, 34, 34, 11, 33, 122, 33, 223, 11}; - - addBufferToModel(model, values); - size_t valueBufferViewIndex = model.bufferViews.size() - 1; - - ExtensionModelExtStructuralMetadata& metadata = - model.addExtension(); - - Schema& schema = metadata.schema.emplace(); - Class& testClass = schema.classes["TestClass"]; - ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; - testClassProperty.type = ClassProperty::Type::SCALAR; - testClassProperty.componentType = ClassProperty::ComponentType::UINT32; - testClassProperty.array = true; - testClassProperty.count = 3; - - PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); - propertyTable.classProperty = "TestClass"; - propertyTable.count = static_cast( - values.size() / static_cast(testClassProperty.count.value())); - - PropertyTableProperty& propertyTableProperty = - propertyTable.properties["TestClassProperty"]; - propertyTableProperty.values = - static_cast(model.bufferViews.size() - 1); - - PropertyTableView view(model, propertyTable); - REQUIRE(view.status() == PropertyTableViewStatus::Valid); - REQUIRE(view.size() == propertyTable.count); - - const ClassProperty* classProperty = - view.getClassProperty("TestClassProperty"); - REQUIRE(classProperty); - REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); - REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT32); - REQUIRE(classProperty->array); - REQUIRE(classProperty->count == 3); - - SECTION("Access the right type") { - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>("TestClassProperty"); - REQUIRE(arrayProperty.status() == PropertyTablePropertyViewStatus::Valid); - - for (int64_t i = 0; i < arrayProperty.size(); ++i) { - PropertyArrayView member = arrayProperty.get(i); - for (int64_t j = 0; j < member.size(); ++j) { - REQUIRE(member[j] == values[static_cast(i * 3 + j)]); - } - } - } - - SECTION("Wrong type") { - PropertyTablePropertyView> boolArrayInvalid = - view.getPropertyView>("TestClassProperty"); - REQUIRE( - boolArrayInvalid.status() == - PropertyTablePropertyViewStatus::ErrorTypeMismatch); - - PropertyTablePropertyView> uvec2ArrayInvalid = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - uvec2ArrayInvalid.status() == - PropertyTablePropertyViewStatus::ErrorTypeMismatch); - } - - SECTION("Wrong component type") { - PropertyTablePropertyView> int32ArrayInvalid = - view.getPropertyView>("TestClassProperty"); - REQUIRE( - int32ArrayInvalid.status() == - PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); - } - - SECTION("Not an array type") { - PropertyTablePropertyView uint32Invalid = - view.getPropertyView("TestClassProperty"); - REQUIRE( - uint32Invalid.status() == - PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); - } - - SECTION("Buffer size is not a multiple of type size") { - model.bufferViews[valueBufferViewIndex].byteLength = 13; - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>("TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus:: - ErrorBufferViewSizeNotDivisibleByTypeSize); - } - - SECTION("Negative component count") { - testClassProperty.count = -1; - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>("TestClassProperty"); - REQUIRE( - arrayProperty.status() == PropertyTablePropertyViewStatus:: - ErrorArrayCountAndOffsetBufferDontExist); - } - - SECTION("Value buffer doesn't fit into property table count") { - testClassProperty.count = 55; - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>("TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus:: - ErrorBufferViewSizeDoesNotMatchPropertyTableCount); - } -} - -TEST_CASE("Test variable-length scalar array") { - Model model; - - std::vector> expected{ - {12, 33, 11, 344, 112, 444, 1}, - {}, - {}, - {122, 23, 333, 12}, - {}, - {333, 311, 22, 34}, - {}, - {33, 1888, 233, 33019}}; - size_t numOfElements = 0; - for (const auto& expectedMember : expected) { - numOfElements += expectedMember.size(); - } - - std::vector values(numOfElements * sizeof(uint16_t)); - std::vector offsets((expected.size() + 1) * sizeof(uint64_t)); - uint64_t* offsetValue = reinterpret_cast(offsets.data()); - for (size_t i = 0; i < expected.size(); ++i) { - std::memcpy( - values.data() + offsetValue[i], - expected[i].data(), - expected[i].size() * sizeof(uint16_t)); - offsetValue[i + 1] = offsetValue[i] + expected[i].size() * sizeof(uint16_t); - } - - addBufferToModel(model, values); - size_t valueBufferIndex = model.buffers.size() - 1; - size_t valueBufferViewIndex = model.bufferViews.size() - 1; - - addBufferToModel(model, offsets); - size_t offsetBufferIndex = model.buffers.size() - 1; - size_t offsetBufferViewIndex = model.bufferViews.size() - 1; - - ExtensionModelExtStructuralMetadata& metadata = - model.addExtension(); - - Schema& schema = metadata.schema.emplace(); - Class& testClass = schema.classes["TestClass"]; - ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; - testClassProperty.type = ClassProperty::Type::SCALAR; - testClassProperty.componentType = ClassProperty::ComponentType::UINT16; - testClassProperty.array = true; - - PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); - propertyTable.classProperty = "TestClass"; - propertyTable.count = static_cast(expected.size()); - - PropertyTableProperty& propertyTableProperty = - propertyTable.properties["TestClassProperty"]; - propertyTableProperty.values = static_cast(valueBufferViewIndex); - propertyTableProperty.arrayOffsets = - static_cast(offsetBufferViewIndex); - propertyTableProperty.arrayOffsetType = - PropertyTableProperty::ArrayOffsetType::UINT64; - - PropertyTableView view(model, propertyTable); - REQUIRE(view.status() == PropertyTableViewStatus::Valid); - REQUIRE(view.size() == propertyTable.count); - - const ClassProperty* classProperty = - view.getClassProperty("TestClassProperty"); - REQUIRE(classProperty); - REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); - REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT16); - REQUIRE(classProperty->array); - REQUIRE(!classProperty->count); - - SECTION("Access the correct type") { - PropertyTablePropertyView> property = - view.getPropertyView>("TestClassProperty"); - REQUIRE(property.status() == PropertyTablePropertyViewStatus::Valid); - for (size_t i = 0; i < expected.size(); ++i) { - PropertyArrayView valueMember = - property.get(static_cast(i)); - REQUIRE(valueMember.size() == static_cast(expected[i].size())); - for (size_t j = 0; j < expected[i].size(); ++j) { - REQUIRE(expected[i][j] == valueMember[static_cast(j)]); - } - } - } - SECTION("Wrong offset type") { - propertyTableProperty.arrayOffsetType = - PropertyTableProperty::ArrayOffsetType::UINT8; - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>("TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus:: - ErrorBufferViewSizeDoesNotMatchPropertyTableCount); - - propertyTableProperty.arrayOffsetType = - PropertyTableProperty::ArrayOffsetType::UINT16; - arrayProperty = - view.getPropertyView>("TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus:: - ErrorBufferViewSizeDoesNotMatchPropertyTableCount); - - propertyTableProperty.arrayOffsetType = "NONSENSE"; - arrayProperty = - view.getPropertyView>("TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); - - propertyTableProperty.arrayOffsetType = ""; - propertyTableProperty.stringOffsetType = - PropertyTableProperty::StringOffsetType::UINT64; - arrayProperty = - view.getPropertyView>("TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); - } - - SECTION("Offset values are not sorted ascending") { - propertyTableProperty.arrayOffsetType = - PropertyTableProperty::ArrayOffsetType::UINT64; - uint64_t* offset = reinterpret_cast( - model.buffers[offsetBufferIndex].cesium.data.data()); - offset[propertyTable.count] = 0; - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>("TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus::ErrorArrayOffsetsNotSorted); - } - - SECTION("Offset value points outside of value buffer") { - uint64_t* offset = reinterpret_cast( - model.buffers[offsetBufferIndex].cesium.data.data()); - offset[propertyTable.count] = - static_cast(model.buffers[valueBufferIndex].byteLength + 4); - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>("TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus::ErrorArrayOffsetOutOfBounds); - } - - SECTION("Count and offset buffer are both present") { - testClassProperty.count = 3; - PropertyTablePropertyView> property = - view.getPropertyView>("TestClassProperty"); - REQUIRE( - property.status() == - PropertyTablePropertyViewStatus::ErrorArrayCountAndOffsetBufferCoexist); - } -} - -TEST_CASE("Test fixed-length vecN array") { - Model model; - std::vector values = { - glm::ivec3(12, 34, -30), - glm::ivec3(-2, 0, 1), - glm::ivec3(1, 2, 8), - glm::ivec3(-100, 84, 6), - glm::ivec3(2, -2, -2), - glm::ivec3(40, 61, 3), - }; - - addBufferToModel(model, values); - size_t valueBufferViewIndex = model.bufferViews.size() - 1; - - ExtensionModelExtStructuralMetadata& metadata = - model.addExtension(); - - Schema& schema = metadata.schema.emplace(); - Class& testClass = schema.classes["TestClass"]; - ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; - testClassProperty.type = ClassProperty::Type::VEC3; - testClassProperty.componentType = ClassProperty::ComponentType::INT32; - testClassProperty.array = true; - testClassProperty.count = 2; - - PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); - propertyTable.classProperty = "TestClass"; - propertyTable.count = static_cast( - values.size() / static_cast(testClassProperty.count.value())); - - PropertyTableProperty& propertyTableProperty = - propertyTable.properties["TestClassProperty"]; - propertyTableProperty.values = - static_cast(model.bufferViews.size() - 1); - - PropertyTableView view(model, propertyTable); - REQUIRE(view.status() == PropertyTableViewStatus::Valid); - REQUIRE(view.size() == propertyTable.count); - - const ClassProperty* classProperty = - view.getClassProperty("TestClassProperty"); - REQUIRE(classProperty); - REQUIRE(classProperty->type == ClassProperty::Type::VEC3); - REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT32); - REQUIRE(classProperty->array); - REQUIRE(classProperty->count == 2); - - SECTION("Access the right type") { - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE(arrayProperty.status() == PropertyTablePropertyViewStatus::Valid); - - for (int64_t i = 0; i < arrayProperty.size(); ++i) { - PropertyArrayView member = arrayProperty.get(i); - for (int64_t j = 0; j < member.size(); ++j) { - REQUIRE(member[j] == values[static_cast(i * 2 + j)]); - } - } - } - - SECTION("Wrong type") { - PropertyTablePropertyView> int32ArrayInvalid = - view.getPropertyView>("TestClassProperty"); - REQUIRE( - int32ArrayInvalid.status() == - PropertyTablePropertyViewStatus::ErrorTypeMismatch); - - PropertyTablePropertyView> ivec2ArrayInvalid = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - ivec2ArrayInvalid.status() == - PropertyTablePropertyViewStatus::ErrorTypeMismatch); - } - - SECTION("Wrong component type") { - PropertyTablePropertyView> uvec3ArrayInvalid = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - uvec3ArrayInvalid.status() == - PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); - } - - SECTION("Not an array type") { - PropertyTablePropertyView ivec3Invalid = - view.getPropertyView("TestClassProperty"); - REQUIRE( - ivec3Invalid.status() == - PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); - } - - SECTION("Buffer size is not a multiple of type size") { - model.bufferViews[valueBufferViewIndex].byteLength = 13; - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus:: - ErrorBufferViewSizeNotDivisibleByTypeSize); - } - - SECTION("Negative component count") { - testClassProperty.count = -1; - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == PropertyTablePropertyViewStatus:: - ErrorArrayCountAndOffsetBufferDontExist); - } - - SECTION("Value buffer doesn't fit into property table count") { - testClassProperty.count = 55; - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus:: - ErrorBufferViewSizeDoesNotMatchPropertyTableCount); - } -} - -TEST_CASE("Test variable-length vecN array") { - Model model; - // clang-format off - std::vector> expected{ - { glm::ivec3(12, 34, -30), glm::ivec3(-2, 0, 1) }, - { glm::ivec3(1, 2, 8), }, - {}, - { glm::ivec3(-100, 84, 6), glm::ivec3(2, -2, -2), glm::ivec3(40, 61, 3) }, - { glm::ivec3(-1, 4, -7) }, - }; - // clang-format on - - size_t numOfElements = 0; - for (const auto& expectedMember : expected) { - numOfElements += expectedMember.size(); - } - - std::vector values(numOfElements * sizeof(glm::ivec3)); - std::vector offsets((expected.size() + 1) * sizeof(uint64_t)); - uint64_t* offsetValue = reinterpret_cast(offsets.data()); - for (size_t i = 0; i < expected.size(); ++i) { - std::memcpy( - values.data() + offsetValue[i], - expected[i].data(), - expected[i].size() * sizeof(glm::ivec3)); - offsetValue[i + 1] = - offsetValue[i] + expected[i].size() * sizeof(glm::ivec3); - } - - addBufferToModel(model, values); - size_t valueBufferIndex = model.buffers.size() - 1; - size_t valueBufferViewIndex = model.bufferViews.size() - 1; - - addBufferToModel(model, offsets); - size_t offsetBufferIndex = model.buffers.size() - 1; - size_t offsetBufferViewIndex = model.bufferViews.size() - 1; - - ExtensionModelExtStructuralMetadata& metadata = - model.addExtension(); - - Schema& schema = metadata.schema.emplace(); - Class& testClass = schema.classes["TestClass"]; - ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; - testClassProperty.type = ClassProperty::Type::VEC3; - testClassProperty.componentType = ClassProperty::ComponentType::INT32; - testClassProperty.array = true; - - PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); - propertyTable.classProperty = "TestClass"; - propertyTable.count = static_cast(expected.size()); - - PropertyTableProperty& propertyTableProperty = - propertyTable.properties["TestClassProperty"]; - propertyTableProperty.values = static_cast(valueBufferViewIndex); - propertyTableProperty.arrayOffsets = - static_cast(offsetBufferViewIndex); - propertyTableProperty.arrayOffsetType = - PropertyTableProperty::ArrayOffsetType::UINT64; - - PropertyTableView view(model, propertyTable); - REQUIRE(view.status() == PropertyTableViewStatus::Valid); - REQUIRE(view.size() == propertyTable.count); - - const ClassProperty* classProperty = - view.getClassProperty("TestClassProperty"); - REQUIRE(classProperty); - REQUIRE(classProperty->type == ClassProperty::Type::VEC3); - REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT32); - REQUIRE(classProperty->array); - REQUIRE(!classProperty->count); - - SECTION("Access the correct type") { - PropertyTablePropertyView> property = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE(property.status() == PropertyTablePropertyViewStatus::Valid); - for (size_t i = 0; i < expected.size(); ++i) { - PropertyArrayView valueMember = - property.get(static_cast(i)); - REQUIRE(valueMember.size() == static_cast(expected[i].size())); - for (size_t j = 0; j < expected[i].size(); ++j) { - REQUIRE(expected[i][j] == valueMember[static_cast(j)]); - } - } - } - - SECTION("Wrong offset type") { - propertyTableProperty.arrayOffsetType = - PropertyTableProperty::ArrayOffsetType::UINT8; - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus:: - ErrorBufferViewSizeDoesNotMatchPropertyTableCount); - - propertyTableProperty.arrayOffsetType = - PropertyTableProperty::ArrayOffsetType::UINT16; - arrayProperty = view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus:: - ErrorBufferViewSizeDoesNotMatchPropertyTableCount); - - propertyTableProperty.arrayOffsetType = "NONSENSE"; - arrayProperty = view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); - - propertyTableProperty.arrayOffsetType = ""; - propertyTableProperty.stringOffsetType = - PropertyTableProperty::StringOffsetType::UINT64; - arrayProperty = view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); - } - - SECTION("Offset values are not sorted ascending") { - propertyTableProperty.arrayOffsetType = - PropertyTableProperty::ArrayOffsetType::UINT64; - uint64_t* offset = reinterpret_cast( - model.buffers[offsetBufferIndex].cesium.data.data()); - offset[propertyTable.count] = 0; - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus::ErrorArrayOffsetsNotSorted); - } - - SECTION("Offset value points outside of value buffer") { - uint64_t* offset = reinterpret_cast( - model.buffers[offsetBufferIndex].cesium.data.data()); - offset[propertyTable.count] = - static_cast(model.buffers[valueBufferIndex].byteLength + 4); - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus::ErrorArrayOffsetOutOfBounds); - } - - SECTION("Count and offset buffer are both present") { - testClassProperty.count = 3; - PropertyTablePropertyView> property = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - property.status() == - PropertyTablePropertyViewStatus::ErrorArrayCountAndOffsetBufferCoexist); - } -} - -TEST_CASE("Test fixed-length matN array") { - Model model; - // clang-format off - std::vector values = { - glm::i32mat2x2( - 12, 34, - -30, 20), - glm::i32mat2x2( - -2, -2, - 0, 1), - glm::i32mat2x2( - 1, 2, - 8, 5), - glm::i32mat2x2( - -100, 3, - 84, 6), - glm::i32mat2x2( - 2, 12, - -2, -2), - glm::i32mat2x2( - 40, 61, - 7, -3), - }; - // clang-format on - - addBufferToModel(model, values); - size_t valueBufferViewIndex = model.bufferViews.size() - 1; - - ExtensionModelExtStructuralMetadata& metadata = - model.addExtension(); - - Schema& schema = metadata.schema.emplace(); - Class& testClass = schema.classes["TestClass"]; - ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; - testClassProperty.type = ClassProperty::Type::MAT2; - testClassProperty.componentType = ClassProperty::ComponentType::INT32; - testClassProperty.array = true; - testClassProperty.count = 2; - - PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); - propertyTable.classProperty = "TestClass"; - propertyTable.count = static_cast( - values.size() / static_cast(testClassProperty.count.value())); - - PropertyTableProperty& propertyTableProperty = - propertyTable.properties["TestClassProperty"]; - propertyTableProperty.values = - static_cast(model.bufferViews.size() - 1); - - PropertyTableView view(model, propertyTable); - REQUIRE(view.status() == PropertyTableViewStatus::Valid); - REQUIRE(view.size() == propertyTable.count); - - const ClassProperty* classProperty = - view.getClassProperty("TestClassProperty"); - REQUIRE(classProperty); - REQUIRE(classProperty->type == ClassProperty::Type::MAT2); - REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT32); - REQUIRE(classProperty->array); - REQUIRE(classProperty->count == 2); - - SECTION("Access the right type") { - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE(arrayProperty.status() == PropertyTablePropertyViewStatus::Valid); - - for (int64_t i = 0; i < arrayProperty.size(); ++i) { - PropertyArrayView member = arrayProperty.get(i); - for (int64_t j = 0; j < member.size(); ++j) { - REQUIRE(member[j] == values[static_cast(i * 2 + j)]); - } - } - } - - SECTION("Wrong type") { - PropertyTablePropertyView> int32ArrayInvalid = - view.getPropertyView>("TestClassProperty"); - REQUIRE( - int32ArrayInvalid.status() == - PropertyTablePropertyViewStatus::ErrorTypeMismatch); - - PropertyTablePropertyView> ivec2ArrayInvalid = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - ivec2ArrayInvalid.status() == - PropertyTablePropertyViewStatus::ErrorTypeMismatch); - } - - SECTION("Wrong component type") { - PropertyTablePropertyView> - u32mat2x2ArrayInvalid = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - u32mat2x2ArrayInvalid.status() == - PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); - } - - SECTION("Not an array type") { - PropertyTablePropertyView ivec3Invalid = - view.getPropertyView("TestClassProperty"); - REQUIRE( - ivec3Invalid.status() == - PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); - } - - SECTION("Buffer size is not a multiple of type size") { - model.bufferViews[valueBufferViewIndex].byteLength = 13; - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus:: - ErrorBufferViewSizeNotDivisibleByTypeSize); - } - - SECTION("Negative component count") { - testClassProperty.count = -1; - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == PropertyTablePropertyViewStatus:: - ErrorArrayCountAndOffsetBufferDontExist); - } - - SECTION("Value buffer doesn't fit into property table count") { - testClassProperty.count = 55; - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus:: - ErrorBufferViewSizeDoesNotMatchPropertyTableCount); - } -} - -TEST_CASE("Test variable-length matN array") { - Model model; - // clang-format off - std::vector data0{ - glm::i32mat2x2( - 3, -2, - 1, 0), - glm::i32mat2x2( - 40, 3, - 8, -9) - }; - std::vector data1{ - glm::i32mat2x2( - 1, 10, - 7, 8), - }; - std::vector data2{ - glm::i32mat2x2( - 18, 0, - 1, 17), - glm::i32mat2x2( - -4, -2, - -9, 1), - glm::i32mat2x2( - 1, 8, - -99, 3), - }; - // clang-format on - - std::vector> - expected{data0, {}, data1, data2, {}}; - - size_t numOfElements = 0; - for (const auto& expectedMember : expected) { - numOfElements += expectedMember.size(); - } - - std::vector values(numOfElements * sizeof(glm::i32mat2x2)); - std::vector offsets((expected.size() + 1) * sizeof(uint64_t)); - uint64_t* offsetValue = reinterpret_cast(offsets.data()); - for (size_t i = 0; i < expected.size(); ++i) { - std::memcpy( - values.data() + offsetValue[i], - expected[i].data(), - expected[i].size() * sizeof(glm::i32mat2x2)); - offsetValue[i + 1] = - offsetValue[i] + expected[i].size() * sizeof(glm::i32mat2x2); - } - - addBufferToModel(model, values); - size_t valueBufferIndex = model.buffers.size() - 1; - size_t valueBufferViewIndex = model.bufferViews.size() - 1; - - addBufferToModel(model, offsets); - size_t offsetBufferIndex = model.buffers.size() - 1; - size_t offsetBufferViewIndex = model.bufferViews.size() - 1; - - ExtensionModelExtStructuralMetadata& metadata = - model.addExtension(); - - Schema& schema = metadata.schema.emplace(); - Class& testClass = schema.classes["TestClass"]; - ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; - testClassProperty.type = ClassProperty::Type::MAT2; - testClassProperty.componentType = ClassProperty::ComponentType::INT32; - testClassProperty.array = true; - - PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); - propertyTable.classProperty = "TestClass"; - propertyTable.count = static_cast(expected.size()); - - PropertyTableProperty& propertyTableProperty = - propertyTable.properties["TestClassProperty"]; - propertyTableProperty.values = static_cast(valueBufferViewIndex); - propertyTableProperty.arrayOffsets = - static_cast(offsetBufferViewIndex); - propertyTableProperty.arrayOffsetType = - PropertyTableProperty::ArrayOffsetType::UINT64; - - PropertyTableView view(model, propertyTable); - REQUIRE(view.status() == PropertyTableViewStatus::Valid); - REQUIRE(view.size() == propertyTable.count); - - const ClassProperty* classProperty = - view.getClassProperty("TestClassProperty"); - REQUIRE(classProperty); - REQUIRE(classProperty->type == ClassProperty::Type::MAT2); - REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT32); - REQUIRE(classProperty->array); - REQUIRE(!classProperty->count); - - SECTION("Access the correct type") { - PropertyTablePropertyView> property = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE(property.status() == PropertyTablePropertyViewStatus::Valid); - for (size_t i = 0; i < expected.size(); ++i) { - PropertyArrayView valueMember = - property.get(static_cast(i)); - REQUIRE(valueMember.size() == static_cast(expected[i].size())); - for (size_t j = 0; j < expected[i].size(); ++j) { - REQUIRE(expected[i][j] == valueMember[static_cast(j)]); - } - } - } - - SECTION("Wrong offset type") { - propertyTableProperty.arrayOffsetType = - PropertyTableProperty::ArrayOffsetType::UINT8; - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus:: - ErrorBufferViewSizeDoesNotMatchPropertyTableCount); - - propertyTableProperty.arrayOffsetType = - PropertyTableProperty::ArrayOffsetType::UINT16; - arrayProperty = view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus:: - ErrorBufferViewSizeDoesNotMatchPropertyTableCount); - - propertyTableProperty.arrayOffsetType = "NONSENSE"; - arrayProperty = view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); - - propertyTableProperty.arrayOffsetType = ""; - propertyTableProperty.stringOffsetType = - PropertyTableProperty::StringOffsetType::UINT64; - arrayProperty = view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); - } - - SECTION("Offset values are not sorted ascending") { - propertyTableProperty.arrayOffsetType = - PropertyTableProperty::ArrayOffsetType::UINT64; - uint64_t* offset = reinterpret_cast( - model.buffers[offsetBufferIndex].cesium.data.data()); - offset[propertyTable.count] = 0; - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus::ErrorArrayOffsetsNotSorted); - } - - SECTION("Offset value points outside of value buffer") { - uint64_t* offset = reinterpret_cast( - model.buffers[offsetBufferIndex].cesium.data.data()); - offset[propertyTable.count] = - static_cast(model.buffers[valueBufferIndex].byteLength + 4); - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus::ErrorArrayOffsetOutOfBounds); - } - - SECTION("Count and offset buffer are both present") { - testClassProperty.count = 3; - PropertyTablePropertyView> property = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - property.status() == - PropertyTablePropertyViewStatus::ErrorArrayCountAndOffsetBufferCoexist); - } -} - -TEST_CASE("Test fixed-length boolean array") { - Model model; - - std::vector expected = { - true, - false, - false, - true, - false, - false, - true, - true, - true, - false, - false, - true}; - std::vector values; - size_t requiredBytesSize = static_cast( - glm::ceil(static_cast(expected.size()) / 8.0)); - values.resize(requiredBytesSize); - for (size_t i = 0; i < expected.size(); ++i) { - uint8_t expectedValue = expected[i]; - size_t byteIndex = i / 8; - size_t bitIndex = i % 8; - values[byteIndex] = - static_cast((expectedValue << bitIndex) | values[byteIndex]); - } - - addBufferToModel(model, values); - - ExtensionModelExtStructuralMetadata& metadata = - model.addExtension(); - - Schema& schema = metadata.schema.emplace(); - Class& testClass = schema.classes["TestClass"]; - ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; - testClassProperty.type = ClassProperty::Type::BOOLEAN; - testClassProperty.array = true; - testClassProperty.count = 3; - - PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); - propertyTable.classProperty = "TestClass"; - propertyTable.count = static_cast( - expected.size() / static_cast(testClassProperty.count.value())); - - PropertyTableProperty& propertyTableProperty = - propertyTable.properties["TestClassProperty"]; - propertyTableProperty.values = - static_cast(model.bufferViews.size() - 1); - - PropertyTableView view(model, propertyTable); - REQUIRE(view.status() == PropertyTableViewStatus::Valid); - REQUIRE(view.size() == propertyTable.count); - - const ClassProperty* classProperty = - view.getClassProperty("TestClassProperty"); - REQUIRE(classProperty); - REQUIRE(classProperty->type == ClassProperty::Type::BOOLEAN); - REQUIRE(classProperty->array); - REQUIRE(classProperty->count == 3); - - SECTION("Access correct type") { - PropertyTablePropertyView> boolArrayProperty = - view.getPropertyView>("TestClassProperty"); - REQUIRE( - boolArrayProperty.status() == PropertyTablePropertyViewStatus::Valid); - REQUIRE(boolArrayProperty.size() == propertyTable.count); - REQUIRE(boolArrayProperty.size() > 0); - for (int64_t i = 0; i < boolArrayProperty.size(); ++i) { - PropertyArrayView valueMember = boolArrayProperty.get(i); - for (int64_t j = 0; j < valueMember.size(); ++j) { - REQUIRE(valueMember[j] == expected[static_cast(i * 3 + j)]); - } - } - } - - SECTION("Wrong type") { - PropertyTablePropertyView> uint8ArrayInvalid = - view.getPropertyView>("TestClassProperty"); - REQUIRE( - uint8ArrayInvalid.status() == - PropertyTablePropertyViewStatus::ErrorTypeMismatch); - } - - SECTION("View is not array type") { - PropertyTablePropertyView boolInvalid = - view.getPropertyView("TestClassProperty"); - REQUIRE( - boolInvalid.status() == - PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); - } - - SECTION("Value buffer doesn't have enough required bytes") { - testClassProperty.count = 11; - PropertyTablePropertyView> boolArrayProperty = - view.getPropertyView>("TestClassProperty"); - REQUIRE( - boolArrayProperty.status() == - PropertyTablePropertyViewStatus:: - ErrorBufferViewSizeDoesNotMatchPropertyTableCount); - } - - SECTION("Count is negative") { - testClassProperty.count = -1; - PropertyTablePropertyView> boolArrayProperty = - view.getPropertyView>("TestClassProperty"); - REQUIRE( - boolArrayProperty.status() == - PropertyTablePropertyViewStatus:: - ErrorArrayCountAndOffsetBufferDontExist); - } -} - -TEST_CASE("Test variable-length boolean array") { - Model model; - - std::vector> expected{ - {true, false, true, true, false, true, true}, - {}, - {}, - {}, - {false, false, false, false}, - {true, false, true}, - {false}, - {true, true, true, true, true, false, false}}; - size_t numOfElements = 0; - for (const auto& expectedMember : expected) { - numOfElements += expectedMember.size(); - } - - size_t requiredBytesSize = - static_cast(glm::ceil(static_cast(numOfElements) / 8.0)); - std::vector values(requiredBytesSize); - std::vector offsets((expected.size() + 1) * sizeof(uint64_t)); - uint64_t* offsetValue = reinterpret_cast(offsets.data()); - size_t indexSoFar = 0; - for (size_t i = 0; i < expected.size(); ++i) { - for (size_t j = 0; j < expected[i].size(); ++j) { - uint8_t expectedValue = expected[i][j]; - size_t byteIndex = indexSoFar / 8; - size_t bitIndex = indexSoFar % 8; - values[byteIndex] = static_cast( - (expectedValue << bitIndex) | static_cast(values[byteIndex])); - ++indexSoFar; - } - offsetValue[i + 1] = offsetValue[i] + expected[i].size(); - } - - addBufferToModel(model, values); - size_t valueBufferIndex = model.buffers.size() - 1; - size_t valueBufferViewIndex = model.bufferViews.size() - 1; - - addBufferToModel(model, offsets); - size_t offsetBufferIndex = model.buffers.size() - 1; - size_t offsetBufferViewIndex = model.bufferViews.size() - 1; - - ExtensionModelExtStructuralMetadata& metadata = - model.addExtension(); - - Schema& schema = metadata.schema.emplace(); - Class& testClass = schema.classes["TestClass"]; - ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; - testClassProperty.type = ClassProperty::Type::BOOLEAN; - testClassProperty.array = true; - - PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); - propertyTable.classProperty = "TestClass"; - propertyTable.count = static_cast(expected.size()); - - PropertyTableProperty& propertyTableProperty = - propertyTable.properties["TestClassProperty"]; - propertyTableProperty.values = static_cast(valueBufferViewIndex); - propertyTableProperty.arrayOffsets = - static_cast(offsetBufferViewIndex); - propertyTableProperty.arrayOffsetType = - PropertyTableProperty::ArrayOffsetType::UINT64; - - PropertyTableView view(model, propertyTable); - REQUIRE(view.status() == PropertyTableViewStatus::Valid); - REQUIRE(view.size() == propertyTable.count); - - const ClassProperty* classProperty = - view.getClassProperty("TestClassProperty"); - REQUIRE(classProperty); - REQUIRE(classProperty->type == ClassProperty::Type::BOOLEAN); - REQUIRE(classProperty->array); - REQUIRE(!classProperty->count); - - SECTION("Access correct type") { - PropertyTablePropertyView> boolArrayProperty = - view.getPropertyView>("TestClassProperty"); - REQUIRE( - boolArrayProperty.status() == PropertyTablePropertyViewStatus::Valid); - for (size_t i = 0; i < expected.size(); ++i) { - PropertyArrayView arrayMember = - boolArrayProperty.get(static_cast(i)); - REQUIRE(arrayMember.size() == static_cast(expected[i].size())); - for (size_t j = 0; j < expected[i].size(); ++j) { - REQUIRE(expected[i][j] == arrayMember[static_cast(j)]); - } - } - } - - SECTION("Wrong offset type") { - propertyTableProperty.arrayOffsetType = - PropertyTableProperty::ArrayOffsetType::UINT8; - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>("TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus:: - ErrorBufferViewSizeDoesNotMatchPropertyTableCount); - - propertyTableProperty.arrayOffsetType = - PropertyTableProperty::ArrayOffsetType::UINT16; - arrayProperty = - view.getPropertyView>("TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus:: - ErrorBufferViewSizeDoesNotMatchPropertyTableCount); - - propertyTableProperty.arrayOffsetType = "NONSENSE"; - arrayProperty = - view.getPropertyView>("TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); - - propertyTableProperty.arrayOffsetType = ""; - propertyTableProperty.stringOffsetType = - PropertyTableProperty::StringOffsetType::UINT64; - arrayProperty = - view.getPropertyView>("TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); - } - - SECTION("Offset values are not sorted ascending") { - propertyTableProperty.arrayOffsetType = - PropertyTableProperty::ArrayOffsetType::UINT64; - uint64_t* offset = reinterpret_cast( - model.buffers[offsetBufferIndex].cesium.data.data()); - offset[propertyTable.count] = 0; - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>("TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus::ErrorArrayOffsetsNotSorted); - } - - SECTION("Offset value points outside of value buffer") { - uint64_t* offset = reinterpret_cast( - model.buffers[offsetBufferIndex].cesium.data.data()); - offset[propertyTable.count] = static_cast( - model.buffers[valueBufferIndex].byteLength * 8 + 20); - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>("TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus::ErrorArrayOffsetOutOfBounds); - } - - SECTION("Count and offset buffer both present") { - testClassProperty.count = 3; - PropertyTablePropertyView> boolArrayProperty = - view.getPropertyView>("TestClassProperty"); - REQUIRE( - boolArrayProperty.status() == - PropertyTablePropertyViewStatus::ErrorArrayCountAndOffsetBufferCoexist); - } -} - -TEST_CASE("Test fixed-length arrays of strings") { - Model model; - - std::vector expected{ - "What's up", - "Breaking news!!! Aliens no longer attacks the US first", - "But they still abduct my cows! Those milk thiefs! 👽 🐮", - "I'm not crazy. My mother had me tested 🤪", - "I love you, meat bags! ❤️", - "Book in the freezer"}; - - size_t totalBytes = 0; - for (const std::string& expectedValue : expected) { - totalBytes += expectedValue.size(); - } - - std::vector offsets((expected.size() + 1) * sizeof(uint32_t)); - std::vector values(totalBytes); - uint32_t* offsetValue = reinterpret_cast(offsets.data()); - for (size_t i = 0; i < expected.size(); ++i) { - const std::string& expectedValue = expected[i]; - std::memcpy( - values.data() + offsetValue[i], - expectedValue.c_str(), - expectedValue.size()); - offsetValue[i + 1] = - offsetValue[i] + static_cast(expectedValue.size()); - } - - addBufferToModel(model, values); - size_t valueBufferViewIndex = model.bufferViews.size() - 1; - - addBufferToModel(model, offsets); - size_t offsetBufferViewIndex = model.bufferViews.size() - 1; - - ExtensionModelExtStructuralMetadata& metadata = - model.addExtension(); - - Schema& schema = metadata.schema.emplace(); - Class& testClass = schema.classes["TestClass"]; - ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; - testClassProperty.type = ClassProperty::Type::STRING; - testClassProperty.array = true; - testClassProperty.count = 2; - - PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); - propertyTable.classProperty = "TestClass"; - propertyTable.count = static_cast( - expected.size() / static_cast(testClassProperty.count.value())); - - PropertyTableProperty& propertyTableProperty = - propertyTable.properties["TestClassProperty"]; - propertyTableProperty.stringOffsetType = - PropertyTableProperty::StringOffsetType::UINT32; - propertyTableProperty.values = static_cast(valueBufferViewIndex); - propertyTableProperty.stringOffsets = - static_cast(offsetBufferViewIndex); - - PropertyTableView view(model, propertyTable); - REQUIRE(view.status() == PropertyTableViewStatus::Valid); - REQUIRE(view.size() == propertyTable.count); - - const ClassProperty* classProperty = - view.getClassProperty("TestClassProperty"); - REQUIRE(classProperty); - REQUIRE(classProperty->type == ClassProperty::Type::STRING); - REQUIRE(classProperty->array); - REQUIRE(classProperty->count == 2); - - SECTION("Access correct type") { - PropertyTablePropertyView> - stringProperty = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE(stringProperty.status() == PropertyTablePropertyViewStatus::Valid); - REQUIRE(stringProperty.size() == 3); - - PropertyArrayView v0 = stringProperty.get(0); - REQUIRE(v0.size() == 2); - REQUIRE(v0[0] == "What's up"); - REQUIRE(v0[1] == "Breaking news!!! Aliens no longer attacks the US first"); - - PropertyArrayView v1 = stringProperty.get(1); - REQUIRE(v1.size() == 2); - REQUIRE(v1[0] == "But they still abduct my cows! Those milk thiefs! 👽 🐮"); - REQUIRE(v1[1] == "I'm not crazy. My mother had me tested 🤪"); - - PropertyArrayView v2 = stringProperty.get(2); - REQUIRE(v2.size() == 2); - REQUIRE(v2[0] == "I love you, meat bags! ❤️"); - REQUIRE(v2[1] == "Book in the freezer"); - } - - SECTION("Array type mismatch") { - PropertyTablePropertyView stringProperty = - view.getPropertyView("TestClassProperty"); - REQUIRE( - stringProperty.status() == - PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); - } - - SECTION("Count is negative") { - testClassProperty.count = -1; - PropertyTablePropertyView> - stringProperty = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - stringProperty.status() == PropertyTablePropertyViewStatus:: - ErrorArrayCountAndOffsetBufferDontExist); - } - - SECTION("Offset type is unknown") { - propertyTableProperty.stringOffsetType = "NONSENSE"; - PropertyTablePropertyView> - stringProperty = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - stringProperty.status() == - PropertyTablePropertyViewStatus::ErrorInvalidStringOffsetType); - - propertyTableProperty.stringOffsetType = ""; - propertyTableProperty.arrayOffsetType = - PropertyTableProperty::ArrayOffsetType::UINT32; - stringProperty = view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - stringProperty.status() == - PropertyTablePropertyViewStatus::ErrorInvalidStringOffsetType); - } - - SECTION("String offsets don't exist") { - propertyTableProperty.stringOffsets = -1; - PropertyTablePropertyView> - stringProperty = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - stringProperty.status() == - PropertyTablePropertyViewStatus::ErrorInvalidStringOffsetBufferView); - } -} - -TEST_CASE("Test variable-length arrays of strings") { - Model model; - - std::vector> expected{ - {"What's up"}, - {"Breaking news!!! Aliens no longer attacks the US first", - "But they still abduct my cows! Those milk thiefs! 👽 🐮"}, - {"I'm not crazy. My mother had me tested 🤪", - "I love you, meat bags! ❤️", - "Book in the freezer"}}; - - size_t totalBytes = 0; - size_t numOfElements = 0; - for (const auto& expectedValues : expected) { - for (const auto& value : expectedValues) { - totalBytes += value.size(); - } - - numOfElements += expectedValues.size(); - } - - std::vector offsets((expected.size() + 1) * sizeof(uint32_t)); - std::vector stringOffsets((numOfElements + 1) * sizeof(uint32_t)); - std::vector values(totalBytes); - uint32_t* offsetValue = reinterpret_cast(offsets.data()); - uint32_t* stringOffsetValue = - reinterpret_cast(stringOffsets.data()); - size_t strOffsetIdx = 0; - for (size_t i = 0; i < expected.size(); ++i) { - for (size_t j = 0; j < expected[i].size(); ++j) { - const std::string& expectedValue = expected[i][j]; - std::memcpy( - values.data() + stringOffsetValue[strOffsetIdx], - expectedValue.c_str(), - expectedValue.size()); - - stringOffsetValue[strOffsetIdx + 1] = - stringOffsetValue[strOffsetIdx] + - static_cast(expectedValue.size()); - ++strOffsetIdx; - } - - offsetValue[i + 1] = - offsetValue[i] + - static_cast(expected[i].size() * sizeof(uint32_t)); - } - - addBufferToModel(model, values); - size_t valueBufferViewIndex = model.bufferViews.size() - 1; - - addBufferToModel(model, offsets); - size_t arrayOffsetBuffer = model.buffers.size() - 1; - size_t arrayOffsetBufferView = model.bufferViews.size() - 1; - - addBufferToModel(model, stringOffsets); - size_t stringOffsetBuffer = model.buffers.size() - 1; - size_t stringOffsetBufferView = model.bufferViews.size() - 1; - - ExtensionModelExtStructuralMetadata& metadata = - model.addExtension(); - - Schema& schema = metadata.schema.emplace(); - Class& testClass = schema.classes["TestClass"]; - ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; - testClassProperty.type = ClassProperty::Type::STRING; - testClassProperty.array = true; - - PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); - propertyTable.classProperty = "TestClass"; - propertyTable.count = static_cast(expected.size()); - - PropertyTableProperty& propertyTableProperty = - propertyTable.properties["TestClassProperty"]; - propertyTableProperty.arrayOffsetType = - PropertyTableProperty::ArrayOffsetType::UINT32; - propertyTableProperty.stringOffsetType = - PropertyTableProperty::StringOffsetType::UINT32; - propertyTableProperty.values = static_cast(valueBufferViewIndex); - propertyTableProperty.arrayOffsets = - static_cast(arrayOffsetBufferView); - propertyTableProperty.stringOffsets = - static_cast(stringOffsetBufferView); - - PropertyTableView view(model, propertyTable); - REQUIRE(view.status() == PropertyTableViewStatus::Valid); - REQUIRE(view.size() == propertyTable.count); - - const ClassProperty* classProperty = - view.getClassProperty("TestClassProperty"); - REQUIRE(classProperty); - REQUIRE(classProperty->type == ClassProperty::Type::STRING); - REQUIRE(classProperty->array); - REQUIRE(!classProperty->componentType); - REQUIRE(!classProperty->count); - - SECTION("Access correct type") { - PropertyTablePropertyView> - stringProperty = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE(stringProperty.status() == PropertyTablePropertyViewStatus::Valid); - for (size_t i = 0; i < expected.size(); ++i) { - PropertyArrayView stringArray = - stringProperty.get(static_cast(i)); - for (size_t j = 0; j < expected[i].size(); ++j) { - REQUIRE(stringArray[static_cast(j)] == expected[i][j]); - } - } - } - - SECTION("Wrong array offset type") { - propertyTableProperty.arrayOffsetType = - PropertyTableProperty::ArrayOffsetType::UINT8; - PropertyTablePropertyView> - arrayProperty = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus:: - ErrorBufferViewSizeDoesNotMatchPropertyTableCount); - - propertyTableProperty.arrayOffsetType = - PropertyTableProperty::ArrayOffsetType::UINT16; - arrayProperty = view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus:: - ErrorBufferViewSizeDoesNotMatchPropertyTableCount); - - propertyTableProperty.arrayOffsetType = "NONSENSE"; - arrayProperty = view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); - propertyTableProperty.arrayOffsetType = - PropertyTableProperty::ArrayOffsetType::UINT32; - } - - SECTION("Wrong string offset type") { - propertyTableProperty.stringOffsetType = - PropertyTableProperty::StringOffsetType::UINT8; - PropertyTablePropertyView> - arrayProperty = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus:: - ErrorBufferViewSizeDoesNotMatchPropertyTableCount); - - propertyTableProperty.stringOffsetType = - PropertyTableProperty::StringOffsetType::UINT16; - arrayProperty = view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus:: - ErrorBufferViewSizeDoesNotMatchPropertyTableCount); - - propertyTableProperty.stringOffsetType = "NONSENSE"; - arrayProperty = view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus::ErrorInvalidStringOffsetType); - propertyTableProperty.stringOffsetType = - PropertyTableProperty::StringOffsetType::UINT32; - } - - SECTION("Array offset values are not sorted ascending") { - uint32_t* offset = reinterpret_cast( - model.buffers[arrayOffsetBuffer].cesium.data.data()); - offset[0] = static_cast(1000); - PropertyTablePropertyView> - arrayProperty = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus::ErrorArrayOffsetsNotSorted); - offset[0] = 0; - } - - SECTION("String offset values are not sorted ascending") { - uint32_t* offset = reinterpret_cast( - model.buffers[stringOffsetBuffer].cesium.data.data()); - offset[0] = static_cast(1000); - PropertyTablePropertyView> - arrayProperty = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus::ErrorStringOffsetsNotSorted); - offset[0] = 0; - } - - SECTION("Array offset value points outside of value buffer") { - uint32_t* offset = reinterpret_cast( - model.buffers[arrayOffsetBuffer].cesium.data.data()); - uint32_t previousValue = offset[propertyTable.count]; - offset[propertyTable.count] = static_cast(100000); - PropertyTablePropertyView> - arrayProperty = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus::ErrorArrayOffsetOutOfBounds); - offset[propertyTable.count] = previousValue; - } - - SECTION("String offset value points outside of value buffer") { - uint32_t* offset = reinterpret_cast( - model.buffers[stringOffsetBuffer].cesium.data.data()); - uint32_t previousValue = offset[6]; - offset[6] = static_cast(100000); - PropertyTablePropertyView> - arrayProperty = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus::ErrorStringOffsetOutOfBounds); - offset[6] = previousValue; - } - - SECTION("Count and offset buffer both present") { - testClassProperty.count = 3; - PropertyTablePropertyView> - boolArrayProperty = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - boolArrayProperty.status() == - PropertyTablePropertyViewStatus::ErrorArrayCountAndOffsetBufferCoexist); - } -} - -TEST_CASE("Test callback on invalid property table view") { - Model model; - ExtensionModelExtStructuralMetadata& metadata = - model.addExtension(); - metadata.schema.emplace(); - - // Property table has a nonexistent class. - PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); - propertyTable.classProperty = "TestClass"; - propertyTable.count = static_cast(5); - - PropertyTableProperty& propertyTableProperty = - propertyTable.properties["TestClassProperty"]; - propertyTableProperty.values = static_cast(-1); - - PropertyTableView view(model, propertyTable); - REQUIRE(view.status() == PropertyTableViewStatus::ErrorClassNotFound); - REQUIRE(view.size() == 0); - - const ClassProperty* classProperty = - view.getClassProperty("TestClassProperty"); - REQUIRE(!classProperty); - - uint32_t invokedCallbackCount = 0; - view.getPropertyView( - "TestClassProperty", - [&invokedCallbackCount]( - const std::string& /*propertyName*/, - auto propertyValue) mutable { - invokedCallbackCount++; - REQUIRE( - propertyValue.status() == - PropertyTablePropertyViewStatus::ErrorInvalidPropertyTable); - REQUIRE(propertyValue.size() == 0); - }); - - REQUIRE(invokedCallbackCount == 1); -} - -TEST_CASE("Test callback for invalid PropertyTableProperty") { - Model model; - ExtensionModelExtStructuralMetadata& metadata = - model.addExtension(); - - Schema& schema = metadata.schema.emplace(); - Class& testClass = schema.classes["TestClass"]; - ClassProperty& testClassProperty = testClass.properties["InvalidProperty"]; - testClassProperty.type = ClassProperty::Type::SCALAR; - testClassProperty.componentType = ClassProperty::ComponentType::UINT32; - - PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); - propertyTable.classProperty = "TestClass"; - propertyTable.count = static_cast(5); - - PropertyTableProperty& propertyTableProperty = - propertyTable.properties["InvalidProperty"]; - propertyTableProperty.values = static_cast(-1); - - PropertyTableView view(model, propertyTable); - REQUIRE(view.status() == PropertyTableViewStatus::Valid); - REQUIRE(view.size() == propertyTable.count); - - const ClassProperty* classProperty = view.getClassProperty("InvalidProperty"); - REQUIRE(classProperty); - - classProperty = view.getClassProperty("NonexistentProperty"); - REQUIRE(!classProperty); - - uint32_t invokedCallbackCount = 0; - auto testCallback = [&invokedCallbackCount]( - const std::string& /*propertyName*/, - auto propertyValue) mutable { - invokedCallbackCount++; - REQUIRE(propertyValue.status() != PropertyTablePropertyViewStatus::Valid); - REQUIRE(propertyValue.size() == 0); - }; - - view.getPropertyView("InvalidProperty", testCallback); - view.getPropertyView("NonexistentProperty", testCallback); - - REQUIRE(invokedCallbackCount == 2); -} - -TEST_CASE("Test callback for scalar PropertyTableProperty") { - Model model; - std::vector values = {12, 34, 30, 11, 34, 34, 11, 33, 122, 33}; - - addBufferToModel(model, values); - size_t valueBufferViewIndex = model.bufferViews.size() - 1; - - ExtensionModelExtStructuralMetadata& metadata = - model.addExtension(); - - Schema& schema = metadata.schema.emplace(); - Class& testClass = schema.classes["TestClass"]; - ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; - testClassProperty.type = ClassProperty::Type::SCALAR; - testClassProperty.componentType = ClassProperty::ComponentType::UINT32; - - PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); - propertyTable.classProperty = "TestClass"; - propertyTable.count = static_cast(values.size()); - - PropertyTableProperty& propertyTableProperty = - propertyTable.properties["TestClassProperty"]; - propertyTableProperty.values = static_cast(valueBufferViewIndex); - - PropertyTableView view(model, propertyTable); - REQUIRE(view.status() == PropertyTableViewStatus::Valid); - REQUIRE(view.size() == propertyTable.count); - - const ClassProperty* classProperty = - view.getClassProperty("TestClassProperty"); - REQUIRE(classProperty); - REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); - REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT32); - REQUIRE(!classProperty->array); - REQUIRE(classProperty->count == std::nullopt); - - uint32_t invokedCallbackCount = 0; - view.getPropertyView( - "TestClassProperty", - [&values, &invokedCallbackCount]( - const std::string& /*propertyName*/, - auto propertyValue) mutable { - invokedCallbackCount++; - if constexpr (std::is_same_v< - PropertyTablePropertyView, - decltype(propertyValue)>) { - REQUIRE( - propertyValue.status() == PropertyTablePropertyViewStatus::Valid); - REQUIRE(propertyValue.size() > 0); - - for (int64_t i = 0; i < propertyValue.size(); ++i) { - REQUIRE( - static_cast(propertyValue.get(i)) == - values[static_cast(i)]); - } - } else { - FAIL( - "getPropertyView returned PropertyTablePropertyView of incorrect " - "type for TestClassProperty."); - } - }); - - REQUIRE(invokedCallbackCount == 1); -} - -TEST_CASE("Test callback for vecN PropertyTableProperty") { - Model model; - std::vector values = { - glm::ivec3(-12, 34, 30), - glm::ivec3(11, 73, 0), - glm::ivec3(-2, 6, 12), - glm::ivec3(-4, 8, -13)}; - - addBufferToModel(model, values); - size_t valueBufferViewIndex = model.bufferViews.size() - 1; - - ExtensionModelExtStructuralMetadata& metadata = - model.addExtension(); - - Schema& schema = metadata.schema.emplace(); - Class& testClass = schema.classes["TestClass"]; - ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; - testClassProperty.type = ClassProperty::Type::VEC3; - testClassProperty.componentType = ClassProperty::ComponentType::INT32; - - PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); - propertyTable.classProperty = "TestClass"; - propertyTable.count = static_cast(values.size()); - - PropertyTableProperty& propertyTableProperty = - propertyTable.properties["TestClassProperty"]; - propertyTableProperty.values = static_cast(valueBufferViewIndex); - - PropertyTableView view(model, propertyTable); - REQUIRE(view.status() == PropertyTableViewStatus::Valid); - REQUIRE(view.size() == propertyTable.count); - - const ClassProperty* classProperty = - view.getClassProperty("TestClassProperty"); - REQUIRE(classProperty); - REQUIRE(classProperty->type == ClassProperty::Type::VEC3); - REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT32); - REQUIRE(classProperty->count == std::nullopt); - REQUIRE(!classProperty->array); - - uint32_t invokedCallbackCount = 0; - view.getPropertyView( - "TestClassProperty", - [&values, &invokedCallbackCount]( - const std::string& /*propertyName*/, - auto propertyValue) mutable { - invokedCallbackCount++; - REQUIRE( - propertyValue.status() == PropertyTablePropertyViewStatus::Valid); - REQUIRE(propertyValue.size() > 0); - - if constexpr (std::is_same_v< - PropertyTablePropertyView, - decltype(propertyValue)>) { - for (int64_t i = 0; i < propertyValue.size(); ++i) { - REQUIRE( - static_cast(propertyValue.get(i)) == - values[static_cast(i)]); - } - } else { - FAIL( - "getPropertyView returned PropertyTablePropertyView of incorrect " - "type for TestClassProperty."); - } - }); - - REQUIRE(invokedCallbackCount == 1); -} - -TEST_CASE("Test callback for matN PropertyTableProperty") { - Model model; - // clang-format off - std::vector values = { - glm::u32mat2x2( - 12, 34, - 30, 1), - glm::u32mat2x2( - 11, 8, - 73, 102), - glm::u32mat2x2( - 1, 0, - 63, 2), - glm::u32mat2x2( - 4, 8, - 3, 23)}; - // clang-format on - - addBufferToModel(model, values); - size_t valueBufferViewIndex = model.bufferViews.size() - 1; - - ExtensionModelExtStructuralMetadata& metadata = - model.addExtension(); - - Schema& schema = metadata.schema.emplace(); - Class& testClass = schema.classes["TestClass"]; - ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; - testClassProperty.type = ClassProperty::Type::MAT2; - testClassProperty.componentType = ClassProperty::ComponentType::UINT32; - - PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); - propertyTable.classProperty = "TestClass"; - propertyTable.count = static_cast(values.size()); - - PropertyTableProperty& propertyTableProperty = - propertyTable.properties["TestClassProperty"]; - propertyTableProperty.values = static_cast(valueBufferViewIndex); - - PropertyTableView view(model, propertyTable); - REQUIRE(view.status() == PropertyTableViewStatus::Valid); - REQUIRE(view.size() == propertyTable.count); - - const ClassProperty* classProperty = - view.getClassProperty("TestClassProperty"); - REQUIRE(classProperty); - REQUIRE(classProperty->type == ClassProperty::Type::MAT2); - REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT32); - REQUIRE(classProperty->count == std::nullopt); - REQUIRE(!classProperty->array); - - uint32_t invokedCallbackCount = 0; - view.getPropertyView( - "TestClassProperty", - [&values, &invokedCallbackCount]( - const std::string& /*propertyName*/, - auto propertyValue) mutable { - REQUIRE( - propertyValue.status() == PropertyTablePropertyViewStatus::Valid); - REQUIRE(propertyValue.size() > 0); - - if constexpr (std::is_same_v< - PropertyTablePropertyView, - decltype(propertyValue)>) { - for (int64_t i = 0; i < propertyValue.size(); ++i) { - REQUIRE( - static_cast(propertyValue.get(i)) == - values[static_cast(i)]); - } - } else { - FAIL( - "getPropertyView returned PropertyTablePropertyView of incorrect " - "type for TestClassProperty."); - } - invokedCallbackCount++; - }); - - REQUIRE(invokedCallbackCount == 1); -} - -TEST_CASE("Test callback for boolean PropertyTableProperty") { - Model model; - - int64_t instanceCount = 21; - std::vector expected; - std::vector values; - values.resize(3); - for (int64_t i = 0; i < instanceCount; ++i) { - if (i % 2 == 0) { - expected.emplace_back(true); - } else { - expected.emplace_back(false); - } - - uint8_t expectedValue = expected.back(); - int64_t byteIndex = i / 8; - int64_t bitIndex = i % 8; - values[static_cast(byteIndex)] = static_cast( - (expectedValue << bitIndex) | values[static_cast(byteIndex)]); - } - - addBufferToModel(model, values); - - ExtensionModelExtStructuralMetadata& metadata = - model.addExtension(); - - Schema& schema = metadata.schema.emplace(); - Class& testClass = schema.classes["TestClass"]; - ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; - testClassProperty.type = ClassProperty::Type::BOOLEAN; - - PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); - propertyTable.classProperty = "TestClass"; - propertyTable.count = static_cast(instanceCount); - - PropertyTableProperty& propertyTableProperty = - propertyTable.properties["TestClassProperty"]; - propertyTableProperty.values = - static_cast(model.bufferViews.size() - 1); - - PropertyTableView view(model, propertyTable); - REQUIRE(view.status() == PropertyTableViewStatus::Valid); - REQUIRE(view.size() == propertyTable.count); - - const ClassProperty* classProperty = - view.getClassProperty("TestClassProperty"); - REQUIRE(classProperty); - REQUIRE(classProperty->type == ClassProperty::Type::BOOLEAN); - REQUIRE(classProperty->componentType == std::nullopt); - REQUIRE(classProperty->count == std::nullopt); - REQUIRE(!classProperty->array); - - uint32_t invokedCallbackCount = 0; - view.getPropertyView( - "TestClassProperty", - [&expected, &invokedCallbackCount]( - const std::string& /*propertyName*/, - auto propertyValue) mutable { - invokedCallbackCount++; - - REQUIRE( - propertyValue.status() == PropertyTablePropertyViewStatus::Valid); - REQUIRE(propertyValue.size() > 0); - - if constexpr (std::is_same_v< - PropertyTablePropertyView, - decltype(propertyValue)>) { - for (int64_t i = 0; i < propertyValue.size(); ++i) { - REQUIRE( - static_cast(propertyValue.get(i)) == - expected[static_cast(i)]); - } - } else { - FAIL( - "getPropertyView returned PropertyTablePropertyView of incorrect " - "type for TestClassProperty."); - } - }); - - REQUIRE(invokedCallbackCount == 1); -} - -TEST_CASE("Test callback for string PropertyTableProperty") { - Model model; - - std::vector expected{"What's up", "Test_0", "Test_1", "", "Hi"}; - size_t totalBytes = 0; - for (const std::string& expectedValue : expected) { - totalBytes += expectedValue.size(); - } - - std::vector stringOffsets( - (expected.size() + 1) * sizeof(uint32_t)); - std::vector values(totalBytes); - uint32_t* offsetValue = reinterpret_cast(stringOffsets.data()); - for (size_t i = 0; i < expected.size(); ++i) { - const std::string& expectedValue = expected[i]; - std::memcpy( - values.data() + offsetValue[i], - expectedValue.c_str(), - expectedValue.size()); - offsetValue[i + 1] = - offsetValue[i] + static_cast(expectedValue.size()); - } - - addBufferToModel(model, values); - size_t valueBufferViewIndex = model.bufferViews.size() - 1; - - addBufferToModel(model, stringOffsets); - size_t offsetBufferViewIndex = model.bufferViews.size() - 1; - - ExtensionModelExtStructuralMetadata& metadata = - model.addExtension(); - - Schema& schema = metadata.schema.emplace(); - Class& testClass = schema.classes["TestClass"]; - ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; - testClassProperty.type = ClassProperty::Type::STRING; - - PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); - propertyTable.classProperty = "TestClass"; - propertyTable.count = static_cast(expected.size()); - - PropertyTableProperty& propertyTableProperty = - propertyTable.properties["TestClassProperty"]; - propertyTableProperty.stringOffsetType = - PropertyTableProperty::StringOffsetType::UINT32; - propertyTableProperty.values = static_cast(valueBufferViewIndex); - propertyTableProperty.stringOffsets = - static_cast(offsetBufferViewIndex); - - PropertyTableView view(model, propertyTable); - REQUIRE(view.status() == PropertyTableViewStatus::Valid); - REQUIRE(view.size() == propertyTable.count); - - const ClassProperty* classProperty = - view.getClassProperty("TestClassProperty"); - REQUIRE(classProperty); - REQUIRE(classProperty->type == ClassProperty::Type::STRING); - REQUIRE(classProperty->componentType == std::nullopt); - REQUIRE(classProperty->count == std::nullopt); - REQUIRE(!classProperty->array); - - uint32_t invokedCallbackCount = 0; - view.getPropertyView( - "TestClassProperty", - [&expected, &invokedCallbackCount]( - const std::string& /*propertyName*/, - auto propertyValue) mutable { - invokedCallbackCount++; - REQUIRE( - propertyValue.status() == PropertyTablePropertyViewStatus::Valid); - REQUIRE(propertyValue.size() > 0); - - if constexpr (std::is_same_v< - PropertyTablePropertyView, - decltype(propertyValue)>) { - for (int64_t i = 0; i < propertyValue.size(); ++i) { - REQUIRE( - static_cast(propertyValue.get(i)) == - expected[static_cast(i)]); - } - } else { - FAIL( - "getPropertyView returned PropertyTablePropertyView of incorrect " - "type for TestClassProperty."); - } - }); - - REQUIRE(invokedCallbackCount == 1); -} - -TEST_CASE("Test callback for scalar array PropertyTableProperty") { - Model model; - std::vector values = - {12, 34, 30, 11, 34, 34, 11, 33, 122, 33, 223, 11}; - - addBufferToModel(model, values); - - ExtensionModelExtStructuralMetadata& metadata = - model.addExtension(); - - Schema& schema = metadata.schema.emplace(); - Class& testClass = schema.classes["TestClass"]; - ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; - testClassProperty.type = ClassProperty::Type::SCALAR; - testClassProperty.componentType = ClassProperty::ComponentType::UINT32; - testClassProperty.array = true; - testClassProperty.count = 3; - - PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); - propertyTable.classProperty = "TestClass"; - propertyTable.count = static_cast( - values.size() / static_cast(testClassProperty.count.value())); - - PropertyTableProperty& propertyTableProperty = - propertyTable.properties["TestClassProperty"]; - propertyTableProperty.values = - static_cast(model.bufferViews.size() - 1); - - PropertyTableView view(model, propertyTable); - REQUIRE(view.status() == PropertyTableViewStatus::Valid); - REQUIRE(view.size() == propertyTable.count); - - const ClassProperty* classProperty = - view.getClassProperty("TestClassProperty"); - REQUIRE(classProperty); - REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); - REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT32); - REQUIRE(classProperty->array); - REQUIRE(classProperty->count == 3); - - uint32_t invokedCallbackCount = 0; - view.getPropertyView( - "TestClassProperty", - [&values, &invokedCallbackCount]( - const std::string& /*propertyName*/, - auto propertyValue) { - invokedCallbackCount++; - REQUIRE( - propertyValue.status() == PropertyTablePropertyViewStatus::Valid); - REQUIRE(propertyValue.size() > 0); - - if constexpr (std::is_same_v< - PropertyTablePropertyView< - PropertyArrayView>, - decltype(propertyValue)>) { - for (int64_t i = 0; i < propertyValue.size(); ++i) { - PropertyArrayView member = propertyValue.get(i); - for (int64_t j = 0; j < member.size(); ++j) { - REQUIRE(member[j] == values[static_cast(i * 3 + j)]); - } - } - } else { - FAIL( - "getPropertyView returned PropertyTablePropertyView of incorrect " - "type for TestClassProperty."); - } - }); - - REQUIRE(invokedCallbackCount == 1); -} - -TEST_CASE("Test callback for vecN array PropertyTableProperty") { - Model model; - std::vector values = { - glm::ivec3(12, 34, -30), - glm::ivec3(-2, 0, 1), - glm::ivec3(1, 2, 8), - glm::ivec3(-100, 84, 6), - glm::ivec3(2, -2, -2), - glm::ivec3(40, 61, 3), - }; - - addBufferToModel(model, values); - - ExtensionModelExtStructuralMetadata& metadata = - model.addExtension(); - - Schema& schema = metadata.schema.emplace(); - Class& testClass = schema.classes["TestClass"]; - ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; - testClassProperty.type = ClassProperty::Type::VEC3; - testClassProperty.componentType = ClassProperty::ComponentType::INT32; - testClassProperty.array = true; - testClassProperty.count = 2; - - PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); - propertyTable.classProperty = "TestClass"; - propertyTable.count = static_cast( - values.size() / static_cast(testClassProperty.count.value())); - - PropertyTableProperty& propertyTableProperty = - propertyTable.properties["TestClassProperty"]; - propertyTableProperty.values = - static_cast(model.bufferViews.size() - 1); - - PropertyTableView view(model, propertyTable); - REQUIRE(view.status() == PropertyTableViewStatus::Valid); - REQUIRE(view.size() == propertyTable.count); - - const ClassProperty* classProperty = - view.getClassProperty("TestClassProperty"); - REQUIRE(classProperty); - REQUIRE(classProperty->type == ClassProperty::Type::VEC3); - REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT32); - REQUIRE(classProperty->array); - REQUIRE(classProperty->count == 2); - - uint32_t invokedCallbackCount = 0; - view.getPropertyView( - "TestClassProperty", - [&values, &invokedCallbackCount]( - const std::string& /*propertyName*/, - auto propertyValue) { - invokedCallbackCount++; - REQUIRE( - propertyValue.status() == PropertyTablePropertyViewStatus::Valid); - REQUIRE(propertyValue.size() > 0); - - if constexpr (std::is_same_v< - PropertyTablePropertyView< - PropertyArrayView>, - decltype(propertyValue)>) { - for (int64_t i = 0; i < propertyValue.size(); ++i) { - PropertyArrayView member = propertyValue.get(i); - for (int64_t j = 0; j < member.size(); ++j) { - REQUIRE(member[j] == values[static_cast(i * 2 + j)]); - } - } - } else { - FAIL( - "getPropertyView returned PropertyTablePropertyView of incorrect " - "type for TestClassProperty."); - } - }); - - REQUIRE(invokedCallbackCount == 1); -} - -TEST_CASE("Test callback for matN array PropertyTableProperty") { - Model model; - // clang-format off - std::vector values = { - glm::i32mat2x2( - 12, 34, - -30, 20), - glm::i32mat2x2( - -2, -2, - 0, 1), - glm::i32mat2x2( - 1, 2, - 8, 5), - glm::i32mat2x2( - -100, 3, - 84, 6), - glm::i32mat2x2( - 2, 12, - -2, -2), - glm::i32mat2x2( - 40, 61, - 7, -3), - }; - // clang-format on - - addBufferToModel(model, values); - - ExtensionModelExtStructuralMetadata& metadata = - model.addExtension(); - - Schema& schema = metadata.schema.emplace(); - Class& testClass = schema.classes["TestClass"]; - ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; - testClassProperty.type = ClassProperty::Type::MAT2; - testClassProperty.componentType = ClassProperty::ComponentType::INT32; - testClassProperty.array = true; - testClassProperty.count = 2; - - PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); - propertyTable.classProperty = "TestClass"; - propertyTable.count = static_cast( - values.size() / static_cast(testClassProperty.count.value())); - - PropertyTableProperty& propertyTableProperty = - propertyTable.properties["TestClassProperty"]; - propertyTableProperty.values = - static_cast(model.bufferViews.size() - 1); - - PropertyTableView view(model, propertyTable); - REQUIRE(view.status() == PropertyTableViewStatus::Valid); - REQUIRE(view.size() == propertyTable.count); - - const ClassProperty* classProperty = - view.getClassProperty("TestClassProperty"); - REQUIRE(classProperty); - REQUIRE(classProperty->type == ClassProperty::Type::MAT2); - REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT32); - REQUIRE(classProperty->array); - REQUIRE(classProperty->count == 2); - - uint32_t invokedCallbackCount = 0; - view.getPropertyView( - "TestClassProperty", - [&values, &invokedCallbackCount]( - const std::string& /*propertyName*/, - auto propertyValue) { - invokedCallbackCount++; - REQUIRE( - propertyValue.status() == PropertyTablePropertyViewStatus::Valid); - REQUIRE(propertyValue.size() > 0); - - if constexpr (std::is_same_v< - PropertyTablePropertyView< - PropertyArrayView>, - decltype(propertyValue)>) { - for (int64_t i = 0; i < propertyValue.size(); ++i) { - PropertyArrayView member = propertyValue.get(i); - for (int64_t j = 0; j < member.size(); ++j) { - REQUIRE(member[j] == values[static_cast(i * 2 + j)]); - } - } - } else { - FAIL( - "getPropertyView returned PropertyTablePropertyView of incorrect " - "type for TestClassProperty."); - } - }); - - REQUIRE(invokedCallbackCount == 1); -} - -TEST_CASE("Test callback for boolean array PropertyTableProperty") { - Model model; - - std::vector expected = { - true, - false, - false, - true, - false, - false, - true, - true, - true, - false, - false, - true}; - std::vector values; - size_t requiredBytesSize = static_cast( - glm::ceil(static_cast(expected.size()) / 8.0)); - values.resize(requiredBytesSize); - for (size_t i = 0; i < expected.size(); ++i) { - uint8_t expectedValue = expected[i]; - size_t byteIndex = i / 8; - size_t bitIndex = i % 8; - values[byteIndex] = - static_cast((expectedValue << bitIndex) | values[byteIndex]); - } - - addBufferToModel(model, values); - - ExtensionModelExtStructuralMetadata& metadata = - model.addExtension(); - - Schema& schema = metadata.schema.emplace(); - Class& testClass = schema.classes["TestClass"]; - ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; - testClassProperty.type = ClassProperty::Type::BOOLEAN; - testClassProperty.array = true; - testClassProperty.count = 3; - - PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); - propertyTable.classProperty = "TestClass"; - propertyTable.count = static_cast( - expected.size() / static_cast(testClassProperty.count.value())); - - PropertyTableProperty& propertyTableProperty = - propertyTable.properties["TestClassProperty"]; - propertyTableProperty.values = - static_cast(model.bufferViews.size() - 1); - - PropertyTableView view(model, propertyTable); - REQUIRE(view.status() == PropertyTableViewStatus::Valid); - REQUIRE(view.size() == propertyTable.count); - - const ClassProperty* classProperty = - view.getClassProperty("TestClassProperty"); - REQUIRE(classProperty); - REQUIRE(classProperty->type == ClassProperty::Type::BOOLEAN); - REQUIRE(classProperty->array); - REQUIRE(classProperty->count == 3); - - uint32_t invokedCallbackCount = 0; - view.getPropertyView( - "TestClassProperty", - [&expected, &invokedCallbackCount]( - const std::string& /*propertyName*/, - auto propertyValue) { - invokedCallbackCount++; - REQUIRE( - propertyValue.status() == PropertyTablePropertyViewStatus::Valid); - REQUIRE(propertyValue.size() > 0); - - if constexpr (std::is_same_v< - PropertyTablePropertyView>, - decltype(propertyValue)>) { - for (int64_t i = 0; i < propertyValue.size(); ++i) { - PropertyArrayView member = propertyValue.get(i); - for (int64_t j = 0; j < member.size(); ++j) { - REQUIRE(member[j] == expected[static_cast(i * 3 + j)]); - } - } - } else { - FAIL( - "getPropertyView returned PropertyTablePropertyView of incorrect " - "type for TestClassProperty."); - } - }); - - REQUIRE(invokedCallbackCount == 1); -} - -TEST_CASE("Test callback for string array PropertyTableProperty") { - Model model; - - std::vector expected{ - "What's up", - "Breaking news!!! Aliens no longer attacks the US first", - "But they still abduct my cows! Those milk thiefs! 👽 🐮", - "I'm not crazy. My mother had me tested 🤪", - "I love you, meat bags! ❤️", - "Book in the freezer"}; - - size_t totalBytes = 0; - for (const std::string& expectedValue : expected) { - totalBytes += expectedValue.size(); - } - - std::vector offsets((expected.size() + 1) * sizeof(uint32_t)); - std::vector values(totalBytes); - uint32_t* offsetValue = reinterpret_cast(offsets.data()); - for (size_t i = 0; i < expected.size(); ++i) { - const std::string& expectedValue = expected[i]; - std::memcpy( - values.data() + offsetValue[i], - expectedValue.c_str(), - expectedValue.size()); - offsetValue[i + 1] = - offsetValue[i] + static_cast(expectedValue.size()); - } - - addBufferToModel(model, values); - size_t valueBufferViewIndex = model.bufferViews.size() - 1; - - addBufferToModel(model, offsets); - size_t offsetBufferViewIndex = model.bufferViews.size() - 1; - - ExtensionModelExtStructuralMetadata& metadata = - model.addExtension(); - - Schema& schema = metadata.schema.emplace(); - Class& testClass = schema.classes["TestClass"]; - ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; - testClassProperty.type = ClassProperty::Type::STRING; - testClassProperty.array = true; - testClassProperty.count = 2; - - PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); - propertyTable.classProperty = "TestClass"; - propertyTable.count = static_cast( - expected.size() / static_cast(testClassProperty.count.value())); - - PropertyTableProperty& propertyTableProperty = - propertyTable.properties["TestClassProperty"]; - propertyTableProperty.stringOffsetType = - PropertyTableProperty::StringOffsetType::UINT32; - propertyTableProperty.values = static_cast(valueBufferViewIndex); - propertyTableProperty.stringOffsets = - static_cast(offsetBufferViewIndex); - - PropertyTableView view(model, propertyTable); - REQUIRE(view.status() == PropertyTableViewStatus::Valid); - REQUIRE(view.size() == propertyTable.count); - - const ClassProperty* classProperty = - view.getClassProperty("TestClassProperty"); - REQUIRE(classProperty); - REQUIRE(classProperty->type == ClassProperty::Type::STRING); - REQUIRE(classProperty->array); - REQUIRE(classProperty->count == 2); - - uint32_t invokedCallbackCount = 0; - view.getPropertyView( - "TestClassProperty", - [&invokedCallbackCount]( - const std::string& /*propertyName*/, - auto propertyValue) { - invokedCallbackCount++; - REQUIRE( - propertyValue.status() == PropertyTablePropertyViewStatus::Valid); - REQUIRE(propertyValue.size() == 3); - - if constexpr (std::is_same_v< - PropertyTablePropertyView< - PropertyArrayView>, - decltype(propertyValue)>) { - PropertyArrayView v0 = propertyValue.get(0); - REQUIRE(v0.size() == 2); - REQUIRE(v0[0] == "What's up"); - REQUIRE( - v0[1] == - "Breaking news!!! Aliens no longer attacks the US first"); - - PropertyArrayView v1 = propertyValue.get(1); - REQUIRE(v1.size() == 2); - REQUIRE( - v1[0] == "But they still abduct my cows! Those milk thiefs! 👽 🐮"); - REQUIRE(v1[1] == "I'm not crazy. My mother had me tested 🤪"); - - PropertyArrayView v2 = propertyValue.get(2); - REQUIRE(v2.size() == 2); - REQUIRE(v2[0] == "I love you, meat bags! ❤️"); - REQUIRE(v2[1] == "Book in the freezer"); - } else { - FAIL( - "getPropertyView returned PropertyTablePropertyView of incorrect " - "type for TestClassProperty."); - } - }); - - REQUIRE(invokedCallbackCount == 1); -} +//#include "CesiumGltf/PropertyTableView.h" +// +//#include +// +//#include +// +//using namespace CesiumGltf; +// +//template +//void addBufferToModel(Model& model, const std::vector& values) { +// Buffer& valueBuffer = model.buffers.emplace_back(); +// valueBuffer.cesium.data.resize(values.size() * sizeof(T)); +// valueBuffer.byteLength = static_cast(valueBuffer.cesium.data.size()); +// std::memcpy( +// valueBuffer.cesium.data.data(), +// values.data(), +// valueBuffer.cesium.data.size()); +// +// BufferView& valueBufferView = model.bufferViews.emplace_back(); +// valueBufferView.buffer = static_cast(model.buffers.size() - 1); +// valueBufferView.byteOffset = 0; +// valueBufferView.byteLength = valueBuffer.byteLength; +//} +// +//TEST_CASE("Test PropertyTableView on model without EXT_structural_metadata " +// "extension") { +// Model model; +// +// // Create an erroneously isolated property table. +// PropertyTable propertyTable; +// propertyTable.classProperty = "TestClass"; +// propertyTable.count = static_cast(10); +// +// PropertyTableProperty& propertyTableProperty = +// propertyTable.properties["TestClassProperty"]; +// propertyTableProperty.values = static_cast(0); +// +// PropertyTableView view(model, propertyTable); +// REQUIRE( +// view.status() == PropertyTableViewStatus::ErrorMissingMetadataExtension); +// REQUIRE(view.size() == 0); +// +// const ClassProperty* classProperty = +// view.getClassProperty("TestClassProperty"); +// REQUIRE(!classProperty); +//} +// +//TEST_CASE("Test PropertyTableView on model without metadata schema") { +// Model model; +// +// ExtensionModelExtStructuralMetadata& metadata = +// model.addExtension(); +// +// PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); +// propertyTable.classProperty = "TestClass"; +// propertyTable.count = static_cast(10); +// +// PropertyTableProperty& propertyTableProperty = +// propertyTable.properties["TestClassProperty"]; +// propertyTableProperty.values = static_cast(0); +// +// PropertyTableView view(model, propertyTable); +// REQUIRE(view.status() == PropertyTableViewStatus::ErrorMissingSchema); +// REQUIRE(view.size() == 0); +// +// const ClassProperty* classProperty = +// view.getClassProperty("TestClassProperty"); +// REQUIRE(!classProperty); +//} +// +//TEST_CASE("Test property table with nonexistent class") { +// Model model; +// +// ExtensionModelExtStructuralMetadata& metadata = +// model.addExtension(); +// +// Schema& schema = metadata.schema.emplace(); +// Class& testClass = schema.classes["TestClass"]; +// ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; +// testClassProperty.type = ClassProperty::Type::SCALAR; +// testClassProperty.componentType = ClassProperty::ComponentType::UINT32; +// +// PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); +// propertyTable.classProperty = "I Don't Exist"; +// propertyTable.count = static_cast(10); +// +// PropertyTableProperty& propertyTableProperty = +// propertyTable.properties["TestClassProperty"]; +// propertyTableProperty.values = static_cast(0); +// +// PropertyTableView view(model, propertyTable); +// REQUIRE(view.status() == PropertyTableViewStatus::ErrorClassNotFound); +// REQUIRE(view.size() == 0); +// +// const ClassProperty* classProperty = +// view.getClassProperty("TestClassProperty"); +// REQUIRE(!classProperty); +//} +// +//TEST_CASE("Test scalar PropertyTableProperty") { +// Model model; +// std::vector values = {12, 34, 30, 11, 34, 34, 11, 33, 122, 33}; +// +// addBufferToModel(model, values); +// size_t valueBufferIndex = model.buffers.size() - 1; +// size_t valueBufferViewIndex = model.bufferViews.size() - 1; +// +// ExtensionModelExtStructuralMetadata& metadata = +// model.addExtension(); +// +// Schema& schema = metadata.schema.emplace(); +// Class& testClass = schema.classes["TestClass"]; +// ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; +// testClassProperty.type = ClassProperty::Type::SCALAR; +// testClassProperty.componentType = ClassProperty::ComponentType::UINT32; +// +// PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); +// propertyTable.classProperty = "TestClass"; +// propertyTable.count = static_cast(values.size()); +// +// PropertyTableProperty& propertyTableProperty = +// propertyTable.properties["TestClassProperty"]; +// propertyTableProperty.values = static_cast(valueBufferViewIndex); +// +// PropertyTableView view(model, propertyTable); +// REQUIRE(view.status() == PropertyTableViewStatus::Valid); +// REQUIRE(view.size() == propertyTable.count); +// +// const ClassProperty* classProperty = +// view.getClassProperty("TestClassProperty"); +// REQUIRE(classProperty); +// REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); +// REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT32); +// REQUIRE(classProperty->count == std::nullopt); +// REQUIRE(!classProperty->array); +// +// SECTION("Access correct type") { +// PropertyTablePropertyView uint32Property = +// view.getPropertyView("TestClassProperty"); +// REQUIRE(uint32Property.status() == PropertyTablePropertyViewStatus::Valid); +// REQUIRE(uint32Property.size() > 0); +// +// for (int64_t i = 0; i < uint32Property.size(); ++i) { +// REQUIRE(uint32Property.get(i) == values[static_cast(i)]); +// } +// } +// +// SECTION("Access wrong type") { +// PropertyTablePropertyView uvec3Invalid = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// uvec3Invalid.status() == +// PropertyTablePropertyViewStatus::ErrorTypeMismatch); +// +// PropertyTablePropertyView u32mat3x3Invalid = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// u32mat3x3Invalid.status() == +// PropertyTablePropertyViewStatus::ErrorTypeMismatch); +// +// PropertyTablePropertyView boolInvalid = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// boolInvalid.status() == +// PropertyTablePropertyViewStatus::ErrorTypeMismatch); +// +// PropertyTablePropertyView stringInvalid = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// stringInvalid.status() == +// PropertyTablePropertyViewStatus::ErrorTypeMismatch); +// } +// +// SECTION("Access wrong component type") { +// PropertyTablePropertyView uint8Invalid = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// uint8Invalid.status() == +// PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); +// +// PropertyTablePropertyView int32Invalid = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// int32Invalid.status() == +// PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); +// +// PropertyTablePropertyView uint64Invalid = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// uint64Invalid.status() == +// PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); +// } +// +// SECTION("Access incorrectly as array") { +// PropertyTablePropertyView> uint32ArrayInvalid = +// view.getPropertyView>("TestClassProperty"); +// REQUIRE( +// uint32ArrayInvalid.status() == +// PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); +// } +// +// SECTION("Wrong buffer index") { +// model.bufferViews[valueBufferViewIndex].buffer = 2; +// PropertyTablePropertyView uint32Property = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// uint32Property.status() == +// PropertyTablePropertyViewStatus::ErrorInvalidValueBuffer); +// } +// +// SECTION("Wrong buffer view index") { +// propertyTableProperty.values = -1; +// PropertyTablePropertyView uint32Property = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// uint32Property.status() == +// PropertyTablePropertyViewStatus::ErrorInvalidValueBufferView); +// } +// +// SECTION("Buffer view points outside of the real buffer length") { +// model.buffers[valueBufferIndex].cesium.data.resize(12); +// PropertyTablePropertyView uint32Property = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// uint32Property.status() == +// PropertyTablePropertyViewStatus::ErrorBufferViewOutOfBounds); +// } +// +// SECTION("Buffer view length isn't multiple of sizeof(T)") { +// model.bufferViews[valueBufferViewIndex].byteLength = 13; +// PropertyTablePropertyView uint32Property = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// uint32Property.status() == +// PropertyTablePropertyViewStatus:: +// ErrorBufferViewSizeNotDivisibleByTypeSize); +// } +// +// SECTION("Buffer view length doesn't match with propertyTableCount") { +// model.bufferViews[valueBufferViewIndex].byteLength = 12; +// PropertyTablePropertyView uint32Property = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// uint32Property.status() == +// PropertyTablePropertyViewStatus:: +// ErrorBufferViewSizeDoesNotMatchPropertyTableCount); +// } +//} +// +//TEST_CASE("Test vecN PropertyTableProperty") { +// Model model; +// std::vector values = { +// glm::ivec3(-12, 34, 30), +// glm::ivec3(11, 73, 0), +// glm::ivec3(-2, 6, 12), +// glm::ivec3(-4, 8, -13)}; +// +// addBufferToModel(model, values); +// size_t valueBufferIndex = model.buffers.size() - 1; +// size_t valueBufferViewIndex = model.bufferViews.size() - 1; +// +// ExtensionModelExtStructuralMetadata& metadata = +// model.addExtension(); +// +// Schema& schema = metadata.schema.emplace(); +// Class& testClass = schema.classes["TestClass"]; +// ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; +// testClassProperty.type = ClassProperty::Type::VEC3; +// testClassProperty.componentType = ClassProperty::ComponentType::INT32; +// +// PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); +// propertyTable.classProperty = "TestClass"; +// propertyTable.count = static_cast(values.size()); +// +// PropertyTableProperty& propertyTableProperty = +// propertyTable.properties["TestClassProperty"]; +// propertyTableProperty.values = static_cast(valueBufferViewIndex); +// +// PropertyTableView view(model, propertyTable); +// REQUIRE(view.status() == PropertyTableViewStatus::Valid); +// REQUIRE(view.size() == propertyTable.count); +// +// const ClassProperty* classProperty = +// view.getClassProperty("TestClassProperty"); +// REQUIRE(classProperty); +// REQUIRE(classProperty->type == ClassProperty::Type::VEC3); +// REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT32); +// REQUIRE(classProperty->count == std::nullopt); +// REQUIRE(!classProperty->array); +// +// SECTION("Access correct type") { +// PropertyTablePropertyView ivec3Property = +// view.getPropertyView("TestClassProperty"); +// REQUIRE(ivec3Property.status() == PropertyTablePropertyViewStatus::Valid); +// REQUIRE(ivec3Property.size() > 0); +// +// for (int64_t i = 0; i < ivec3Property.size(); ++i) { +// REQUIRE(ivec3Property.get(i) == values[static_cast(i)]); +// } +// } +// +// SECTION("Access wrong type") { +// PropertyTablePropertyView int32Invalid = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// int32Invalid.status() == +// PropertyTablePropertyViewStatus::ErrorTypeMismatch); +// +// PropertyTablePropertyView ivec2Invalid = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// ivec2Invalid.status() == +// PropertyTablePropertyViewStatus::ErrorTypeMismatch); +// +// PropertyTablePropertyView i32mat3x3Invalid = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// i32mat3x3Invalid.status() == +// PropertyTablePropertyViewStatus::ErrorTypeMismatch); +// +// PropertyTablePropertyView boolInvalid = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// boolInvalid.status() == +// PropertyTablePropertyViewStatus::ErrorTypeMismatch); +// +// PropertyTablePropertyView stringInvalid = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// stringInvalid.status() == +// PropertyTablePropertyViewStatus::ErrorTypeMismatch); +// } +// +// SECTION("Access wrong component type") { +// PropertyTablePropertyView u8vec3Invalid = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// u8vec3Invalid.status() == +// PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); +// +// PropertyTablePropertyView i16vec3Invalid = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// i16vec3Invalid.status() == +// PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); +// +// PropertyTablePropertyView vec3Invalid = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// vec3Invalid.status() == +// PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); +// } +// +// SECTION("Access incorrectly as array") { +// PropertyTablePropertyView> ivec3ArrayInvalid = +// view.getPropertyView>( +// "TestClassProperty"); +// REQUIRE( +// ivec3ArrayInvalid.status() == +// PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); +// } +// +// SECTION("Wrong buffer index") { +// model.bufferViews[valueBufferViewIndex].buffer = 2; +// PropertyTablePropertyView ivec3Property = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// ivec3Property.status() == +// PropertyTablePropertyViewStatus::ErrorInvalidValueBuffer); +// } +// +// SECTION("Wrong buffer view index") { +// propertyTableProperty.values = -1; +// PropertyTablePropertyView ivec3Property = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// ivec3Property.status() == +// PropertyTablePropertyViewStatus::ErrorInvalidValueBufferView); +// } +// +// SECTION("Buffer view points outside of the real buffer length") { +// model.buffers[valueBufferIndex].cesium.data.resize(12); +// PropertyTablePropertyView ivec3Property = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// ivec3Property.status() == +// PropertyTablePropertyViewStatus::ErrorBufferViewOutOfBounds); +// } +// +// SECTION("Buffer view length isn't multiple of sizeof(T)") { +// model.bufferViews[valueBufferViewIndex].byteLength = 11; +// PropertyTablePropertyView ivec3Property = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// ivec3Property.status() == +// PropertyTablePropertyViewStatus:: +// ErrorBufferViewSizeNotDivisibleByTypeSize); +// } +// +// SECTION("Buffer view length doesn't match with propertyTableCount") { +// model.bufferViews[valueBufferViewIndex].byteLength = 12; +// PropertyTablePropertyView ivec3Property = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// ivec3Property.status() == +// PropertyTablePropertyViewStatus:: +// ErrorBufferViewSizeDoesNotMatchPropertyTableCount); +// } +//} +// +//TEST_CASE("Test matN PropertyTableProperty") { +// Model model; +// // clang-format off +// std::vector values = { +// glm::u32mat2x2( +// 12, 34, +// 30, 1), +// glm::u32mat2x2( +// 11, 8, +// 73, 102), +// glm::u32mat2x2( +// 1, 0, +// 63, 2), +// glm::u32mat2x2( +// 4, 8, +// 3, 23)}; +// // clang-format on +// +// addBufferToModel(model, values); +// size_t valueBufferIndex = model.buffers.size() - 1; +// size_t valueBufferViewIndex = model.bufferViews.size() - 1; +// +// ExtensionModelExtStructuralMetadata& metadata = +// model.addExtension(); +// +// Schema& schema = metadata.schema.emplace(); +// Class& testClass = schema.classes["TestClass"]; +// ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; +// testClassProperty.type = ClassProperty::Type::MAT2; +// testClassProperty.componentType = ClassProperty::ComponentType::UINT32; +// +// PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); +// propertyTable.classProperty = "TestClass"; +// propertyTable.count = static_cast(values.size()); +// +// PropertyTableProperty& propertyTableProperty = +// propertyTable.properties["TestClassProperty"]; +// propertyTableProperty.values = static_cast(valueBufferViewIndex); +// +// PropertyTableView view(model, propertyTable); +// REQUIRE(view.status() == PropertyTableViewStatus::Valid); +// REQUIRE(view.size() == propertyTable.count); +// +// const ClassProperty* classProperty = +// view.getClassProperty("TestClassProperty"); +// REQUIRE(classProperty); +// REQUIRE(classProperty->type == ClassProperty::Type::MAT2); +// REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT32); +// REQUIRE(classProperty->count == std::nullopt); +// REQUIRE(!classProperty->array); +// +// SECTION("Access correct type") { +// PropertyTablePropertyView u32mat2x2Property = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// u32mat2x2Property.status() == PropertyTablePropertyViewStatus::Valid); +// REQUIRE(u32mat2x2Property.size() > 0); +// +// for (int64_t i = 0; i < u32mat2x2Property.size(); ++i) { +// REQUIRE(u32mat2x2Property.get(i) == values[static_cast(i)]); +// } +// } +// +// SECTION("Access wrong type") { +// PropertyTablePropertyView uint32Invalid = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// uint32Invalid.status() == +// PropertyTablePropertyViewStatus::ErrorTypeMismatch); +// +// PropertyTablePropertyView uvec2Invalid = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// uvec2Invalid.status() == +// PropertyTablePropertyViewStatus::ErrorTypeMismatch); +// +// PropertyTablePropertyView u32mat4x4Invalid = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// u32mat4x4Invalid.status() == +// PropertyTablePropertyViewStatus::ErrorTypeMismatch); +// +// PropertyTablePropertyView boolInvalid = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// boolInvalid.status() == +// PropertyTablePropertyViewStatus::ErrorTypeMismatch); +// +// PropertyTablePropertyView stringInvalid = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// stringInvalid.status() == +// PropertyTablePropertyViewStatus::ErrorTypeMismatch); +// } +// +// SECTION("Access wrong component type") { +// PropertyTablePropertyView u8mat2x2Invalid = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// u8mat2x2Invalid.status() == +// PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); +// +// PropertyTablePropertyView i32mat2x2Invalid = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// i32mat2x2Invalid.status() == +// PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); +// +// PropertyTablePropertyView mat2Invalid = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// mat2Invalid.status() == +// PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); +// } +// +// SECTION("Access incorrectly as array") { +// PropertyTablePropertyView> +// u32mat2x2ArrayInvalid = +// view.getPropertyView>( +// "TestClassProperty"); +// REQUIRE( +// u32mat2x2ArrayInvalid.status() == +// PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); +// } +// +// SECTION("Wrong buffer index") { +// model.bufferViews[valueBufferViewIndex].buffer = 2; +// PropertyTablePropertyView u32mat2x2Property = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// u32mat2x2Property.status() == +// PropertyTablePropertyViewStatus::ErrorInvalidValueBuffer); +// } +// +// SECTION("Wrong buffer view index") { +// propertyTableProperty.values = -1; +// PropertyTablePropertyView u32mat2x2Property = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// u32mat2x2Property.status() == +// PropertyTablePropertyViewStatus::ErrorInvalidValueBufferView); +// } +// +// SECTION("Buffer view points outside of the real buffer length") { +// model.buffers[valueBufferIndex].cesium.data.resize(sizeof(glm::u32mat2x2)); +// PropertyTablePropertyView u32mat2x2Property = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// u32mat2x2Property.status() == +// PropertyTablePropertyViewStatus::ErrorBufferViewOutOfBounds); +// } +// +// SECTION("Buffer view length isn't multiple of sizeof(T)") { +// model.bufferViews[valueBufferViewIndex].byteLength = +// sizeof(glm::u32mat2x2) * 4 - 1; +// PropertyTablePropertyView u32mat2x2Property = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// u32mat2x2Property.status() == +// PropertyTablePropertyViewStatus:: +// ErrorBufferViewSizeNotDivisibleByTypeSize); +// } +// +// SECTION("Buffer view length doesn't match with propertyTableCount") { +// model.bufferViews[valueBufferViewIndex].byteLength = sizeof(glm::u32mat2x2); +// +// PropertyTablePropertyView u32mat2x2Property = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// u32mat2x2Property.status() == +// PropertyTablePropertyViewStatus:: +// ErrorBufferViewSizeDoesNotMatchPropertyTableCount); +// } +//} +// +//TEST_CASE("Test boolean PropertyTableProperty") { +// Model model; +// +// int64_t instanceCount = 21; +// std::vector expected; +// std::vector values; +// values.resize(3); +// for (int64_t i = 0; i < instanceCount; ++i) { +// if (i % 2 == 0) { +// expected.emplace_back(true); +// } else { +// expected.emplace_back(false); +// } +// +// uint8_t expectedValue = expected.back(); +// int64_t byteIndex = i / 8; +// int64_t bitIndex = i % 8; +// values[static_cast(byteIndex)] = static_cast( +// (expectedValue << bitIndex) | values[static_cast(byteIndex)]); +// } +// addBufferToModel(model, values); +// +// ExtensionModelExtStructuralMetadata& metadata = +// model.addExtension(); +// +// Schema& schema = metadata.schema.emplace(); +// Class& testClass = schema.classes["TestClass"]; +// ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; +// testClassProperty.type = ClassProperty::Type::BOOLEAN; +// +// PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); +// propertyTable.classProperty = "TestClass"; +// propertyTable.count = static_cast(instanceCount); +// +// PropertyTableProperty& propertyTableProperty = +// propertyTable.properties["TestClassProperty"]; +// propertyTableProperty.values = +// static_cast(model.bufferViews.size() - 1); +// +// PropertyTableView view(model, propertyTable); +// REQUIRE(view.status() == PropertyTableViewStatus::Valid); +// REQUIRE(view.size() == propertyTable.count); +// +// const ClassProperty* classProperty = +// view.getClassProperty("TestClassProperty"); +// REQUIRE(classProperty); +// REQUIRE(classProperty->type == ClassProperty::Type::BOOLEAN); +// REQUIRE(classProperty->componentType == std::nullopt); +// REQUIRE(classProperty->count == std::nullopt); +// REQUIRE(!classProperty->array); +// +// SECTION("Access correct type") { +// PropertyTablePropertyView boolProperty = +// view.getPropertyView("TestClassProperty"); +// REQUIRE(boolProperty.status() == PropertyTablePropertyViewStatus::Valid); +// REQUIRE(boolProperty.size() == instanceCount); +// for (int64_t i = 0; i < boolProperty.size(); ++i) { +// bool expectedValue = expected[static_cast(i)]; +// REQUIRE(boolProperty.get(i) == expectedValue); +// } +// } +// +// SECTION("Buffer size doesn't match with propertyTableCount") { +// propertyTable.count = 66; +// PropertyTablePropertyView boolProperty = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// boolProperty.status() == +// PropertyTablePropertyViewStatus:: +// ErrorBufferViewSizeDoesNotMatchPropertyTableCount); +// } +//} +// +//TEST_CASE("Test string PropertyTableProperty") { +// Model model; +// +// std::vector expected{"What's up", "Test_0", "Test_1", "", "Hi"}; +// size_t totalBytes = 0; +// for (const std::string& expectedValue : expected) { +// totalBytes += expectedValue.size(); +// } +// +// std::vector stringOffsets( +// (expected.size() + 1) * sizeof(uint32_t)); +// std::vector values(totalBytes); +// uint32_t* offsetValue = reinterpret_cast(stringOffsets.data()); +// for (size_t i = 0; i < expected.size(); ++i) { +// const std::string& expectedValue = expected[i]; +// std::memcpy( +// values.data() + offsetValue[i], +// expectedValue.c_str(), +// expectedValue.size()); +// offsetValue[i + 1] = +// offsetValue[i] + static_cast(expectedValue.size()); +// } +// +// addBufferToModel(model, values); +// size_t valueBufferIndex = model.buffers.size() - 1; +// size_t valueBufferViewIndex = model.bufferViews.size() - 1; +// +// addBufferToModel(model, stringOffsets); +// size_t offsetBufferIndex = model.buffers.size() - 1; +// size_t offsetBufferViewIndex = model.bufferViews.size() - 1; +// +// ExtensionModelExtStructuralMetadata& metadata = +// model.addExtension(); +// +// Schema& schema = metadata.schema.emplace(); +// Class& testClass = schema.classes["TestClass"]; +// ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; +// testClassProperty.type = ClassProperty::Type::STRING; +// +// PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); +// propertyTable.classProperty = "TestClass"; +// propertyTable.count = static_cast(expected.size()); +// +// PropertyTableProperty& propertyTableProperty = +// propertyTable.properties["TestClassProperty"]; +// propertyTableProperty.stringOffsetType = +// PropertyTableProperty::StringOffsetType::UINT32; +// propertyTableProperty.values = static_cast(valueBufferViewIndex); +// propertyTableProperty.stringOffsets = +// static_cast(offsetBufferViewIndex); +// +// PropertyTableView view(model, propertyTable); +// REQUIRE(view.status() == PropertyTableViewStatus::Valid); +// REQUIRE(view.size() == propertyTable.count); +// +// const ClassProperty* classProperty = +// view.getClassProperty("TestClassProperty"); +// REQUIRE(classProperty); +// REQUIRE(classProperty->type == ClassProperty::Type::STRING); +// REQUIRE(classProperty->componentType == std::nullopt); +// REQUIRE(classProperty->count == std::nullopt); +// REQUIRE(!classProperty->array); +// +// SECTION("Access correct type") { +// PropertyTablePropertyView stringProperty = +// view.getPropertyView("TestClassProperty"); +// REQUIRE(stringProperty.status() == PropertyTablePropertyViewStatus::Valid); +// for (size_t i = 0; i < expected.size(); ++i) { +// REQUIRE(stringProperty.get(static_cast(i)) == expected[i]); +// } +// } +// +// SECTION("Wrong array type") { +// PropertyTablePropertyView> +// stringArrayInvalid = +// view.getPropertyView>( +// "TestClassProperty"); +// REQUIRE( +// stringArrayInvalid.status() == +// PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); +// } +// +// SECTION("Wrong offset type") { +// propertyTableProperty.stringOffsetType = +// PropertyTableProperty::StringOffsetType::UINT8; +// PropertyTablePropertyView stringProperty = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// stringProperty.status() == +// PropertyTablePropertyViewStatus:: +// ErrorBufferViewSizeDoesNotMatchPropertyTableCount); +// +// propertyTableProperty.stringOffsetType = +// PropertyTableProperty::StringOffsetType::UINT64; +// stringProperty = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// stringProperty.status() == +// PropertyTablePropertyViewStatus:: +// ErrorBufferViewSizeDoesNotMatchPropertyTableCount); +// +// propertyTableProperty.stringOffsetType = "NONSENSE"; +// stringProperty = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// stringProperty.status() == +// PropertyTablePropertyViewStatus::ErrorInvalidStringOffsetType); +// +// propertyTableProperty.stringOffsetType = ""; +// propertyTableProperty.arrayOffsetType = +// PropertyTableProperty::StringOffsetType::UINT32; +// stringProperty = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// stringProperty.status() == +// PropertyTablePropertyViewStatus::ErrorInvalidStringOffsetType); +// } +// +// SECTION("Offset values are not sorted ascending") { +// uint32_t* offset = reinterpret_cast( +// model.buffers[offsetBufferIndex].cesium.data.data()); +// offset[2] = +// static_cast(model.buffers[valueBufferIndex].byteLength + 4); +// PropertyTablePropertyView stringProperty = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// stringProperty.status() == +// PropertyTablePropertyViewStatus::ErrorStringOffsetsNotSorted); +// } +// +// SECTION("Offset value points outside of value buffer") { +// uint32_t* offset = reinterpret_cast( +// model.buffers[offsetBufferIndex].cesium.data.data()); +// offset[propertyTable.count] = +// static_cast(model.buffers[valueBufferIndex].byteLength + 4); +// PropertyTablePropertyView stringProperty = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// stringProperty.status() == +// PropertyTablePropertyViewStatus::ErrorStringOffsetOutOfBounds); +// } +//} +// +//TEST_CASE("Test fixed-length scalar array") { +// Model model; +// std::vector values = +// {12, 34, 30, 11, 34, 34, 11, 33, 122, 33, 223, 11}; +// +// addBufferToModel(model, values); +// size_t valueBufferViewIndex = model.bufferViews.size() - 1; +// +// ExtensionModelExtStructuralMetadata& metadata = +// model.addExtension(); +// +// Schema& schema = metadata.schema.emplace(); +// Class& testClass = schema.classes["TestClass"]; +// ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; +// testClassProperty.type = ClassProperty::Type::SCALAR; +// testClassProperty.componentType = ClassProperty::ComponentType::UINT32; +// testClassProperty.array = true; +// testClassProperty.count = 3; +// +// PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); +// propertyTable.classProperty = "TestClass"; +// propertyTable.count = static_cast( +// values.size() / static_cast(testClassProperty.count.value())); +// +// PropertyTableProperty& propertyTableProperty = +// propertyTable.properties["TestClassProperty"]; +// propertyTableProperty.values = +// static_cast(model.bufferViews.size() - 1); +// +// PropertyTableView view(model, propertyTable); +// REQUIRE(view.status() == PropertyTableViewStatus::Valid); +// REQUIRE(view.size() == propertyTable.count); +// +// const ClassProperty* classProperty = +// view.getClassProperty("TestClassProperty"); +// REQUIRE(classProperty); +// REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); +// REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT32); +// REQUIRE(classProperty->array); +// REQUIRE(classProperty->count == 3); +// +// SECTION("Access the right type") { +// PropertyTablePropertyView> arrayProperty = +// view.getPropertyView>("TestClassProperty"); +// REQUIRE(arrayProperty.status() == PropertyTablePropertyViewStatus::Valid); +// +// for (int64_t i = 0; i < arrayProperty.size(); ++i) { +// PropertyArrayView member = arrayProperty.get(i); +// for (int64_t j = 0; j < member.size(); ++j) { +// REQUIRE(member[j] == values[static_cast(i * 3 + j)]); +// } +// } +// } +// +// SECTION("Wrong type") { +// PropertyTablePropertyView> boolArrayInvalid = +// view.getPropertyView>("TestClassProperty"); +// REQUIRE( +// boolArrayInvalid.status() == +// PropertyTablePropertyViewStatus::ErrorTypeMismatch); +// +// PropertyTablePropertyView> uvec2ArrayInvalid = +// view.getPropertyView>( +// "TestClassProperty"); +// REQUIRE( +// uvec2ArrayInvalid.status() == +// PropertyTablePropertyViewStatus::ErrorTypeMismatch); +// } +// +// SECTION("Wrong component type") { +// PropertyTablePropertyView> int32ArrayInvalid = +// view.getPropertyView>("TestClassProperty"); +// REQUIRE( +// int32ArrayInvalid.status() == +// PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); +// } +// +// SECTION("Not an array type") { +// PropertyTablePropertyView uint32Invalid = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// uint32Invalid.status() == +// PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); +// } +// +// SECTION("Buffer size is not a multiple of type size") { +// model.bufferViews[valueBufferViewIndex].byteLength = 13; +// PropertyTablePropertyView> arrayProperty = +// view.getPropertyView>("TestClassProperty"); +// REQUIRE( +// arrayProperty.status() == +// PropertyTablePropertyViewStatus:: +// ErrorBufferViewSizeNotDivisibleByTypeSize); +// } +// +// SECTION("Negative component count") { +// testClassProperty.count = -1; +// PropertyTablePropertyView> arrayProperty = +// view.getPropertyView>("TestClassProperty"); +// REQUIRE( +// arrayProperty.status() == PropertyTablePropertyViewStatus:: +// ErrorArrayCountAndOffsetBufferDontExist); +// } +// +// SECTION("Value buffer doesn't fit into property table count") { +// testClassProperty.count = 55; +// PropertyTablePropertyView> arrayProperty = +// view.getPropertyView>("TestClassProperty"); +// REQUIRE( +// arrayProperty.status() == +// PropertyTablePropertyViewStatus:: +// ErrorBufferViewSizeDoesNotMatchPropertyTableCount); +// } +//} +// +//TEST_CASE("Test variable-length scalar array") { +// Model model; +// +// std::vector> expected{ +// {12, 33, 11, 344, 112, 444, 1}, +// {}, +// {}, +// {122, 23, 333, 12}, +// {}, +// {333, 311, 22, 34}, +// {}, +// {33, 1888, 233, 33019}}; +// size_t numOfElements = 0; +// for (const auto& expectedMember : expected) { +// numOfElements += expectedMember.size(); +// } +// +// std::vector values(numOfElements * sizeof(uint16_t)); +// std::vector offsets((expected.size() + 1) * sizeof(uint64_t)); +// uint64_t* offsetValue = reinterpret_cast(offsets.data()); +// for (size_t i = 0; i < expected.size(); ++i) { +// std::memcpy( +// values.data() + offsetValue[i], +// expected[i].data(), +// expected[i].size() * sizeof(uint16_t)); +// offsetValue[i + 1] = offsetValue[i] + expected[i].size() * sizeof(uint16_t); +// } +// +// addBufferToModel(model, values); +// size_t valueBufferIndex = model.buffers.size() - 1; +// size_t valueBufferViewIndex = model.bufferViews.size() - 1; +// +// addBufferToModel(model, offsets); +// size_t offsetBufferIndex = model.buffers.size() - 1; +// size_t offsetBufferViewIndex = model.bufferViews.size() - 1; +// +// ExtensionModelExtStructuralMetadata& metadata = +// model.addExtension(); +// +// Schema& schema = metadata.schema.emplace(); +// Class& testClass = schema.classes["TestClass"]; +// ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; +// testClassProperty.type = ClassProperty::Type::SCALAR; +// testClassProperty.componentType = ClassProperty::ComponentType::UINT16; +// testClassProperty.array = true; +// +// PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); +// propertyTable.classProperty = "TestClass"; +// propertyTable.count = static_cast(expected.size()); +// +// PropertyTableProperty& propertyTableProperty = +// propertyTable.properties["TestClassProperty"]; +// propertyTableProperty.values = static_cast(valueBufferViewIndex); +// propertyTableProperty.arrayOffsets = +// static_cast(offsetBufferViewIndex); +// propertyTableProperty.arrayOffsetType = +// PropertyTableProperty::ArrayOffsetType::UINT64; +// +// PropertyTableView view(model, propertyTable); +// REQUIRE(view.status() == PropertyTableViewStatus::Valid); +// REQUIRE(view.size() == propertyTable.count); +// +// const ClassProperty* classProperty = +// view.getClassProperty("TestClassProperty"); +// REQUIRE(classProperty); +// REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); +// REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT16); +// REQUIRE(classProperty->array); +// REQUIRE(!classProperty->count); +// +// SECTION("Access the correct type") { +// PropertyTablePropertyView> property = +// view.getPropertyView>("TestClassProperty"); +// REQUIRE(property.status() == PropertyTablePropertyViewStatus::Valid); +// for (size_t i = 0; i < expected.size(); ++i) { +// PropertyArrayView valueMember = +// property.get(static_cast(i)); +// REQUIRE(valueMember.size() == static_cast(expected[i].size())); +// for (size_t j = 0; j < expected[i].size(); ++j) { +// REQUIRE(expected[i][j] == valueMember[static_cast(j)]); +// } +// } +// } +// SECTION("Wrong offset type") { +// propertyTableProperty.arrayOffsetType = +// PropertyTableProperty::ArrayOffsetType::UINT8; +// PropertyTablePropertyView> arrayProperty = +// view.getPropertyView>("TestClassProperty"); +// REQUIRE( +// arrayProperty.status() == +// PropertyTablePropertyViewStatus:: +// ErrorBufferViewSizeDoesNotMatchPropertyTableCount); +// +// propertyTableProperty.arrayOffsetType = +// PropertyTableProperty::ArrayOffsetType::UINT16; +// arrayProperty = +// view.getPropertyView>("TestClassProperty"); +// REQUIRE( +// arrayProperty.status() == +// PropertyTablePropertyViewStatus:: +// ErrorBufferViewSizeDoesNotMatchPropertyTableCount); +// +// propertyTableProperty.arrayOffsetType = "NONSENSE"; +// arrayProperty = +// view.getPropertyView>("TestClassProperty"); +// REQUIRE( +// arrayProperty.status() == +// PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); +// +// propertyTableProperty.arrayOffsetType = ""; +// propertyTableProperty.stringOffsetType = +// PropertyTableProperty::StringOffsetType::UINT64; +// arrayProperty = +// view.getPropertyView>("TestClassProperty"); +// REQUIRE( +// arrayProperty.status() == +// PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); +// } +// +// SECTION("Offset values are not sorted ascending") { +// propertyTableProperty.arrayOffsetType = +// PropertyTableProperty::ArrayOffsetType::UINT64; +// uint64_t* offset = reinterpret_cast( +// model.buffers[offsetBufferIndex].cesium.data.data()); +// offset[propertyTable.count] = 0; +// PropertyTablePropertyView> arrayProperty = +// view.getPropertyView>("TestClassProperty"); +// REQUIRE( +// arrayProperty.status() == +// PropertyTablePropertyViewStatus::ErrorArrayOffsetsNotSorted); +// } +// +// SECTION("Offset value points outside of value buffer") { +// uint64_t* offset = reinterpret_cast( +// model.buffers[offsetBufferIndex].cesium.data.data()); +// offset[propertyTable.count] = +// static_cast(model.buffers[valueBufferIndex].byteLength + 4); +// PropertyTablePropertyView> arrayProperty = +// view.getPropertyView>("TestClassProperty"); +// REQUIRE( +// arrayProperty.status() == +// PropertyTablePropertyViewStatus::ErrorArrayOffsetOutOfBounds); +// } +// +// SECTION("Count and offset buffer are both present") { +// testClassProperty.count = 3; +// PropertyTablePropertyView> property = +// view.getPropertyView>("TestClassProperty"); +// REQUIRE( +// property.status() == +// PropertyTablePropertyViewStatus::ErrorArrayCountAndOffsetBufferCoexist); +// } +//} +// +//TEST_CASE("Test fixed-length vecN array") { +// Model model; +// std::vector values = { +// glm::ivec3(12, 34, -30), +// glm::ivec3(-2, 0, 1), +// glm::ivec3(1, 2, 8), +// glm::ivec3(-100, 84, 6), +// glm::ivec3(2, -2, -2), +// glm::ivec3(40, 61, 3), +// }; +// +// addBufferToModel(model, values); +// size_t valueBufferViewIndex = model.bufferViews.size() - 1; +// +// ExtensionModelExtStructuralMetadata& metadata = +// model.addExtension(); +// +// Schema& schema = metadata.schema.emplace(); +// Class& testClass = schema.classes["TestClass"]; +// ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; +// testClassProperty.type = ClassProperty::Type::VEC3; +// testClassProperty.componentType = ClassProperty::ComponentType::INT32; +// testClassProperty.array = true; +// testClassProperty.count = 2; +// +// PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); +// propertyTable.classProperty = "TestClass"; +// propertyTable.count = static_cast( +// values.size() / static_cast(testClassProperty.count.value())); +// +// PropertyTableProperty& propertyTableProperty = +// propertyTable.properties["TestClassProperty"]; +// propertyTableProperty.values = +// static_cast(model.bufferViews.size() - 1); +// +// PropertyTableView view(model, propertyTable); +// REQUIRE(view.status() == PropertyTableViewStatus::Valid); +// REQUIRE(view.size() == propertyTable.count); +// +// const ClassProperty* classProperty = +// view.getClassProperty("TestClassProperty"); +// REQUIRE(classProperty); +// REQUIRE(classProperty->type == ClassProperty::Type::VEC3); +// REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT32); +// REQUIRE(classProperty->array); +// REQUIRE(classProperty->count == 2); +// +// SECTION("Access the right type") { +// PropertyTablePropertyView> arrayProperty = +// view.getPropertyView>( +// "TestClassProperty"); +// REQUIRE(arrayProperty.status() == PropertyTablePropertyViewStatus::Valid); +// +// for (int64_t i = 0; i < arrayProperty.size(); ++i) { +// PropertyArrayView member = arrayProperty.get(i); +// for (int64_t j = 0; j < member.size(); ++j) { +// REQUIRE(member[j] == values[static_cast(i * 2 + j)]); +// } +// } +// } +// +// SECTION("Wrong type") { +// PropertyTablePropertyView> int32ArrayInvalid = +// view.getPropertyView>("TestClassProperty"); +// REQUIRE( +// int32ArrayInvalid.status() == +// PropertyTablePropertyViewStatus::ErrorTypeMismatch); +// +// PropertyTablePropertyView> ivec2ArrayInvalid = +// view.getPropertyView>( +// "TestClassProperty"); +// REQUIRE( +// ivec2ArrayInvalid.status() == +// PropertyTablePropertyViewStatus::ErrorTypeMismatch); +// } +// +// SECTION("Wrong component type") { +// PropertyTablePropertyView> uvec3ArrayInvalid = +// view.getPropertyView>( +// "TestClassProperty"); +// REQUIRE( +// uvec3ArrayInvalid.status() == +// PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); +// } +// +// SECTION("Not an array type") { +// PropertyTablePropertyView ivec3Invalid = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// ivec3Invalid.status() == +// PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); +// } +// +// SECTION("Buffer size is not a multiple of type size") { +// model.bufferViews[valueBufferViewIndex].byteLength = 13; +// PropertyTablePropertyView> arrayProperty = +// view.getPropertyView>( +// "TestClassProperty"); +// REQUIRE( +// arrayProperty.status() == +// PropertyTablePropertyViewStatus:: +// ErrorBufferViewSizeNotDivisibleByTypeSize); +// } +// +// SECTION("Negative component count") { +// testClassProperty.count = -1; +// PropertyTablePropertyView> arrayProperty = +// view.getPropertyView>( +// "TestClassProperty"); +// REQUIRE( +// arrayProperty.status() == PropertyTablePropertyViewStatus:: +// ErrorArrayCountAndOffsetBufferDontExist); +// } +// +// SECTION("Value buffer doesn't fit into property table count") { +// testClassProperty.count = 55; +// PropertyTablePropertyView> arrayProperty = +// view.getPropertyView>( +// "TestClassProperty"); +// REQUIRE( +// arrayProperty.status() == +// PropertyTablePropertyViewStatus:: +// ErrorBufferViewSizeDoesNotMatchPropertyTableCount); +// } +//} +// +//TEST_CASE("Test variable-length vecN array") { +// Model model; +// // clang-format off +// std::vector> expected{ +// { glm::ivec3(12, 34, -30), glm::ivec3(-2, 0, 1) }, +// { glm::ivec3(1, 2, 8), }, +// {}, +// { glm::ivec3(-100, 84, 6), glm::ivec3(2, -2, -2), glm::ivec3(40, 61, 3) }, +// { glm::ivec3(-1, 4, -7) }, +// }; +// // clang-format on +// +// size_t numOfElements = 0; +// for (const auto& expectedMember : expected) { +// numOfElements += expectedMember.size(); +// } +// +// std::vector values(numOfElements * sizeof(glm::ivec3)); +// std::vector offsets((expected.size() + 1) * sizeof(uint64_t)); +// uint64_t* offsetValue = reinterpret_cast(offsets.data()); +// for (size_t i = 0; i < expected.size(); ++i) { +// std::memcpy( +// values.data() + offsetValue[i], +// expected[i].data(), +// expected[i].size() * sizeof(glm::ivec3)); +// offsetValue[i + 1] = +// offsetValue[i] + expected[i].size() * sizeof(glm::ivec3); +// } +// +// addBufferToModel(model, values); +// size_t valueBufferIndex = model.buffers.size() - 1; +// size_t valueBufferViewIndex = model.bufferViews.size() - 1; +// +// addBufferToModel(model, offsets); +// size_t offsetBufferIndex = model.buffers.size() - 1; +// size_t offsetBufferViewIndex = model.bufferViews.size() - 1; +// +// ExtensionModelExtStructuralMetadata& metadata = +// model.addExtension(); +// +// Schema& schema = metadata.schema.emplace(); +// Class& testClass = schema.classes["TestClass"]; +// ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; +// testClassProperty.type = ClassProperty::Type::VEC3; +// testClassProperty.componentType = ClassProperty::ComponentType::INT32; +// testClassProperty.array = true; +// +// PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); +// propertyTable.classProperty = "TestClass"; +// propertyTable.count = static_cast(expected.size()); +// +// PropertyTableProperty& propertyTableProperty = +// propertyTable.properties["TestClassProperty"]; +// propertyTableProperty.values = static_cast(valueBufferViewIndex); +// propertyTableProperty.arrayOffsets = +// static_cast(offsetBufferViewIndex); +// propertyTableProperty.arrayOffsetType = +// PropertyTableProperty::ArrayOffsetType::UINT64; +// +// PropertyTableView view(model, propertyTable); +// REQUIRE(view.status() == PropertyTableViewStatus::Valid); +// REQUIRE(view.size() == propertyTable.count); +// +// const ClassProperty* classProperty = +// view.getClassProperty("TestClassProperty"); +// REQUIRE(classProperty); +// REQUIRE(classProperty->type == ClassProperty::Type::VEC3); +// REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT32); +// REQUIRE(classProperty->array); +// REQUIRE(!classProperty->count); +// +// SECTION("Access the correct type") { +// PropertyTablePropertyView> property = +// view.getPropertyView>( +// "TestClassProperty"); +// REQUIRE(property.status() == PropertyTablePropertyViewStatus::Valid); +// for (size_t i = 0; i < expected.size(); ++i) { +// PropertyArrayView valueMember = +// property.get(static_cast(i)); +// REQUIRE(valueMember.size() == static_cast(expected[i].size())); +// for (size_t j = 0; j < expected[i].size(); ++j) { +// REQUIRE(expected[i][j] == valueMember[static_cast(j)]); +// } +// } +// } +// +// SECTION("Wrong offset type") { +// propertyTableProperty.arrayOffsetType = +// PropertyTableProperty::ArrayOffsetType::UINT8; +// PropertyTablePropertyView> arrayProperty = +// view.getPropertyView>( +// "TestClassProperty"); +// REQUIRE( +// arrayProperty.status() == +// PropertyTablePropertyViewStatus:: +// ErrorBufferViewSizeDoesNotMatchPropertyTableCount); +// +// propertyTableProperty.arrayOffsetType = +// PropertyTableProperty::ArrayOffsetType::UINT16; +// arrayProperty = view.getPropertyView>( +// "TestClassProperty"); +// REQUIRE( +// arrayProperty.status() == +// PropertyTablePropertyViewStatus:: +// ErrorBufferViewSizeDoesNotMatchPropertyTableCount); +// +// propertyTableProperty.arrayOffsetType = "NONSENSE"; +// arrayProperty = view.getPropertyView>( +// "TestClassProperty"); +// REQUIRE( +// arrayProperty.status() == +// PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); +// +// propertyTableProperty.arrayOffsetType = ""; +// propertyTableProperty.stringOffsetType = +// PropertyTableProperty::StringOffsetType::UINT64; +// arrayProperty = view.getPropertyView>( +// "TestClassProperty"); +// REQUIRE( +// arrayProperty.status() == +// PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); +// } +// +// SECTION("Offset values are not sorted ascending") { +// propertyTableProperty.arrayOffsetType = +// PropertyTableProperty::ArrayOffsetType::UINT64; +// uint64_t* offset = reinterpret_cast( +// model.buffers[offsetBufferIndex].cesium.data.data()); +// offset[propertyTable.count] = 0; +// PropertyTablePropertyView> arrayProperty = +// view.getPropertyView>( +// "TestClassProperty"); +// REQUIRE( +// arrayProperty.status() == +// PropertyTablePropertyViewStatus::ErrorArrayOffsetsNotSorted); +// } +// +// SECTION("Offset value points outside of value buffer") { +// uint64_t* offset = reinterpret_cast( +// model.buffers[offsetBufferIndex].cesium.data.data()); +// offset[propertyTable.count] = +// static_cast(model.buffers[valueBufferIndex].byteLength + 4); +// PropertyTablePropertyView> arrayProperty = +// view.getPropertyView>( +// "TestClassProperty"); +// REQUIRE( +// arrayProperty.status() == +// PropertyTablePropertyViewStatus::ErrorArrayOffsetOutOfBounds); +// } +// +// SECTION("Count and offset buffer are both present") { +// testClassProperty.count = 3; +// PropertyTablePropertyView> property = +// view.getPropertyView>( +// "TestClassProperty"); +// REQUIRE( +// property.status() == +// PropertyTablePropertyViewStatus::ErrorArrayCountAndOffsetBufferCoexist); +// } +//} +// +//TEST_CASE("Test fixed-length matN array") { +// Model model; +// // clang-format off +// std::vector values = { +// glm::i32mat2x2( +// 12, 34, +// -30, 20), +// glm::i32mat2x2( +// -2, -2, +// 0, 1), +// glm::i32mat2x2( +// 1, 2, +// 8, 5), +// glm::i32mat2x2( +// -100, 3, +// 84, 6), +// glm::i32mat2x2( +// 2, 12, +// -2, -2), +// glm::i32mat2x2( +// 40, 61, +// 7, -3), +// }; +// // clang-format on +// +// addBufferToModel(model, values); +// size_t valueBufferViewIndex = model.bufferViews.size() - 1; +// +// ExtensionModelExtStructuralMetadata& metadata = +// model.addExtension(); +// +// Schema& schema = metadata.schema.emplace(); +// Class& testClass = schema.classes["TestClass"]; +// ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; +// testClassProperty.type = ClassProperty::Type::MAT2; +// testClassProperty.componentType = ClassProperty::ComponentType::INT32; +// testClassProperty.array = true; +// testClassProperty.count = 2; +// +// PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); +// propertyTable.classProperty = "TestClass"; +// propertyTable.count = static_cast( +// values.size() / static_cast(testClassProperty.count.value())); +// +// PropertyTableProperty& propertyTableProperty = +// propertyTable.properties["TestClassProperty"]; +// propertyTableProperty.values = +// static_cast(model.bufferViews.size() - 1); +// +// PropertyTableView view(model, propertyTable); +// REQUIRE(view.status() == PropertyTableViewStatus::Valid); +// REQUIRE(view.size() == propertyTable.count); +// +// const ClassProperty* classProperty = +// view.getClassProperty("TestClassProperty"); +// REQUIRE(classProperty); +// REQUIRE(classProperty->type == ClassProperty::Type::MAT2); +// REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT32); +// REQUIRE(classProperty->array); +// REQUIRE(classProperty->count == 2); +// +// SECTION("Access the right type") { +// PropertyTablePropertyView> arrayProperty = +// view.getPropertyView>( +// "TestClassProperty"); +// REQUIRE(arrayProperty.status() == PropertyTablePropertyViewStatus::Valid); +// +// for (int64_t i = 0; i < arrayProperty.size(); ++i) { +// PropertyArrayView member = arrayProperty.get(i); +// for (int64_t j = 0; j < member.size(); ++j) { +// REQUIRE(member[j] == values[static_cast(i * 2 + j)]); +// } +// } +// } +// +// SECTION("Wrong type") { +// PropertyTablePropertyView> int32ArrayInvalid = +// view.getPropertyView>("TestClassProperty"); +// REQUIRE( +// int32ArrayInvalid.status() == +// PropertyTablePropertyViewStatus::ErrorTypeMismatch); +// +// PropertyTablePropertyView> ivec2ArrayInvalid = +// view.getPropertyView>( +// "TestClassProperty"); +// REQUIRE( +// ivec2ArrayInvalid.status() == +// PropertyTablePropertyViewStatus::ErrorTypeMismatch); +// } +// +// SECTION("Wrong component type") { +// PropertyTablePropertyView> +// u32mat2x2ArrayInvalid = +// view.getPropertyView>( +// "TestClassProperty"); +// REQUIRE( +// u32mat2x2ArrayInvalid.status() == +// PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); +// } +// +// SECTION("Not an array type") { +// PropertyTablePropertyView ivec3Invalid = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// ivec3Invalid.status() == +// PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); +// } +// +// SECTION("Buffer size is not a multiple of type size") { +// model.bufferViews[valueBufferViewIndex].byteLength = 13; +// PropertyTablePropertyView> arrayProperty = +// view.getPropertyView>( +// "TestClassProperty"); +// REQUIRE( +// arrayProperty.status() == +// PropertyTablePropertyViewStatus:: +// ErrorBufferViewSizeNotDivisibleByTypeSize); +// } +// +// SECTION("Negative component count") { +// testClassProperty.count = -1; +// PropertyTablePropertyView> arrayProperty = +// view.getPropertyView>( +// "TestClassProperty"); +// REQUIRE( +// arrayProperty.status() == PropertyTablePropertyViewStatus:: +// ErrorArrayCountAndOffsetBufferDontExist); +// } +// +// SECTION("Value buffer doesn't fit into property table count") { +// testClassProperty.count = 55; +// PropertyTablePropertyView> arrayProperty = +// view.getPropertyView>( +// "TestClassProperty"); +// REQUIRE( +// arrayProperty.status() == +// PropertyTablePropertyViewStatus:: +// ErrorBufferViewSizeDoesNotMatchPropertyTableCount); +// } +//} +// +//TEST_CASE("Test variable-length matN array") { +// Model model; +// // clang-format off +// std::vector data0{ +// glm::i32mat2x2( +// 3, -2, +// 1, 0), +// glm::i32mat2x2( +// 40, 3, +// 8, -9) +// }; +// std::vector data1{ +// glm::i32mat2x2( +// 1, 10, +// 7, 8), +// }; +// std::vector data2{ +// glm::i32mat2x2( +// 18, 0, +// 1, 17), +// glm::i32mat2x2( +// -4, -2, +// -9, 1), +// glm::i32mat2x2( +// 1, 8, +// -99, 3), +// }; +// // clang-format on +// +// std::vector> +// expected{data0, {}, data1, data2, {}}; +// +// size_t numOfElements = 0; +// for (const auto& expectedMember : expected) { +// numOfElements += expectedMember.size(); +// } +// +// std::vector values(numOfElements * sizeof(glm::i32mat2x2)); +// std::vector offsets((expected.size() + 1) * sizeof(uint64_t)); +// uint64_t* offsetValue = reinterpret_cast(offsets.data()); +// for (size_t i = 0; i < expected.size(); ++i) { +// std::memcpy( +// values.data() + offsetValue[i], +// expected[i].data(), +// expected[i].size() * sizeof(glm::i32mat2x2)); +// offsetValue[i + 1] = +// offsetValue[i] + expected[i].size() * sizeof(glm::i32mat2x2); +// } +// +// addBufferToModel(model, values); +// size_t valueBufferIndex = model.buffers.size() - 1; +// size_t valueBufferViewIndex = model.bufferViews.size() - 1; +// +// addBufferToModel(model, offsets); +// size_t offsetBufferIndex = model.buffers.size() - 1; +// size_t offsetBufferViewIndex = model.bufferViews.size() - 1; +// +// ExtensionModelExtStructuralMetadata& metadata = +// model.addExtension(); +// +// Schema& schema = metadata.schema.emplace(); +// Class& testClass = schema.classes["TestClass"]; +// ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; +// testClassProperty.type = ClassProperty::Type::MAT2; +// testClassProperty.componentType = ClassProperty::ComponentType::INT32; +// testClassProperty.array = true; +// +// PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); +// propertyTable.classProperty = "TestClass"; +// propertyTable.count = static_cast(expected.size()); +// +// PropertyTableProperty& propertyTableProperty = +// propertyTable.properties["TestClassProperty"]; +// propertyTableProperty.values = static_cast(valueBufferViewIndex); +// propertyTableProperty.arrayOffsets = +// static_cast(offsetBufferViewIndex); +// propertyTableProperty.arrayOffsetType = +// PropertyTableProperty::ArrayOffsetType::UINT64; +// +// PropertyTableView view(model, propertyTable); +// REQUIRE(view.status() == PropertyTableViewStatus::Valid); +// REQUIRE(view.size() == propertyTable.count); +// +// const ClassProperty* classProperty = +// view.getClassProperty("TestClassProperty"); +// REQUIRE(classProperty); +// REQUIRE(classProperty->type == ClassProperty::Type::MAT2); +// REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT32); +// REQUIRE(classProperty->array); +// REQUIRE(!classProperty->count); +// +// SECTION("Access the correct type") { +// PropertyTablePropertyView> property = +// view.getPropertyView>( +// "TestClassProperty"); +// REQUIRE(property.status() == PropertyTablePropertyViewStatus::Valid); +// for (size_t i = 0; i < expected.size(); ++i) { +// PropertyArrayView valueMember = +// property.get(static_cast(i)); +// REQUIRE(valueMember.size() == static_cast(expected[i].size())); +// for (size_t j = 0; j < expected[i].size(); ++j) { +// REQUIRE(expected[i][j] == valueMember[static_cast(j)]); +// } +// } +// } +// +// SECTION("Wrong offset type") { +// propertyTableProperty.arrayOffsetType = +// PropertyTableProperty::ArrayOffsetType::UINT8; +// PropertyTablePropertyView> arrayProperty = +// view.getPropertyView>( +// "TestClassProperty"); +// REQUIRE( +// arrayProperty.status() == +// PropertyTablePropertyViewStatus:: +// ErrorBufferViewSizeDoesNotMatchPropertyTableCount); +// +// propertyTableProperty.arrayOffsetType = +// PropertyTableProperty::ArrayOffsetType::UINT16; +// arrayProperty = view.getPropertyView>( +// "TestClassProperty"); +// REQUIRE( +// arrayProperty.status() == +// PropertyTablePropertyViewStatus:: +// ErrorBufferViewSizeDoesNotMatchPropertyTableCount); +// +// propertyTableProperty.arrayOffsetType = "NONSENSE"; +// arrayProperty = view.getPropertyView>( +// "TestClassProperty"); +// REQUIRE( +// arrayProperty.status() == +// PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); +// +// propertyTableProperty.arrayOffsetType = ""; +// propertyTableProperty.stringOffsetType = +// PropertyTableProperty::StringOffsetType::UINT64; +// arrayProperty = view.getPropertyView>( +// "TestClassProperty"); +// REQUIRE( +// arrayProperty.status() == +// PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); +// } +// +// SECTION("Offset values are not sorted ascending") { +// propertyTableProperty.arrayOffsetType = +// PropertyTableProperty::ArrayOffsetType::UINT64; +// uint64_t* offset = reinterpret_cast( +// model.buffers[offsetBufferIndex].cesium.data.data()); +// offset[propertyTable.count] = 0; +// PropertyTablePropertyView> arrayProperty = +// view.getPropertyView>( +// "TestClassProperty"); +// REQUIRE( +// arrayProperty.status() == +// PropertyTablePropertyViewStatus::ErrorArrayOffsetsNotSorted); +// } +// +// SECTION("Offset value points outside of value buffer") { +// uint64_t* offset = reinterpret_cast( +// model.buffers[offsetBufferIndex].cesium.data.data()); +// offset[propertyTable.count] = +// static_cast(model.buffers[valueBufferIndex].byteLength + 4); +// PropertyTablePropertyView> arrayProperty = +// view.getPropertyView>( +// "TestClassProperty"); +// REQUIRE( +// arrayProperty.status() == +// PropertyTablePropertyViewStatus::ErrorArrayOffsetOutOfBounds); +// } +// +// SECTION("Count and offset buffer are both present") { +// testClassProperty.count = 3; +// PropertyTablePropertyView> property = +// view.getPropertyView>( +// "TestClassProperty"); +// REQUIRE( +// property.status() == +// PropertyTablePropertyViewStatus::ErrorArrayCountAndOffsetBufferCoexist); +// } +//} +// +//TEST_CASE("Test fixed-length boolean array") { +// Model model; +// +// std::vector expected = { +// true, +// false, +// false, +// true, +// false, +// false, +// true, +// true, +// true, +// false, +// false, +// true}; +// std::vector values; +// size_t requiredBytesSize = static_cast( +// glm::ceil(static_cast(expected.size()) / 8.0)); +// values.resize(requiredBytesSize); +// for (size_t i = 0; i < expected.size(); ++i) { +// uint8_t expectedValue = expected[i]; +// size_t byteIndex = i / 8; +// size_t bitIndex = i % 8; +// values[byteIndex] = +// static_cast((expectedValue << bitIndex) | values[byteIndex]); +// } +// +// addBufferToModel(model, values); +// +// ExtensionModelExtStructuralMetadata& metadata = +// model.addExtension(); +// +// Schema& schema = metadata.schema.emplace(); +// Class& testClass = schema.classes["TestClass"]; +// ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; +// testClassProperty.type = ClassProperty::Type::BOOLEAN; +// testClassProperty.array = true; +// testClassProperty.count = 3; +// +// PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); +// propertyTable.classProperty = "TestClass"; +// propertyTable.count = static_cast( +// expected.size() / static_cast(testClassProperty.count.value())); +// +// PropertyTableProperty& propertyTableProperty = +// propertyTable.properties["TestClassProperty"]; +// propertyTableProperty.values = +// static_cast(model.bufferViews.size() - 1); +// +// PropertyTableView view(model, propertyTable); +// REQUIRE(view.status() == PropertyTableViewStatus::Valid); +// REQUIRE(view.size() == propertyTable.count); +// +// const ClassProperty* classProperty = +// view.getClassProperty("TestClassProperty"); +// REQUIRE(classProperty); +// REQUIRE(classProperty->type == ClassProperty::Type::BOOLEAN); +// REQUIRE(classProperty->array); +// REQUIRE(classProperty->count == 3); +// +// SECTION("Access correct type") { +// PropertyTablePropertyView> boolArrayProperty = +// view.getPropertyView>("TestClassProperty"); +// REQUIRE( +// boolArrayProperty.status() == PropertyTablePropertyViewStatus::Valid); +// REQUIRE(boolArrayProperty.size() == propertyTable.count); +// REQUIRE(boolArrayProperty.size() > 0); +// for (int64_t i = 0; i < boolArrayProperty.size(); ++i) { +// PropertyArrayView valueMember = boolArrayProperty.get(i); +// for (int64_t j = 0; j < valueMember.size(); ++j) { +// REQUIRE(valueMember[j] == expected[static_cast(i * 3 + j)]); +// } +// } +// } +// +// SECTION("Wrong type") { +// PropertyTablePropertyView> uint8ArrayInvalid = +// view.getPropertyView>("TestClassProperty"); +// REQUIRE( +// uint8ArrayInvalid.status() == +// PropertyTablePropertyViewStatus::ErrorTypeMismatch); +// } +// +// SECTION("View is not array type") { +// PropertyTablePropertyView boolInvalid = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// boolInvalid.status() == +// PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); +// } +// +// SECTION("Value buffer doesn't have enough required bytes") { +// testClassProperty.count = 11; +// PropertyTablePropertyView> boolArrayProperty = +// view.getPropertyView>("TestClassProperty"); +// REQUIRE( +// boolArrayProperty.status() == +// PropertyTablePropertyViewStatus:: +// ErrorBufferViewSizeDoesNotMatchPropertyTableCount); +// } +// +// SECTION("Count is negative") { +// testClassProperty.count = -1; +// PropertyTablePropertyView> boolArrayProperty = +// view.getPropertyView>("TestClassProperty"); +// REQUIRE( +// boolArrayProperty.status() == +// PropertyTablePropertyViewStatus:: +// ErrorArrayCountAndOffsetBufferDontExist); +// } +//} +// +//TEST_CASE("Test variable-length boolean array") { +// Model model; +// +// std::vector> expected{ +// {true, false, true, true, false, true, true}, +// {}, +// {}, +// {}, +// {false, false, false, false}, +// {true, false, true}, +// {false}, +// {true, true, true, true, true, false, false}}; +// size_t numOfElements = 0; +// for (const auto& expectedMember : expected) { +// numOfElements += expectedMember.size(); +// } +// +// size_t requiredBytesSize = +// static_cast(glm::ceil(static_cast(numOfElements) / 8.0)); +// std::vector values(requiredBytesSize); +// std::vector offsets((expected.size() + 1) * sizeof(uint64_t)); +// uint64_t* offsetValue = reinterpret_cast(offsets.data()); +// size_t indexSoFar = 0; +// for (size_t i = 0; i < expected.size(); ++i) { +// for (size_t j = 0; j < expected[i].size(); ++j) { +// uint8_t expectedValue = expected[i][j]; +// size_t byteIndex = indexSoFar / 8; +// size_t bitIndex = indexSoFar % 8; +// values[byteIndex] = static_cast( +// (expectedValue << bitIndex) | static_cast(values[byteIndex])); +// ++indexSoFar; +// } +// offsetValue[i + 1] = offsetValue[i] + expected[i].size(); +// } +// +// addBufferToModel(model, values); +// size_t valueBufferIndex = model.buffers.size() - 1; +// size_t valueBufferViewIndex = model.bufferViews.size() - 1; +// +// addBufferToModel(model, offsets); +// size_t offsetBufferIndex = model.buffers.size() - 1; +// size_t offsetBufferViewIndex = model.bufferViews.size() - 1; +// +// ExtensionModelExtStructuralMetadata& metadata = +// model.addExtension(); +// +// Schema& schema = metadata.schema.emplace(); +// Class& testClass = schema.classes["TestClass"]; +// ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; +// testClassProperty.type = ClassProperty::Type::BOOLEAN; +// testClassProperty.array = true; +// +// PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); +// propertyTable.classProperty = "TestClass"; +// propertyTable.count = static_cast(expected.size()); +// +// PropertyTableProperty& propertyTableProperty = +// propertyTable.properties["TestClassProperty"]; +// propertyTableProperty.values = static_cast(valueBufferViewIndex); +// propertyTableProperty.arrayOffsets = +// static_cast(offsetBufferViewIndex); +// propertyTableProperty.arrayOffsetType = +// PropertyTableProperty::ArrayOffsetType::UINT64; +// +// PropertyTableView view(model, propertyTable); +// REQUIRE(view.status() == PropertyTableViewStatus::Valid); +// REQUIRE(view.size() == propertyTable.count); +// +// const ClassProperty* classProperty = +// view.getClassProperty("TestClassProperty"); +// REQUIRE(classProperty); +// REQUIRE(classProperty->type == ClassProperty::Type::BOOLEAN); +// REQUIRE(classProperty->array); +// REQUIRE(!classProperty->count); +// +// SECTION("Access correct type") { +// PropertyTablePropertyView> boolArrayProperty = +// view.getPropertyView>("TestClassProperty"); +// REQUIRE( +// boolArrayProperty.status() == PropertyTablePropertyViewStatus::Valid); +// for (size_t i = 0; i < expected.size(); ++i) { +// PropertyArrayView arrayMember = +// boolArrayProperty.get(static_cast(i)); +// REQUIRE(arrayMember.size() == static_cast(expected[i].size())); +// for (size_t j = 0; j < expected[i].size(); ++j) { +// REQUIRE(expected[i][j] == arrayMember[static_cast(j)]); +// } +// } +// } +// +// SECTION("Wrong offset type") { +// propertyTableProperty.arrayOffsetType = +// PropertyTableProperty::ArrayOffsetType::UINT8; +// PropertyTablePropertyView> arrayProperty = +// view.getPropertyView>("TestClassProperty"); +// REQUIRE( +// arrayProperty.status() == +// PropertyTablePropertyViewStatus:: +// ErrorBufferViewSizeDoesNotMatchPropertyTableCount); +// +// propertyTableProperty.arrayOffsetType = +// PropertyTableProperty::ArrayOffsetType::UINT16; +// arrayProperty = +// view.getPropertyView>("TestClassProperty"); +// REQUIRE( +// arrayProperty.status() == +// PropertyTablePropertyViewStatus:: +// ErrorBufferViewSizeDoesNotMatchPropertyTableCount); +// +// propertyTableProperty.arrayOffsetType = "NONSENSE"; +// arrayProperty = +// view.getPropertyView>("TestClassProperty"); +// REQUIRE( +// arrayProperty.status() == +// PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); +// +// propertyTableProperty.arrayOffsetType = ""; +// propertyTableProperty.stringOffsetType = +// PropertyTableProperty::StringOffsetType::UINT64; +// arrayProperty = +// view.getPropertyView>("TestClassProperty"); +// REQUIRE( +// arrayProperty.status() == +// PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); +// } +// +// SECTION("Offset values are not sorted ascending") { +// propertyTableProperty.arrayOffsetType = +// PropertyTableProperty::ArrayOffsetType::UINT64; +// uint64_t* offset = reinterpret_cast( +// model.buffers[offsetBufferIndex].cesium.data.data()); +// offset[propertyTable.count] = 0; +// PropertyTablePropertyView> arrayProperty = +// view.getPropertyView>("TestClassProperty"); +// REQUIRE( +// arrayProperty.status() == +// PropertyTablePropertyViewStatus::ErrorArrayOffsetsNotSorted); +// } +// +// SECTION("Offset value points outside of value buffer") { +// uint64_t* offset = reinterpret_cast( +// model.buffers[offsetBufferIndex].cesium.data.data()); +// offset[propertyTable.count] = static_cast( +// model.buffers[valueBufferIndex].byteLength * 8 + 20); +// PropertyTablePropertyView> arrayProperty = +// view.getPropertyView>("TestClassProperty"); +// REQUIRE( +// arrayProperty.status() == +// PropertyTablePropertyViewStatus::ErrorArrayOffsetOutOfBounds); +// } +// +// SECTION("Count and offset buffer both present") { +// testClassProperty.count = 3; +// PropertyTablePropertyView> boolArrayProperty = +// view.getPropertyView>("TestClassProperty"); +// REQUIRE( +// boolArrayProperty.status() == +// PropertyTablePropertyViewStatus::ErrorArrayCountAndOffsetBufferCoexist); +// } +//} +// +//TEST_CASE("Test fixed-length arrays of strings") { +// Model model; +// +// std::vector expected{ +// "What's up", +// "Breaking news!!! Aliens no longer attacks the US first", +// "But they still abduct my cows! Those milk thiefs! 👽 🐮", +// "I'm not crazy. My mother had me tested 🤪", +// "I love you, meat bags! ❤️", +// "Book in the freezer"}; +// +// size_t totalBytes = 0; +// for (const std::string& expectedValue : expected) { +// totalBytes += expectedValue.size(); +// } +// +// std::vector offsets((expected.size() + 1) * sizeof(uint32_t)); +// std::vector values(totalBytes); +// uint32_t* offsetValue = reinterpret_cast(offsets.data()); +// for (size_t i = 0; i < expected.size(); ++i) { +// const std::string& expectedValue = expected[i]; +// std::memcpy( +// values.data() + offsetValue[i], +// expectedValue.c_str(), +// expectedValue.size()); +// offsetValue[i + 1] = +// offsetValue[i] + static_cast(expectedValue.size()); +// } +// +// addBufferToModel(model, values); +// size_t valueBufferViewIndex = model.bufferViews.size() - 1; +// +// addBufferToModel(model, offsets); +// size_t offsetBufferViewIndex = model.bufferViews.size() - 1; +// +// ExtensionModelExtStructuralMetadata& metadata = +// model.addExtension(); +// +// Schema& schema = metadata.schema.emplace(); +// Class& testClass = schema.classes["TestClass"]; +// ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; +// testClassProperty.type = ClassProperty::Type::STRING; +// testClassProperty.array = true; +// testClassProperty.count = 2; +// +// PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); +// propertyTable.classProperty = "TestClass"; +// propertyTable.count = static_cast( +// expected.size() / static_cast(testClassProperty.count.value())); +// +// PropertyTableProperty& propertyTableProperty = +// propertyTable.properties["TestClassProperty"]; +// propertyTableProperty.stringOffsetType = +// PropertyTableProperty::StringOffsetType::UINT32; +// propertyTableProperty.values = static_cast(valueBufferViewIndex); +// propertyTableProperty.stringOffsets = +// static_cast(offsetBufferViewIndex); +// +// PropertyTableView view(model, propertyTable); +// REQUIRE(view.status() == PropertyTableViewStatus::Valid); +// REQUIRE(view.size() == propertyTable.count); +// +// const ClassProperty* classProperty = +// view.getClassProperty("TestClassProperty"); +// REQUIRE(classProperty); +// REQUIRE(classProperty->type == ClassProperty::Type::STRING); +// REQUIRE(classProperty->array); +// REQUIRE(classProperty->count == 2); +// +// SECTION("Access correct type") { +// PropertyTablePropertyView> +// stringProperty = +// view.getPropertyView>( +// "TestClassProperty"); +// REQUIRE(stringProperty.status() == PropertyTablePropertyViewStatus::Valid); +// REQUIRE(stringProperty.size() == 3); +// +// PropertyArrayView v0 = stringProperty.get(0); +// REQUIRE(v0.size() == 2); +// REQUIRE(v0[0] == "What's up"); +// REQUIRE(v0[1] == "Breaking news!!! Aliens no longer attacks the US first"); +// +// PropertyArrayView v1 = stringProperty.get(1); +// REQUIRE(v1.size() == 2); +// REQUIRE(v1[0] == "But they still abduct my cows! Those milk thiefs! 👽 🐮"); +// REQUIRE(v1[1] == "I'm not crazy. My mother had me tested 🤪"); +// +// PropertyArrayView v2 = stringProperty.get(2); +// REQUIRE(v2.size() == 2); +// REQUIRE(v2[0] == "I love you, meat bags! ❤️"); +// REQUIRE(v2[1] == "Book in the freezer"); +// } +// +// SECTION("Array type mismatch") { +// PropertyTablePropertyView stringProperty = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// stringProperty.status() == +// PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); +// } +// +// SECTION("Count is negative") { +// testClassProperty.count = -1; +// PropertyTablePropertyView> +// stringProperty = +// view.getPropertyView>( +// "TestClassProperty"); +// REQUIRE( +// stringProperty.status() == PropertyTablePropertyViewStatus:: +// ErrorArrayCountAndOffsetBufferDontExist); +// } +// +// SECTION("Offset type is unknown") { +// propertyTableProperty.stringOffsetType = "NONSENSE"; +// PropertyTablePropertyView> +// stringProperty = +// view.getPropertyView>( +// "TestClassProperty"); +// REQUIRE( +// stringProperty.status() == +// PropertyTablePropertyViewStatus::ErrorInvalidStringOffsetType); +// +// propertyTableProperty.stringOffsetType = ""; +// propertyTableProperty.arrayOffsetType = +// PropertyTableProperty::ArrayOffsetType::UINT32; +// stringProperty = view.getPropertyView>( +// "TestClassProperty"); +// REQUIRE( +// stringProperty.status() == +// PropertyTablePropertyViewStatus::ErrorInvalidStringOffsetType); +// } +// +// SECTION("String offsets don't exist") { +// propertyTableProperty.stringOffsets = -1; +// PropertyTablePropertyView> +// stringProperty = +// view.getPropertyView>( +// "TestClassProperty"); +// REQUIRE( +// stringProperty.status() == +// PropertyTablePropertyViewStatus::ErrorInvalidStringOffsetBufferView); +// } +//} +// +//TEST_CASE("Test variable-length arrays of strings") { +// Model model; +// +// std::vector> expected{ +// {"What's up"}, +// {"Breaking news!!! Aliens no longer attacks the US first", +// "But they still abduct my cows! Those milk thiefs! 👽 🐮"}, +// {"I'm not crazy. My mother had me tested 🤪", +// "I love you, meat bags! ❤️", +// "Book in the freezer"}}; +// +// size_t totalBytes = 0; +// size_t numOfElements = 0; +// for (const auto& expectedValues : expected) { +// for (const auto& value : expectedValues) { +// totalBytes += value.size(); +// } +// +// numOfElements += expectedValues.size(); +// } +// +// std::vector offsets((expected.size() + 1) * sizeof(uint32_t)); +// std::vector stringOffsets((numOfElements + 1) * sizeof(uint32_t)); +// std::vector values(totalBytes); +// uint32_t* offsetValue = reinterpret_cast(offsets.data()); +// uint32_t* stringOffsetValue = +// reinterpret_cast(stringOffsets.data()); +// size_t strOffsetIdx = 0; +// for (size_t i = 0; i < expected.size(); ++i) { +// for (size_t j = 0; j < expected[i].size(); ++j) { +// const std::string& expectedValue = expected[i][j]; +// std::memcpy( +// values.data() + stringOffsetValue[strOffsetIdx], +// expectedValue.c_str(), +// expectedValue.size()); +// +// stringOffsetValue[strOffsetIdx + 1] = +// stringOffsetValue[strOffsetIdx] + +// static_cast(expectedValue.size()); +// ++strOffsetIdx; +// } +// +// offsetValue[i + 1] = +// offsetValue[i] + +// static_cast(expected[i].size() * sizeof(uint32_t)); +// } +// +// addBufferToModel(model, values); +// size_t valueBufferViewIndex = model.bufferViews.size() - 1; +// +// addBufferToModel(model, offsets); +// size_t arrayOffsetBuffer = model.buffers.size() - 1; +// size_t arrayOffsetBufferView = model.bufferViews.size() - 1; +// +// addBufferToModel(model, stringOffsets); +// size_t stringOffsetBuffer = model.buffers.size() - 1; +// size_t stringOffsetBufferView = model.bufferViews.size() - 1; +// +// ExtensionModelExtStructuralMetadata& metadata = +// model.addExtension(); +// +// Schema& schema = metadata.schema.emplace(); +// Class& testClass = schema.classes["TestClass"]; +// ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; +// testClassProperty.type = ClassProperty::Type::STRING; +// testClassProperty.array = true; +// +// PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); +// propertyTable.classProperty = "TestClass"; +// propertyTable.count = static_cast(expected.size()); +// +// PropertyTableProperty& propertyTableProperty = +// propertyTable.properties["TestClassProperty"]; +// propertyTableProperty.arrayOffsetType = +// PropertyTableProperty::ArrayOffsetType::UINT32; +// propertyTableProperty.stringOffsetType = +// PropertyTableProperty::StringOffsetType::UINT32; +// propertyTableProperty.values = static_cast(valueBufferViewIndex); +// propertyTableProperty.arrayOffsets = +// static_cast(arrayOffsetBufferView); +// propertyTableProperty.stringOffsets = +// static_cast(stringOffsetBufferView); +// +// PropertyTableView view(model, propertyTable); +// REQUIRE(view.status() == PropertyTableViewStatus::Valid); +// REQUIRE(view.size() == propertyTable.count); +// +// const ClassProperty* classProperty = +// view.getClassProperty("TestClassProperty"); +// REQUIRE(classProperty); +// REQUIRE(classProperty->type == ClassProperty::Type::STRING); +// REQUIRE(classProperty->array); +// REQUIRE(!classProperty->componentType); +// REQUIRE(!classProperty->count); +// +// SECTION("Access correct type") { +// PropertyTablePropertyView> +// stringProperty = +// view.getPropertyView>( +// "TestClassProperty"); +// REQUIRE(stringProperty.status() == PropertyTablePropertyViewStatus::Valid); +// for (size_t i = 0; i < expected.size(); ++i) { +// PropertyArrayView stringArray = +// stringProperty.get(static_cast(i)); +// for (size_t j = 0; j < expected[i].size(); ++j) { +// REQUIRE(stringArray[static_cast(j)] == expected[i][j]); +// } +// } +// } +// +// SECTION("Wrong array offset type") { +// propertyTableProperty.arrayOffsetType = +// PropertyTableProperty::ArrayOffsetType::UINT8; +// PropertyTablePropertyView> +// arrayProperty = +// view.getPropertyView>( +// "TestClassProperty"); +// REQUIRE( +// arrayProperty.status() == +// PropertyTablePropertyViewStatus:: +// ErrorBufferViewSizeDoesNotMatchPropertyTableCount); +// +// propertyTableProperty.arrayOffsetType = +// PropertyTableProperty::ArrayOffsetType::UINT16; +// arrayProperty = view.getPropertyView>( +// "TestClassProperty"); +// REQUIRE( +// arrayProperty.status() == +// PropertyTablePropertyViewStatus:: +// ErrorBufferViewSizeDoesNotMatchPropertyTableCount); +// +// propertyTableProperty.arrayOffsetType = "NONSENSE"; +// arrayProperty = view.getPropertyView>( +// "TestClassProperty"); +// REQUIRE( +// arrayProperty.status() == +// PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); +// propertyTableProperty.arrayOffsetType = +// PropertyTableProperty::ArrayOffsetType::UINT32; +// } +// +// SECTION("Wrong string offset type") { +// propertyTableProperty.stringOffsetType = +// PropertyTableProperty::StringOffsetType::UINT8; +// PropertyTablePropertyView> +// arrayProperty = +// view.getPropertyView>( +// "TestClassProperty"); +// REQUIRE( +// arrayProperty.status() == +// PropertyTablePropertyViewStatus:: +// ErrorBufferViewSizeDoesNotMatchPropertyTableCount); +// +// propertyTableProperty.stringOffsetType = +// PropertyTableProperty::StringOffsetType::UINT16; +// arrayProperty = view.getPropertyView>( +// "TestClassProperty"); +// REQUIRE( +// arrayProperty.status() == +// PropertyTablePropertyViewStatus:: +// ErrorBufferViewSizeDoesNotMatchPropertyTableCount); +// +// propertyTableProperty.stringOffsetType = "NONSENSE"; +// arrayProperty = view.getPropertyView>( +// "TestClassProperty"); +// REQUIRE( +// arrayProperty.status() == +// PropertyTablePropertyViewStatus::ErrorInvalidStringOffsetType); +// propertyTableProperty.stringOffsetType = +// PropertyTableProperty::StringOffsetType::UINT32; +// } +// +// SECTION("Array offset values are not sorted ascending") { +// uint32_t* offset = reinterpret_cast( +// model.buffers[arrayOffsetBuffer].cesium.data.data()); +// offset[0] = static_cast(1000); +// PropertyTablePropertyView> +// arrayProperty = +// view.getPropertyView>( +// "TestClassProperty"); +// REQUIRE( +// arrayProperty.status() == +// PropertyTablePropertyViewStatus::ErrorArrayOffsetsNotSorted); +// offset[0] = 0; +// } +// +// SECTION("String offset values are not sorted ascending") { +// uint32_t* offset = reinterpret_cast( +// model.buffers[stringOffsetBuffer].cesium.data.data()); +// offset[0] = static_cast(1000); +// PropertyTablePropertyView> +// arrayProperty = +// view.getPropertyView>( +// "TestClassProperty"); +// REQUIRE( +// arrayProperty.status() == +// PropertyTablePropertyViewStatus::ErrorStringOffsetsNotSorted); +// offset[0] = 0; +// } +// +// SECTION("Array offset value points outside of value buffer") { +// uint32_t* offset = reinterpret_cast( +// model.buffers[arrayOffsetBuffer].cesium.data.data()); +// uint32_t previousValue = offset[propertyTable.count]; +// offset[propertyTable.count] = static_cast(100000); +// PropertyTablePropertyView> +// arrayProperty = +// view.getPropertyView>( +// "TestClassProperty"); +// REQUIRE( +// arrayProperty.status() == +// PropertyTablePropertyViewStatus::ErrorArrayOffsetOutOfBounds); +// offset[propertyTable.count] = previousValue; +// } +// +// SECTION("String offset value points outside of value buffer") { +// uint32_t* offset = reinterpret_cast( +// model.buffers[stringOffsetBuffer].cesium.data.data()); +// uint32_t previousValue = offset[6]; +// offset[6] = static_cast(100000); +// PropertyTablePropertyView> +// arrayProperty = +// view.getPropertyView>( +// "TestClassProperty"); +// REQUIRE( +// arrayProperty.status() == +// PropertyTablePropertyViewStatus::ErrorStringOffsetOutOfBounds); +// offset[6] = previousValue; +// } +// +// SECTION("Count and offset buffer both present") { +// testClassProperty.count = 3; +// PropertyTablePropertyView> +// boolArrayProperty = +// view.getPropertyView>( +// "TestClassProperty"); +// REQUIRE( +// boolArrayProperty.status() == +// PropertyTablePropertyViewStatus::ErrorArrayCountAndOffsetBufferCoexist); +// } +//} +// +//TEST_CASE("Test callback on invalid property table view") { +// Model model; +// ExtensionModelExtStructuralMetadata& metadata = +// model.addExtension(); +// metadata.schema.emplace(); +// +// // Property table has a nonexistent class. +// PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); +// propertyTable.classProperty = "TestClass"; +// propertyTable.count = static_cast(5); +// +// PropertyTableProperty& propertyTableProperty = +// propertyTable.properties["TestClassProperty"]; +// propertyTableProperty.values = static_cast(-1); +// +// PropertyTableView view(model, propertyTable); +// REQUIRE(view.status() == PropertyTableViewStatus::ErrorClassNotFound); +// REQUIRE(view.size() == 0); +// +// const ClassProperty* classProperty = +// view.getClassProperty("TestClassProperty"); +// REQUIRE(!classProperty); +// +// uint32_t invokedCallbackCount = 0; +// view.getPropertyView( +// "TestClassProperty", +// [&invokedCallbackCount]( +// const std::string& /*propertyName*/, +// auto propertyValue) mutable { +// invokedCallbackCount++; +// REQUIRE( +// propertyValue.status() == +// PropertyTablePropertyViewStatus::ErrorInvalidPropertyTable); +// REQUIRE(propertyValue.size() == 0); +// }); +// +// REQUIRE(invokedCallbackCount == 1); +//} +// +//TEST_CASE("Test callback for invalid PropertyTableProperty") { +// Model model; +// ExtensionModelExtStructuralMetadata& metadata = +// model.addExtension(); +// +// Schema& schema = metadata.schema.emplace(); +// Class& testClass = schema.classes["TestClass"]; +// ClassProperty& testClassProperty = testClass.properties["InvalidProperty"]; +// testClassProperty.type = ClassProperty::Type::SCALAR; +// testClassProperty.componentType = ClassProperty::ComponentType::UINT32; +// +// PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); +// propertyTable.classProperty = "TestClass"; +// propertyTable.count = static_cast(5); +// +// PropertyTableProperty& propertyTableProperty = +// propertyTable.properties["InvalidProperty"]; +// propertyTableProperty.values = static_cast(-1); +// +// PropertyTableView view(model, propertyTable); +// REQUIRE(view.status() == PropertyTableViewStatus::Valid); +// REQUIRE(view.size() == propertyTable.count); +// +// const ClassProperty* classProperty = view.getClassProperty("InvalidProperty"); +// REQUIRE(classProperty); +// +// classProperty = view.getClassProperty("NonexistentProperty"); +// REQUIRE(!classProperty); +// +// uint32_t invokedCallbackCount = 0; +// auto testCallback = [&invokedCallbackCount]( +// const std::string& /*propertyName*/, +// auto propertyValue) mutable { +// invokedCallbackCount++; +// REQUIRE(propertyValue.status() != PropertyTablePropertyViewStatus::Valid); +// REQUIRE(propertyValue.size() == 0); +// }; +// +// view.getPropertyView("InvalidProperty", testCallback); +// view.getPropertyView("NonexistentProperty", testCallback); +// +// REQUIRE(invokedCallbackCount == 2); +//} +// +//TEST_CASE("Test callback for scalar PropertyTableProperty") { +// Model model; +// std::vector values = {12, 34, 30, 11, 34, 34, 11, 33, 122, 33}; +// +// addBufferToModel(model, values); +// size_t valueBufferViewIndex = model.bufferViews.size() - 1; +// +// ExtensionModelExtStructuralMetadata& metadata = +// model.addExtension(); +// +// Schema& schema = metadata.schema.emplace(); +// Class& testClass = schema.classes["TestClass"]; +// ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; +// testClassProperty.type = ClassProperty::Type::SCALAR; +// testClassProperty.componentType = ClassProperty::ComponentType::UINT32; +// +// PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); +// propertyTable.classProperty = "TestClass"; +// propertyTable.count = static_cast(values.size()); +// +// PropertyTableProperty& propertyTableProperty = +// propertyTable.properties["TestClassProperty"]; +// propertyTableProperty.values = static_cast(valueBufferViewIndex); +// +// PropertyTableView view(model, propertyTable); +// REQUIRE(view.status() == PropertyTableViewStatus::Valid); +// REQUIRE(view.size() == propertyTable.count); +// +// const ClassProperty* classProperty = +// view.getClassProperty("TestClassProperty"); +// REQUIRE(classProperty); +// REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); +// REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT32); +// REQUIRE(!classProperty->array); +// REQUIRE(classProperty->count == std::nullopt); +// +// uint32_t invokedCallbackCount = 0; +// view.getPropertyView( +// "TestClassProperty", +// [&values, &invokedCallbackCount]( +// const std::string& /*propertyName*/, +// auto propertyValue) mutable { +// invokedCallbackCount++; +// if constexpr (std::is_same_v< +// PropertyTablePropertyView, +// decltype(propertyValue)>) { +// REQUIRE( +// propertyValue.status() == PropertyTablePropertyViewStatus::Valid); +// REQUIRE(propertyValue.size() > 0); +// +// for (int64_t i = 0; i < propertyValue.size(); ++i) { +// REQUIRE( +// static_cast(propertyValue.get(i)) == +// values[static_cast(i)]); +// } +// } else { +// FAIL( +// "getPropertyView returned PropertyTablePropertyView of incorrect " +// "type for TestClassProperty."); +// } +// }); +// +// REQUIRE(invokedCallbackCount == 1); +//} +// +//TEST_CASE("Test callback for vecN PropertyTableProperty") { +// Model model; +// std::vector values = { +// glm::ivec3(-12, 34, 30), +// glm::ivec3(11, 73, 0), +// glm::ivec3(-2, 6, 12), +// glm::ivec3(-4, 8, -13)}; +// +// addBufferToModel(model, values); +// size_t valueBufferViewIndex = model.bufferViews.size() - 1; +// +// ExtensionModelExtStructuralMetadata& metadata = +// model.addExtension(); +// +// Schema& schema = metadata.schema.emplace(); +// Class& testClass = schema.classes["TestClass"]; +// ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; +// testClassProperty.type = ClassProperty::Type::VEC3; +// testClassProperty.componentType = ClassProperty::ComponentType::INT32; +// +// PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); +// propertyTable.classProperty = "TestClass"; +// propertyTable.count = static_cast(values.size()); +// +// PropertyTableProperty& propertyTableProperty = +// propertyTable.properties["TestClassProperty"]; +// propertyTableProperty.values = static_cast(valueBufferViewIndex); +// +// PropertyTableView view(model, propertyTable); +// REQUIRE(view.status() == PropertyTableViewStatus::Valid); +// REQUIRE(view.size() == propertyTable.count); +// +// const ClassProperty* classProperty = +// view.getClassProperty("TestClassProperty"); +// REQUIRE(classProperty); +// REQUIRE(classProperty->type == ClassProperty::Type::VEC3); +// REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT32); +// REQUIRE(classProperty->count == std::nullopt); +// REQUIRE(!classProperty->array); +// +// uint32_t invokedCallbackCount = 0; +// view.getPropertyView( +// "TestClassProperty", +// [&values, &invokedCallbackCount]( +// const std::string& /*propertyName*/, +// auto propertyValue) mutable { +// invokedCallbackCount++; +// REQUIRE( +// propertyValue.status() == PropertyTablePropertyViewStatus::Valid); +// REQUIRE(propertyValue.size() > 0); +// +// if constexpr (std::is_same_v< +// PropertyTablePropertyView, +// decltype(propertyValue)>) { +// for (int64_t i = 0; i < propertyValue.size(); ++i) { +// REQUIRE( +// static_cast(propertyValue.get(i)) == +// values[static_cast(i)]); +// } +// } else { +// FAIL( +// "getPropertyView returned PropertyTablePropertyView of incorrect " +// "type for TestClassProperty."); +// } +// }); +// +// REQUIRE(invokedCallbackCount == 1); +//} +// +//TEST_CASE("Test callback for matN PropertyTableProperty") { +// Model model; +// // clang-format off +// std::vector values = { +// glm::u32mat2x2( +// 12, 34, +// 30, 1), +// glm::u32mat2x2( +// 11, 8, +// 73, 102), +// glm::u32mat2x2( +// 1, 0, +// 63, 2), +// glm::u32mat2x2( +// 4, 8, +// 3, 23)}; +// // clang-format on +// +// addBufferToModel(model, values); +// size_t valueBufferViewIndex = model.bufferViews.size() - 1; +// +// ExtensionModelExtStructuralMetadata& metadata = +// model.addExtension(); +// +// Schema& schema = metadata.schema.emplace(); +// Class& testClass = schema.classes["TestClass"]; +// ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; +// testClassProperty.type = ClassProperty::Type::MAT2; +// testClassProperty.componentType = ClassProperty::ComponentType::UINT32; +// +// PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); +// propertyTable.classProperty = "TestClass"; +// propertyTable.count = static_cast(values.size()); +// +// PropertyTableProperty& propertyTableProperty = +// propertyTable.properties["TestClassProperty"]; +// propertyTableProperty.values = static_cast(valueBufferViewIndex); +// +// PropertyTableView view(model, propertyTable); +// REQUIRE(view.status() == PropertyTableViewStatus::Valid); +// REQUIRE(view.size() == propertyTable.count); +// +// const ClassProperty* classProperty = +// view.getClassProperty("TestClassProperty"); +// REQUIRE(classProperty); +// REQUIRE(classProperty->type == ClassProperty::Type::MAT2); +// REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT32); +// REQUIRE(classProperty->count == std::nullopt); +// REQUIRE(!classProperty->array); +// +// uint32_t invokedCallbackCount = 0; +// view.getPropertyView( +// "TestClassProperty", +// [&values, &invokedCallbackCount]( +// const std::string& /*propertyName*/, +// auto propertyValue) mutable { +// REQUIRE( +// propertyValue.status() == PropertyTablePropertyViewStatus::Valid); +// REQUIRE(propertyValue.size() > 0); +// +// if constexpr (std::is_same_v< +// PropertyTablePropertyView, +// decltype(propertyValue)>) { +// for (int64_t i = 0; i < propertyValue.size(); ++i) { +// REQUIRE( +// static_cast(propertyValue.get(i)) == +// values[static_cast(i)]); +// } +// } else { +// FAIL( +// "getPropertyView returned PropertyTablePropertyView of incorrect " +// "type for TestClassProperty."); +// } +// invokedCallbackCount++; +// }); +// +// REQUIRE(invokedCallbackCount == 1); +//} +// +//TEST_CASE("Test callback for boolean PropertyTableProperty") { +// Model model; +// +// int64_t instanceCount = 21; +// std::vector expected; +// std::vector values; +// values.resize(3); +// for (int64_t i = 0; i < instanceCount; ++i) { +// if (i % 2 == 0) { +// expected.emplace_back(true); +// } else { +// expected.emplace_back(false); +// } +// +// uint8_t expectedValue = expected.back(); +// int64_t byteIndex = i / 8; +// int64_t bitIndex = i % 8; +// values[static_cast(byteIndex)] = static_cast( +// (expectedValue << bitIndex) | values[static_cast(byteIndex)]); +// } +// +// addBufferToModel(model, values); +// +// ExtensionModelExtStructuralMetadata& metadata = +// model.addExtension(); +// +// Schema& schema = metadata.schema.emplace(); +// Class& testClass = schema.classes["TestClass"]; +// ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; +// testClassProperty.type = ClassProperty::Type::BOOLEAN; +// +// PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); +// propertyTable.classProperty = "TestClass"; +// propertyTable.count = static_cast(instanceCount); +// +// PropertyTableProperty& propertyTableProperty = +// propertyTable.properties["TestClassProperty"]; +// propertyTableProperty.values = +// static_cast(model.bufferViews.size() - 1); +// +// PropertyTableView view(model, propertyTable); +// REQUIRE(view.status() == PropertyTableViewStatus::Valid); +// REQUIRE(view.size() == propertyTable.count); +// +// const ClassProperty* classProperty = +// view.getClassProperty("TestClassProperty"); +// REQUIRE(classProperty); +// REQUIRE(classProperty->type == ClassProperty::Type::BOOLEAN); +// REQUIRE(classProperty->componentType == std::nullopt); +// REQUIRE(classProperty->count == std::nullopt); +// REQUIRE(!classProperty->array); +// +// uint32_t invokedCallbackCount = 0; +// view.getPropertyView( +// "TestClassProperty", +// [&expected, &invokedCallbackCount]( +// const std::string& /*propertyName*/, +// auto propertyValue) mutable { +// invokedCallbackCount++; +// +// REQUIRE( +// propertyValue.status() == PropertyTablePropertyViewStatus::Valid); +// REQUIRE(propertyValue.size() > 0); +// +// if constexpr (std::is_same_v< +// PropertyTablePropertyView, +// decltype(propertyValue)>) { +// for (int64_t i = 0; i < propertyValue.size(); ++i) { +// REQUIRE( +// static_cast(propertyValue.get(i)) == +// expected[static_cast(i)]); +// } +// } else { +// FAIL( +// "getPropertyView returned PropertyTablePropertyView of incorrect " +// "type for TestClassProperty."); +// } +// }); +// +// REQUIRE(invokedCallbackCount == 1); +//} +// +//TEST_CASE("Test callback for string PropertyTableProperty") { +// Model model; +// +// std::vector expected{"What's up", "Test_0", "Test_1", "", "Hi"}; +// size_t totalBytes = 0; +// for (const std::string& expectedValue : expected) { +// totalBytes += expectedValue.size(); +// } +// +// std::vector stringOffsets( +// (expected.size() + 1) * sizeof(uint32_t)); +// std::vector values(totalBytes); +// uint32_t* offsetValue = reinterpret_cast(stringOffsets.data()); +// for (size_t i = 0; i < expected.size(); ++i) { +// const std::string& expectedValue = expected[i]; +// std::memcpy( +// values.data() + offsetValue[i], +// expectedValue.c_str(), +// expectedValue.size()); +// offsetValue[i + 1] = +// offsetValue[i] + static_cast(expectedValue.size()); +// } +// +// addBufferToModel(model, values); +// size_t valueBufferViewIndex = model.bufferViews.size() - 1; +// +// addBufferToModel(model, stringOffsets); +// size_t offsetBufferViewIndex = model.bufferViews.size() - 1; +// +// ExtensionModelExtStructuralMetadata& metadata = +// model.addExtension(); +// +// Schema& schema = metadata.schema.emplace(); +// Class& testClass = schema.classes["TestClass"]; +// ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; +// testClassProperty.type = ClassProperty::Type::STRING; +// +// PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); +// propertyTable.classProperty = "TestClass"; +// propertyTable.count = static_cast(expected.size()); +// +// PropertyTableProperty& propertyTableProperty = +// propertyTable.properties["TestClassProperty"]; +// propertyTableProperty.stringOffsetType = +// PropertyTableProperty::StringOffsetType::UINT32; +// propertyTableProperty.values = static_cast(valueBufferViewIndex); +// propertyTableProperty.stringOffsets = +// static_cast(offsetBufferViewIndex); +// +// PropertyTableView view(model, propertyTable); +// REQUIRE(view.status() == PropertyTableViewStatus::Valid); +// REQUIRE(view.size() == propertyTable.count); +// +// const ClassProperty* classProperty = +// view.getClassProperty("TestClassProperty"); +// REQUIRE(classProperty); +// REQUIRE(classProperty->type == ClassProperty::Type::STRING); +// REQUIRE(classProperty->componentType == std::nullopt); +// REQUIRE(classProperty->count == std::nullopt); +// REQUIRE(!classProperty->array); +// +// uint32_t invokedCallbackCount = 0; +// view.getPropertyView( +// "TestClassProperty", +// [&expected, &invokedCallbackCount]( +// const std::string& /*propertyName*/, +// auto propertyValue) mutable { +// invokedCallbackCount++; +// REQUIRE( +// propertyValue.status() == PropertyTablePropertyViewStatus::Valid); +// REQUIRE(propertyValue.size() > 0); +// +// if constexpr (std::is_same_v< +// PropertyTablePropertyView, +// decltype(propertyValue)>) { +// for (int64_t i = 0; i < propertyValue.size(); ++i) { +// REQUIRE( +// static_cast(propertyValue.get(i)) == +// expected[static_cast(i)]); +// } +// } else { +// FAIL( +// "getPropertyView returned PropertyTablePropertyView of incorrect " +// "type for TestClassProperty."); +// } +// }); +// +// REQUIRE(invokedCallbackCount == 1); +//} +// +//TEST_CASE("Test callback for scalar array PropertyTableProperty") { +// Model model; +// std::vector values = +// {12, 34, 30, 11, 34, 34, 11, 33, 122, 33, 223, 11}; +// +// addBufferToModel(model, values); +// +// ExtensionModelExtStructuralMetadata& metadata = +// model.addExtension(); +// +// Schema& schema = metadata.schema.emplace(); +// Class& testClass = schema.classes["TestClass"]; +// ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; +// testClassProperty.type = ClassProperty::Type::SCALAR; +// testClassProperty.componentType = ClassProperty::ComponentType::UINT32; +// testClassProperty.array = true; +// testClassProperty.count = 3; +// +// PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); +// propertyTable.classProperty = "TestClass"; +// propertyTable.count = static_cast( +// values.size() / static_cast(testClassProperty.count.value())); +// +// PropertyTableProperty& propertyTableProperty = +// propertyTable.properties["TestClassProperty"]; +// propertyTableProperty.values = +// static_cast(model.bufferViews.size() - 1); +// +// PropertyTableView view(model, propertyTable); +// REQUIRE(view.status() == PropertyTableViewStatus::Valid); +// REQUIRE(view.size() == propertyTable.count); +// +// const ClassProperty* classProperty = +// view.getClassProperty("TestClassProperty"); +// REQUIRE(classProperty); +// REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); +// REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT32); +// REQUIRE(classProperty->array); +// REQUIRE(classProperty->count == 3); +// +// uint32_t invokedCallbackCount = 0; +// view.getPropertyView( +// "TestClassProperty", +// [&values, &invokedCallbackCount]( +// const std::string& /*propertyName*/, +// auto propertyValue) { +// invokedCallbackCount++; +// REQUIRE( +// propertyValue.status() == PropertyTablePropertyViewStatus::Valid); +// REQUIRE(propertyValue.size() > 0); +// +// if constexpr (std::is_same_v< +// PropertyTablePropertyView< +// PropertyArrayView>, +// decltype(propertyValue)>) { +// for (int64_t i = 0; i < propertyValue.size(); ++i) { +// PropertyArrayView member = propertyValue.get(i); +// for (int64_t j = 0; j < member.size(); ++j) { +// REQUIRE(member[j] == values[static_cast(i * 3 + j)]); +// } +// } +// } else { +// FAIL( +// "getPropertyView returned PropertyTablePropertyView of incorrect " +// "type for TestClassProperty."); +// } +// }); +// +// REQUIRE(invokedCallbackCount == 1); +//} +// +//TEST_CASE("Test callback for vecN array PropertyTableProperty") { +// Model model; +// std::vector values = { +// glm::ivec3(12, 34, -30), +// glm::ivec3(-2, 0, 1), +// glm::ivec3(1, 2, 8), +// glm::ivec3(-100, 84, 6), +// glm::ivec3(2, -2, -2), +// glm::ivec3(40, 61, 3), +// }; +// +// addBufferToModel(model, values); +// +// ExtensionModelExtStructuralMetadata& metadata = +// model.addExtension(); +// +// Schema& schema = metadata.schema.emplace(); +// Class& testClass = schema.classes["TestClass"]; +// ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; +// testClassProperty.type = ClassProperty::Type::VEC3; +// testClassProperty.componentType = ClassProperty::ComponentType::INT32; +// testClassProperty.array = true; +// testClassProperty.count = 2; +// +// PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); +// propertyTable.classProperty = "TestClass"; +// propertyTable.count = static_cast( +// values.size() / static_cast(testClassProperty.count.value())); +// +// PropertyTableProperty& propertyTableProperty = +// propertyTable.properties["TestClassProperty"]; +// propertyTableProperty.values = +// static_cast(model.bufferViews.size() - 1); +// +// PropertyTableView view(model, propertyTable); +// REQUIRE(view.status() == PropertyTableViewStatus::Valid); +// REQUIRE(view.size() == propertyTable.count); +// +// const ClassProperty* classProperty = +// view.getClassProperty("TestClassProperty"); +// REQUIRE(classProperty); +// REQUIRE(classProperty->type == ClassProperty::Type::VEC3); +// REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT32); +// REQUIRE(classProperty->array); +// REQUIRE(classProperty->count == 2); +// +// uint32_t invokedCallbackCount = 0; +// view.getPropertyView( +// "TestClassProperty", +// [&values, &invokedCallbackCount]( +// const std::string& /*propertyName*/, +// auto propertyValue) { +// invokedCallbackCount++; +// REQUIRE( +// propertyValue.status() == PropertyTablePropertyViewStatus::Valid); +// REQUIRE(propertyValue.size() > 0); +// +// if constexpr (std::is_same_v< +// PropertyTablePropertyView< +// PropertyArrayView>, +// decltype(propertyValue)>) { +// for (int64_t i = 0; i < propertyValue.size(); ++i) { +// PropertyArrayView member = propertyValue.get(i); +// for (int64_t j = 0; j < member.size(); ++j) { +// REQUIRE(member[j] == values[static_cast(i * 2 + j)]); +// } +// } +// } else { +// FAIL( +// "getPropertyView returned PropertyTablePropertyView of incorrect " +// "type for TestClassProperty."); +// } +// }); +// +// REQUIRE(invokedCallbackCount == 1); +//} +// +//TEST_CASE("Test callback for matN array PropertyTableProperty") { +// Model model; +// // clang-format off +// std::vector values = { +// glm::i32mat2x2( +// 12, 34, +// -30, 20), +// glm::i32mat2x2( +// -2, -2, +// 0, 1), +// glm::i32mat2x2( +// 1, 2, +// 8, 5), +// glm::i32mat2x2( +// -100, 3, +// 84, 6), +// glm::i32mat2x2( +// 2, 12, +// -2, -2), +// glm::i32mat2x2( +// 40, 61, +// 7, -3), +// }; +// // clang-format on +// +// addBufferToModel(model, values); +// +// ExtensionModelExtStructuralMetadata& metadata = +// model.addExtension(); +// +// Schema& schema = metadata.schema.emplace(); +// Class& testClass = schema.classes["TestClass"]; +// ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; +// testClassProperty.type = ClassProperty::Type::MAT2; +// testClassProperty.componentType = ClassProperty::ComponentType::INT32; +// testClassProperty.array = true; +// testClassProperty.count = 2; +// +// PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); +// propertyTable.classProperty = "TestClass"; +// propertyTable.count = static_cast( +// values.size() / static_cast(testClassProperty.count.value())); +// +// PropertyTableProperty& propertyTableProperty = +// propertyTable.properties["TestClassProperty"]; +// propertyTableProperty.values = +// static_cast(model.bufferViews.size() - 1); +// +// PropertyTableView view(model, propertyTable); +// REQUIRE(view.status() == PropertyTableViewStatus::Valid); +// REQUIRE(view.size() == propertyTable.count); +// +// const ClassProperty* classProperty = +// view.getClassProperty("TestClassProperty"); +// REQUIRE(classProperty); +// REQUIRE(classProperty->type == ClassProperty::Type::MAT2); +// REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT32); +// REQUIRE(classProperty->array); +// REQUIRE(classProperty->count == 2); +// +// uint32_t invokedCallbackCount = 0; +// view.getPropertyView( +// "TestClassProperty", +// [&values, &invokedCallbackCount]( +// const std::string& /*propertyName*/, +// auto propertyValue) { +// invokedCallbackCount++; +// REQUIRE( +// propertyValue.status() == PropertyTablePropertyViewStatus::Valid); +// REQUIRE(propertyValue.size() > 0); +// +// if constexpr (std::is_same_v< +// PropertyTablePropertyView< +// PropertyArrayView>, +// decltype(propertyValue)>) { +// for (int64_t i = 0; i < propertyValue.size(); ++i) { +// PropertyArrayView member = propertyValue.get(i); +// for (int64_t j = 0; j < member.size(); ++j) { +// REQUIRE(member[j] == values[static_cast(i * 2 + j)]); +// } +// } +// } else { +// FAIL( +// "getPropertyView returned PropertyTablePropertyView of incorrect " +// "type for TestClassProperty."); +// } +// }); +// +// REQUIRE(invokedCallbackCount == 1); +//} +// +//TEST_CASE("Test callback for boolean array PropertyTableProperty") { +// Model model; +// +// std::vector expected = { +// true, +// false, +// false, +// true, +// false, +// false, +// true, +// true, +// true, +// false, +// false, +// true}; +// std::vector values; +// size_t requiredBytesSize = static_cast( +// glm::ceil(static_cast(expected.size()) / 8.0)); +// values.resize(requiredBytesSize); +// for (size_t i = 0; i < expected.size(); ++i) { +// uint8_t expectedValue = expected[i]; +// size_t byteIndex = i / 8; +// size_t bitIndex = i % 8; +// values[byteIndex] = +// static_cast((expectedValue << bitIndex) | values[byteIndex]); +// } +// +// addBufferToModel(model, values); +// +// ExtensionModelExtStructuralMetadata& metadata = +// model.addExtension(); +// +// Schema& schema = metadata.schema.emplace(); +// Class& testClass = schema.classes["TestClass"]; +// ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; +// testClassProperty.type = ClassProperty::Type::BOOLEAN; +// testClassProperty.array = true; +// testClassProperty.count = 3; +// +// PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); +// propertyTable.classProperty = "TestClass"; +// propertyTable.count = static_cast( +// expected.size() / static_cast(testClassProperty.count.value())); +// +// PropertyTableProperty& propertyTableProperty = +// propertyTable.properties["TestClassProperty"]; +// propertyTableProperty.values = +// static_cast(model.bufferViews.size() - 1); +// +// PropertyTableView view(model, propertyTable); +// REQUIRE(view.status() == PropertyTableViewStatus::Valid); +// REQUIRE(view.size() == propertyTable.count); +// +// const ClassProperty* classProperty = +// view.getClassProperty("TestClassProperty"); +// REQUIRE(classProperty); +// REQUIRE(classProperty->type == ClassProperty::Type::BOOLEAN); +// REQUIRE(classProperty->array); +// REQUIRE(classProperty->count == 3); +// +// uint32_t invokedCallbackCount = 0; +// view.getPropertyView( +// "TestClassProperty", +// [&expected, &invokedCallbackCount]( +// const std::string& /*propertyName*/, +// auto propertyValue) { +// invokedCallbackCount++; +// REQUIRE( +// propertyValue.status() == PropertyTablePropertyViewStatus::Valid); +// REQUIRE(propertyValue.size() > 0); +// +// if constexpr (std::is_same_v< +// PropertyTablePropertyView>, +// decltype(propertyValue)>) { +// for (int64_t i = 0; i < propertyValue.size(); ++i) { +// PropertyArrayView member = propertyValue.get(i); +// for (int64_t j = 0; j < member.size(); ++j) { +// REQUIRE(member[j] == expected[static_cast(i * 3 + j)]); +// } +// } +// } else { +// FAIL( +// "getPropertyView returned PropertyTablePropertyView of incorrect " +// "type for TestClassProperty."); +// } +// }); +// +// REQUIRE(invokedCallbackCount == 1); +//} +// +//TEST_CASE("Test callback for string array PropertyTableProperty") { +// Model model; +// +// std::vector expected{ +// "What's up", +// "Breaking news!!! Aliens no longer attacks the US first", +// "But they still abduct my cows! Those milk thiefs! 👽 🐮", +// "I'm not crazy. My mother had me tested 🤪", +// "I love you, meat bags! ❤️", +// "Book in the freezer"}; +// +// size_t totalBytes = 0; +// for (const std::string& expectedValue : expected) { +// totalBytes += expectedValue.size(); +// } +// +// std::vector offsets((expected.size() + 1) * sizeof(uint32_t)); +// std::vector values(totalBytes); +// uint32_t* offsetValue = reinterpret_cast(offsets.data()); +// for (size_t i = 0; i < expected.size(); ++i) { +// const std::string& expectedValue = expected[i]; +// std::memcpy( +// values.data() + offsetValue[i], +// expectedValue.c_str(), +// expectedValue.size()); +// offsetValue[i + 1] = +// offsetValue[i] + static_cast(expectedValue.size()); +// } +// +// addBufferToModel(model, values); +// size_t valueBufferViewIndex = model.bufferViews.size() - 1; +// +// addBufferToModel(model, offsets); +// size_t offsetBufferViewIndex = model.bufferViews.size() - 1; +// +// ExtensionModelExtStructuralMetadata& metadata = +// model.addExtension(); +// +// Schema& schema = metadata.schema.emplace(); +// Class& testClass = schema.classes["TestClass"]; +// ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; +// testClassProperty.type = ClassProperty::Type::STRING; +// testClassProperty.array = true; +// testClassProperty.count = 2; +// +// PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); +// propertyTable.classProperty = "TestClass"; +// propertyTable.count = static_cast( +// expected.size() / static_cast(testClassProperty.count.value())); +// +// PropertyTableProperty& propertyTableProperty = +// propertyTable.properties["TestClassProperty"]; +// propertyTableProperty.stringOffsetType = +// PropertyTableProperty::StringOffsetType::UINT32; +// propertyTableProperty.values = static_cast(valueBufferViewIndex); +// propertyTableProperty.stringOffsets = +// static_cast(offsetBufferViewIndex); +// +// PropertyTableView view(model, propertyTable); +// REQUIRE(view.status() == PropertyTableViewStatus::Valid); +// REQUIRE(view.size() == propertyTable.count); +// +// const ClassProperty* classProperty = +// view.getClassProperty("TestClassProperty"); +// REQUIRE(classProperty); +// REQUIRE(classProperty->type == ClassProperty::Type::STRING); +// REQUIRE(classProperty->array); +// REQUIRE(classProperty->count == 2); +// +// uint32_t invokedCallbackCount = 0; +// view.getPropertyView( +// "TestClassProperty", +// [&invokedCallbackCount]( +// const std::string& /*propertyName*/, +// auto propertyValue) { +// invokedCallbackCount++; +// REQUIRE( +// propertyValue.status() == PropertyTablePropertyViewStatus::Valid); +// REQUIRE(propertyValue.size() == 3); +// +// if constexpr (std::is_same_v< +// PropertyTablePropertyView< +// PropertyArrayView>, +// decltype(propertyValue)>) { +// PropertyArrayView v0 = propertyValue.get(0); +// REQUIRE(v0.size() == 2); +// REQUIRE(v0[0] == "What's up"); +// REQUIRE( +// v0[1] == +// "Breaking news!!! Aliens no longer attacks the US first"); +// +// PropertyArrayView v1 = propertyValue.get(1); +// REQUIRE(v1.size() == 2); +// REQUIRE( +// v1[0] == "But they still abduct my cows! Those milk thiefs! 👽 🐮"); +// REQUIRE(v1[1] == "I'm not crazy. My mother had me tested 🤪"); +// +// PropertyArrayView v2 = propertyValue.get(2); +// REQUIRE(v2.size() == 2); +// REQUIRE(v2[0] == "I love you, meat bags! ❤️"); +// REQUIRE(v2[1] == "Book in the freezer"); +// } else { +// FAIL( +// "getPropertyView returned PropertyTablePropertyView of incorrect " +// "type for TestClassProperty."); +// } +// }); +// +// REQUIRE(invokedCallbackCount == 1); +//} diff --git a/CesiumGltf/test/TestPropertyView.cpp b/CesiumGltf/test/TestPropertyView.cpp new file mode 100644 index 000000000..ee3bf466b --- /dev/null +++ b/CesiumGltf/test/TestPropertyView.cpp @@ -0,0 +1,359 @@ +#include "CesiumGltf/PropertyView.h" + +#include + +using namespace CesiumGltf; +using namespace CesiumUtility; + +TEST_CASE("Constructs empty PropertyView") { + PropertyView view; + REQUIRE(view.arrayCount() == 0); + REQUIRE(!view.normalized()); + REQUIRE(!view.offset()); + REQUIRE(!view.scale()); + REQUIRE(!view.max()); + REQUIRE(!view.min()); + REQUIRE(!view.required()); + REQUIRE(!view.noData()); + REQUIRE(!view.defaultValue()); +} + +TEST_CASE("Boolean PropertyView") { + SECTION("Ignores non-applicable properties") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::BOOLEAN; + classProperty.normalized = true; + classProperty.offset = JsonValue(true); + classProperty.scale = JsonValue(true); + classProperty.max = JsonValue(true); + classProperty.min = JsonValue(true); + classProperty.noData = JsonValue(true); + + PropertyView view(classProperty); + REQUIRE(!view.normalized()); + REQUIRE(!view.offset()); + REQUIRE(!view.scale()); + REQUIRE(!view.max()); + REQUIRE(!view.min()); + REQUIRE(!view.noData()); + } + + SECTION("Ignores count (array is false)") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::BOOLEAN; + classProperty.count = 5; + + PropertyView view(classProperty); + REQUIRE(view.arrayCount() == 0); + } + + SECTION("Constructs with defaultProperty") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::BOOLEAN; + classProperty.required = false; + classProperty.defaultProperty = JsonValue(false); + + PropertyView view(classProperty); + REQUIRE(!view.required()); + REQUIRE(view.defaultValue()); + REQUIRE(!*view.defaultValue()); + } + + SECTION("Ignores defaultProperty when required is true") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::BOOLEAN; + classProperty.required = true; + classProperty.defaultProperty = JsonValue(false); + + PropertyView view(classProperty); + REQUIRE(view.required()); + REQUIRE(!view.defaultValue()); + } + + SECTION("Returns nullopt for invalid types") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::BOOLEAN; + classProperty.defaultProperty = JsonValue(1); + + PropertyView view(classProperty); + REQUIRE(!view.required()); + REQUIRE(!view.defaultValue()); + } +} + +TEST_CASE("Scalar PropertyView") { + SECTION("Constructs with non-optional properties") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::SCALAR; + classProperty.componentType = ClassProperty::ComponentType::UINT8; + classProperty.normalized = true; + classProperty.required = true; + + PropertyView view(classProperty); + REQUIRE(view.normalized()); + REQUIRE(view.required()); + + REQUIRE(view.arrayCount() == 0); + REQUIRE(!view.offset()); + REQUIRE(!view.scale()); + REQUIRE(!view.max()); + REQUIRE(!view.min()); + REQUIRE(!view.noData()); + REQUIRE(!view.defaultValue()); + } + + SECTION("Ignores count (array is false)") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::SCALAR; + classProperty.componentType = ClassProperty::ComponentType::UINT8; + classProperty.count = 5; + + PropertyView view(classProperty); + REQUIRE(view.arrayCount() == 0); + } + + SECTION("Constructs with offset, scale, max, and min") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::SCALAR; + classProperty.componentType = ClassProperty::ComponentType::INT16; + classProperty.offset = JsonValue(5); + classProperty.scale = JsonValue(2); + classProperty.max = JsonValue(10); + classProperty.min = JsonValue(-10); + + PropertyView view(classProperty); + REQUIRE(view.offset()); + REQUIRE(view.scale()); + REQUIRE(view.max()); + REQUIRE(view.min()); + + REQUIRE(*view.offset() == 5); + REQUIRE(*view.scale() == 2); + REQUIRE(*view.max() == 10); + REQUIRE(*view.min() == -10); + } + + SECTION("Returns nullopt for out-of-bounds offset, scale, max, and min") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::SCALAR; + classProperty.componentType = ClassProperty::ComponentType::INT8; + classProperty.offset = JsonValue(129); + classProperty.scale = JsonValue(255); + classProperty.max = JsonValue(1000); + classProperty.min = JsonValue(-1000); + + PropertyView view(classProperty); + REQUIRE(!view.offset()); + REQUIRE(!view.scale()); + REQUIRE(!view.max()); + REQUIRE(!view.min()); + } + + SECTION("Rounds numbers where possible") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::SCALAR; + classProperty.componentType = ClassProperty::ComponentType::INT8; + classProperty.offset = JsonValue(-5.5); + classProperty.scale = JsonValue(2.4); + classProperty.max = JsonValue(150); + classProperty.min = JsonValue(-150); + + PropertyView view(classProperty); + REQUIRE(view.offset()); + REQUIRE(view.scale()); + REQUIRE(!view.max()); + REQUIRE(!view.min()); + + REQUIRE(*view.offset() == -5); + REQUIRE(*view.scale() == 2); + } + + SECTION("Returns nullopt for invalid types") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::SCALAR; + classProperty.componentType = ClassProperty::ComponentType::INT8; + classProperty.offset = JsonValue("10"); + classProperty.scale = JsonValue(false); + classProperty.max = JsonValue(JsonValue::Array()); + classProperty.min = JsonValue(JsonValue::Array{10}); + + PropertyView view(classProperty); + REQUIRE(!view.offset()); + REQUIRE(!view.scale()); + REQUIRE(!view.max()); + REQUIRE(!view.min()); + } + + SECTION("Constructs with noData and defaultProperty") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::SCALAR; + classProperty.componentType = ClassProperty::ComponentType::UINT8; + classProperty.required = false; + classProperty.noData = JsonValue(0); + classProperty.defaultProperty = JsonValue(1); + + PropertyView view(classProperty); + REQUIRE(!view.required()); + REQUIRE(view.noData()); + REQUIRE(view.defaultValue()); + + REQUIRE(*view.noData() == 0); + REQUIRE(*view.defaultValue() == 1); + } + + SECTION("Ignores noData and defaultProperty when required is true") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::SCALAR; + classProperty.componentType = ClassProperty::ComponentType::UINT8; + classProperty.required = true; + classProperty.noData = JsonValue(0); + classProperty.defaultProperty = JsonValue(1); + + PropertyView view(classProperty); + REQUIRE(view.required()); + REQUIRE(!view.noData()); + REQUIRE(!view.defaultValue()); + } +} + +TEST_CASE("String PropertyView") { + SECTION("Ignores non-applicable properties") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::STRING; + classProperty.normalized = true; + classProperty.offset = JsonValue("test"); + classProperty.scale = JsonValue("test"); + classProperty.max = JsonValue("test"); + classProperty.min = JsonValue("test"); + + PropertyView view(classProperty); + REQUIRE(!view.normalized()); + REQUIRE(!view.offset()); + REQUIRE(!view.scale()); + REQUIRE(!view.max()); + REQUIRE(!view.min()); + } + + SECTION("Ignores count (array is false)") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::STRING; + classProperty.count = 5; + + PropertyView view(classProperty); + REQUIRE(view.arrayCount() == 0); + } + + SECTION("Constructs with noData and defaultProperty") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::STRING; + classProperty.required = false; + classProperty.noData = JsonValue("null"); + classProperty.defaultProperty = JsonValue("default"); + + PropertyView view(classProperty); + REQUIRE(!view.required()); + REQUIRE(view.noData()); + REQUIRE(view.defaultValue()); + + REQUIRE(*view.noData() == "null"); + REQUIRE(*view.defaultValue() == "default"); + } + + SECTION("Ignores noData and defaultProperty when required is true") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::STRING; + classProperty.required = true; + classProperty.noData = JsonValue("null"); + classProperty.defaultProperty = JsonValue("default"); + + PropertyView view(classProperty); + REQUIRE(view.required()); + REQUIRE(!view.noData()); + REQUIRE(!view.defaultValue()); + } + + SECTION("Returns nullopt for invalid types") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::STRING; + classProperty.noData = JsonValue::Array{JsonValue("null")}; + classProperty.defaultProperty = JsonValue(true); + + PropertyView view(classProperty); + REQUIRE(!view.required()); + REQUIRE(!view.noData()); + REQUIRE(!view.defaultValue()); + } +} + +TEST_CASE("Boolean Array PropertyView") { + SECTION("Ignores non-applicable properties") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::BOOLEAN; + classProperty.normalized = true; + classProperty.offset = JsonValue::Array{JsonValue(true)}; + classProperty.scale = JsonValue::Array{JsonValue(true)}; + classProperty.max = JsonValue::Array{JsonValue(true)}; + classProperty.min = JsonValue::Array{JsonValue(true)}; + classProperty.noData = JsonValue::Array{JsonValue(true)}; + + PropertyView> view(classProperty); + REQUIRE(!view.normalized()); + REQUIRE(!view.offset()); + REQUIRE(!view.scale()); + REQUIRE(!view.max()); + REQUIRE(!view.min()); + REQUIRE(!view.noData()); + } + + SECTION("Constructs with count") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::BOOLEAN; + classProperty.array = true; + classProperty.count = 5; + + PropertyView> view(classProperty); + REQUIRE(view.arrayCount() == 5); + } + + SECTION("Constructs with defaultProperty") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::BOOLEAN; + classProperty.array = true; + classProperty.required = false; + classProperty.defaultProperty = + JsonValue::Array{JsonValue(false), JsonValue(true)}; + + PropertyView> view(classProperty); + REQUIRE(!view.required()); + REQUIRE(view.defaultValue()); + + const auto defaultValue = *view.defaultValue(); + REQUIRE(defaultValue.size() == 2); + REQUIRE(!defaultValue[0]); + REQUIRE(defaultValue[1]); + } + + SECTION("Ignores defaultProperty when required is true") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::BOOLEAN; + classProperty.array = true; + classProperty.required = true; + classProperty.defaultProperty = + JsonValue::Array{JsonValue(false), JsonValue(true)}; + + PropertyView view(classProperty); + REQUIRE(view.required()); + REQUIRE(!view.defaultValue()); + } + + SECTION("Returns nullopt for invalid types") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::BOOLEAN; + classProperty.array = true; + classProperty.defaultProperty = JsonValue(true); + + PropertyView view(classProperty); + REQUIRE(!view.required()); + REQUIRE(!view.defaultValue()); + } +} diff --git a/CesiumUtility/include/CesiumUtility/JsonValue.h b/CesiumUtility/include/CesiumUtility/JsonValue.h index 47773ed14..8a4b2afc0 100644 --- a/CesiumUtility/include/CesiumUtility/JsonValue.h +++ b/CesiumUtility/include/CesiumUtility/JsonValue.h @@ -413,7 +413,7 @@ class CESIUMUTILITY_API JsonValue final { /** * @brief Gets the array from the value. - * @return The arrayj. + * @return The array. * @throws std::bad_variant_access if the underlying type is not a * JsonValue::Array */ From 400f832d32fe3cf389500314fb1a5e49f21b9978 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 16 Aug 2023 10:54:01 +1000 Subject: [PATCH 162/421] Update npm dependencies and regenerate 3DTiles/glTF classes. --- .../include/Cesium3DTiles/BoundingVolume.h | 11 +- .../generated/include/Cesium3DTiles/Buffer.h | 10 +- .../include/Cesium3DTiles/ClassProperty.h | 20 +- .../include/Cesium3DTiles/ClassStatistics.h | 3 +- .../include/Cesium3DTiles/Properties.h | 4 +- .../include/Cesium3DTiles/Statistics.h | 5 +- .../generated/include/Cesium3DTiles/Tileset.h | 1 + .../include/CesiumAsync/Impl/RemoveFuture.h | 20 +- .../CesiumGltf/FeatureTexturePropertyView.h | 4 +- package-lock.json | 214 ++------ tools/generate-classes/package-lock.json | 497 ++++++++++++------ 11 files changed, 423 insertions(+), 366 deletions(-) diff --git a/Cesium3DTiles/generated/include/Cesium3DTiles/BoundingVolume.h b/Cesium3DTiles/generated/include/Cesium3DTiles/BoundingVolume.h index 8fd79eab1..f193d0c67 100644 --- a/Cesium3DTiles/generated/include/Cesium3DTiles/BoundingVolume.h +++ b/Cesium3DTiles/generated/include/Cesium3DTiles/BoundingVolume.h @@ -31,15 +31,20 @@ struct CESIUM3DTILES_API BoundingVolume final /** * @brief An array of six numbers that define a bounding geographic region in * EPSG:4979 coordinates with the order [west, south, east, north, minimum - * height, maximum height]. Longitudes and latitudes are in radians, and - * heights are in meters above (or below) the WGS84 ellipsoid. + * height, maximum height]. Longitudes and latitudes are in radians. The range + * for latitudes is [-PI/2,PI/2]. The range for longitudes is [-PI,PI]. The + * value that is given as the 'south' of the region shall not be larger than + * the value for the 'north' of the region. The heights are in meters above + * (or below) the WGS84 ellipsoid. The 'minimum height' shall not be larger + * than the 'maximum height'. */ std::vector region; /** * @brief An array of four numbers that define a bounding sphere. The first * three elements define the x, y, and z values for the center of the sphere. - * The last element (with index 3) defines the radius in meters. + * The last element (with index 3) defines the radius in meters. The radius + * shall not be negative. */ std::vector sphere; }; diff --git a/Cesium3DTiles/generated/include/Cesium3DTiles/Buffer.h b/Cesium3DTiles/generated/include/Cesium3DTiles/Buffer.h index 765ac75f3..6645cc5b0 100644 --- a/Cesium3DTiles/generated/include/Cesium3DTiles/Buffer.h +++ b/Cesium3DTiles/generated/include/Cesium3DTiles/Buffer.h @@ -19,11 +19,11 @@ struct CESIUM3DTILES_API Buffer final : public CesiumUtility::ExtensibleObject { static inline constexpr const char* TypeName = "Buffer"; /** - * @brief The URI (or IRI) of the external schema file. Relative paths are - * relative to the file containing the buffer JSON. `uri` is required when - * using the JSON subtree format and not required when using the binary - * subtree format - when omitted the buffer refers to the binary chunk of the - * subtree file. Data URIs are not allowed. + * @brief The URI (or IRI) of the file that contains the binary buffer data. + * Relative paths are relative to the file containing the buffer JSON. `uri` + * is required when using the JSON subtree format and not required when using + * the binary subtree format - when omitted the buffer refers to the binary + * chunk of the subtree file. Data URIs are not allowed. */ std::optional uri; diff --git a/Cesium3DTiles/generated/include/Cesium3DTiles/ClassProperty.h b/Cesium3DTiles/generated/include/Cesium3DTiles/ClassProperty.h index be2fd9740..3ce7883af 100644 --- a/Cesium3DTiles/generated/include/Cesium3DTiles/ClassProperty.h +++ b/Cesium3DTiles/generated/include/Cesium3DTiles/ClassProperty.h @@ -45,8 +45,8 @@ struct CESIUM3DTILES_API ClassProperty final }; /** - * @brief Known values for The datatype of the element's components. Only - * applicable to `SCALAR`, `VECN`, and `MATN` types. + * @brief Known values for The datatype of the element's components. Required + * for `SCALAR`, `VECN`, and `MATN` types, and disallowed for other types. */ struct ComponentType { inline static const std::string INT8 = "INT8"; @@ -89,8 +89,8 @@ struct CESIUM3DTILES_API ClassProperty final std::string type = Type::SCALAR; /** - * @brief The datatype of the element's components. Only applicable to - * `SCALAR`, `VECN`, and `MATN` types. + * @brief The datatype of the element's components. Required for `SCALAR`, + * `VECN`, and `MATN` types, and disallowed for other types. * * Known values are defined in {@link ComponentType}. * @@ -99,7 +99,7 @@ struct CESIUM3DTILES_API ClassProperty final /** * @brief Enum ID as declared in the `enums` dictionary. Required when `type` - * is `ENUM`. + * is `ENUM`. Disallowed when `type` is not `ENUM` */ std::optional enumType; @@ -129,14 +129,16 @@ struct CESIUM3DTILES_API ClassProperty final /** * @brief An offset to apply to property values. Only applicable to `SCALAR`, * `VECN`, and `MATN` types when the component type is `FLOAT32` or `FLOAT64`, - * or when the property is `normalized`. + * or when the property is `normalized`. Not applicable to variable-length + * arrays. */ std::optional offset; /** * @brief A scale to apply to property values. Only applicable to `SCALAR`, * `VECN`, and `MATN` types when the component type is `FLOAT32` or `FLOAT64`, - * or when the property is `normalized`. + * or when the property is `normalized`. Not applicable to variable-length + * arrays. */ std::optional scale; @@ -144,7 +146,7 @@ struct CESIUM3DTILES_API ClassProperty final * @brief Maximum allowed value for the property. Only applicable to `SCALAR`, * `VECN`, and `MATN` types. This is the maximum of all property values, after * the transforms based on the `normalized`, `offset`, and `scale` properties - * have been applied. + * have been applied. Not applicable to variable-length arrays. */ std::optional max; @@ -152,7 +154,7 @@ struct CESIUM3DTILES_API ClassProperty final * @brief Minimum allowed value for the property. Only applicable to `SCALAR`, * `VECN`, and `MATN` types. This is the minimum of all property values, after * the transforms based on the `normalized`, `offset`, and `scale` properties - * have been applied. + * have been applied. Not applicable to variable-length arrays. */ std::optional min; diff --git a/Cesium3DTiles/generated/include/Cesium3DTiles/ClassStatistics.h b/Cesium3DTiles/generated/include/Cesium3DTiles/ClassStatistics.h index 456668598..821b8c52e 100644 --- a/Cesium3DTiles/generated/include/Cesium3DTiles/ClassStatistics.h +++ b/Cesium3DTiles/generated/include/Cesium3DTiles/ClassStatistics.h @@ -13,7 +13,8 @@ namespace Cesium3DTiles { /** - * @brief Statistics about entities that conform to a class. + * @brief Statistics about entities that conform to a class that was defined in + * a metadata schema. */ struct CESIUM3DTILES_API ClassStatistics final : public CesiumUtility::ExtensibleObject { diff --git a/Cesium3DTiles/generated/include/Cesium3DTiles/Properties.h b/Cesium3DTiles/generated/include/Cesium3DTiles/Properties.h index 353ed9921..86affa635 100644 --- a/Cesium3DTiles/generated/include/Cesium3DTiles/Properties.h +++ b/Cesium3DTiles/generated/include/Cesium3DTiles/Properties.h @@ -16,13 +16,13 @@ struct CESIUM3DTILES_API Properties final /** * @brief The maximum value of this property of all the features in the - * tileset. + * tileset. The maximum value shall not be larger than the minimum value. */ double maximum = double(); /** * @brief The minimum value of this property of all the features in the - * tileset. + * tileset. The maximum value shall not be larger than the minimum value. */ double minimum = double(); }; diff --git a/Cesium3DTiles/generated/include/Cesium3DTiles/Statistics.h b/Cesium3DTiles/generated/include/Cesium3DTiles/Statistics.h index 2fb0b6bba..121f595a4 100644 --- a/Cesium3DTiles/generated/include/Cesium3DTiles/Statistics.h +++ b/Cesium3DTiles/generated/include/Cesium3DTiles/Statistics.h @@ -19,8 +19,9 @@ struct CESIUM3DTILES_API Statistics final /** * @brief A dictionary, where each key corresponds to a class ID in the - * `classes` dictionary and each value is an object containing statistics - * about entities that conform to the class. + * `classes` dictionary of the metatata schema that was defined for the + * tileset that contains these statistics. Each value is an object containing + * statistics about entities that conform to the class. */ std::unordered_map classes; }; diff --git a/Cesium3DTiles/generated/include/Cesium3DTiles/Tileset.h b/Cesium3DTiles/generated/include/Cesium3DTiles/Tileset.h index 2746daedf..0b0b7ea1b 100644 --- a/Cesium3DTiles/generated/include/Cesium3DTiles/Tileset.h +++ b/Cesium3DTiles/generated/include/Cesium3DTiles/Tileset.h @@ -84,6 +84,7 @@ struct CESIUM3DTILES_API Tileset final /** * @brief Names of 3D Tiles extensions required to properly load this tileset. + * Each element of this array shall also be contained in `extensionsUsed`. */ std::vector extensionsRequired; }; diff --git a/CesiumAsync/include/CesiumAsync/Impl/RemoveFuture.h b/CesiumAsync/include/CesiumAsync/Impl/RemoveFuture.h index 0602ac619..dc93a4bf4 100644 --- a/CesiumAsync/include/CesiumAsync/Impl/RemoveFuture.h +++ b/CesiumAsync/include/CesiumAsync/Impl/RemoveFuture.h @@ -11,14 +11,24 @@ namespace CesiumImpl { // Begin omitting doxgen warnings for Impl namespace //! @cond Doxygen_Suppress -template struct RemoveFuture { typedef T type; }; -template struct RemoveFuture> { typedef T type; }; -template struct RemoveFuture> { typedef T type; }; -template struct RemoveFuture> { typedef T type; }; +template struct RemoveFuture { + typedef T type; +}; +template struct RemoveFuture> { + typedef T type; +}; +template struct RemoveFuture> { + typedef T type; +}; +template struct RemoveFuture> { + typedef T type; +}; template struct RemoveFuture> { typedef T type; }; -template struct RemoveFuture> { typedef T type; }; +template struct RemoveFuture> { + typedef T type; +}; template struct RemoveFuture> { typedef T type; }; diff --git a/CesiumGltf/include/CesiumGltf/FeatureTexturePropertyView.h b/CesiumGltf/include/CesiumGltf/FeatureTexturePropertyView.h index bbe87a546..adc6942a6 100644 --- a/CesiumGltf/include/CesiumGltf/FeatureTexturePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/FeatureTexturePropertyView.h @@ -94,7 +94,9 @@ struct FeatureTexturePropertyChannelOffsets { * @tparam T The component type, must correspond to a valid * {@link FeatureTexturePropertyComponentType}. */ -template struct FeatureTexturePropertyValue { T components[4]; }; +template struct FeatureTexturePropertyValue { + T components[4]; +}; /** * @brief A view of the data specified by a property from a diff --git a/package-lock.json b/package-lock.json index 417e9d5c4..cc1500034 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,21 +1,21 @@ { "name": "cesium-native", - "version": "0.21.3", - "lockfileVersion": 2, + "version": "0.26.0", + "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "cesium-native", - "version": "0.21.3", + "version": "0.26.0", "license": "Apache-2.0", "devDependencies": { "clang-format": "^1.5.0" } }, "node_modules/async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", "dev": true }, "node_modules/balanced-match": { @@ -35,12 +35,12 @@ } }, "node_modules/clang-format": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/clang-format/-/clang-format-1.6.0.tgz", - "integrity": "sha512-W3/L7fWkA8DoLkz9UGjrRnNi+J5a5TuS2HDLqk6WsicpOzb66MBu4eY/EcXhicHriVnAXWQVyk5/VeHWY6w4ow==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/clang-format/-/clang-format-1.8.0.tgz", + "integrity": "sha512-pK8gzfu55/lHzIpQ1givIbWfn3eXnU7SfxqIwVgnn5jEM6j4ZJYjpFqFs4iSBPNedzRMmfjYjuQhu657WAXHXw==", "dev": true, "dependencies": { - "async": "^1.5.2", + "async": "^3.2.3", "glob": "^7.0.0", "resolve": "^1.1.6" }, @@ -53,13 +53,13 @@ "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, "node_modules/function-bind": { @@ -69,15 +69,15 @@ "dev": true }, "node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" }, @@ -103,7 +103,7 @@ "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dev": true, "dependencies": { "once": "^1.3.0", @@ -117,9 +117,9 @@ "dev": true }, "node_modules/is-core-module": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", - "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz", + "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==", "dev": true, "dependencies": { "has": "^1.0.3" @@ -129,9 +129,9 @@ } }, "node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "dependencies": { "brace-expansion": "^1.1.7" @@ -143,7 +143,7 @@ "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, "dependencies": { "wrappy": "1" @@ -152,7 +152,7 @@ "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, "engines": { "node": ">=0.10.0" @@ -165,12 +165,12 @@ "dev": true }, "node_modules/resolve": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", - "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", + "version": "1.22.4", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz", + "integrity": "sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==", "dev": true, "dependencies": { - "is-core-module": "^2.8.1", + "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -196,161 +196,7 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - } - }, - "dependencies": { - "async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", - "dev": true - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "clang-format": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/clang-format/-/clang-format-1.6.0.tgz", - "integrity": "sha512-W3/L7fWkA8DoLkz9UGjrRnNi+J5a5TuS2HDLqk6WsicpOzb66MBu4eY/EcXhicHriVnAXWQVyk5/VeHWY6w4ow==", - "dev": true, - "requires": { - "async": "^1.5.2", - "glob": "^7.0.0", - "resolve": "^1.1.6" - } - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "is-core-module": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", - "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "resolve": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", - "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", - "dev": true, - "requires": { - "is-core-module": "^2.8.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - }, - "supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true } } diff --git a/tools/generate-classes/package-lock.json b/tools/generate-classes/package-lock.json index 6ebb71d0c..dc395172f 100644 --- a/tools/generate-classes/package-lock.json +++ b/tools/generate-classes/package-lock.json @@ -1,236 +1,384 @@ { "name": "generate-classes", "version": "1.0.0", - "lockfileVersion": 1, + "lockfileVersion": 3, "requires": true, - "dependencies": { - "@types/concat-stream": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@types/concat-stream/-/concat-stream-1.6.0.tgz", - "integrity": "sha1-OU2+C7X+5Gs42JZzXoto7yOQ0A0=", - "requires": { + "packages": { + "": { + "name": "generate-classes", + "version": "1.0.0", + "license": "UNLICENSED", + "dependencies": { + "lodash": "^4.17.20", + "sync-request": "^6.1.0", + "yargs": "^16.2.0" + }, + "devDependencies": { + "prettier": "^2.2.1" + } + }, + "node_modules/@types/concat-stream": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@types/concat-stream/-/concat-stream-1.6.1.tgz", + "integrity": "sha512-eHE4cQPoj6ngxBZMvVf6Hw7Mh4jMW4U9lpGmS5GBPB9RYxlFg+CHaVN7ErNY4W9XfLIEn20b4VDYaIrbq0q4uA==", + "dependencies": { "@types/node": "*" } }, - "@types/form-data": { + "node_modules/@types/form-data": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-0.0.33.tgz", - "integrity": "sha1-yayFsqX9GENbjIXZ7LUObWyJP/g=", - "requires": { + "integrity": "sha512-8BSvG1kGm83cyJITQMZSulnl6QV8jqAGreJsc5tPu1Jq0vTSOiY/k24Wx82JRpWwZSqrala6sd5rWi6aNXvqcw==", + "dependencies": { "@types/node": "*" } }, - "@types/node": { - "version": "10.17.51", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.51.tgz", - "integrity": "sha512-KANw+MkL626tq90l++hGelbl67irOJzGhUJk6a1Bt8QHOeh9tztJx+L0AqttraWKinmZn7Qi5lJZJzx45Gq0dg==" - }, - "@types/qs": { - "version": "6.9.5", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.5.tgz", - "integrity": "sha512-/JHkVHtx/REVG0VVToGRGH2+23hsYLHdyG+GrvoUGlGAd0ErauXDyvHtRI/7H7mzLm+tBCKA7pfcpkQ1lf58iQ==" - }, - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" + "node_modules/@types/node": { + "version": "10.17.60", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz", + "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==" + }, + "node_modules/@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } }, - "ansi-styles": { + "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { + "dependencies": { "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "asap": { + "node_modules/asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" }, - "asynckit": { + "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, - "buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "caseless": { + "node_modules/caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" }, - "cliui": { + "node_modules/cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "requires": { + "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^7.0.0" } }, - "color-convert": { + "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { + "dependencies": { "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, - "color-name": { + "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, - "combined-stream": { + "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "requires": { + "dependencies": { "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" } }, - "concat-stream": { + "node_modules/concat-stream": { "version": "1.6.2", "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "requires": { + "engines": [ + "node >= 0.8" + ], + "dependencies": { "buffer-from": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^2.2.2", "typedarray": "^0.0.6" } }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" }, - "delayed-stream": { + "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } }, - "emoji-regex": { + "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, - "escalade": { + "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "engines": { + "node": ">=6" + } }, - "form-data": { + "node_modules/form-data": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", - "requires": { + "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.6", "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" } }, - "get-caller-file": { + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", + "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "get-port": { + "node_modules/get-port": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/get-port/-/get-port-3.2.0.tgz", - "integrity": "sha1-3Xzn3hh8Bsi/NTeWrHHgmfCYDrw=" + "integrity": "sha512-x5UJKlgeUiNT8nyo/AcnwLnZuZNcSjSw0kogRB+Whd1fjjFq4B1hySFxSFWWSn4mIBzg3sRNUDFYc4g5gjPoLg==", + "engines": { + "node": ">=4" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "http-basic": { + "node_modules/http-basic": { "version": "8.1.3", "resolved": "https://registry.npmjs.org/http-basic/-/http-basic-8.1.3.tgz", "integrity": "sha512-/EcDMwJZh3mABI2NhGfHOGOeOZITqfkEO4p/xK+l3NpyncIHUQBoMvCSF/b5GqvKtySC2srL/GGG3+EtlqlmCw==", - "requires": { + "dependencies": { "caseless": "^0.12.0", "concat-stream": "^1.6.2", "http-response-object": "^3.0.1", "parse-cache-control": "^1.0.1" + }, + "engines": { + "node": ">=6.0.0" } }, - "http-response-object": { + "node_modules/http-response-object": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/http-response-object/-/http-response-object-3.0.2.tgz", "integrity": "sha512-bqX0XTF6fnXSQcEJ2Iuyr75yVakyjIDCqroJQ/aHfSdlM743Cwqoi2nDYMzLGWUcuTWGWy8AAvOKXTfiv6q9RA==", - "requires": { + "dependencies": { "@types/node": "^10.0.3" } }, - "inherits": { + "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, - "is-fullwidth-code-point": { + "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } }, - "isarray": { + "node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } }, - "mime-db": { - "version": "1.45.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.45.0.tgz", - "integrity": "sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w==" + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } }, - "mime-types": { - "version": "2.1.28", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.28.tgz", - "integrity": "sha512-0TO2yJ5YHYr7M2zzT7gDU1tbwHxEUWBCLt0lscSNpcdAfFyJOVEpRYNS7EXVcTLNj/25QO8gulHC5JtTzSE2UQ==", - "requires": { - "mime-db": "1.45.0" + "node_modules/object-inspect": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "parse-cache-control": { + "node_modules/parse-cache-control": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parse-cache-control/-/parse-cache-control-1.0.1.tgz", - "integrity": "sha1-juqz5U+laSD+Fro493+iGqzC104=" - }, - "prettier": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz", - "integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==", - "dev": true + "integrity": "sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg==" + }, + "node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } }, - "process-nextick-args": { + "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, - "promise": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/promise/-/promise-8.1.0.tgz", - "integrity": "sha512-W04AqnILOL/sPRXziNicCjSNRruLAuIHEOVBazepu0545DDNGYHz7ar9ZgZ1fMU8/MA4mVxp5rkBWRi6OXIy3Q==", - "requires": { + "node_modules/promise": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz", + "integrity": "sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==", + "dependencies": { "asap": "~2.0.6" } }, - "qs": { - "version": "6.9.6", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz", - "integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ==" + "node_modules/qs": { + "version": "6.11.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz", + "integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", @@ -240,65 +388,90 @@ "util-deprecate": "~1.0.1" } }, - "require-directory": { + "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "engines": { + "node": ">=0.10.0" + } }, - "safe-buffer": { + "node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, - "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "string_decoder": { + "node_modules/string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { + "dependencies": { "safe-buffer": "~5.1.0" } }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "requires": { - "ansi-regex": "^5.0.0" + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, - "sync-request": { + "node_modules/sync-request": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/sync-request/-/sync-request-6.1.0.tgz", "integrity": "sha512-8fjNkrNlNCrVc/av+Jn+xxqfCjYaBoHqCsDz6mt030UMxJGr+GSfCV1dQt2gRtlL63+VPidwDVLr7V2OcTSdRw==", - "requires": { + "dependencies": { "http-response-object": "^3.0.1", "sync-rpc": "^1.2.1", "then-request": "^6.0.0" + }, + "engines": { + "node": ">=8.0.0" } }, - "sync-rpc": { + "node_modules/sync-rpc": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/sync-rpc/-/sync-rpc-1.3.6.tgz", "integrity": "sha512-J8jTXuZzRlvU7HemDgHi3pGnh/rkoqR/OZSjhTyyZrEkkYQbk7Z33AXp37mkPfPpfdOuj7Ex3H/TJM1z48uPQw==", - "requires": { + "dependencies": { "get-port": "^3.1.0" } }, - "then-request": { + "node_modules/then-request": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/then-request/-/then-request-6.0.2.tgz", "integrity": "sha512-3ZBiG7JvP3wbDzA9iNY5zJQcHL4jn/0BWtXIkagfz7QgOL/LqjCEOBQuJNZfu0XYnv5JhKh+cDxCPM4ILrqruA==", - "requires": { + "dependencies": { "@types/concat-stream": "^1.6.0", "@types/form-data": "0.0.33", "@types/node": "^8.0.0", @@ -311,44 +484,54 @@ "promise": "^8.0.0", "qs": "^6.4.0" }, - "dependencies": { - "@types/node": { - "version": "8.10.66", - "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.66.tgz", - "integrity": "sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw==" - } + "engines": { + "node": ">=6.0.0" } }, - "typedarray": { + "node_modules/then-request/node_modules/@types/node": { + "version": "8.10.66", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.66.tgz", + "integrity": "sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw==" + }, + "node_modules/typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" }, - "util-deprecate": { + "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, - "wrap-ansi": { + "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "requires": { + "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "y18n": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.5.tgz", - "integrity": "sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg==" + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } }, - "yargs": { + "node_modules/yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "requires": { + "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", @@ -356,12 +539,18 @@ "string-width": "^4.2.0", "y18n": "^5.0.5", "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" } }, - "yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==" + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "engines": { + "node": ">=10" + } } } } From a74caa6439803e59d4d5e11a79057c784e5020f7 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 16 Aug 2023 13:54:42 +1000 Subject: [PATCH 163/421] Capture unknown properties in an "unsupported" JsonObject. --- .../test/TestTilesetReader.cpp | 63 +++ .../tileset-with-unsupported-properties.json | 428 ++++++++++++++++++ .../ExtensibleObjectJsonHandler.h | 1 + .../src/ExtensibleObjectJsonHandler.cpp | 7 +- .../include/CesiumUtility/ExtensibleObject.h | 9 + 5 files changed, 507 insertions(+), 1 deletion(-) create mode 100644 Cesium3DTilesReader/test/data/tileset-with-unsupported-properties.json diff --git a/Cesium3DTilesReader/test/TestTilesetReader.cpp b/Cesium3DTilesReader/test/TestTilesetReader.cpp index eeb89afb5..d9c244456 100644 --- a/Cesium3DTilesReader/test/TestTilesetReader.cpp +++ b/Cesium3DTilesReader/test/TestTilesetReader.cpp @@ -310,3 +310,66 @@ TEST_CASE("Reads custom extension") { auto& zeroExtensions = withoutCustomExt.tileset->extensions; REQUIRE(zeroExtensions.empty()); } + +TEST_CASE("Reads tileset JSON with unsupported properties") { + using namespace std::string_literals; + + std::filesystem::path tilesetFile = Cesium3DTilesReader_TEST_DATA_DIR; + tilesetFile /= "tileset-with-unsupported-properties.json"; + std::vector data = readFile(tilesetFile); + Cesium3DTilesReader::TilesetReader reader; + Cesium3DTilesReader::TilesetReaderResult result = reader.readTileset(data); + CHECK(result.errors.empty()); + CHECK(result.warnings.empty()); + REQUIRE(result.tileset); + + const CesiumUtility::JsonValue::Object& unsupported = + result.tileset->asset.unsupported; + + auto itString = unsupported.find("someString"); + REQUIRE(itString != unsupported.end()); + REQUIRE(itString->second.isString()); + REQUIRE(itString->second.getString() == "A"); + + auto itDouble = unsupported.find("someDouble"); + REQUIRE(itDouble != unsupported.end()); + REQUIRE(itDouble->second.isDouble()); + REQUIRE(itDouble->second.getDouble() == 2.1); + + auto itInt = unsupported.find("someInt"); + REQUIRE(itInt != unsupported.end()); + REQUIRE(itInt->second.isUint64()); + REQUIRE(itInt->second.getUint64() == 5); + + auto itSignedInt = unsupported.find("someSignedInt"); + REQUIRE(itSignedInt != unsupported.end()); + REQUIRE(itSignedInt->second.isInt64()); + REQUIRE(itSignedInt->second.getInt64() == -5); + + auto itBool = unsupported.find("someBool"); + REQUIRE(itBool != unsupported.end()); + REQUIRE(itBool->second.isBool()); + REQUIRE(itBool->second.getBool() == true); + + auto itArray = unsupported.find("someArray"); + REQUIRE(itArray != unsupported.end()); + REQUIRE(itArray->second.isArray()); + const CesiumUtility::JsonValue::Array& array = itArray->second.getArray(); + REQUIRE(array.size() == 1); + REQUIRE(array[0].isString()); + REQUIRE(array[0].getString() == "hi"); + + auto itObject = unsupported.find("someObject"); + REQUIRE(itObject != unsupported.end()); + REQUIRE(itObject->second.isObject()); + const CesiumUtility::JsonValue::Object& o = itObject->second.getObject(); + REQUIRE(o.size() == 1); + auto itObjectValue = o.find("value"); + REQUIRE(itObjectValue != o.end()); + REQUIRE(itObjectValue->second.isString()); + REQUIRE(itObjectValue->second.getString() == "test"); + + auto itNull = unsupported.find("someNull"); + REQUIRE(itNull != unsupported.end()); + REQUIRE(itNull->second.isNull()); +} diff --git a/Cesium3DTilesReader/test/data/tileset-with-unsupported-properties.json b/Cesium3DTilesReader/test/data/tileset-with-unsupported-properties.json new file mode 100644 index 000000000..5cc5455f8 --- /dev/null +++ b/Cesium3DTilesReader/test/data/tileset-with-unsupported-properties.json @@ -0,0 +1,428 @@ +{ + "asset": { + "someString": "A", + "someDouble": 2.1, + "someInt": 5, + "someSignedInt": -5, + "someBool": true, + "someArray": ["hi"], + "someObject": { + "value": "test" + }, + "someNull": null, + "version" : "1.0" + }, + "properties": { + "Longitude": { + "minimum": -0.0005589940528287436, + "maximum": 0.0001096066770252439 + }, + "Latitude": { + "minimum": 0.8987242766850329, + "maximum": 0.899060112939701 + }, + "Height": { + "minimum": 1, + "maximum": 241.6 + } + }, + "geometricError": 494.50961650991815, + "root": { + "boundingVolume": { + "region": [ + -0.0005682966577418737, + 0.8987233516605286, + 0.00011646582098558159, + 0.8990603398325034, + 0, + 241.6 + ] + }, + "content": { + "boundingVolume": { + "region": [ + -0.0004001690908972599, + 0.8988700116775743, + 0.00010096729722787196, + 0.8989625664878067, + 0, + 241.6 + ] + }, + "uri": "0/0/0.b3dm" + }, + "geometricError": 268.37878244706053, + "refine": "ADD", + "children": [ + { + "boundingVolume": { + "region": [ + -0.0004853062518095434, + 0.898741188925484, + -0.0002736676267127107, + 0.8989037314387226, + 0, + 158.4 + ] + }, + "content": { + "boundingVolume": { + "region": [ + -0.0004058588642587614, + 0.898746512179703, + -0.0002736676267127107, + 0.8989037314387226, + 0, + 158.4 + ] + }, + "uri": "1/0/0.b3dm" + }, + "geometricError": 159.43385994848, + "children": [ + { + "boundingVolume": { + "region": [ + -0.0004853062518095434, + 0.898741188925484, + -0.0003930656008416433, + 0.898818995703538, + 0, + 66.7 + ] + }, + "content": { + "uri": "2/0/0.b3dm" + }, + "geometricError": 10.831613588830955 + }, + { + "boundingVolume": { + "region": [ + -0.0003984063083527456, + 0.8987434753068045, + -0.00028070130359824817, + 0.8988027117816164, + 0, + 48.2 + ] + }, + "content": { + "uri": "2/1/0.b3dm" + }, + "geometricError": 11.833855250694043 + }, + { + "boundingVolume": { + "region": [ + -0.00039631191325035245, + 0.8988008442793176, + -0.000280491864088009, + 0.8989002407802179, + 0, + 78.1 + ] + }, + "content": { + "uri": "2/1/1.b3dm" + }, + "geometricError": 24.187299340965403 + }, + { + "boundingVolume": { + "region": [ + -0.00047979101137324135, + 0.8988092742196048, + -0.0003937113726648811, + 0.898901462510695, + 0, + 122.2 + ] + }, + "content": { + "uri": "2/0/1.b3dm" + }, + "geometricError": 48.508446081365975 + } + ] + }, + { + "boundingVolume": { + "region": [ + -0.0002874033679259065, + 0.8987233516605286, + 0.00009827949017980081, + 0.8988939226883266, + 0, + 75.2 + ] + }, + "content": { + "boundingVolume": { + "region": [ + -0.00028230700651008276, + 0.8987309438427749, + -0.00011402236003278958, + 0.8988939226883266, + 0, + 75.2 + ] + }, + "uri": "1/1/0.b3dm" + }, + "geometricError": 132.82048511777703, + "children": [ + { + "boundingVolume": { + "region": [ + -0.0002874033679259065, + 0.8987293381398633, + -0.00018024015185345448, + 0.8987995352823785, + 0, + 38.1 + ] + }, + "content": { + "uri": "2/2/0.b3dm" + }, + "geometricError": 4.206139430532202 + }, + { + "boundingVolume": { + "region": [ + -0.00018407987620784196, + 0.8987233516605286, + -0.0000894132175796695, + 0.8987836876927705, + 0, + 72.9 + ] + }, + "content": { + "uri": "2/3/0.b3dm" + }, + "geometricError": 0 + }, + { + "boundingVolume": { + "region": [ + -0.00018493508754131914, + 0.8987896218122265, + 0.00009827949017980081, + 0.8988920377327339, + 0, + 46.9 + ] + }, + "content": { + "uri": "2/3/1.b3dm" + }, + "geometricError": 0 + }, + { + "boundingVolume": { + "region": [ + -0.00027722809838677943, + 0.8987969696483782, + -0.00017832028967626075, + 0.8988916014004213, + 0, + 55.4 + ] + }, + "content": { + "uri": "2/2/1.b3dm" + }, + "geometricError": 0 + } + ] + }, + { + "boundingVolume": { + "region": [ + -0.0002821848334624433, + 0.8988867144785156, + 0.00011646582098558159, + 0.8990603398325034, + 0, + 158 + ] + }, + "content": { + "boundingVolume": { + "region": [ + -0.0002782229360604159, + 0.8989292306990948, + 0.000006946410422937427, + 0.899046220118855, + 0, + 158 + ] + }, + "uri": "1/1/1.b3dm" + }, + "geometricError": 156.46285780389445, + "children": [ + { + "boundingVolume": { + "region": [ + -0.00027865926837341453, + 0.8988880758353316, + -0.00014501940754820897, + 0.8989746092596459, + 0, + 77.3 + ] + }, + "content": { + "uri": "2/2/2.b3dm" + }, + "geometricError": 0 + }, + { + "boundingVolume": { + "region": [ + -0.00015598007525073333, + 0.8988867144785156, + 0.00011646582098558159, + 0.8989826028676196, + 0, + 106.2 + ] + }, + "content": { + "uri": "2/3/2.b3dm" + }, + "geometricError": 0 + }, + { + "boundingVolume": { + "region": [ + -0.00015252432333178438, + 0.8989769130942584, + 0.00003328342883553189, + 0.8990603398325034, + 0, + 67.9 + ] + }, + "content": { + "uri": "2/3/3.b3dm" + }, + "geometricError": 8.010233984367021 + }, + { + "boundingVolume": { + "region": [ + -0.0002821848334624433, + 0.8989765465751156, + -0.0001477072145962801, + 0.899040914317929, + 0, + 76 + ] + }, + "content": { + "uri": "2/2/3.b3dm" + }, + "geometricError": 40.38435697163592 + } + ] + }, + { + "boundingVolume": { + "region": [ + -0.0005682966577418737, + 0.8989007643789939, + -0.0002669481090925327, + 0.8990582279841088, + 0, + 204 + ] + }, + "content": { + "boundingVolume": { + "region": [ + -0.0005526410543514849, + 0.8989100669839071, + -0.0002669481090925327, + 0.8990037911647392, + 0, + 204 + ] + }, + "uri": "1/0/1.b3dm" + }, + "geometricError": 149.600454457028, + "children": [ + { + "boundingVolume": { + "region": [ + -0.0005474399731805417, + 0.8989017068567899, + -0.00040917498983755046, + 0.8990014698768336, + 0, + 81.2 + ] + }, + "content": { + "uri": "2/0/2.b3dm" + }, + "geometricError": 0 + }, + { + "boundingVolume": { + "region": [ + -0.00041203732981082115, + 0.8989007643789939, + -0.00027176521782803744, + 0.8989922894449685, + 0, + 108.7 + ] + }, + "content": { + "uri": "2/1/2.b3dm" + }, + "geometricError": 0 + }, + { + "boundingVolume": { + "region": [ + -0.0004253716452960582, + 0.8989891478523147, + -0.0002760587277879431, + 0.8990362368355337, + 0, + 30.1 + ] + }, + "content": { + "uri": "2/1/3.b3dm" + }, + "geometricError": 18.837170280352364 + }, + { + "boundingVolume": { + "region": [ + -0.0005682966577418737, + 0.8989984853638134, + -0.000407220221075317, + 0.8990582279841088, + 0, + 53.3 + ] + }, + "content": { + "uri": "2/0/3.b3dm" + }, + "geometricError": 67.4774528507299 + } + ] + } + ] + } +} + diff --git a/CesiumJsonReader/include/CesiumJsonReader/ExtensibleObjectJsonHandler.h b/CesiumJsonReader/include/CesiumJsonReader/ExtensibleObjectJsonHandler.h index d4b4cf295..f4cfd9649 100644 --- a/CesiumJsonReader/include/CesiumJsonReader/ExtensibleObjectJsonHandler.h +++ b/CesiumJsonReader/include/CesiumJsonReader/ExtensibleObjectJsonHandler.h @@ -29,5 +29,6 @@ class ExtensibleObjectJsonHandler : public CesiumJsonReader::ObjectJsonHandler { CesiumJsonReader::JsonObjectJsonHandler> _extras; ExtensionsJsonHandler _extensions; + JsonObjectJsonHandler _unsupported; }; } // namespace CesiumJsonReader diff --git a/CesiumJsonReader/src/ExtensibleObjectJsonHandler.cpp b/CesiumJsonReader/src/ExtensibleObjectJsonHandler.cpp index 920884049..5a6d1c8e0 100644 --- a/CesiumJsonReader/src/ExtensibleObjectJsonHandler.cpp +++ b/CesiumJsonReader/src/ExtensibleObjectJsonHandler.cpp @@ -29,6 +29,11 @@ IJsonHandler* ExtensibleObjectJsonHandler::readObjectKeyExtensibleObject( return &this->_extensions; } - return this->ignoreAndContinue(); + // Add this unknown property to the unsupported dictionary. + auto it = o.unsupported.emplace(str, CesiumUtility::JsonValue()).first; + this->setCurrentKey(it->first.c_str()); + CesiumUtility::JsonValue& value = it->second; + this->_unsupported.reset(this, &value); + return &this->_unsupported; } } // namespace CesiumJsonReader diff --git a/CesiumUtility/include/CesiumUtility/ExtensibleObject.h b/CesiumUtility/include/CesiumUtility/ExtensibleObject.h index 803fcbcf0..a205bcca2 100644 --- a/CesiumUtility/include/CesiumUtility/ExtensibleObject.h +++ b/CesiumUtility/include/CesiumUtility/ExtensibleObject.h @@ -95,5 +95,14 @@ struct CESIUMUTILITY_API ExtensibleObject { * best portability. */ JsonValue::Object extras; + + /** + * @brief Unsupported properties that exist on this object but do not have any + * representation in the statically-typed classes. + * + * These properties may be invalid, or they may represent deprecated, + * experimental, or next-version properties. + */ + JsonValue::Object unsupported; }; } // namespace CesiumUtility From 5d70051a7863da7ff392bbc730b084773d36024d Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 16 Aug 2023 14:45:51 +1000 Subject: [PATCH 164/421] Making storing unknown properties optional. --- .../test/TestTilesetReader.cpp | 56 ++++++++++++------- .../ExtensibleObjectJsonHandler.h | 3 +- .../CesiumJsonReader/ExtensionReaderContext.h | 21 +++++++ .../src/ExtensibleObjectJsonHandler.cpp | 24 +++++--- .../include/CesiumUtility/ExtensibleObject.h | 4 +- 5 files changed, 79 insertions(+), 29 deletions(-) diff --git a/Cesium3DTilesReader/test/TestTilesetReader.cpp b/Cesium3DTilesReader/test/TestTilesetReader.cpp index d9c244456..794835c34 100644 --- a/Cesium3DTilesReader/test/TestTilesetReader.cpp +++ b/Cesium3DTilesReader/test/TestTilesetReader.cpp @@ -311,7 +311,7 @@ TEST_CASE("Reads custom extension") { REQUIRE(zeroExtensions.empty()); } -TEST_CASE("Reads tileset JSON with unsupported properties") { +TEST_CASE("Reads tileset JSON with unknown properties") { using namespace std::string_literals; std::filesystem::path tilesetFile = Cesium3DTilesReader_TEST_DATA_DIR; @@ -323,44 +323,44 @@ TEST_CASE("Reads tileset JSON with unsupported properties") { CHECK(result.warnings.empty()); REQUIRE(result.tileset); - const CesiumUtility::JsonValue::Object& unsupported = - result.tileset->asset.unsupported; + const CesiumUtility::JsonValue::Object& unknownProperties = + result.tileset->asset.unknownProperties; - auto itString = unsupported.find("someString"); - REQUIRE(itString != unsupported.end()); + auto itString = unknownProperties.find("someString"); + REQUIRE(itString != unknownProperties.end()); REQUIRE(itString->second.isString()); REQUIRE(itString->second.getString() == "A"); - auto itDouble = unsupported.find("someDouble"); - REQUIRE(itDouble != unsupported.end()); + auto itDouble = unknownProperties.find("someDouble"); + REQUIRE(itDouble != unknownProperties.end()); REQUIRE(itDouble->second.isDouble()); REQUIRE(itDouble->second.getDouble() == 2.1); - auto itInt = unsupported.find("someInt"); - REQUIRE(itInt != unsupported.end()); + auto itInt = unknownProperties.find("someInt"); + REQUIRE(itInt != unknownProperties.end()); REQUIRE(itInt->second.isUint64()); REQUIRE(itInt->second.getUint64() == 5); - auto itSignedInt = unsupported.find("someSignedInt"); - REQUIRE(itSignedInt != unsupported.end()); + auto itSignedInt = unknownProperties.find("someSignedInt"); + REQUIRE(itSignedInt != unknownProperties.end()); REQUIRE(itSignedInt->second.isInt64()); REQUIRE(itSignedInt->second.getInt64() == -5); - auto itBool = unsupported.find("someBool"); - REQUIRE(itBool != unsupported.end()); + auto itBool = unknownProperties.find("someBool"); + REQUIRE(itBool != unknownProperties.end()); REQUIRE(itBool->second.isBool()); REQUIRE(itBool->second.getBool() == true); - auto itArray = unsupported.find("someArray"); - REQUIRE(itArray != unsupported.end()); + auto itArray = unknownProperties.find("someArray"); + REQUIRE(itArray != unknownProperties.end()); REQUIRE(itArray->second.isArray()); const CesiumUtility::JsonValue::Array& array = itArray->second.getArray(); REQUIRE(array.size() == 1); REQUIRE(array[0].isString()); REQUIRE(array[0].getString() == "hi"); - auto itObject = unsupported.find("someObject"); - REQUIRE(itObject != unsupported.end()); + auto itObject = unknownProperties.find("someObject"); + REQUIRE(itObject != unknownProperties.end()); REQUIRE(itObject->second.isObject()); const CesiumUtility::JsonValue::Object& o = itObject->second.getObject(); REQUIRE(o.size() == 1); @@ -369,7 +369,25 @@ TEST_CASE("Reads tileset JSON with unsupported properties") { REQUIRE(itObjectValue->second.isString()); REQUIRE(itObjectValue->second.getString() == "test"); - auto itNull = unsupported.find("someNull"); - REQUIRE(itNull != unsupported.end()); + auto itNull = unknownProperties.find("someNull"); + REQUIRE(itNull != unknownProperties.end()); REQUIRE(itNull->second.isNull()); } + +TEST_CASE("Reads tileset JSON with unknown properties and ignores them when requested") { + using namespace std::string_literals; + + std::filesystem::path tilesetFile = Cesium3DTilesReader_TEST_DATA_DIR; + tilesetFile /= "tileset-with-unsupported-properties.json"; + std::vector data = readFile(tilesetFile); + Cesium3DTilesReader::TilesetReader reader; + reader.getExtensions().setCaptureUnknownProperties(false); + Cesium3DTilesReader::TilesetReaderResult result = reader.readTileset(data); + CHECK(result.errors.empty()); + CHECK(result.warnings.empty()); + REQUIRE(result.tileset); + + const CesiumUtility::JsonValue::Object& unknownProperties = + result.tileset->asset.unknownProperties; + CHECK(unknownProperties.empty()); +} diff --git a/CesiumJsonReader/include/CesiumJsonReader/ExtensibleObjectJsonHandler.h b/CesiumJsonReader/include/CesiumJsonReader/ExtensibleObjectJsonHandler.h index f4cfd9649..d2fc34bc3 100644 --- a/CesiumJsonReader/include/CesiumJsonReader/ExtensibleObjectJsonHandler.h +++ b/CesiumJsonReader/include/CesiumJsonReader/ExtensibleObjectJsonHandler.h @@ -29,6 +29,7 @@ class ExtensibleObjectJsonHandler : public CesiumJsonReader::ObjectJsonHandler { CesiumJsonReader::JsonObjectJsonHandler> _extras; ExtensionsJsonHandler _extensions; - JsonObjectJsonHandler _unsupported; + JsonObjectJsonHandler _unknownProperties; + bool _captureUnknownProperties; }; } // namespace CesiumJsonReader diff --git a/CesiumJsonReader/include/CesiumJsonReader/ExtensionReaderContext.h b/CesiumJsonReader/include/CesiumJsonReader/ExtensionReaderContext.h index 7453a519d..7c6830dba 100644 --- a/CesiumJsonReader/include/CesiumJsonReader/ExtensionReaderContext.h +++ b/CesiumJsonReader/include/CesiumJsonReader/ExtensionReaderContext.h @@ -44,6 +44,26 @@ enum class ExtensionState { class CESIUMJSONREADER_API ExtensionReaderContext { public: + /** + * @brief Gets a value indicating whether the values of unknown properties are + * captured in the {@link ExtensibleObject::unknownProperties} field. + * + * If this is false, unknown properties are completely ignored. + */ + bool getCaptureUnknownProperties() const { + return this->_captureUnknownProperties; + } + + /** + * @brief Sets a value indicating whether the values of unknown properties are + * captured in the {@link ExtensibleObject::unknownProperties} field. + * + * If this is false, unknown properties are completely ignored. + */ + void setCaptureUnknownProperties(bool value) { + this->_captureUnknownProperties = value; + } + /** * @brief Registers an extension for an object. * @@ -119,6 +139,7 @@ class CESIUMJSONREADER_API ExtensionReaderContext { ExtensionNameMap _extensions; std::unordered_map _extensionStates; + bool _captureUnknownProperties = true; }; } // namespace CesiumJsonReader diff --git a/CesiumJsonReader/src/ExtensibleObjectJsonHandler.cpp b/CesiumJsonReader/src/ExtensibleObjectJsonHandler.cpp index 5a6d1c8e0..f98860461 100644 --- a/CesiumJsonReader/src/ExtensibleObjectJsonHandler.cpp +++ b/CesiumJsonReader/src/ExtensibleObjectJsonHandler.cpp @@ -1,5 +1,6 @@ #include "CesiumJsonReader/ExtensibleObjectJsonHandler.h" +#include "CesiumJsonReader/ExtensionReaderContext.h" #include "CesiumJsonReader/ExtensionsJsonHandler.h" #include "CesiumJsonReader/JsonHandler.h" #include "CesiumJsonReader/ObjectJsonHandler.h" @@ -7,7 +8,10 @@ namespace CesiumJsonReader { ExtensibleObjectJsonHandler::ExtensibleObjectJsonHandler( const ExtensionReaderContext& context) noexcept - : ObjectJsonHandler(), _extras(), _extensions(context) {} + : ObjectJsonHandler(), + _extras(), + _extensions(context), + _captureUnknownProperties(context.getCaptureUnknownProperties()) {} void ExtensibleObjectJsonHandler::reset( IJsonHandler* pParent, @@ -29,11 +33,17 @@ IJsonHandler* ExtensibleObjectJsonHandler::readObjectKeyExtensibleObject( return &this->_extensions; } - // Add this unknown property to the unsupported dictionary. - auto it = o.unsupported.emplace(str, CesiumUtility::JsonValue()).first; - this->setCurrentKey(it->first.c_str()); - CesiumUtility::JsonValue& value = it->second; - this->_unsupported.reset(this, &value); - return &this->_unsupported; + if (this->_captureUnknownProperties) { + // Add this unknown property to unknownProperties. + auto it = + o.unknownProperties.emplace(str, CesiumUtility::JsonValue()).first; + this->setCurrentKey(it->first.c_str()); + CesiumUtility::JsonValue& value = it->second; + this->_unknownProperties.reset(this, &value); + return &this->_unknownProperties; + } else { + // Ignore this unknown property. + return this->ignoreAndContinue(); + } } } // namespace CesiumJsonReader diff --git a/CesiumUtility/include/CesiumUtility/ExtensibleObject.h b/CesiumUtility/include/CesiumUtility/ExtensibleObject.h index a205bcca2..187230046 100644 --- a/CesiumUtility/include/CesiumUtility/ExtensibleObject.h +++ b/CesiumUtility/include/CesiumUtility/ExtensibleObject.h @@ -97,12 +97,12 @@ struct CESIUMUTILITY_API ExtensibleObject { JsonValue::Object extras; /** - * @brief Unsupported properties that exist on this object but do not have any + * @brief Unknown properties that exist on this object but do not have any * representation in the statically-typed classes. * * These properties may be invalid, or they may represent deprecated, * experimental, or next-version properties. */ - JsonValue::Object unsupported; + JsonValue::Object unknownProperties; }; } // namespace CesiumUtility From 7c2393fb2e66cd51081065b5888c5f80f50510f4 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 16 Aug 2023 14:49:58 +1000 Subject: [PATCH 165/421] Update CHANGES.md. --- CHANGES.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index cb27b4d7f..d74d65603 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,11 @@ # Change Log +### ? - ? + +##### Additions :tada: + +- Unknown properties in objects read with a `JsonReader` are now stored in the `unknownProperties` property on `ExtensibleObject` by default. To ignore them, as was done in previous versions, call `setCaptureUnknownProperties` on `ExtensionReaderContext`. + ### v0.27.0 - 2023-09-01 ##### Fixes :wrench: From 07d64139a254c4b8b2b85666ffc3b74186b165f1 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 16 Aug 2023 15:22:29 +1000 Subject: [PATCH 166/421] Rename ExtensionReaderContext to JsonReaderOptions. --- CHANGES.md | 6 +- .../generated/src/AssetJsonHandler.h | 5 +- .../generated/src/AvailabilityJsonHandler.h | 4 +- .../generated/src/BoundingVolumeJsonHandler.h | 4 +- .../generated/src/BufferJsonHandler.h | 4 +- .../generated/src/BufferViewJsonHandler.h | 4 +- .../generated/src/ClassJsonHandler.h | 5 +- .../generated/src/ClassPropertyJsonHandler.h | 4 +- .../src/ClassStatisticsJsonHandler.h | 4 +- .../generated/src/ContentJsonHandler.h | 4 +- .../generated/src/EnumJsonHandler.h | 5 +- .../generated/src/EnumValueJsonHandler.h | 4 +- ...ension3dTilesBoundingVolumeS2JsonHandler.h | 4 +- .../generated/src/GeneratedJsonHandlers.cpp | 162 +++---- .../generated/src/GroupMetadataJsonHandler.h | 4 +- .../generated/src/ImplicitTilingJsonHandler.h | 4 +- .../generated/src/MetadataEntityJsonHandler.h | 4 +- .../generated/src/PropertiesJsonHandler.h | 4 +- .../src/PropertyStatisticsJsonHandler.h | 4 +- .../generated/src/PropertyTableJsonHandler.h | 4 +- .../src/PropertyTablePropertyJsonHandler.h | 4 +- .../generated/src/SchemaJsonHandler.h | 4 +- .../generated/src/StatisticsJsonHandler.h | 4 +- .../generated/src/SubtreeJsonHandler.h | 4 +- .../generated/src/SubtreesJsonHandler.h | 4 +- .../generated/src/TileJsonHandler.h | 5 +- .../generated/src/TilesetJsonHandler.h | 4 +- .../generated/src/registerExtensions.cpp | 8 +- .../generated/src/registerExtensions.h | 4 +- .../Cesium3DTilesReader/SchemaReader.h | 14 +- .../Cesium3DTilesReader/SubtreeReader.h | 14 +- .../Cesium3DTilesReader/TilesetReader.h | 14 +- Cesium3DTilesReader/src/SchemaReader.cpp | 10 +- Cesium3DTilesReader/src/SubtreeReader.cpp | 10 +- Cesium3DTilesReader/src/TilesetReader.cpp | 10 +- .../test/TestTilesetReader.cpp | 9 +- .../generated/src/AccessorJsonHandler.h | 4 +- .../src/AccessorSparseIndicesJsonHandler.h | 4 +- .../generated/src/AccessorSparseJsonHandler.h | 4 +- .../src/AccessorSparseValuesJsonHandler.h | 4 +- .../src/AnimationChannelJsonHandler.h | 4 +- .../src/AnimationChannelTargetJsonHandler.h | 4 +- .../generated/src/AnimationJsonHandler.h | 4 +- .../src/AnimationSamplerJsonHandler.h | 4 +- .../generated/src/AssetJsonHandler.h | 5 +- .../generated/src/BufferJsonHandler.h | 4 +- .../generated/src/BufferViewJsonHandler.h | 4 +- .../generated/src/CameraJsonHandler.h | 4 +- .../src/CameraOrthographicJsonHandler.h | 4 +- .../src/CameraPerspectiveJsonHandler.h | 4 +- .../generated/src/ClassJsonHandler.h | 5 +- .../generated/src/ClassPropertyJsonHandler.h | 4 +- .../src/ClassStatisticsJsonHandler.h | 4 +- .../generated/src/EnumJsonHandler.h | 5 +- .../generated/src/EnumValueJsonHandler.h | 4 +- ...onBufferExtMeshoptCompressionJsonHandler.h | 4 +- ...fferViewExtMeshoptCompressionJsonHandler.h | 4 +- .../src/ExtensionCesiumRTCJsonHandler.h | 4 +- .../src/ExtensionCesiumTileEdgesJsonHandler.h | 4 +- ...nExtInstanceFeaturesFeatureIdJsonHandler.h | 4 +- .../ExtensionExtInstanceFeaturesJsonHandler.h | 4 +- ...nsionExtMeshFeaturesFeatureIdJsonHandler.h | 4 +- ...tMeshFeaturesFeatureIdTextureJsonHandler.h | 4 +- .../src/ExtensionExtMeshFeaturesJsonHandler.h | 4 +- ...ExtensionExtMeshGpuInstancingJsonHandler.h | 4 +- ...ionExtStructuralMetadataClassJsonHandler.h | 4 +- ...ructuralMetadataClassPropertyJsonHandler.h | 4 +- ...sionExtStructuralMetadataEnumJsonHandler.h | 4 +- ...xtStructuralMetadataEnumValueJsonHandler.h | 4 +- ...uralMetadataPropertyAttributeJsonHandler.h | 4 +- ...dataPropertyAttributePropertyJsonHandler.h | 4 +- ...ructuralMetadataPropertyTableJsonHandler.h | 4 +- ...MetadataPropertyTablePropertyJsonHandler.h | 4 +- ...cturalMetadataPropertyTextureJsonHandler.h | 4 +- ...tadataPropertyTexturePropertyJsonHandler.h | 4 +- ...onExtStructuralMetadataSchemaJsonHandler.h | 4 +- ...ensionKhrDracoMeshCompressionJsonHandler.h | 4 +- .../ExtensionKhrMaterialsUnlitJsonHandler.h | 4 +- .../ExtensionKhrTextureBasisuJsonHandler.h | 4 +- .../ExtensionKhrTextureTransformJsonHandler.h | 4 +- ...shPrimitiveExtFeatureMetadataJsonHandler.h | 4 +- ...rimitiveExtStructuralMetadataJsonHandler.h | 4 +- ...PrimitiveKhrMaterialsVariantsJsonHandler.h | 4 +- ...aterialsVariantsMappingsValueJsonHandler.h | 4 +- ...ensionModelExtFeatureMetadataJsonHandler.h | 4 +- ...ionModelExtStructuralMetadataJsonHandler.h | 4 +- ...sionModelKhrMaterialsVariantsJsonHandler.h | 4 +- ...odelKhrMaterialsVariantsValueJsonHandler.h | 4 +- ...tensionModelMaxarMeshVariantsJsonHandler.h | 4 +- ...onModelMaxarMeshVariantsValueJsonHandler.h | 4 +- ...xtensionNodeMaxarMeshVariantsJsonHandler.h | 4 +- ...axarMeshVariantsMappingsValueJsonHandler.h | 4 +- .../src/ExtensionTextureWebpJsonHandler.h | 4 +- .../src/FeatureIDAttributeJsonHandler.h | 4 +- .../src/FeatureIDTextureJsonHandler.h | 4 +- .../generated/src/FeatureIDsJsonHandler.h | 4 +- .../generated/src/FeatureTableJsonHandler.h | 4 +- .../src/FeatureTablePropertyJsonHandler.h | 4 +- .../generated/src/FeatureTextureJsonHandler.h | 4 +- .../generated/src/GeneratedJsonHandlers.cpp | 452 +++++++++--------- .../generated/src/ImageJsonHandler.h | 5 +- .../generated/src/MaterialJsonHandler.h | 4 +- .../MaterialNormalTextureInfoJsonHandler.h | 4 +- .../MaterialOcclusionTextureInfoJsonHandler.h | 4 +- .../MaterialPBRMetallicRoughnessJsonHandler.h | 4 +- .../generated/src/MeshJsonHandler.h | 5 +- .../generated/src/MeshPrimitiveJsonHandler.h | 4 +- .../generated/src/ModelJsonHandler.h | 5 +- .../generated/src/NodeJsonHandler.h | 5 +- .../src/PropertyStatisticsJsonHandler.h | 4 +- .../generated/src/SamplerJsonHandler.h | 4 +- .../generated/src/SceneJsonHandler.h | 5 +- .../generated/src/SchemaJsonHandler.h | 4 +- .../generated/src/SkinJsonHandler.h | 5 +- .../generated/src/StatisticsJsonHandler.h | 4 +- .../src/TextureAccessorJsonHandler.h | 4 +- .../generated/src/TextureInfoJsonHandler.h | 4 +- .../generated/src/TextureJsonHandler.h | 4 +- .../generated/src/registerExtensions.cpp | 46 +- .../generated/src/registerExtensions.h | 4 +- .../include/CesiumGltfReader/GltfReader.h | 14 +- CesiumGltfReader/src/GltfReader.cpp | 14 +- .../src/NamedObjectJsonHandler.cpp | 2 +- CesiumGltfReader/src/NamedObjectJsonHandler.h | 2 +- CesiumGltfReader/test/TestGltfReader.cpp | 8 +- .../ExtensibleObjectJsonHandler.h | 4 +- .../CesiumJsonReader/ExtensionsJsonHandler.h | 6 +- ...ionReaderContext.h => JsonReaderOptions.h} | 11 +- .../src/ExtensibleObjectJsonHandler.cpp | 4 +- .../src/ExtensionsJsonHandler.cpp | 2 +- ...eaderContext.cpp => JsonReaderOptions.cpp} | 6 +- tools/generate-classes/generate.js | 6 +- .../generateRegisterExtensions.js | 42 +- 133 files changed, 650 insertions(+), 671 deletions(-) rename CesiumJsonReader/include/CesiumJsonReader/{ExtensionReaderContext.h => JsonReaderOptions.h} (93%) rename CesiumJsonReader/src/{ExtensionReaderContext.cpp => JsonReaderOptions.cpp} (96%) diff --git a/CHANGES.md b/CHANGES.md index d74d65603..e7c982ab1 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,9 +2,13 @@ ### ? - ? +##### Breaking Changes :mega: + +- Renamed `ExtensionReaderContext` to `JsonReaderOptions`, and the `getExtensions` method on various JSON reader classes to `getOptions`. + ##### Additions :tada: -- Unknown properties in objects read with a `JsonReader` are now stored in the `unknownProperties` property on `ExtensibleObject` by default. To ignore them, as was done in previous versions, call `setCaptureUnknownProperties` on `ExtensionReaderContext`. +- Unknown properties in objects read with a `JsonReader` are now stored in the `unknownProperties` property on `ExtensibleObject` by default. To ignore them, as was done in previous versions, call `setCaptureUnknownProperties` on `JsonReaderOptions`. ### v0.27.0 - 2023-09-01 diff --git a/Cesium3DTilesReader/generated/src/AssetJsonHandler.h b/Cesium3DTilesReader/generated/src/AssetJsonHandler.h index c342a466d..6db74f19e 100644 --- a/Cesium3DTilesReader/generated/src/AssetJsonHandler.h +++ b/Cesium3DTilesReader/generated/src/AssetJsonHandler.h @@ -7,7 +7,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace Cesium3DTilesReader { @@ -15,8 +15,7 @@ class AssetJsonHandler : public CesiumJsonReader::ExtensibleObjectJsonHandler { public: using ValueType = Cesium3DTiles::Asset; - AssetJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + AssetJsonHandler(const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, Cesium3DTiles::Asset* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; diff --git a/Cesium3DTilesReader/generated/src/AvailabilityJsonHandler.h b/Cesium3DTilesReader/generated/src/AvailabilityJsonHandler.h index 6bbc10a8e..1b7beca51 100644 --- a/Cesium3DTilesReader/generated/src/AvailabilityJsonHandler.h +++ b/Cesium3DTilesReader/generated/src/AvailabilityJsonHandler.h @@ -7,7 +7,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace Cesium3DTilesReader { @@ -17,7 +17,7 @@ class AvailabilityJsonHandler using ValueType = Cesium3DTiles::Availability; AvailabilityJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, Cesium3DTiles::Availability* pObject); diff --git a/Cesium3DTilesReader/generated/src/BoundingVolumeJsonHandler.h b/Cesium3DTilesReader/generated/src/BoundingVolumeJsonHandler.h index 3a71ae8d8..427ba5ecc 100644 --- a/Cesium3DTilesReader/generated/src/BoundingVolumeJsonHandler.h +++ b/Cesium3DTilesReader/generated/src/BoundingVolumeJsonHandler.h @@ -8,7 +8,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace Cesium3DTilesReader { @@ -18,7 +18,7 @@ class BoundingVolumeJsonHandler using ValueType = Cesium3DTiles::BoundingVolume; BoundingVolumeJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, Cesium3DTiles::BoundingVolume* pObject); diff --git a/Cesium3DTilesReader/generated/src/BufferJsonHandler.h b/Cesium3DTilesReader/generated/src/BufferJsonHandler.h index 5a8a7b3d6..f65f8a0dc 100644 --- a/Cesium3DTilesReader/generated/src/BufferJsonHandler.h +++ b/Cesium3DTilesReader/generated/src/BufferJsonHandler.h @@ -8,7 +8,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace Cesium3DTilesReader { @@ -17,7 +17,7 @@ class BufferJsonHandler : public CesiumJsonReader::ExtensibleObjectJsonHandler { using ValueType = Cesium3DTiles::Buffer; BufferJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, Cesium3DTiles::Buffer* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; diff --git a/Cesium3DTilesReader/generated/src/BufferViewJsonHandler.h b/Cesium3DTilesReader/generated/src/BufferViewJsonHandler.h index fbf3ed763..9aa615ba9 100644 --- a/Cesium3DTilesReader/generated/src/BufferViewJsonHandler.h +++ b/Cesium3DTilesReader/generated/src/BufferViewJsonHandler.h @@ -8,7 +8,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace Cesium3DTilesReader { @@ -18,7 +18,7 @@ class BufferViewJsonHandler using ValueType = Cesium3DTiles::BufferView; BufferViewJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, Cesium3DTiles::BufferView* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; diff --git a/Cesium3DTilesReader/generated/src/ClassJsonHandler.h b/Cesium3DTilesReader/generated/src/ClassJsonHandler.h index 92a754a67..cec450f02 100644 --- a/Cesium3DTilesReader/generated/src/ClassJsonHandler.h +++ b/Cesium3DTilesReader/generated/src/ClassJsonHandler.h @@ -10,7 +10,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace Cesium3DTilesReader { @@ -18,8 +18,7 @@ class ClassJsonHandler : public CesiumJsonReader::ExtensibleObjectJsonHandler { public: using ValueType = Cesium3DTiles::Class; - ClassJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + ClassJsonHandler(const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, Cesium3DTiles::Class* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; diff --git a/Cesium3DTilesReader/generated/src/ClassPropertyJsonHandler.h b/Cesium3DTilesReader/generated/src/ClassPropertyJsonHandler.h index f1620e285..b8975f9a6 100644 --- a/Cesium3DTilesReader/generated/src/ClassPropertyJsonHandler.h +++ b/Cesium3DTilesReader/generated/src/ClassPropertyJsonHandler.h @@ -10,7 +10,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace Cesium3DTilesReader { @@ -20,7 +20,7 @@ class ClassPropertyJsonHandler using ValueType = Cesium3DTiles::ClassProperty; ClassPropertyJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, Cesium3DTiles::ClassProperty* pObject); diff --git a/Cesium3DTilesReader/generated/src/ClassStatisticsJsonHandler.h b/Cesium3DTilesReader/generated/src/ClassStatisticsJsonHandler.h index 76a804e8c..68ce0c809 100644 --- a/Cesium3DTilesReader/generated/src/ClassStatisticsJsonHandler.h +++ b/Cesium3DTilesReader/generated/src/ClassStatisticsJsonHandler.h @@ -10,7 +10,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace Cesium3DTilesReader { @@ -20,7 +20,7 @@ class ClassStatisticsJsonHandler using ValueType = Cesium3DTiles::ClassStatistics; ClassStatisticsJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, Cesium3DTiles::ClassStatistics* pObject); diff --git a/Cesium3DTilesReader/generated/src/ContentJsonHandler.h b/Cesium3DTilesReader/generated/src/ContentJsonHandler.h index 60b1216c7..d48e81184 100644 --- a/Cesium3DTilesReader/generated/src/ContentJsonHandler.h +++ b/Cesium3DTilesReader/generated/src/ContentJsonHandler.h @@ -11,7 +11,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace Cesium3DTilesReader { @@ -21,7 +21,7 @@ class ContentJsonHandler using ValueType = Cesium3DTiles::Content; ContentJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, Cesium3DTiles::Content* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; diff --git a/Cesium3DTilesReader/generated/src/EnumJsonHandler.h b/Cesium3DTilesReader/generated/src/EnumJsonHandler.h index fd778395d..68fa71eae 100644 --- a/Cesium3DTilesReader/generated/src/EnumJsonHandler.h +++ b/Cesium3DTilesReader/generated/src/EnumJsonHandler.h @@ -10,7 +10,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace Cesium3DTilesReader { @@ -18,8 +18,7 @@ class EnumJsonHandler : public CesiumJsonReader::ExtensibleObjectJsonHandler { public: using ValueType = Cesium3DTiles::Enum; - EnumJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + EnumJsonHandler(const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, Cesium3DTiles::Enum* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; diff --git a/Cesium3DTilesReader/generated/src/EnumValueJsonHandler.h b/Cesium3DTilesReader/generated/src/EnumValueJsonHandler.h index ca41d33b2..4b5abc1e8 100644 --- a/Cesium3DTilesReader/generated/src/EnumValueJsonHandler.h +++ b/Cesium3DTilesReader/generated/src/EnumValueJsonHandler.h @@ -8,7 +8,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace Cesium3DTilesReader { @@ -18,7 +18,7 @@ class EnumValueJsonHandler using ValueType = Cesium3DTiles::EnumValue; EnumValueJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, Cesium3DTiles::EnumValue* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; diff --git a/Cesium3DTilesReader/generated/src/Extension3dTilesBoundingVolumeS2JsonHandler.h b/Cesium3DTilesReader/generated/src/Extension3dTilesBoundingVolumeS2JsonHandler.h index 5f74bbe17..dcfee86c0 100644 --- a/Cesium3DTilesReader/generated/src/Extension3dTilesBoundingVolumeS2JsonHandler.h +++ b/Cesium3DTilesReader/generated/src/Extension3dTilesBoundingVolumeS2JsonHandler.h @@ -8,7 +8,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace Cesium3DTilesReader { @@ -22,7 +22,7 @@ class Extension3dTilesBoundingVolumeS2JsonHandler "3DTILES_bounding_volume_S2"; Extension3dTilesBoundingVolumeS2JsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset( IJsonHandler* pParentHandler, Cesium3DTiles::Extension3dTilesBoundingVolumeS2* pObject); diff --git a/Cesium3DTilesReader/generated/src/GeneratedJsonHandlers.cpp b/Cesium3DTilesReader/generated/src/GeneratedJsonHandlers.cpp index 7e2b4db89..9fec23d8c 100644 --- a/Cesium3DTilesReader/generated/src/GeneratedJsonHandlers.cpp +++ b/Cesium3DTilesReader/generated/src/GeneratedJsonHandlers.cpp @@ -11,8 +11,8 @@ namespace Cesium3DTilesReader { Extension3dTilesBoundingVolumeS2JsonHandler:: Extension3dTilesBoundingVolumeS2JsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _token(), _minimumHeight(), _maximumHeight() {} @@ -78,9 +78,9 @@ CesiumJsonReader::IJsonHandler* Extension3dTilesBoundingVolumeS2JsonHandler:: namespace Cesium3DTilesReader { StatisticsJsonHandler::StatisticsJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _classes(context) {} + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), + _classes(options) {} void StatisticsJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, @@ -123,10 +123,10 @@ CesiumJsonReader::IJsonHandler* StatisticsJsonHandler::readObjectKeyStatistics( namespace Cesium3DTilesReader { ClassStatisticsJsonHandler::ClassStatisticsJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _count(), - _properties(context) {} + _properties(options) {} void ClassStatisticsJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, @@ -172,8 +172,8 @@ ClassStatisticsJsonHandler::readObjectKeyClassStatistics( namespace Cesium3DTilesReader { PropertyStatisticsJsonHandler::PropertyStatisticsJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _min(), _max(), _mean(), @@ -242,14 +242,14 @@ PropertyStatisticsJsonHandler::readObjectKeyPropertyStatistics( namespace Cesium3DTilesReader { SchemaJsonHandler::SchemaJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _id(), _name(), _description(), _version(), - _classes(context), - _enums(context) {} + _classes(options), + _enums(options) {} void SchemaJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, @@ -302,12 +302,12 @@ CesiumJsonReader::IJsonHandler* SchemaJsonHandler::readObjectKeySchema( namespace Cesium3DTilesReader { EnumJsonHandler::EnumJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _name(), _description(), _valueType(), - _values(context) {} + _values(options) {} void EnumJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, @@ -356,8 +356,8 @@ CesiumJsonReader::IJsonHandler* EnumJsonHandler::readObjectKeyEnum( namespace Cesium3DTilesReader { EnumValueJsonHandler::EnumValueJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _name(), _description(), _value() {} @@ -407,11 +407,11 @@ CesiumJsonReader::IJsonHandler* EnumValueJsonHandler::readObjectKeyEnumValue( namespace Cesium3DTilesReader { ClassJsonHandler::ClassJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _name(), _description(), - _properties(context) {} + _properties(options) {} void ClassJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, @@ -458,8 +458,8 @@ CesiumJsonReader::IJsonHandler* ClassJsonHandler::readObjectKeyClass( namespace Cesium3DTilesReader { ClassPropertyJsonHandler::ClassPropertyJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _name(), _description(), _type(), @@ -549,17 +549,17 @@ ClassPropertyJsonHandler::readObjectKeyClassProperty( namespace Cesium3DTilesReader { SubtreeJsonHandler::SubtreeJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _buffers(context), - _bufferViews(context), - _propertyTables(context), - _tileAvailability(context), - _contentAvailability(context), - _childSubtreeAvailability(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), + _buffers(options), + _bufferViews(options), + _propertyTables(options), + _tileAvailability(options), + _contentAvailability(options), + _childSubtreeAvailability(options), _tileMetadata(), _contentMetadata(), - _subtreeMetadata(context) {} + _subtreeMetadata(options) {} void SubtreeJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, @@ -633,8 +633,8 @@ CesiumJsonReader::IJsonHandler* SubtreeJsonHandler::readObjectKeySubtree( namespace Cesium3DTilesReader { MetadataEntityJsonHandler::MetadataEntityJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _classProperty(), _properties() {} @@ -682,8 +682,8 @@ MetadataEntityJsonHandler::readObjectKeyMetadataEntity( namespace Cesium3DTilesReader { AvailabilityJsonHandler::AvailabilityJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _bitstream(), _availableCount(), _constant() {} @@ -734,12 +734,12 @@ AvailabilityJsonHandler::readObjectKeyAvailability( namespace Cesium3DTilesReader { PropertyTableJsonHandler::PropertyTableJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _name(), _classProperty(), _count(), - _properties(context) {} + _properties(options) {} void PropertyTableJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, @@ -789,8 +789,8 @@ PropertyTableJsonHandler::readObjectKeyPropertyTable( namespace Cesium3DTilesReader { PropertyTablePropertyJsonHandler::PropertyTablePropertyJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _values(), _arrayOffsets(), _stringOffsets(), @@ -865,8 +865,8 @@ PropertyTablePropertyJsonHandler::readObjectKeyPropertyTableProperty( namespace Cesium3DTilesReader { BufferViewJsonHandler::BufferViewJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _buffer(), _byteOffset(), _byteLength(), @@ -919,8 +919,8 @@ CesiumJsonReader::IJsonHandler* BufferViewJsonHandler::readObjectKeyBufferView( namespace Cesium3DTilesReader { BufferJsonHandler::BufferJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _uri(), _byteLength(), _name() {} @@ -970,17 +970,17 @@ CesiumJsonReader::IJsonHandler* BufferJsonHandler::readObjectKeyBuffer( namespace Cesium3DTilesReader { TilesetJsonHandler::TilesetJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _asset(context), - _properties(context), - _schema(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), + _asset(options), + _properties(options), + _schema(options), _schemaUri(), - _statistics(context), - _groups(context), - _metadata(context), + _statistics(options), + _groups(options), + _metadata(options), _geometricError(), - _root(context), + _root(options), _extensionsUsed(), _extensionsRequired() {} @@ -1048,18 +1048,18 @@ CesiumJsonReader::IJsonHandler* TilesetJsonHandler::readObjectKeyTileset( namespace Cesium3DTilesReader { TileJsonHandler::TileJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _boundingVolume(context), - _viewerRequestVolume(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), + _boundingVolume(options), + _viewerRequestVolume(options), _geometricError(), _refine(), _transform(), - _content(context), - _contents(context), - _metadata(context), - _implicitTiling(context), - _children(context) {} + _content(options), + _contents(options), + _metadata(options), + _implicitTiling(options), + _children(options) {} void TileJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, @@ -1123,12 +1123,12 @@ CesiumJsonReader::IJsonHandler* TileJsonHandler::readObjectKeyTile( namespace Cesium3DTilesReader { ImplicitTilingJsonHandler::ImplicitTilingJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _subdivisionScheme(), _subtreeLevels(), _availableLevels(), - _subtrees(context) {} + _subtrees(options) {} void ImplicitTilingJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, @@ -1184,8 +1184,8 @@ ImplicitTilingJsonHandler::readObjectKeyImplicitTiling( namespace Cesium3DTilesReader { SubtreesJsonHandler::SubtreesJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _uri() {} + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _uri() {} void SubtreesJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, @@ -1228,11 +1228,11 @@ CesiumJsonReader::IJsonHandler* SubtreesJsonHandler::readObjectKeySubtrees( namespace Cesium3DTilesReader { ContentJsonHandler::ContentJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _boundingVolume(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), + _boundingVolume(options), _uri(), - _metadata(context), + _metadata(options), _group() {} void ContentJsonHandler::reset( @@ -1282,8 +1282,8 @@ CesiumJsonReader::IJsonHandler* ContentJsonHandler::readObjectKeyContent( namespace Cesium3DTilesReader { BoundingVolumeJsonHandler::BoundingVolumeJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _box(), _region(), _sphere() {} @@ -1334,8 +1334,8 @@ BoundingVolumeJsonHandler::readObjectKeyBoundingVolume( namespace Cesium3DTilesReader { GroupMetadataJsonHandler::GroupMetadataJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : MetadataEntityJsonHandler(context) {} + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : MetadataEntityJsonHandler(options) {} void GroupMetadataJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, @@ -1378,8 +1378,8 @@ GroupMetadataJsonHandler::readObjectKeyGroupMetadata( namespace Cesium3DTilesReader { PropertiesJsonHandler::PropertiesJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _maximum(), _minimum() {} @@ -1426,8 +1426,8 @@ CesiumJsonReader::IJsonHandler* PropertiesJsonHandler::readObjectKeyProperties( namespace Cesium3DTilesReader { AssetJsonHandler::AssetJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _version(), _tilesetVersion() {} diff --git a/Cesium3DTilesReader/generated/src/GroupMetadataJsonHandler.h b/Cesium3DTilesReader/generated/src/GroupMetadataJsonHandler.h index 93ccdf658..a3ca30596 100644 --- a/Cesium3DTilesReader/generated/src/GroupMetadataJsonHandler.h +++ b/Cesium3DTilesReader/generated/src/GroupMetadataJsonHandler.h @@ -7,7 +7,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace Cesium3DTilesReader { @@ -16,7 +16,7 @@ class GroupMetadataJsonHandler : public MetadataEntityJsonHandler { using ValueType = Cesium3DTiles::GroupMetadata; GroupMetadataJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, Cesium3DTiles::GroupMetadata* pObject); diff --git a/Cesium3DTilesReader/generated/src/ImplicitTilingJsonHandler.h b/Cesium3DTilesReader/generated/src/ImplicitTilingJsonHandler.h index 7f9156e50..16ccd304f 100644 --- a/Cesium3DTilesReader/generated/src/ImplicitTilingJsonHandler.h +++ b/Cesium3DTilesReader/generated/src/ImplicitTilingJsonHandler.h @@ -10,7 +10,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace Cesium3DTilesReader { @@ -20,7 +20,7 @@ class ImplicitTilingJsonHandler using ValueType = Cesium3DTiles::ImplicitTiling; ImplicitTilingJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, Cesium3DTiles::ImplicitTiling* pObject); diff --git a/Cesium3DTilesReader/generated/src/MetadataEntityJsonHandler.h b/Cesium3DTilesReader/generated/src/MetadataEntityJsonHandler.h index da249f9f7..921e0d8cd 100644 --- a/Cesium3DTilesReader/generated/src/MetadataEntityJsonHandler.h +++ b/Cesium3DTilesReader/generated/src/MetadataEntityJsonHandler.h @@ -9,7 +9,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace Cesium3DTilesReader { @@ -19,7 +19,7 @@ class MetadataEntityJsonHandler using ValueType = Cesium3DTiles::MetadataEntity; MetadataEntityJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, Cesium3DTiles::MetadataEntity* pObject); diff --git a/Cesium3DTilesReader/generated/src/PropertiesJsonHandler.h b/Cesium3DTilesReader/generated/src/PropertiesJsonHandler.h index 1701d98b7..09a4073b8 100644 --- a/Cesium3DTilesReader/generated/src/PropertiesJsonHandler.h +++ b/Cesium3DTilesReader/generated/src/PropertiesJsonHandler.h @@ -7,7 +7,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace Cesium3DTilesReader { @@ -17,7 +17,7 @@ class PropertiesJsonHandler using ValueType = Cesium3DTiles::Properties; PropertiesJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, Cesium3DTiles::Properties* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; diff --git a/Cesium3DTilesReader/generated/src/PropertyStatisticsJsonHandler.h b/Cesium3DTilesReader/generated/src/PropertyStatisticsJsonHandler.h index 1f8148734..70570a09b 100644 --- a/Cesium3DTilesReader/generated/src/PropertyStatisticsJsonHandler.h +++ b/Cesium3DTilesReader/generated/src/PropertyStatisticsJsonHandler.h @@ -8,7 +8,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace Cesium3DTilesReader { @@ -18,7 +18,7 @@ class PropertyStatisticsJsonHandler using ValueType = Cesium3DTiles::PropertyStatistics; PropertyStatisticsJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset( IJsonHandler* pParentHandler, Cesium3DTiles::PropertyStatistics* pObject); diff --git a/Cesium3DTilesReader/generated/src/PropertyTableJsonHandler.h b/Cesium3DTilesReader/generated/src/PropertyTableJsonHandler.h index 4de460b82..c09765807 100644 --- a/Cesium3DTilesReader/generated/src/PropertyTableJsonHandler.h +++ b/Cesium3DTilesReader/generated/src/PropertyTableJsonHandler.h @@ -11,7 +11,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace Cesium3DTilesReader { @@ -21,7 +21,7 @@ class PropertyTableJsonHandler using ValueType = Cesium3DTiles::PropertyTable; PropertyTableJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, Cesium3DTiles::PropertyTable* pObject); diff --git a/Cesium3DTilesReader/generated/src/PropertyTablePropertyJsonHandler.h b/Cesium3DTilesReader/generated/src/PropertyTablePropertyJsonHandler.h index 875e66914..9f2b674d2 100644 --- a/Cesium3DTilesReader/generated/src/PropertyTablePropertyJsonHandler.h +++ b/Cesium3DTilesReader/generated/src/PropertyTablePropertyJsonHandler.h @@ -9,7 +9,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace Cesium3DTilesReader { @@ -19,7 +19,7 @@ class PropertyTablePropertyJsonHandler using ValueType = Cesium3DTiles::PropertyTableProperty; PropertyTablePropertyJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset( IJsonHandler* pParentHandler, Cesium3DTiles::PropertyTableProperty* pObject); diff --git a/Cesium3DTilesReader/generated/src/SchemaJsonHandler.h b/Cesium3DTilesReader/generated/src/SchemaJsonHandler.h index 7a8a5a6b0..dae6770d2 100644 --- a/Cesium3DTilesReader/generated/src/SchemaJsonHandler.h +++ b/Cesium3DTilesReader/generated/src/SchemaJsonHandler.h @@ -11,7 +11,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace Cesium3DTilesReader { @@ -20,7 +20,7 @@ class SchemaJsonHandler : public CesiumJsonReader::ExtensibleObjectJsonHandler { using ValueType = Cesium3DTiles::Schema; SchemaJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, Cesium3DTiles::Schema* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; diff --git a/Cesium3DTilesReader/generated/src/StatisticsJsonHandler.h b/Cesium3DTilesReader/generated/src/StatisticsJsonHandler.h index d8a3c8854..a1d97a939 100644 --- a/Cesium3DTilesReader/generated/src/StatisticsJsonHandler.h +++ b/Cesium3DTilesReader/generated/src/StatisticsJsonHandler.h @@ -9,7 +9,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace Cesium3DTilesReader { @@ -19,7 +19,7 @@ class StatisticsJsonHandler using ValueType = Cesium3DTiles::Statistics; StatisticsJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, Cesium3DTiles::Statistics* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; diff --git a/Cesium3DTilesReader/generated/src/SubtreeJsonHandler.h b/Cesium3DTilesReader/generated/src/SubtreeJsonHandler.h index b16adb7a2..98bb31c33 100644 --- a/Cesium3DTilesReader/generated/src/SubtreeJsonHandler.h +++ b/Cesium3DTilesReader/generated/src/SubtreeJsonHandler.h @@ -14,7 +14,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace Cesium3DTilesReader { @@ -24,7 +24,7 @@ class SubtreeJsonHandler using ValueType = Cesium3DTiles::Subtree; SubtreeJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, Cesium3DTiles::Subtree* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; diff --git a/Cesium3DTilesReader/generated/src/SubtreesJsonHandler.h b/Cesium3DTilesReader/generated/src/SubtreesJsonHandler.h index 9e5877b42..1669066e8 100644 --- a/Cesium3DTilesReader/generated/src/SubtreesJsonHandler.h +++ b/Cesium3DTilesReader/generated/src/SubtreesJsonHandler.h @@ -7,7 +7,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace Cesium3DTilesReader { @@ -17,7 +17,7 @@ class SubtreesJsonHandler using ValueType = Cesium3DTiles::Subtrees; SubtreesJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, Cesium3DTiles::Subtrees* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; diff --git a/Cesium3DTilesReader/generated/src/TileJsonHandler.h b/Cesium3DTilesReader/generated/src/TileJsonHandler.h index be84c0633..49797617e 100644 --- a/Cesium3DTilesReader/generated/src/TileJsonHandler.h +++ b/Cesium3DTilesReader/generated/src/TileJsonHandler.h @@ -14,7 +14,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace Cesium3DTilesReader { @@ -22,8 +22,7 @@ class TileJsonHandler : public CesiumJsonReader::ExtensibleObjectJsonHandler { public: using ValueType = Cesium3DTiles::Tile; - TileJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + TileJsonHandler(const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, Cesium3DTiles::Tile* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; diff --git a/Cesium3DTilesReader/generated/src/TilesetJsonHandler.h b/Cesium3DTilesReader/generated/src/TilesetJsonHandler.h index f6a9c8982..66a9e1f57 100644 --- a/Cesium3DTilesReader/generated/src/TilesetJsonHandler.h +++ b/Cesium3DTilesReader/generated/src/TilesetJsonHandler.h @@ -18,7 +18,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace Cesium3DTilesReader { @@ -28,7 +28,7 @@ class TilesetJsonHandler using ValueType = Cesium3DTiles::Tileset; TilesetJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, Cesium3DTiles::Tileset* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; diff --git a/Cesium3DTilesReader/generated/src/registerExtensions.cpp b/Cesium3DTilesReader/generated/src/registerExtensions.cpp index e76327565..6e67a0329 100644 --- a/Cesium3DTilesReader/generated/src/registerExtensions.cpp +++ b/Cesium3DTilesReader/generated/src/registerExtensions.cpp @@ -6,13 +6,13 @@ #include "Extension3dTilesBoundingVolumeS2JsonHandler.h" #include -#include +#include namespace Cesium3DTilesReader { -void registerExtensions(CesiumJsonReader::ExtensionReaderContext& context) { - (void)context; - context.registerExtension< +void registerExtensions(CesiumJsonReader::JsonReaderOptions& options) { + (void)options; + options.registerExtension< Cesium3DTiles::BoundingVolume, Extension3dTilesBoundingVolumeS2JsonHandler>(); } diff --git a/Cesium3DTilesReader/generated/src/registerExtensions.h b/Cesium3DTilesReader/generated/src/registerExtensions.h index 3c67af28a..580920877 100644 --- a/Cesium3DTilesReader/generated/src/registerExtensions.h +++ b/Cesium3DTilesReader/generated/src/registerExtensions.h @@ -3,9 +3,9 @@ #pragma once namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } // namespace CesiumJsonReader namespace Cesium3DTilesReader { -void registerExtensions(CesiumJsonReader::ExtensionReaderContext& context); +void registerExtensions(CesiumJsonReader::JsonReaderOptions& options); } diff --git a/Cesium3DTilesReader/include/Cesium3DTilesReader/SchemaReader.h b/Cesium3DTilesReader/include/Cesium3DTilesReader/SchemaReader.h index 4b7203224..1dd433aa1 100644 --- a/Cesium3DTilesReader/include/Cesium3DTilesReader/SchemaReader.h +++ b/Cesium3DTilesReader/include/Cesium3DTilesReader/SchemaReader.h @@ -3,7 +3,7 @@ #include "Cesium3DTilesReader/Library.h" #include -#include +#include #include @@ -48,16 +48,14 @@ class CESIUM3DTILESREADER_API SchemaReader { SchemaReader(); /** - * @brief Gets the context used to control how extensions are loaded from a - * schema. + * @brief Gets the options controlling how the JSON is read. */ - CesiumJsonReader::ExtensionReaderContext& getExtensions(); + CesiumJsonReader::JsonReaderOptions& getOptions(); /** - * @brief Gets the context used to control how extensions are loaded from a - * schema. + * @brief Gets the options controlling how the JSON is read. */ - const CesiumJsonReader::ExtensionReaderContext& getExtensions() const; + const CesiumJsonReader::JsonReaderOptions& getOptions() const; /** * @brief Reads a schema. @@ -68,7 +66,7 @@ class CESIUM3DTILESREADER_API SchemaReader { SchemaReaderResult readSchema(const gsl::span& data) const; private: - CesiumJsonReader::ExtensionReaderContext _context; + CesiumJsonReader::JsonReaderOptions _context; }; } // namespace Cesium3DTilesReader diff --git a/Cesium3DTilesReader/include/Cesium3DTilesReader/SubtreeReader.h b/Cesium3DTilesReader/include/Cesium3DTilesReader/SubtreeReader.h index 1f1d2f64a..05f3c35bd 100644 --- a/Cesium3DTilesReader/include/Cesium3DTilesReader/SubtreeReader.h +++ b/Cesium3DTilesReader/include/Cesium3DTilesReader/SubtreeReader.h @@ -3,7 +3,7 @@ #include "Cesium3DTilesReader/Library.h" #include -#include +#include #include @@ -48,16 +48,14 @@ class CESIUM3DTILESREADER_API SubtreeReader { SubtreeReader(); /** - * @brief Gets the context used to control how extensions are loaded from a - * subtree. + * @brief Gets the options controlling how the JSON is read. */ - CesiumJsonReader::ExtensionReaderContext& getExtensions(); + CesiumJsonReader::JsonReaderOptions& getOptions(); /** - * @brief Gets the context used to control how extensions are loaded from a - * subtree. + * @brief Gets the options controlling how the JSON is read. */ - const CesiumJsonReader::ExtensionReaderContext& getExtensions() const; + const CesiumJsonReader::JsonReaderOptions& getOptions() const; /** * @brief Reads a subtree. @@ -69,7 +67,7 @@ class CESIUM3DTILESREADER_API SubtreeReader { SubtreeReaderResult readSubtree(const gsl::span& data) const; private: - CesiumJsonReader::ExtensionReaderContext _context; + CesiumJsonReader::JsonReaderOptions _context; }; } // namespace Cesium3DTilesReader diff --git a/Cesium3DTilesReader/include/Cesium3DTilesReader/TilesetReader.h b/Cesium3DTilesReader/include/Cesium3DTilesReader/TilesetReader.h index b5a573693..ba2741cd7 100644 --- a/Cesium3DTilesReader/include/Cesium3DTilesReader/TilesetReader.h +++ b/Cesium3DTilesReader/include/Cesium3DTilesReader/TilesetReader.h @@ -3,7 +3,7 @@ #include "Cesium3DTilesReader/Library.h" #include -#include +#include #include @@ -48,16 +48,14 @@ class CESIUM3DTILESREADER_API TilesetReader { TilesetReader(); /** - * @brief Gets the context used to control how extensions are loaded from a - * tileset. + * @brief Gets the options controlling how the JSON is read. */ - CesiumJsonReader::ExtensionReaderContext& getExtensions(); + CesiumJsonReader::JsonReaderOptions& getOptions(); /** - * @brief Gets the context used to control how extensions are loaded from a - * tileset. + * @brief Gets the options controlling how the JSON is read. */ - const CesiumJsonReader::ExtensionReaderContext& getExtensions() const; + const CesiumJsonReader::JsonReaderOptions& getOptions() const; /** * @brief Reads a tileset. @@ -68,7 +66,7 @@ class CESIUM3DTILESREADER_API TilesetReader { TilesetReaderResult readTileset(const gsl::span& data) const; private: - CesiumJsonReader::ExtensionReaderContext _context; + CesiumJsonReader::JsonReaderOptions _context; }; } // namespace Cesium3DTilesReader diff --git a/Cesium3DTilesReader/src/SchemaReader.cpp b/Cesium3DTilesReader/src/SchemaReader.cpp index 5cffa17c9..6d76f9f95 100644 --- a/Cesium3DTilesReader/src/SchemaReader.cpp +++ b/Cesium3DTilesReader/src/SchemaReader.cpp @@ -11,7 +11,7 @@ namespace Cesium3DTilesReader { namespace { SchemaReaderResult readSchemaJson( - const CesiumJsonReader::ExtensionReaderContext& context, + const CesiumJsonReader::JsonReaderOptions& context, const gsl::span& data) { CESIUM_TRACE("Cesium3DTilesReader::SchemaReader::readSchemaJson"); @@ -30,19 +30,17 @@ SchemaReaderResult readSchemaJson( SchemaReader::SchemaReader() { registerExtensions(this->_context); } -CesiumJsonReader::ExtensionReaderContext& SchemaReader::getExtensions() { +CesiumJsonReader::JsonReaderOptions& SchemaReader::getOptions() { return this->_context; } -const CesiumJsonReader::ExtensionReaderContext& -SchemaReader::getExtensions() const { +const CesiumJsonReader::JsonReaderOptions& SchemaReader::getOptions() const { return this->_context; } SchemaReaderResult SchemaReader::readSchema(const gsl::span& data) const { - const CesiumJsonReader::ExtensionReaderContext& context = - this->getExtensions(); + const CesiumJsonReader::JsonReaderOptions& context = this->getOptions(); SchemaReaderResult result = readSchemaJson(context, data); return result; diff --git a/Cesium3DTilesReader/src/SubtreeReader.cpp b/Cesium3DTilesReader/src/SubtreeReader.cpp index 841b08590..c121abf22 100644 --- a/Cesium3DTilesReader/src/SubtreeReader.cpp +++ b/Cesium3DTilesReader/src/SubtreeReader.cpp @@ -11,7 +11,7 @@ namespace Cesium3DTilesReader { namespace { SubtreeReaderResult readSubtreeJson( - const CesiumJsonReader::ExtensionReaderContext& context, + const CesiumJsonReader::JsonReaderOptions& context, const gsl::span& data) { CESIUM_TRACE("Cesium3DTilesReader::SubtreeReader::readSubtreeJson"); @@ -30,19 +30,17 @@ SubtreeReaderResult readSubtreeJson( SubtreeReader::SubtreeReader() { registerExtensions(this->_context); } -CesiumJsonReader::ExtensionReaderContext& SubtreeReader::getExtensions() { +CesiumJsonReader::JsonReaderOptions& SubtreeReader::getOptions() { return this->_context; } -const CesiumJsonReader::ExtensionReaderContext& -SubtreeReader::getExtensions() const { +const CesiumJsonReader::JsonReaderOptions& SubtreeReader::getOptions() const { return this->_context; } SubtreeReaderResult SubtreeReader::readSubtree(const gsl::span& data) const { - const CesiumJsonReader::ExtensionReaderContext& context = - this->getExtensions(); + const CesiumJsonReader::JsonReaderOptions& context = this->getOptions(); SubtreeReaderResult result = readSubtreeJson(context, data); return result; diff --git a/Cesium3DTilesReader/src/TilesetReader.cpp b/Cesium3DTilesReader/src/TilesetReader.cpp index b81d520a9..ba6ab2942 100644 --- a/Cesium3DTilesReader/src/TilesetReader.cpp +++ b/Cesium3DTilesReader/src/TilesetReader.cpp @@ -11,7 +11,7 @@ namespace Cesium3DTilesReader { namespace { TilesetReaderResult readTilesetJson( - const CesiumJsonReader::ExtensionReaderContext& context, + const CesiumJsonReader::JsonReaderOptions& context, const gsl::span& data) { CESIUM_TRACE("Cesium3DTilesReader::TilesetReader::readTilesetJson"); @@ -30,19 +30,17 @@ TilesetReaderResult readTilesetJson( TilesetReader::TilesetReader() { registerExtensions(this->_context); } -CesiumJsonReader::ExtensionReaderContext& TilesetReader::getExtensions() { +CesiumJsonReader::JsonReaderOptions& TilesetReader::getOptions() { return this->_context; } -const CesiumJsonReader::ExtensionReaderContext& -TilesetReader::getExtensions() const { +const CesiumJsonReader::JsonReaderOptions& TilesetReader::getOptions() const { return this->_context; } TilesetReaderResult TilesetReader::readTileset(const gsl::span& data) const { - const CesiumJsonReader::ExtensionReaderContext& context = - this->getExtensions(); + const CesiumJsonReader::JsonReaderOptions& context = this->getOptions(); TilesetReaderResult result = readTilesetJson(context, data); return result; diff --git a/Cesium3DTilesReader/test/TestTilesetReader.cpp b/Cesium3DTilesReader/test/TestTilesetReader.cpp index 794835c34..83a9c7acf 100644 --- a/Cesium3DTilesReader/test/TestTilesetReader.cpp +++ b/Cesium3DTilesReader/test/TestTilesetReader.cpp @@ -296,10 +296,10 @@ TEST_CASE("Reads custom extension") { pB->getValuePtrForKey("another")->getStringOrDefault("") == "Goodbye"); // Repeat test but this time the extension should be skipped. - reader.getExtensions().setExtensionState( + reader.getOptions().setExtensionState( "A", CesiumJsonReader::ExtensionState::Disabled); - reader.getExtensions().setExtensionState( + reader.getOptions().setExtensionState( "B", CesiumJsonReader::ExtensionState::Disabled); @@ -374,14 +374,15 @@ TEST_CASE("Reads tileset JSON with unknown properties") { REQUIRE(itNull->second.isNull()); } -TEST_CASE("Reads tileset JSON with unknown properties and ignores them when requested") { +TEST_CASE("Reads tileset JSON with unknown properties and ignores them when " + "requested") { using namespace std::string_literals; std::filesystem::path tilesetFile = Cesium3DTilesReader_TEST_DATA_DIR; tilesetFile /= "tileset-with-unsupported-properties.json"; std::vector data = readFile(tilesetFile); Cesium3DTilesReader::TilesetReader reader; - reader.getExtensions().setCaptureUnknownProperties(false); + reader.getOptions().setCaptureUnknownProperties(false); Cesium3DTilesReader::TilesetReaderResult result = reader.readTileset(data); CHECK(result.errors.empty()); CHECK(result.warnings.empty()); diff --git a/CesiumGltfReader/generated/src/AccessorJsonHandler.h b/CesiumGltfReader/generated/src/AccessorJsonHandler.h index d8e0b83ca..b05251a3a 100644 --- a/CesiumGltfReader/generated/src/AccessorJsonHandler.h +++ b/CesiumGltfReader/generated/src/AccessorJsonHandler.h @@ -13,7 +13,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -22,7 +22,7 @@ class AccessorJsonHandler : public CesiumGltfReader::NamedObjectJsonHandler { using ValueType = CesiumGltf::Accessor; AccessorJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, CesiumGltf::Accessor* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; diff --git a/CesiumGltfReader/generated/src/AccessorSparseIndicesJsonHandler.h b/CesiumGltfReader/generated/src/AccessorSparseIndicesJsonHandler.h index 8a4ac2240..095ad628e 100644 --- a/CesiumGltfReader/generated/src/AccessorSparseIndicesJsonHandler.h +++ b/CesiumGltfReader/generated/src/AccessorSparseIndicesJsonHandler.h @@ -7,7 +7,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -17,7 +17,7 @@ class AccessorSparseIndicesJsonHandler using ValueType = CesiumGltf::AccessorSparseIndices; AccessorSparseIndicesJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset( IJsonHandler* pParentHandler, CesiumGltf::AccessorSparseIndices* pObject); diff --git a/CesiumGltfReader/generated/src/AccessorSparseJsonHandler.h b/CesiumGltfReader/generated/src/AccessorSparseJsonHandler.h index 8acd02c79..4552aebb7 100644 --- a/CesiumGltfReader/generated/src/AccessorSparseJsonHandler.h +++ b/CesiumGltfReader/generated/src/AccessorSparseJsonHandler.h @@ -10,7 +10,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -20,7 +20,7 @@ class AccessorSparseJsonHandler using ValueType = CesiumGltf::AccessorSparse; AccessorSparseJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, CesiumGltf::AccessorSparse* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; diff --git a/CesiumGltfReader/generated/src/AccessorSparseValuesJsonHandler.h b/CesiumGltfReader/generated/src/AccessorSparseValuesJsonHandler.h index c8a4e51f4..0ccdb2a31 100644 --- a/CesiumGltfReader/generated/src/AccessorSparseValuesJsonHandler.h +++ b/CesiumGltfReader/generated/src/AccessorSparseValuesJsonHandler.h @@ -7,7 +7,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -17,7 +17,7 @@ class AccessorSparseValuesJsonHandler using ValueType = CesiumGltf::AccessorSparseValues; AccessorSparseValuesJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset( IJsonHandler* pParentHandler, CesiumGltf::AccessorSparseValues* pObject); diff --git a/CesiumGltfReader/generated/src/AnimationChannelJsonHandler.h b/CesiumGltfReader/generated/src/AnimationChannelJsonHandler.h index 619648075..bad3ba4ea 100644 --- a/CesiumGltfReader/generated/src/AnimationChannelJsonHandler.h +++ b/CesiumGltfReader/generated/src/AnimationChannelJsonHandler.h @@ -9,7 +9,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -19,7 +19,7 @@ class AnimationChannelJsonHandler using ValueType = CesiumGltf::AnimationChannel; AnimationChannelJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, CesiumGltf::AnimationChannel* pObject); diff --git a/CesiumGltfReader/generated/src/AnimationChannelTargetJsonHandler.h b/CesiumGltfReader/generated/src/AnimationChannelTargetJsonHandler.h index 79e62627a..a78964ea5 100644 --- a/CesiumGltfReader/generated/src/AnimationChannelTargetJsonHandler.h +++ b/CesiumGltfReader/generated/src/AnimationChannelTargetJsonHandler.h @@ -8,7 +8,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -18,7 +18,7 @@ class AnimationChannelTargetJsonHandler using ValueType = CesiumGltf::AnimationChannelTarget; AnimationChannelTargetJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset( IJsonHandler* pParentHandler, CesiumGltf::AnimationChannelTarget* pObject); diff --git a/CesiumGltfReader/generated/src/AnimationJsonHandler.h b/CesiumGltfReader/generated/src/AnimationJsonHandler.h index 6f36f3bbd..03790c723 100644 --- a/CesiumGltfReader/generated/src/AnimationJsonHandler.h +++ b/CesiumGltfReader/generated/src/AnimationJsonHandler.h @@ -10,7 +10,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -19,7 +19,7 @@ class AnimationJsonHandler : public CesiumGltfReader::NamedObjectJsonHandler { using ValueType = CesiumGltf::Animation; AnimationJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, CesiumGltf::Animation* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; diff --git a/CesiumGltfReader/generated/src/AnimationSamplerJsonHandler.h b/CesiumGltfReader/generated/src/AnimationSamplerJsonHandler.h index f0a28dd90..ac389ed2d 100644 --- a/CesiumGltfReader/generated/src/AnimationSamplerJsonHandler.h +++ b/CesiumGltfReader/generated/src/AnimationSamplerJsonHandler.h @@ -8,7 +8,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -18,7 +18,7 @@ class AnimationSamplerJsonHandler using ValueType = CesiumGltf::AnimationSampler; AnimationSamplerJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, CesiumGltf::AnimationSampler* pObject); diff --git a/CesiumGltfReader/generated/src/AssetJsonHandler.h b/CesiumGltfReader/generated/src/AssetJsonHandler.h index dec617e00..03f579d8a 100644 --- a/CesiumGltfReader/generated/src/AssetJsonHandler.h +++ b/CesiumGltfReader/generated/src/AssetJsonHandler.h @@ -7,7 +7,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -15,8 +15,7 @@ class AssetJsonHandler : public CesiumJsonReader::ExtensibleObjectJsonHandler { public: using ValueType = CesiumGltf::Asset; - AssetJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + AssetJsonHandler(const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, CesiumGltf::Asset* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; diff --git a/CesiumGltfReader/generated/src/BufferJsonHandler.h b/CesiumGltfReader/generated/src/BufferJsonHandler.h index 03c829a75..f38305bfa 100644 --- a/CesiumGltfReader/generated/src/BufferJsonHandler.h +++ b/CesiumGltfReader/generated/src/BufferJsonHandler.h @@ -9,7 +9,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -18,7 +18,7 @@ class BufferJsonHandler : public CesiumGltfReader::NamedObjectJsonHandler { using ValueType = CesiumGltf::Buffer; BufferJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, CesiumGltf::Buffer* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; diff --git a/CesiumGltfReader/generated/src/BufferViewJsonHandler.h b/CesiumGltfReader/generated/src/BufferViewJsonHandler.h index 38e7cbc40..ebc4d254b 100644 --- a/CesiumGltfReader/generated/src/BufferViewJsonHandler.h +++ b/CesiumGltfReader/generated/src/BufferViewJsonHandler.h @@ -8,7 +8,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -17,7 +17,7 @@ class BufferViewJsonHandler : public CesiumGltfReader::NamedObjectJsonHandler { using ValueType = CesiumGltf::BufferView; BufferViewJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, CesiumGltf::BufferView* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; diff --git a/CesiumGltfReader/generated/src/CameraJsonHandler.h b/CesiumGltfReader/generated/src/CameraJsonHandler.h index 89d2c05f4..4bc021a3d 100644 --- a/CesiumGltfReader/generated/src/CameraJsonHandler.h +++ b/CesiumGltfReader/generated/src/CameraJsonHandler.h @@ -10,7 +10,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -19,7 +19,7 @@ class CameraJsonHandler : public CesiumGltfReader::NamedObjectJsonHandler { using ValueType = CesiumGltf::Camera; CameraJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, CesiumGltf::Camera* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; diff --git a/CesiumGltfReader/generated/src/CameraOrthographicJsonHandler.h b/CesiumGltfReader/generated/src/CameraOrthographicJsonHandler.h index dacb79769..0a9b160be 100644 --- a/CesiumGltfReader/generated/src/CameraOrthographicJsonHandler.h +++ b/CesiumGltfReader/generated/src/CameraOrthographicJsonHandler.h @@ -7,7 +7,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -17,7 +17,7 @@ class CameraOrthographicJsonHandler using ValueType = CesiumGltf::CameraOrthographic; CameraOrthographicJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, CesiumGltf::CameraOrthographic* pObject); diff --git a/CesiumGltfReader/generated/src/CameraPerspectiveJsonHandler.h b/CesiumGltfReader/generated/src/CameraPerspectiveJsonHandler.h index 5107f76ec..9a09d51cf 100644 --- a/CesiumGltfReader/generated/src/CameraPerspectiveJsonHandler.h +++ b/CesiumGltfReader/generated/src/CameraPerspectiveJsonHandler.h @@ -7,7 +7,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -17,7 +17,7 @@ class CameraPerspectiveJsonHandler using ValueType = CesiumGltf::CameraPerspective; CameraPerspectiveJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, CesiumGltf::CameraPerspective* pObject); diff --git a/CesiumGltfReader/generated/src/ClassJsonHandler.h b/CesiumGltfReader/generated/src/ClassJsonHandler.h index 580791944..2db370898 100644 --- a/CesiumGltfReader/generated/src/ClassJsonHandler.h +++ b/CesiumGltfReader/generated/src/ClassJsonHandler.h @@ -10,7 +10,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -18,8 +18,7 @@ class ClassJsonHandler : public CesiumJsonReader::ExtensibleObjectJsonHandler { public: using ValueType = CesiumGltf::Class; - ClassJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + ClassJsonHandler(const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, CesiumGltf::Class* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; diff --git a/CesiumGltfReader/generated/src/ClassPropertyJsonHandler.h b/CesiumGltfReader/generated/src/ClassPropertyJsonHandler.h index aa176fe01..ac9473100 100644 --- a/CesiumGltfReader/generated/src/ClassPropertyJsonHandler.h +++ b/CesiumGltfReader/generated/src/ClassPropertyJsonHandler.h @@ -10,7 +10,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -20,7 +20,7 @@ class ClassPropertyJsonHandler using ValueType = CesiumGltf::ClassProperty; ClassPropertyJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, CesiumGltf::ClassProperty* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; diff --git a/CesiumGltfReader/generated/src/ClassStatisticsJsonHandler.h b/CesiumGltfReader/generated/src/ClassStatisticsJsonHandler.h index 840492669..00a7a78ef 100644 --- a/CesiumGltfReader/generated/src/ClassStatisticsJsonHandler.h +++ b/CesiumGltfReader/generated/src/ClassStatisticsJsonHandler.h @@ -10,7 +10,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -20,7 +20,7 @@ class ClassStatisticsJsonHandler using ValueType = CesiumGltf::ClassStatistics; ClassStatisticsJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, CesiumGltf::ClassStatistics* pObject); diff --git a/CesiumGltfReader/generated/src/EnumJsonHandler.h b/CesiumGltfReader/generated/src/EnumJsonHandler.h index 355e88790..f7f9b09a4 100644 --- a/CesiumGltfReader/generated/src/EnumJsonHandler.h +++ b/CesiumGltfReader/generated/src/EnumJsonHandler.h @@ -10,7 +10,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -18,8 +18,7 @@ class EnumJsonHandler : public CesiumJsonReader::ExtensibleObjectJsonHandler { public: using ValueType = CesiumGltf::Enum; - EnumJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + EnumJsonHandler(const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, CesiumGltf::Enum* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; diff --git a/CesiumGltfReader/generated/src/EnumValueJsonHandler.h b/CesiumGltfReader/generated/src/EnumValueJsonHandler.h index d56d90722..abc3c0e2d 100644 --- a/CesiumGltfReader/generated/src/EnumValueJsonHandler.h +++ b/CesiumGltfReader/generated/src/EnumValueJsonHandler.h @@ -8,7 +8,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -18,7 +18,7 @@ class EnumValueJsonHandler using ValueType = CesiumGltf::EnumValue; EnumValueJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, CesiumGltf::EnumValue* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; diff --git a/CesiumGltfReader/generated/src/ExtensionBufferExtMeshoptCompressionJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionBufferExtMeshoptCompressionJsonHandler.h index 76e308858..7c4871195 100644 --- a/CesiumGltfReader/generated/src/ExtensionBufferExtMeshoptCompressionJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionBufferExtMeshoptCompressionJsonHandler.h @@ -7,7 +7,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -20,7 +20,7 @@ class ExtensionBufferExtMeshoptCompressionJsonHandler static inline constexpr const char* ExtensionName = "EXT_meshopt_compression"; ExtensionBufferExtMeshoptCompressionJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset( IJsonHandler* pParentHandler, CesiumGltf::ExtensionBufferExtMeshoptCompression* pObject); diff --git a/CesiumGltfReader/generated/src/ExtensionBufferViewExtMeshoptCompressionJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionBufferViewExtMeshoptCompressionJsonHandler.h index 52957d79f..c8783361e 100644 --- a/CesiumGltfReader/generated/src/ExtensionBufferViewExtMeshoptCompressionJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionBufferViewExtMeshoptCompressionJsonHandler.h @@ -8,7 +8,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -21,7 +21,7 @@ class ExtensionBufferViewExtMeshoptCompressionJsonHandler static inline constexpr const char* ExtensionName = "EXT_meshopt_compression"; ExtensionBufferViewExtMeshoptCompressionJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset( IJsonHandler* pParentHandler, CesiumGltf::ExtensionBufferViewExtMeshoptCompression* pObject); diff --git a/CesiumGltfReader/generated/src/ExtensionCesiumRTCJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionCesiumRTCJsonHandler.h index f28e40219..84ffc1482 100644 --- a/CesiumGltfReader/generated/src/ExtensionCesiumRTCJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionCesiumRTCJsonHandler.h @@ -8,7 +8,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -21,7 +21,7 @@ class ExtensionCesiumRTCJsonHandler static inline constexpr const char* ExtensionName = "CESIUM_RTC"; ExtensionCesiumRTCJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, CesiumGltf::ExtensionCesiumRTC* pObject); diff --git a/CesiumGltfReader/generated/src/ExtensionCesiumTileEdgesJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionCesiumTileEdgesJsonHandler.h index 5b96c1a3b..d08365fdc 100644 --- a/CesiumGltfReader/generated/src/ExtensionCesiumTileEdgesJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionCesiumTileEdgesJsonHandler.h @@ -7,7 +7,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -20,7 +20,7 @@ class ExtensionCesiumTileEdgesJsonHandler static inline constexpr const char* ExtensionName = "CESIUM_tile_edges"; ExtensionCesiumTileEdgesJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset( IJsonHandler* pParentHandler, CesiumGltf::ExtensionCesiumTileEdges* pObject); diff --git a/CesiumGltfReader/generated/src/ExtensionExtInstanceFeaturesFeatureIdJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionExtInstanceFeaturesFeatureIdJsonHandler.h index 683f6c49c..222845096 100644 --- a/CesiumGltfReader/generated/src/ExtensionExtInstanceFeaturesFeatureIdJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionExtInstanceFeaturesFeatureIdJsonHandler.h @@ -8,7 +8,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -18,7 +18,7 @@ class ExtensionExtInstanceFeaturesFeatureIdJsonHandler using ValueType = CesiumGltf::ExtensionExtInstanceFeaturesFeatureId; ExtensionExtInstanceFeaturesFeatureIdJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset( IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtInstanceFeaturesFeatureId* pObject); diff --git a/CesiumGltfReader/generated/src/ExtensionExtInstanceFeaturesJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionExtInstanceFeaturesJsonHandler.h index 6012ec094..c03c4fada 100644 --- a/CesiumGltfReader/generated/src/ExtensionExtInstanceFeaturesJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionExtInstanceFeaturesJsonHandler.h @@ -9,7 +9,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -22,7 +22,7 @@ class ExtensionExtInstanceFeaturesJsonHandler static inline constexpr const char* ExtensionName = "EXT_instance_features"; ExtensionExtInstanceFeaturesJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset( IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtInstanceFeatures* pObject); diff --git a/CesiumGltfReader/generated/src/ExtensionExtMeshFeaturesFeatureIdJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionExtMeshFeaturesFeatureIdJsonHandler.h index 9c8229e9a..cde669ef4 100644 --- a/CesiumGltfReader/generated/src/ExtensionExtMeshFeaturesFeatureIdJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionExtMeshFeaturesFeatureIdJsonHandler.h @@ -10,7 +10,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -20,7 +20,7 @@ class ExtensionExtMeshFeaturesFeatureIdJsonHandler using ValueType = CesiumGltf::ExtensionExtMeshFeaturesFeatureId; ExtensionExtMeshFeaturesFeatureIdJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset( IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtMeshFeaturesFeatureId* pObject); diff --git a/CesiumGltfReader/generated/src/ExtensionExtMeshFeaturesFeatureIdTextureJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionExtMeshFeaturesFeatureIdTextureJsonHandler.h index 667b85ab6..490e52039 100644 --- a/CesiumGltfReader/generated/src/ExtensionExtMeshFeaturesFeatureIdTextureJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionExtMeshFeaturesFeatureIdTextureJsonHandler.h @@ -9,7 +9,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -19,7 +19,7 @@ class ExtensionExtMeshFeaturesFeatureIdTextureJsonHandler using ValueType = CesiumGltf::ExtensionExtMeshFeaturesFeatureIdTexture; ExtensionExtMeshFeaturesFeatureIdTextureJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset( IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtMeshFeaturesFeatureIdTexture* pObject); diff --git a/CesiumGltfReader/generated/src/ExtensionExtMeshFeaturesJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionExtMeshFeaturesJsonHandler.h index e8609c68f..e39bf64f5 100644 --- a/CesiumGltfReader/generated/src/ExtensionExtMeshFeaturesJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionExtMeshFeaturesJsonHandler.h @@ -9,7 +9,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -22,7 +22,7 @@ class ExtensionExtMeshFeaturesJsonHandler static inline constexpr const char* ExtensionName = "EXT_mesh_features"; ExtensionExtMeshFeaturesJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset( IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtMeshFeatures* pObject); diff --git a/CesiumGltfReader/generated/src/ExtensionExtMeshGpuInstancingJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionExtMeshGpuInstancingJsonHandler.h index 2afc1ebd9..5c0414671 100644 --- a/CesiumGltfReader/generated/src/ExtensionExtMeshGpuInstancingJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionExtMeshGpuInstancingJsonHandler.h @@ -8,7 +8,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -21,7 +21,7 @@ class ExtensionExtMeshGpuInstancingJsonHandler static inline constexpr const char* ExtensionName = "EXT_mesh_gpu_instancing"; ExtensionExtMeshGpuInstancingJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset( IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtMeshGpuInstancing* pObject); diff --git a/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataClassJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataClassJsonHandler.h index ba06f5973..ea781b587 100644 --- a/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataClassJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataClassJsonHandler.h @@ -10,7 +10,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -20,7 +20,7 @@ class ExtensionExtStructuralMetadataClassJsonHandler using ValueType = CesiumGltf::ExtensionExtStructuralMetadataClass; ExtensionExtStructuralMetadataClassJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset( IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtStructuralMetadataClass* pObject); diff --git a/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataClassPropertyJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataClassPropertyJsonHandler.h index 5b4f30f29..b700e6d16 100644 --- a/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataClassPropertyJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataClassPropertyJsonHandler.h @@ -10,7 +10,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -20,7 +20,7 @@ class ExtensionExtStructuralMetadataClassPropertyJsonHandler using ValueType = CesiumGltf::ExtensionExtStructuralMetadataClassProperty; ExtensionExtStructuralMetadataClassPropertyJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset( IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtStructuralMetadataClassProperty* pObject); diff --git a/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataEnumJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataEnumJsonHandler.h index dad293d8e..0b217805f 100644 --- a/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataEnumJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataEnumJsonHandler.h @@ -10,7 +10,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -20,7 +20,7 @@ class ExtensionExtStructuralMetadataEnumJsonHandler using ValueType = CesiumGltf::ExtensionExtStructuralMetadataEnum; ExtensionExtStructuralMetadataEnumJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset( IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtStructuralMetadataEnum* pObject); diff --git a/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataEnumValueJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataEnumValueJsonHandler.h index 477f0bd4b..c3bc0508b 100644 --- a/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataEnumValueJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataEnumValueJsonHandler.h @@ -8,7 +8,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -18,7 +18,7 @@ class ExtensionExtStructuralMetadataEnumValueJsonHandler using ValueType = CesiumGltf::ExtensionExtStructuralMetadataEnumValue; ExtensionExtStructuralMetadataEnumValueJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset( IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtStructuralMetadataEnumValue* pObject); diff --git a/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataPropertyAttributeJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataPropertyAttributeJsonHandler.h index c195cae56..4c2625fe9 100644 --- a/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataPropertyAttributeJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataPropertyAttributeJsonHandler.h @@ -10,7 +10,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -20,7 +20,7 @@ class ExtensionExtStructuralMetadataPropertyAttributeJsonHandler using ValueType = CesiumGltf::ExtensionExtStructuralMetadataPropertyAttribute; ExtensionExtStructuralMetadataPropertyAttributeJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset( IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtStructuralMetadataPropertyAttribute* pObject); diff --git a/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataPropertyAttributePropertyJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataPropertyAttributePropertyJsonHandler.h index 4a91ba0b8..9df9b14bb 100644 --- a/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataPropertyAttributePropertyJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataPropertyAttributePropertyJsonHandler.h @@ -8,7 +8,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -19,7 +19,7 @@ class ExtensionExtStructuralMetadataPropertyAttributePropertyJsonHandler CesiumGltf::ExtensionExtStructuralMetadataPropertyAttributeProperty; ExtensionExtStructuralMetadataPropertyAttributePropertyJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset( IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtStructuralMetadataPropertyAttributeProperty* diff --git a/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataPropertyTableJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataPropertyTableJsonHandler.h index f4f3ee112..61c81400e 100644 --- a/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataPropertyTableJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataPropertyTableJsonHandler.h @@ -11,7 +11,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -21,7 +21,7 @@ class ExtensionExtStructuralMetadataPropertyTableJsonHandler using ValueType = CesiumGltf::ExtensionExtStructuralMetadataPropertyTable; ExtensionExtStructuralMetadataPropertyTableJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset( IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtStructuralMetadataPropertyTable* pObject); diff --git a/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataPropertyTablePropertyJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataPropertyTablePropertyJsonHandler.h index 6051f6973..f867423a8 100644 --- a/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataPropertyTablePropertyJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataPropertyTablePropertyJsonHandler.h @@ -9,7 +9,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -20,7 +20,7 @@ class ExtensionExtStructuralMetadataPropertyTablePropertyJsonHandler CesiumGltf::ExtensionExtStructuralMetadataPropertyTableProperty; ExtensionExtStructuralMetadataPropertyTablePropertyJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset( IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtStructuralMetadataPropertyTableProperty* pObject); diff --git a/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataPropertyTextureJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataPropertyTextureJsonHandler.h index aa8e3c513..ae5c994b2 100644 --- a/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataPropertyTextureJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataPropertyTextureJsonHandler.h @@ -10,7 +10,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -20,7 +20,7 @@ class ExtensionExtStructuralMetadataPropertyTextureJsonHandler using ValueType = CesiumGltf::ExtensionExtStructuralMetadataPropertyTexture; ExtensionExtStructuralMetadataPropertyTextureJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset( IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtStructuralMetadataPropertyTexture* pObject); diff --git a/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataPropertyTexturePropertyJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataPropertyTexturePropertyJsonHandler.h index 2b2527250..f3311aa6f 100644 --- a/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataPropertyTexturePropertyJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataPropertyTexturePropertyJsonHandler.h @@ -10,7 +10,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -21,7 +21,7 @@ class ExtensionExtStructuralMetadataPropertyTexturePropertyJsonHandler CesiumGltf::ExtensionExtStructuralMetadataPropertyTextureProperty; ExtensionExtStructuralMetadataPropertyTexturePropertyJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset( IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtStructuralMetadataPropertyTextureProperty* diff --git a/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataSchemaJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataSchemaJsonHandler.h index 78ba70d0c..63ce7c078 100644 --- a/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataSchemaJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionExtStructuralMetadataSchemaJsonHandler.h @@ -11,7 +11,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -21,7 +21,7 @@ class ExtensionExtStructuralMetadataSchemaJsonHandler using ValueType = CesiumGltf::ExtensionExtStructuralMetadataSchema; ExtensionExtStructuralMetadataSchemaJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset( IJsonHandler* pParentHandler, CesiumGltf::ExtensionExtStructuralMetadataSchema* pObject); diff --git a/CesiumGltfReader/generated/src/ExtensionKhrDracoMeshCompressionJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionKhrDracoMeshCompressionJsonHandler.h index e56e1a948..e3835e421 100644 --- a/CesiumGltfReader/generated/src/ExtensionKhrDracoMeshCompressionJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionKhrDracoMeshCompressionJsonHandler.h @@ -8,7 +8,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -22,7 +22,7 @@ class ExtensionKhrDracoMeshCompressionJsonHandler "KHR_draco_mesh_compression"; ExtensionKhrDracoMeshCompressionJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset( IJsonHandler* pParentHandler, CesiumGltf::ExtensionKhrDracoMeshCompression* pObject); diff --git a/CesiumGltfReader/generated/src/ExtensionKhrMaterialsUnlitJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionKhrMaterialsUnlitJsonHandler.h index 8994113d6..c143b4169 100644 --- a/CesiumGltfReader/generated/src/ExtensionKhrMaterialsUnlitJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionKhrMaterialsUnlitJsonHandler.h @@ -6,7 +6,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -19,7 +19,7 @@ class ExtensionKhrMaterialsUnlitJsonHandler static inline constexpr const char* ExtensionName = "KHR_materials_unlit"; ExtensionKhrMaterialsUnlitJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset( IJsonHandler* pParentHandler, CesiumGltf::ExtensionKhrMaterialsUnlit* pObject); diff --git a/CesiumGltfReader/generated/src/ExtensionKhrTextureBasisuJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionKhrTextureBasisuJsonHandler.h index 5867fd9ae..fe66ed324 100644 --- a/CesiumGltfReader/generated/src/ExtensionKhrTextureBasisuJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionKhrTextureBasisuJsonHandler.h @@ -7,7 +7,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -20,7 +20,7 @@ class ExtensionKhrTextureBasisuJsonHandler static inline constexpr const char* ExtensionName = "KHR_texture_basisu"; ExtensionKhrTextureBasisuJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset( IJsonHandler* pParentHandler, CesiumGltf::ExtensionKhrTextureBasisu* pObject); diff --git a/CesiumGltfReader/generated/src/ExtensionKhrTextureTransformJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionKhrTextureTransformJsonHandler.h index 692c6f458..11585ed94 100644 --- a/CesiumGltfReader/generated/src/ExtensionKhrTextureTransformJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionKhrTextureTransformJsonHandler.h @@ -9,7 +9,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -22,7 +22,7 @@ class ExtensionKhrTextureTransformJsonHandler static inline constexpr const char* ExtensionName = "KHR_texture_transform"; ExtensionKhrTextureTransformJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset( IJsonHandler* pParentHandler, CesiumGltf::ExtensionKhrTextureTransform* pObject); diff --git a/CesiumGltfReader/generated/src/ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler.h index 90f363195..93ec30a16 100644 --- a/CesiumGltfReader/generated/src/ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler.h @@ -11,7 +11,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -24,7 +24,7 @@ class ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler static inline constexpr const char* ExtensionName = "EXT_feature_metadata"; ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset( IJsonHandler* pParentHandler, CesiumGltf::ExtensionMeshPrimitiveExtFeatureMetadata* pObject); diff --git a/CesiumGltfReader/generated/src/ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler.h index 0ec5997e3..cc3da651e 100644 --- a/CesiumGltfReader/generated/src/ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler.h @@ -8,7 +8,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -21,7 +21,7 @@ class ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler static inline constexpr const char* ExtensionName = "EXT_structural_metadata"; ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset( IJsonHandler* pParentHandler, CesiumGltf::ExtensionMeshPrimitiveExtStructuralMetadata* pObject); diff --git a/CesiumGltfReader/generated/src/ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler.h index 4f9b89bc6..28c93e0e3 100644 --- a/CesiumGltfReader/generated/src/ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler.h @@ -9,7 +9,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -22,7 +22,7 @@ class ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler static inline constexpr const char* ExtensionName = "KHR_materials_variants"; ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset( IJsonHandler* pParentHandler, CesiumGltf::ExtensionMeshPrimitiveKhrMaterialsVariants* pObject); diff --git a/CesiumGltfReader/generated/src/ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueJsonHandler.h index f20f2d9f9..a0a4792e4 100644 --- a/CesiumGltfReader/generated/src/ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueJsonHandler.h @@ -9,7 +9,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -20,7 +20,7 @@ class ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueJsonHandler CesiumGltf::ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValue; ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset( IJsonHandler* pParentHandler, CesiumGltf::ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValue* diff --git a/CesiumGltfReader/generated/src/ExtensionModelExtFeatureMetadataJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionModelExtFeatureMetadataJsonHandler.h index 0b4b93203..1fc825848 100644 --- a/CesiumGltfReader/generated/src/ExtensionModelExtFeatureMetadataJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionModelExtFeatureMetadataJsonHandler.h @@ -13,7 +13,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -26,7 +26,7 @@ class ExtensionModelExtFeatureMetadataJsonHandler static inline constexpr const char* ExtensionName = "EXT_feature_metadata"; ExtensionModelExtFeatureMetadataJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset( IJsonHandler* pParentHandler, CesiumGltf::ExtensionModelExtFeatureMetadata* pObject); diff --git a/CesiumGltfReader/generated/src/ExtensionModelExtStructuralMetadataJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionModelExtStructuralMetadataJsonHandler.h index 4d4ce5e18..bce764b8f 100644 --- a/CesiumGltfReader/generated/src/ExtensionModelExtStructuralMetadataJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionModelExtStructuralMetadataJsonHandler.h @@ -13,7 +13,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -26,7 +26,7 @@ class ExtensionModelExtStructuralMetadataJsonHandler static inline constexpr const char* ExtensionName = "EXT_structural_metadata"; ExtensionModelExtStructuralMetadataJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset( IJsonHandler* pParentHandler, CesiumGltf::ExtensionModelExtStructuralMetadata* pObject); diff --git a/CesiumGltfReader/generated/src/ExtensionModelKhrMaterialsVariantsJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionModelKhrMaterialsVariantsJsonHandler.h index bdb32d95c..4c967b4b5 100644 --- a/CesiumGltfReader/generated/src/ExtensionModelKhrMaterialsVariantsJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionModelKhrMaterialsVariantsJsonHandler.h @@ -9,7 +9,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -22,7 +22,7 @@ class ExtensionModelKhrMaterialsVariantsJsonHandler static inline constexpr const char* ExtensionName = "KHR_materials_variants"; ExtensionModelKhrMaterialsVariantsJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset( IJsonHandler* pParentHandler, CesiumGltf::ExtensionModelKhrMaterialsVariants* pObject); diff --git a/CesiumGltfReader/generated/src/ExtensionModelKhrMaterialsVariantsValueJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionModelKhrMaterialsVariantsValueJsonHandler.h index dc4fa0042..7aadd6567 100644 --- a/CesiumGltfReader/generated/src/ExtensionModelKhrMaterialsVariantsValueJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionModelKhrMaterialsVariantsValueJsonHandler.h @@ -8,7 +8,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -18,7 +18,7 @@ class ExtensionModelKhrMaterialsVariantsValueJsonHandler using ValueType = CesiumGltf::ExtensionModelKhrMaterialsVariantsValue; ExtensionModelKhrMaterialsVariantsValueJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset( IJsonHandler* pParentHandler, CesiumGltf::ExtensionModelKhrMaterialsVariantsValue* pObject); diff --git a/CesiumGltfReader/generated/src/ExtensionModelMaxarMeshVariantsJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionModelMaxarMeshVariantsJsonHandler.h index b3bf06964..c98b19818 100644 --- a/CesiumGltfReader/generated/src/ExtensionModelMaxarMeshVariantsJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionModelMaxarMeshVariantsJsonHandler.h @@ -10,7 +10,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -23,7 +23,7 @@ class ExtensionModelMaxarMeshVariantsJsonHandler static inline constexpr const char* ExtensionName = "MAXAR_mesh_variants"; ExtensionModelMaxarMeshVariantsJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset( IJsonHandler* pParentHandler, CesiumGltf::ExtensionModelMaxarMeshVariants* pObject); diff --git a/CesiumGltfReader/generated/src/ExtensionModelMaxarMeshVariantsValueJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionModelMaxarMeshVariantsValueJsonHandler.h index 47eb9a047..718f8184d 100644 --- a/CesiumGltfReader/generated/src/ExtensionModelMaxarMeshVariantsValueJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionModelMaxarMeshVariantsValueJsonHandler.h @@ -8,7 +8,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -18,7 +18,7 @@ class ExtensionModelMaxarMeshVariantsValueJsonHandler using ValueType = CesiumGltf::ExtensionModelMaxarMeshVariantsValue; ExtensionModelMaxarMeshVariantsValueJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset( IJsonHandler* pParentHandler, CesiumGltf::ExtensionModelMaxarMeshVariantsValue* pObject); diff --git a/CesiumGltfReader/generated/src/ExtensionNodeMaxarMeshVariantsJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionNodeMaxarMeshVariantsJsonHandler.h index 49c442f8c..16bb452ce 100644 --- a/CesiumGltfReader/generated/src/ExtensionNodeMaxarMeshVariantsJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionNodeMaxarMeshVariantsJsonHandler.h @@ -9,7 +9,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -22,7 +22,7 @@ class ExtensionNodeMaxarMeshVariantsJsonHandler static inline constexpr const char* ExtensionName = "MAXAR_mesh_variants"; ExtensionNodeMaxarMeshVariantsJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset( IJsonHandler* pParentHandler, CesiumGltf::ExtensionNodeMaxarMeshVariants* pObject); diff --git a/CesiumGltfReader/generated/src/ExtensionNodeMaxarMeshVariantsMappingsValueJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionNodeMaxarMeshVariantsMappingsValueJsonHandler.h index 8882683b8..d6bee16de 100644 --- a/CesiumGltfReader/generated/src/ExtensionNodeMaxarMeshVariantsMappingsValueJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionNodeMaxarMeshVariantsMappingsValueJsonHandler.h @@ -9,7 +9,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -19,7 +19,7 @@ class ExtensionNodeMaxarMeshVariantsMappingsValueJsonHandler using ValueType = CesiumGltf::ExtensionNodeMaxarMeshVariantsMappingsValue; ExtensionNodeMaxarMeshVariantsMappingsValueJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset( IJsonHandler* pParentHandler, CesiumGltf::ExtensionNodeMaxarMeshVariantsMappingsValue* pObject); diff --git a/CesiumGltfReader/generated/src/ExtensionTextureWebpJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionTextureWebpJsonHandler.h index 852d6b165..04ed382e3 100644 --- a/CesiumGltfReader/generated/src/ExtensionTextureWebpJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionTextureWebpJsonHandler.h @@ -7,7 +7,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -20,7 +20,7 @@ class ExtensionTextureWebpJsonHandler static inline constexpr const char* ExtensionName = "EXT_texture_webp"; ExtensionTextureWebpJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset( IJsonHandler* pParentHandler, CesiumGltf::ExtensionTextureWebp* pObject); diff --git a/CesiumGltfReader/generated/src/FeatureIDAttributeJsonHandler.h b/CesiumGltfReader/generated/src/FeatureIDAttributeJsonHandler.h index 3241d8a22..bdbac69d7 100644 --- a/CesiumGltfReader/generated/src/FeatureIDAttributeJsonHandler.h +++ b/CesiumGltfReader/generated/src/FeatureIDAttributeJsonHandler.h @@ -9,7 +9,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -19,7 +19,7 @@ class FeatureIDAttributeJsonHandler using ValueType = CesiumGltf::FeatureIDAttribute; FeatureIDAttributeJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, CesiumGltf::FeatureIDAttribute* pObject); diff --git a/CesiumGltfReader/generated/src/FeatureIDTextureJsonHandler.h b/CesiumGltfReader/generated/src/FeatureIDTextureJsonHandler.h index fda07ea9c..3afc819c1 100644 --- a/CesiumGltfReader/generated/src/FeatureIDTextureJsonHandler.h +++ b/CesiumGltfReader/generated/src/FeatureIDTextureJsonHandler.h @@ -9,7 +9,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -19,7 +19,7 @@ class FeatureIDTextureJsonHandler using ValueType = CesiumGltf::FeatureIDTexture; FeatureIDTextureJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, CesiumGltf::FeatureIDTexture* pObject); diff --git a/CesiumGltfReader/generated/src/FeatureIDsJsonHandler.h b/CesiumGltfReader/generated/src/FeatureIDsJsonHandler.h index 3501f6207..278b171d5 100644 --- a/CesiumGltfReader/generated/src/FeatureIDsJsonHandler.h +++ b/CesiumGltfReader/generated/src/FeatureIDsJsonHandler.h @@ -8,7 +8,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -18,7 +18,7 @@ class FeatureIDsJsonHandler using ValueType = CesiumGltf::FeatureIDs; FeatureIDsJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, CesiumGltf::FeatureIDs* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; diff --git a/CesiumGltfReader/generated/src/FeatureTableJsonHandler.h b/CesiumGltfReader/generated/src/FeatureTableJsonHandler.h index f2d5f34ac..f2669b621 100644 --- a/CesiumGltfReader/generated/src/FeatureTableJsonHandler.h +++ b/CesiumGltfReader/generated/src/FeatureTableJsonHandler.h @@ -11,7 +11,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -21,7 +21,7 @@ class FeatureTableJsonHandler using ValueType = CesiumGltf::FeatureTable; FeatureTableJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, CesiumGltf::FeatureTable* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; diff --git a/CesiumGltfReader/generated/src/FeatureTablePropertyJsonHandler.h b/CesiumGltfReader/generated/src/FeatureTablePropertyJsonHandler.h index fab2ff647..e88b5e786 100644 --- a/CesiumGltfReader/generated/src/FeatureTablePropertyJsonHandler.h +++ b/CesiumGltfReader/generated/src/FeatureTablePropertyJsonHandler.h @@ -8,7 +8,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -18,7 +18,7 @@ class FeatureTablePropertyJsonHandler using ValueType = CesiumGltf::FeatureTableProperty; FeatureTablePropertyJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset( IJsonHandler* pParentHandler, CesiumGltf::FeatureTableProperty* pObject); diff --git a/CesiumGltfReader/generated/src/FeatureTextureJsonHandler.h b/CesiumGltfReader/generated/src/FeatureTextureJsonHandler.h index 0cf3ad846..b0b8d2e55 100644 --- a/CesiumGltfReader/generated/src/FeatureTextureJsonHandler.h +++ b/CesiumGltfReader/generated/src/FeatureTextureJsonHandler.h @@ -10,7 +10,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -20,7 +20,7 @@ class FeatureTextureJsonHandler using ValueType = CesiumGltf::FeatureTexture; FeatureTextureJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, CesiumGltf::FeatureTexture* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; diff --git a/CesiumGltfReader/generated/src/GeneratedJsonHandlers.cpp b/CesiumGltfReader/generated/src/GeneratedJsonHandlers.cpp index c305b3691..660088e7a 100644 --- a/CesiumGltfReader/generated/src/GeneratedJsonHandlers.cpp +++ b/CesiumGltfReader/generated/src/GeneratedJsonHandlers.cpp @@ -10,8 +10,8 @@ namespace CesiumGltfReader { ExtensionCesiumRTCJsonHandler::ExtensionCesiumRTCJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _center() {} + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _center() {} void ExtensionCesiumRTCJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, @@ -67,8 +67,8 @@ ExtensionCesiumRTCJsonHandler::readObjectKeyExtensionCesiumRTC( namespace CesiumGltfReader { ExtensionCesiumTileEdgesJsonHandler::ExtensionCesiumTileEdgesJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _left(), _bottom(), _right(), @@ -137,13 +137,13 @@ namespace CesiumGltfReader { ExtensionModelExtFeatureMetadataJsonHandler:: ExtensionModelExtFeatureMetadataJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _schema(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), + _schema(options), _schemaUri(), - _statistics(context), - _featureTables(context), - _featureTextures(context) {} + _statistics(options), + _featureTables(options), + _featureTextures(options) {} void ExtensionModelExtFeatureMetadataJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, @@ -214,10 +214,10 @@ namespace CesiumGltfReader { ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler:: ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _featureIdAttributes(context), - _featureIdTextures(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), + _featureIdAttributes(options), + _featureIdTextures(options), _featureTextures() {} void ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler::reset( @@ -294,9 +294,9 @@ namespace CesiumGltfReader { ExtensionExtInstanceFeaturesJsonHandler:: ExtensionExtInstanceFeaturesJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _featureIds(context) {} + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), + _featureIds(options) {} void ExtensionExtInstanceFeaturesJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, @@ -354,9 +354,9 @@ CesiumJsonReader::IJsonHandler* ExtensionExtInstanceFeaturesJsonHandler:: namespace CesiumGltfReader { ExtensionExtMeshFeaturesJsonHandler::ExtensionExtMeshFeaturesJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _featureIds(context) {} + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), + _featureIds(options) {} void ExtensionExtMeshFeaturesJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, @@ -415,8 +415,8 @@ namespace CesiumGltfReader { ExtensionExtMeshGpuInstancingJsonHandler:: ExtensionExtMeshGpuInstancingJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _attributes() {} + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _attributes() {} void ExtensionExtMeshGpuInstancingJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, @@ -475,8 +475,8 @@ namespace CesiumGltfReader { ExtensionBufferExtMeshoptCompressionJsonHandler:: ExtensionBufferExtMeshoptCompressionJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _fallback() {} + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _fallback() {} void ExtensionBufferExtMeshoptCompressionJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, @@ -537,8 +537,8 @@ namespace CesiumGltfReader { ExtensionBufferViewExtMeshoptCompressionJsonHandler:: ExtensionBufferViewExtMeshoptCompressionJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _buffer(), _byteOffset(), _byteLength(), @@ -620,13 +620,13 @@ namespace CesiumGltfReader { ExtensionModelExtStructuralMetadataJsonHandler:: ExtensionModelExtStructuralMetadataJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _schema(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), + _schema(options), _schemaUri(), - _propertyTables(context), - _propertyTextures(context), - _propertyAttributes(context) {} + _propertyTables(options), + _propertyTextures(options), + _propertyAttributes(options) {} void ExtensionModelExtStructuralMetadataJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, @@ -700,8 +700,8 @@ namespace CesiumGltfReader { ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler:: ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _propertyTextures(), _propertyAttributes() {} @@ -774,8 +774,8 @@ namespace CesiumGltfReader { ExtensionKhrDracoMeshCompressionJsonHandler:: ExtensionKhrDracoMeshCompressionJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _bufferView(), _attributes() {} @@ -838,8 +838,8 @@ CesiumJsonReader::IJsonHandler* ExtensionKhrDracoMeshCompressionJsonHandler:: namespace CesiumGltfReader { ExtensionKhrMaterialsUnlitJsonHandler::ExtensionKhrMaterialsUnlitJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context) {} + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options) {} void ExtensionKhrMaterialsUnlitJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, @@ -897,9 +897,9 @@ namespace CesiumGltfReader { ExtensionModelKhrMaterialsVariantsJsonHandler:: ExtensionModelKhrMaterialsVariantsJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _variants(context) {} + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), + _variants(options) {} void ExtensionModelKhrMaterialsVariantsJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, @@ -959,9 +959,9 @@ namespace CesiumGltfReader { ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler:: ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _mappings(context) {} + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), + _mappings(options) {} void ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, @@ -1023,8 +1023,8 @@ ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler:: namespace CesiumGltfReader { ExtensionKhrTextureBasisuJsonHandler::ExtensionKhrTextureBasisuJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _source() {} + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _source() {} void ExtensionKhrTextureBasisuJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, @@ -1083,10 +1083,10 @@ namespace CesiumGltfReader { ExtensionModelMaxarMeshVariantsJsonHandler:: ExtensionModelMaxarMeshVariantsJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _defaultProperty(), - _variants(context) {} + _variants(options) {} void ExtensionModelMaxarMeshVariantsJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, @@ -1147,9 +1147,9 @@ namespace CesiumGltfReader { ExtensionNodeMaxarMeshVariantsJsonHandler:: ExtensionNodeMaxarMeshVariantsJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _mappings(context) {} + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), + _mappings(options) {} void ExtensionNodeMaxarMeshVariantsJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, @@ -1208,8 +1208,8 @@ namespace CesiumGltfReader { ExtensionKhrTextureTransformJsonHandler:: ExtensionKhrTextureTransformJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _offset(), _rotation(), _scale(), @@ -1277,8 +1277,8 @@ CesiumJsonReader::IJsonHandler* ExtensionKhrTextureTransformJsonHandler:: namespace CesiumGltfReader { ExtensionTextureWebpJsonHandler::ExtensionTextureWebpJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _source() {} + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _source() {} void ExtensionTextureWebpJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, @@ -1335,8 +1335,8 @@ namespace CesiumGltfReader { ExtensionNodeMaxarMeshVariantsMappingsValueJsonHandler:: ExtensionNodeMaxarMeshVariantsMappingsValueJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _variants(), _mesh(), _name() {} @@ -1390,8 +1390,8 @@ namespace CesiumGltfReader { ExtensionModelMaxarMeshVariantsValueJsonHandler:: ExtensionModelMaxarMeshVariantsValueJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumGltfReader::NamedObjectJsonHandler(context), _name() {} + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumGltfReader::NamedObjectJsonHandler(options), _name() {} void ExtensionModelMaxarMeshVariantsValueJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, @@ -1438,8 +1438,8 @@ namespace CesiumGltfReader { ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueJsonHandler:: ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _variants(), _material(), _name() {} @@ -1497,8 +1497,8 @@ namespace CesiumGltfReader { ExtensionModelKhrMaterialsVariantsValueJsonHandler:: ExtensionModelKhrMaterialsVariantsValueJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumGltfReader::NamedObjectJsonHandler(context), _name() {} + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumGltfReader::NamedObjectJsonHandler(options), _name() {} void ExtensionModelKhrMaterialsVariantsValueJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, @@ -1545,11 +1545,11 @@ namespace CesiumGltfReader { ExtensionExtStructuralMetadataPropertyAttributeJsonHandler:: ExtensionExtStructuralMetadataPropertyAttributeJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _name(), _classProperty(), - _properties(context) {} + _properties(options) {} void ExtensionExtStructuralMetadataPropertyAttributeJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, @@ -1600,8 +1600,8 @@ namespace CesiumGltfReader { ExtensionExtStructuralMetadataPropertyAttributePropertyJsonHandler:: ExtensionExtStructuralMetadataPropertyAttributePropertyJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _attribute(), _offset(), _scale(), @@ -1665,11 +1665,11 @@ namespace CesiumGltfReader { ExtensionExtStructuralMetadataPropertyTextureJsonHandler:: ExtensionExtStructuralMetadataPropertyTextureJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _name(), _classProperty(), - _properties(context) {} + _properties(options) {} void ExtensionExtStructuralMetadataPropertyTextureJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, @@ -1720,8 +1720,8 @@ namespace CesiumGltfReader { ExtensionExtStructuralMetadataPropertyTexturePropertyJsonHandler:: ExtensionExtStructuralMetadataPropertyTexturePropertyJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : TextureInfoJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : TextureInfoJsonHandler(options), _channels(), _offset(), _scale(), @@ -1783,8 +1783,8 @@ ExtensionExtStructuralMetadataPropertyTexturePropertyJsonHandler:: namespace CesiumGltfReader { TextureInfoJsonHandler::TextureInfoJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _index(), _texCoord() {} @@ -1833,12 +1833,12 @@ namespace CesiumGltfReader { ExtensionExtStructuralMetadataPropertyTableJsonHandler:: ExtensionExtStructuralMetadataPropertyTableJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _name(), _classProperty(), _count(), - _properties(context) {} + _properties(options) {} void ExtensionExtStructuralMetadataPropertyTableJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, @@ -1891,8 +1891,8 @@ namespace CesiumGltfReader { ExtensionExtStructuralMetadataPropertyTablePropertyJsonHandler:: ExtensionExtStructuralMetadataPropertyTablePropertyJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _values(), _arrayOffsets(), _stringOffsets(), @@ -1970,14 +1970,14 @@ namespace CesiumGltfReader { ExtensionExtStructuralMetadataSchemaJsonHandler:: ExtensionExtStructuralMetadataSchemaJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _id(), _name(), _description(), _version(), - _classes(context), - _enums(context) {} + _classes(options), + _enums(options) {} void ExtensionExtStructuralMetadataSchemaJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, @@ -2034,12 +2034,12 @@ namespace CesiumGltfReader { ExtensionExtStructuralMetadataEnumJsonHandler:: ExtensionExtStructuralMetadataEnumJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _name(), _description(), _valueType(), - _values(context) {} + _values(options) {} void ExtensionExtStructuralMetadataEnumJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, @@ -2091,8 +2091,8 @@ namespace CesiumGltfReader { ExtensionExtStructuralMetadataEnumValueJsonHandler:: ExtensionExtStructuralMetadataEnumValueJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _name(), _description(), _value() {} @@ -2146,11 +2146,11 @@ namespace CesiumGltfReader { ExtensionExtStructuralMetadataClassJsonHandler:: ExtensionExtStructuralMetadataClassJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _name(), _description(), - _properties(context) {} + _properties(options) {} void ExtensionExtStructuralMetadataClassJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, @@ -2200,8 +2200,8 @@ namespace CesiumGltfReader { ExtensionExtStructuralMetadataClassPropertyJsonHandler:: ExtensionExtStructuralMetadataClassPropertyJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _name(), _description(), _type(), @@ -2294,13 +2294,13 @@ namespace CesiumGltfReader { ExtensionExtMeshFeaturesFeatureIdJsonHandler:: ExtensionExtMeshFeaturesFeatureIdJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _featureCount(), _nullFeatureId(), _label(), _attribute(), - _texture(context), + _texture(options), _propertyTable() {} void ExtensionExtMeshFeaturesFeatureIdJsonHandler::reset( @@ -2357,8 +2357,8 @@ namespace CesiumGltfReader { ExtensionExtMeshFeaturesFeatureIdTextureJsonHandler:: ExtensionExtMeshFeaturesFeatureIdTextureJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : TextureInfoJsonHandler(context), _channels() {} + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : TextureInfoJsonHandler(options), _channels() {} void ExtensionExtMeshFeaturesFeatureIdTextureJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, @@ -2405,8 +2405,8 @@ namespace CesiumGltfReader { ExtensionExtInstanceFeaturesFeatureIdJsonHandler:: ExtensionExtInstanceFeaturesFeatureIdJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _featureCount(), _nullFeatureId(), _label(), @@ -2465,10 +2465,10 @@ ExtensionExtInstanceFeaturesFeatureIdJsonHandler:: namespace CesiumGltfReader { FeatureIDTextureJsonHandler::FeatureIDTextureJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _featureTable(), - _featureIds(context) {} + _featureIds(options) {} void FeatureIDTextureJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, @@ -2514,10 +2514,10 @@ FeatureIDTextureJsonHandler::readObjectKeyFeatureIDTexture( namespace CesiumGltfReader { TextureAccessorJsonHandler::TextureAccessorJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _channels(), - _texture(context) {} + _texture(options) {} void TextureAccessorJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, @@ -2563,10 +2563,10 @@ TextureAccessorJsonHandler::readObjectKeyTextureAccessor( namespace CesiumGltfReader { FeatureIDAttributeJsonHandler::FeatureIDAttributeJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _featureTable(), - _featureIds(context) {} + _featureIds(options) {} void FeatureIDAttributeJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, @@ -2612,8 +2612,8 @@ FeatureIDAttributeJsonHandler::readObjectKeyFeatureIDAttribute( namespace CesiumGltfReader { FeatureIDsJsonHandler::FeatureIDsJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _attribute(), _constant(), _divisor() {} @@ -2663,10 +2663,10 @@ CesiumJsonReader::IJsonHandler* FeatureIDsJsonHandler::readObjectKeyFeatureIDs( namespace CesiumGltfReader { FeatureTextureJsonHandler::FeatureTextureJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _classProperty(), - _properties(context) {} + _properties(options) {} void FeatureTextureJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, @@ -2712,11 +2712,11 @@ FeatureTextureJsonHandler::readObjectKeyFeatureTexture( namespace CesiumGltfReader { FeatureTableJsonHandler::FeatureTableJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _classProperty(), _count(), - _properties(context) {} + _properties(options) {} void FeatureTableJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, @@ -2764,8 +2764,8 @@ FeatureTableJsonHandler::readObjectKeyFeatureTable( namespace CesiumGltfReader { FeatureTablePropertyJsonHandler::FeatureTablePropertyJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _bufferView(), _offsetType(), _arrayOffsetBufferView(), @@ -2825,9 +2825,9 @@ FeatureTablePropertyJsonHandler::readObjectKeyFeatureTableProperty( namespace CesiumGltfReader { StatisticsJsonHandler::StatisticsJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), - _classes(context) {} + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), + _classes(options) {} void StatisticsJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, @@ -2870,10 +2870,10 @@ CesiumJsonReader::IJsonHandler* StatisticsJsonHandler::readObjectKeyStatistics( namespace CesiumGltfReader { ClassStatisticsJsonHandler::ClassStatisticsJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _count(), - _properties(context) {} + _properties(options) {} void ClassStatisticsJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, @@ -2919,8 +2919,8 @@ ClassStatisticsJsonHandler::readObjectKeyClassStatistics( namespace CesiumGltfReader { PropertyStatisticsJsonHandler::PropertyStatisticsJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _min(), _max(), _mean(), @@ -2989,13 +2989,13 @@ PropertyStatisticsJsonHandler::readObjectKeyPropertyStatistics( namespace CesiumGltfReader { SchemaJsonHandler::SchemaJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _name(), _description(), _version(), - _classes(context), - _enums(context) {} + _classes(options), + _enums(options) {} void SchemaJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, @@ -3046,12 +3046,12 @@ CesiumJsonReader::IJsonHandler* SchemaJsonHandler::readObjectKeySchema( namespace CesiumGltfReader { EnumJsonHandler::EnumJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _name(), _description(), _valueType(), - _values(context) {} + _values(options) {} void EnumJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, @@ -3100,8 +3100,8 @@ CesiumJsonReader::IJsonHandler* EnumJsonHandler::readObjectKeyEnum( namespace CesiumGltfReader { EnumValueJsonHandler::EnumValueJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _name(), _description(), _value() {} @@ -3151,11 +3151,11 @@ CesiumJsonReader::IJsonHandler* EnumValueJsonHandler::readObjectKeyEnumValue( namespace CesiumGltfReader { ClassJsonHandler::ClassJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _name(), _description(), - _properties(context) {} + _properties(options) {} void ClassJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, @@ -3202,8 +3202,8 @@ CesiumJsonReader::IJsonHandler* ClassJsonHandler::readObjectKeyClass( namespace CesiumGltfReader { ClassPropertyJsonHandler::ClassPropertyJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _name(), _description(), _type(), @@ -3281,25 +3281,25 @@ ClassPropertyJsonHandler::readObjectKeyClassProperty( namespace CesiumGltfReader { ModelJsonHandler::ModelJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _extensionsUsed(), _extensionsRequired(), - _accessors(context), - _animations(context), - _asset(context), - _buffers(context), - _bufferViews(context), - _cameras(context), - _images(context), - _materials(context), - _meshes(context), - _nodes(context), - _samplers(context), + _accessors(options), + _animations(options), + _asset(options), + _buffers(options), + _bufferViews(options), + _cameras(options), + _images(options), + _materials(options), + _meshes(options), + _nodes(options), + _samplers(options), _scene(), - _scenes(context), - _skins(context), - _textures(context) {} + _scenes(options), + _skins(options), + _textures(options) {} void ModelJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, @@ -3377,8 +3377,8 @@ CesiumJsonReader::IJsonHandler* ModelJsonHandler::readObjectKeyModel( namespace CesiumGltfReader { TextureJsonHandler::TextureJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumGltfReader::NamedObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumGltfReader::NamedObjectJsonHandler(options), _sampler(), _source() {} @@ -3425,8 +3425,8 @@ CesiumJsonReader::IJsonHandler* TextureJsonHandler::readObjectKeyTexture( namespace CesiumGltfReader { SkinJsonHandler::SkinJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumGltfReader::NamedObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumGltfReader::NamedObjectJsonHandler(options), _inverseBindMatrices(), _skeleton(), _joints() {} @@ -3479,8 +3479,8 @@ CesiumJsonReader::IJsonHandler* SkinJsonHandler::readObjectKeySkin( namespace CesiumGltfReader { SceneJsonHandler::SceneJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumGltfReader::NamedObjectJsonHandler(context), _nodes() {} + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumGltfReader::NamedObjectJsonHandler(options), _nodes() {} void SceneJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, @@ -3523,8 +3523,8 @@ CesiumJsonReader::IJsonHandler* SceneJsonHandler::readObjectKeyScene( namespace CesiumGltfReader { SamplerJsonHandler::SamplerJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumGltfReader::NamedObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumGltfReader::NamedObjectJsonHandler(options), _magFilter(), _minFilter(), _wrapS(), @@ -3577,8 +3577,8 @@ CesiumJsonReader::IJsonHandler* SamplerJsonHandler::readObjectKeySampler( namespace CesiumGltfReader { NodeJsonHandler::NodeJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumGltfReader::NamedObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumGltfReader::NamedObjectJsonHandler(options), _camera(), _children(), _skin(), @@ -3646,9 +3646,9 @@ CesiumJsonReader::IJsonHandler* NodeJsonHandler::readObjectKeyNode( namespace CesiumGltfReader { MeshJsonHandler::MeshJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumGltfReader::NamedObjectJsonHandler(context), - _primitives(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumGltfReader::NamedObjectJsonHandler(options), + _primitives(options), _weights() {} void MeshJsonHandler::reset( @@ -3694,8 +3694,8 @@ CesiumJsonReader::IJsonHandler* MeshJsonHandler::readObjectKeyMesh( namespace CesiumGltfReader { MeshPrimitiveJsonHandler::MeshPrimitiveJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _attributes(), _indices(), _material(), @@ -3752,12 +3752,12 @@ MeshPrimitiveJsonHandler::readObjectKeyMeshPrimitive( namespace CesiumGltfReader { MaterialJsonHandler::MaterialJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumGltfReader::NamedObjectJsonHandler(context), - _pbrMetallicRoughness(context), - _normalTexture(context), - _occlusionTexture(context), - _emissiveTexture(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumGltfReader::NamedObjectJsonHandler(options), + _pbrMetallicRoughness(options), + _normalTexture(options), + _occlusionTexture(options), + _emissiveTexture(options), _emissiveFactor(), _alphaMode(), _alphaCutoff(), @@ -3828,8 +3828,8 @@ namespace CesiumGltfReader { MaterialOcclusionTextureInfoJsonHandler:: MaterialOcclusionTextureInfoJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : TextureInfoJsonHandler(context), _strength() {} + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : TextureInfoJsonHandler(options), _strength() {} void MaterialOcclusionTextureInfoJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, @@ -3874,8 +3874,8 @@ CesiumJsonReader::IJsonHandler* MaterialOcclusionTextureInfoJsonHandler:: namespace CesiumGltfReader { MaterialNormalTextureInfoJsonHandler::MaterialNormalTextureInfoJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : TextureInfoJsonHandler(context), _scale() {} + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : TextureInfoJsonHandler(options), _scale() {} void MaterialNormalTextureInfoJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, @@ -3921,13 +3921,13 @@ namespace CesiumGltfReader { MaterialPBRMetallicRoughnessJsonHandler:: MaterialPBRMetallicRoughnessJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _baseColorFactor(), - _baseColorTexture(context), + _baseColorTexture(options), _metallicFactor(), _roughnessFactor(), - _metallicRoughnessTexture(context) {} + _metallicRoughnessTexture(options) {} void MaterialPBRMetallicRoughnessJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, @@ -3992,8 +3992,8 @@ CesiumJsonReader::IJsonHandler* MaterialPBRMetallicRoughnessJsonHandler:: namespace CesiumGltfReader { ImageJsonHandler::ImageJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumGltfReader::NamedObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumGltfReader::NamedObjectJsonHandler(options), _uri(), _mimeType(), _bufferView() {} @@ -4043,10 +4043,10 @@ CesiumJsonReader::IJsonHandler* ImageJsonHandler::readObjectKeyImage( namespace CesiumGltfReader { CameraJsonHandler::CameraJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumGltfReader::NamedObjectJsonHandler(context), - _orthographic(context), - _perspective(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumGltfReader::NamedObjectJsonHandler(options), + _orthographic(options), + _perspective(options), _type() {} void CameraJsonHandler::reset( @@ -4094,8 +4094,8 @@ CesiumJsonReader::IJsonHandler* CameraJsonHandler::readObjectKeyCamera( namespace CesiumGltfReader { CameraPerspectiveJsonHandler::CameraPerspectiveJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _aspectRatio(), _yfov(), _zfar(), @@ -4149,8 +4149,8 @@ CameraPerspectiveJsonHandler::readObjectKeyCameraPerspective( namespace CesiumGltfReader { CameraOrthographicJsonHandler::CameraOrthographicJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _xmag(), _ymag(), _zfar(), @@ -4204,8 +4204,8 @@ CameraOrthographicJsonHandler::readObjectKeyCameraOrthographic( namespace CesiumGltfReader { BufferViewJsonHandler::BufferViewJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumGltfReader::NamedObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumGltfReader::NamedObjectJsonHandler(options), _buffer(), _byteOffset(), _byteLength(), @@ -4261,8 +4261,8 @@ CesiumJsonReader::IJsonHandler* BufferViewJsonHandler::readObjectKeyBufferView( namespace CesiumGltfReader { BufferJsonHandler::BufferJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumGltfReader::NamedObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumGltfReader::NamedObjectJsonHandler(options), _uri(), _byteLength() {} @@ -4309,8 +4309,8 @@ CesiumJsonReader::IJsonHandler* BufferJsonHandler::readObjectKeyBuffer( namespace CesiumGltfReader { AssetJsonHandler::AssetJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _copyright(), _generator(), _version(), @@ -4363,10 +4363,10 @@ CesiumJsonReader::IJsonHandler* AssetJsonHandler::readObjectKeyAsset( namespace CesiumGltfReader { AnimationJsonHandler::AnimationJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumGltfReader::NamedObjectJsonHandler(context), - _channels(context), - _samplers(context) {} + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumGltfReader::NamedObjectJsonHandler(options), + _channels(options), + _samplers(options) {} void AnimationJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, @@ -4411,8 +4411,8 @@ CesiumJsonReader::IJsonHandler* AnimationJsonHandler::readObjectKeyAnimation( namespace CesiumGltfReader { AnimationSamplerJsonHandler::AnimationSamplerJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _input(), _interpolation(), _output() {} @@ -4463,10 +4463,10 @@ AnimationSamplerJsonHandler::readObjectKeyAnimationSampler( namespace CesiumGltfReader { AnimationChannelJsonHandler::AnimationChannelJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _sampler(), - _target(context) {} + _target(options) {} void AnimationChannelJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, @@ -4512,8 +4512,8 @@ AnimationChannelJsonHandler::readObjectKeyAnimationChannel( namespace CesiumGltfReader { AnimationChannelTargetJsonHandler::AnimationChannelTargetJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _node(), _path() {} @@ -4561,8 +4561,8 @@ AnimationChannelTargetJsonHandler::readObjectKeyAnimationChannelTarget( namespace CesiumGltfReader { AccessorJsonHandler::AccessorJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumGltfReader::NamedObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumGltfReader::NamedObjectJsonHandler(options), _bufferView(), _byteOffset(), _componentType(), @@ -4571,7 +4571,7 @@ AccessorJsonHandler::AccessorJsonHandler( _type(), _max(), _min(), - _sparse(context) {} + _sparse(options) {} void AccessorJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, @@ -4630,11 +4630,11 @@ CesiumJsonReader::IJsonHandler* AccessorJsonHandler::readObjectKeyAccessor( namespace CesiumGltfReader { AccessorSparseJsonHandler::AccessorSparseJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _count(), - _indices(context), - _values(context) {} + _indices(options), + _values(options) {} void AccessorSparseJsonHandler::reset( CesiumJsonReader::IJsonHandler* pParentHandler, @@ -4682,8 +4682,8 @@ AccessorSparseJsonHandler::readObjectKeyAccessorSparse( namespace CesiumGltfReader { AccessorSparseValuesJsonHandler::AccessorSparseValuesJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _bufferView(), _byteOffset() {} @@ -4731,8 +4731,8 @@ AccessorSparseValuesJsonHandler::readObjectKeyAccessorSparseValues( namespace CesiumGltfReader { AccessorSparseIndicesJsonHandler::AccessorSparseIndicesJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept - : CesiumJsonReader::ExtensibleObjectJsonHandler(context), + const CesiumJsonReader::JsonReaderOptions& options) noexcept + : CesiumJsonReader::ExtensibleObjectJsonHandler(options), _bufferView(), _byteOffset(), _componentType() {} diff --git a/CesiumGltfReader/generated/src/ImageJsonHandler.h b/CesiumGltfReader/generated/src/ImageJsonHandler.h index 682ba5daf..588d97774 100644 --- a/CesiumGltfReader/generated/src/ImageJsonHandler.h +++ b/CesiumGltfReader/generated/src/ImageJsonHandler.h @@ -9,7 +9,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -17,8 +17,7 @@ class ImageJsonHandler : public CesiumGltfReader::NamedObjectJsonHandler { public: using ValueType = CesiumGltf::Image; - ImageJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + ImageJsonHandler(const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, CesiumGltf::Image* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; diff --git a/CesiumGltfReader/generated/src/MaterialJsonHandler.h b/CesiumGltfReader/generated/src/MaterialJsonHandler.h index 4a5736d35..8f2f3d029 100644 --- a/CesiumGltfReader/generated/src/MaterialJsonHandler.h +++ b/CesiumGltfReader/generated/src/MaterialJsonHandler.h @@ -15,7 +15,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -24,7 +24,7 @@ class MaterialJsonHandler : public CesiumGltfReader::NamedObjectJsonHandler { using ValueType = CesiumGltf::Material; MaterialJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, CesiumGltf::Material* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; diff --git a/CesiumGltfReader/generated/src/MaterialNormalTextureInfoJsonHandler.h b/CesiumGltfReader/generated/src/MaterialNormalTextureInfoJsonHandler.h index afef5199a..dcf547695 100644 --- a/CesiumGltfReader/generated/src/MaterialNormalTextureInfoJsonHandler.h +++ b/CesiumGltfReader/generated/src/MaterialNormalTextureInfoJsonHandler.h @@ -8,7 +8,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -17,7 +17,7 @@ class MaterialNormalTextureInfoJsonHandler : public TextureInfoJsonHandler { using ValueType = CesiumGltf::MaterialNormalTextureInfo; MaterialNormalTextureInfoJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset( IJsonHandler* pParentHandler, CesiumGltf::MaterialNormalTextureInfo* pObject); diff --git a/CesiumGltfReader/generated/src/MaterialOcclusionTextureInfoJsonHandler.h b/CesiumGltfReader/generated/src/MaterialOcclusionTextureInfoJsonHandler.h index c2e63ae30..cfa4321f5 100644 --- a/CesiumGltfReader/generated/src/MaterialOcclusionTextureInfoJsonHandler.h +++ b/CesiumGltfReader/generated/src/MaterialOcclusionTextureInfoJsonHandler.h @@ -8,7 +8,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -17,7 +17,7 @@ class MaterialOcclusionTextureInfoJsonHandler : public TextureInfoJsonHandler { using ValueType = CesiumGltf::MaterialOcclusionTextureInfo; MaterialOcclusionTextureInfoJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset( IJsonHandler* pParentHandler, CesiumGltf::MaterialOcclusionTextureInfo* pObject); diff --git a/CesiumGltfReader/generated/src/MaterialPBRMetallicRoughnessJsonHandler.h b/CesiumGltfReader/generated/src/MaterialPBRMetallicRoughnessJsonHandler.h index 25a956c7d..241770726 100644 --- a/CesiumGltfReader/generated/src/MaterialPBRMetallicRoughnessJsonHandler.h +++ b/CesiumGltfReader/generated/src/MaterialPBRMetallicRoughnessJsonHandler.h @@ -10,7 +10,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -20,7 +20,7 @@ class MaterialPBRMetallicRoughnessJsonHandler using ValueType = CesiumGltf::MaterialPBRMetallicRoughness; MaterialPBRMetallicRoughnessJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset( IJsonHandler* pParentHandler, CesiumGltf::MaterialPBRMetallicRoughness* pObject); diff --git a/CesiumGltfReader/generated/src/MeshJsonHandler.h b/CesiumGltfReader/generated/src/MeshJsonHandler.h index 33eff5259..c1065e698 100644 --- a/CesiumGltfReader/generated/src/MeshJsonHandler.h +++ b/CesiumGltfReader/generated/src/MeshJsonHandler.h @@ -10,7 +10,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -18,8 +18,7 @@ class MeshJsonHandler : public CesiumGltfReader::NamedObjectJsonHandler { public: using ValueType = CesiumGltf::Mesh; - MeshJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + MeshJsonHandler(const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, CesiumGltf::Mesh* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; diff --git a/CesiumGltfReader/generated/src/MeshPrimitiveJsonHandler.h b/CesiumGltfReader/generated/src/MeshPrimitiveJsonHandler.h index 779fe42fa..fa769f937 100644 --- a/CesiumGltfReader/generated/src/MeshPrimitiveJsonHandler.h +++ b/CesiumGltfReader/generated/src/MeshPrimitiveJsonHandler.h @@ -9,7 +9,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -19,7 +19,7 @@ class MeshPrimitiveJsonHandler using ValueType = CesiumGltf::MeshPrimitive; MeshPrimitiveJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, CesiumGltf::MeshPrimitive* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; diff --git a/CesiumGltfReader/generated/src/ModelJsonHandler.h b/CesiumGltfReader/generated/src/ModelJsonHandler.h index 4f5db1a93..8c63ff67d 100644 --- a/CesiumGltfReader/generated/src/ModelJsonHandler.h +++ b/CesiumGltfReader/generated/src/ModelJsonHandler.h @@ -24,7 +24,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -32,8 +32,7 @@ class ModelJsonHandler : public CesiumJsonReader::ExtensibleObjectJsonHandler { public: using ValueType = CesiumGltf::Model; - ModelJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + ModelJsonHandler(const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, CesiumGltf::Model* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; diff --git a/CesiumGltfReader/generated/src/NodeJsonHandler.h b/CesiumGltfReader/generated/src/NodeJsonHandler.h index 504de48e8..bd5ef5159 100644 --- a/CesiumGltfReader/generated/src/NodeJsonHandler.h +++ b/CesiumGltfReader/generated/src/NodeJsonHandler.h @@ -10,7 +10,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -18,8 +18,7 @@ class NodeJsonHandler : public CesiumGltfReader::NamedObjectJsonHandler { public: using ValueType = CesiumGltf::Node; - NodeJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + NodeJsonHandler(const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, CesiumGltf::Node* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; diff --git a/CesiumGltfReader/generated/src/PropertyStatisticsJsonHandler.h b/CesiumGltfReader/generated/src/PropertyStatisticsJsonHandler.h index 6d28e4c5f..fcef7bc20 100644 --- a/CesiumGltfReader/generated/src/PropertyStatisticsJsonHandler.h +++ b/CesiumGltfReader/generated/src/PropertyStatisticsJsonHandler.h @@ -8,7 +8,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -18,7 +18,7 @@ class PropertyStatisticsJsonHandler using ValueType = CesiumGltf::PropertyStatistics; PropertyStatisticsJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, CesiumGltf::PropertyStatistics* pObject); diff --git a/CesiumGltfReader/generated/src/SamplerJsonHandler.h b/CesiumGltfReader/generated/src/SamplerJsonHandler.h index 5db47c51f..e975d6fda 100644 --- a/CesiumGltfReader/generated/src/SamplerJsonHandler.h +++ b/CesiumGltfReader/generated/src/SamplerJsonHandler.h @@ -8,7 +8,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -17,7 +17,7 @@ class SamplerJsonHandler : public CesiumGltfReader::NamedObjectJsonHandler { using ValueType = CesiumGltf::Sampler; SamplerJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, CesiumGltf::Sampler* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; diff --git a/CesiumGltfReader/generated/src/SceneJsonHandler.h b/CesiumGltfReader/generated/src/SceneJsonHandler.h index 7893c8080..5482245d5 100644 --- a/CesiumGltfReader/generated/src/SceneJsonHandler.h +++ b/CesiumGltfReader/generated/src/SceneJsonHandler.h @@ -9,7 +9,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -17,8 +17,7 @@ class SceneJsonHandler : public CesiumGltfReader::NamedObjectJsonHandler { public: using ValueType = CesiumGltf::Scene; - SceneJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + SceneJsonHandler(const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, CesiumGltf::Scene* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; diff --git a/CesiumGltfReader/generated/src/SchemaJsonHandler.h b/CesiumGltfReader/generated/src/SchemaJsonHandler.h index 496e45c14..70fc73f82 100644 --- a/CesiumGltfReader/generated/src/SchemaJsonHandler.h +++ b/CesiumGltfReader/generated/src/SchemaJsonHandler.h @@ -11,7 +11,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -20,7 +20,7 @@ class SchemaJsonHandler : public CesiumJsonReader::ExtensibleObjectJsonHandler { using ValueType = CesiumGltf::Schema; SchemaJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, CesiumGltf::Schema* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; diff --git a/CesiumGltfReader/generated/src/SkinJsonHandler.h b/CesiumGltfReader/generated/src/SkinJsonHandler.h index 7e75457ae..41ae924d2 100644 --- a/CesiumGltfReader/generated/src/SkinJsonHandler.h +++ b/CesiumGltfReader/generated/src/SkinJsonHandler.h @@ -9,7 +9,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -17,8 +17,7 @@ class SkinJsonHandler : public CesiumGltfReader::NamedObjectJsonHandler { public: using ValueType = CesiumGltf::Skin; - SkinJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + SkinJsonHandler(const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, CesiumGltf::Skin* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; diff --git a/CesiumGltfReader/generated/src/StatisticsJsonHandler.h b/CesiumGltfReader/generated/src/StatisticsJsonHandler.h index 5e6eb875e..99de58a79 100644 --- a/CesiumGltfReader/generated/src/StatisticsJsonHandler.h +++ b/CesiumGltfReader/generated/src/StatisticsJsonHandler.h @@ -9,7 +9,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -19,7 +19,7 @@ class StatisticsJsonHandler using ValueType = CesiumGltf::Statistics; StatisticsJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, CesiumGltf::Statistics* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; diff --git a/CesiumGltfReader/generated/src/TextureAccessorJsonHandler.h b/CesiumGltfReader/generated/src/TextureAccessorJsonHandler.h index cb0d84e57..54a0a0381 100644 --- a/CesiumGltfReader/generated/src/TextureAccessorJsonHandler.h +++ b/CesiumGltfReader/generated/src/TextureAccessorJsonHandler.h @@ -9,7 +9,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -19,7 +19,7 @@ class TextureAccessorJsonHandler using ValueType = CesiumGltf::TextureAccessor; TextureAccessorJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, CesiumGltf::TextureAccessor* pObject); diff --git a/CesiumGltfReader/generated/src/TextureInfoJsonHandler.h b/CesiumGltfReader/generated/src/TextureInfoJsonHandler.h index 913f6feb6..f064e0251 100644 --- a/CesiumGltfReader/generated/src/TextureInfoJsonHandler.h +++ b/CesiumGltfReader/generated/src/TextureInfoJsonHandler.h @@ -7,7 +7,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -17,7 +17,7 @@ class TextureInfoJsonHandler using ValueType = CesiumGltf::TextureInfo; TextureInfoJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, CesiumGltf::TextureInfo* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; diff --git a/CesiumGltfReader/generated/src/TextureJsonHandler.h b/CesiumGltfReader/generated/src/TextureJsonHandler.h index d83c59636..4d3f111f1 100644 --- a/CesiumGltfReader/generated/src/TextureJsonHandler.h +++ b/CesiumGltfReader/generated/src/TextureJsonHandler.h @@ -8,7 +8,7 @@ #include namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } namespace CesiumGltfReader { @@ -17,7 +17,7 @@ class TextureJsonHandler : public CesiumGltfReader::NamedObjectJsonHandler { using ValueType = CesiumGltf::Texture; TextureJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, CesiumGltf::Texture* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; diff --git a/CesiumGltfReader/generated/src/registerExtensions.cpp b/CesiumGltfReader/generated/src/registerExtensions.cpp index 15deefecc..0a9bffde5 100644 --- a/CesiumGltfReader/generated/src/registerExtensions.cpp +++ b/CesiumGltfReader/generated/src/registerExtensions.cpp @@ -32,68 +32,68 @@ #include #include #include -#include +#include namespace CesiumGltfReader { -void registerExtensions(CesiumJsonReader::ExtensionReaderContext& context) { - (void)context; - context.registerExtension(); - context.registerExtension< +void registerExtensions(CesiumJsonReader::JsonReaderOptions& options) { + (void)options; + options.registerExtension(); + options.registerExtension< CesiumGltf::Model, ExtensionModelExtFeatureMetadataJsonHandler>(); - context.registerExtension< + options.registerExtension< CesiumGltf::Model, ExtensionModelExtStructuralMetadataJsonHandler>(); - context.registerExtension< + options.registerExtension< CesiumGltf::Model, ExtensionModelKhrMaterialsVariantsJsonHandler>(); - context.registerExtension< + options.registerExtension< CesiumGltf::Model, ExtensionModelMaxarMeshVariantsJsonHandler>(); - context.registerExtension< + options.registerExtension< CesiumGltf::MeshPrimitive, ExtensionCesiumTileEdgesJsonHandler>(); - context.registerExtension< + options.registerExtension< CesiumGltf::MeshPrimitive, ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler>(); - context.registerExtension< + options.registerExtension< CesiumGltf::MeshPrimitive, ExtensionExtMeshFeaturesJsonHandler>(); - context.registerExtension< + options.registerExtension< CesiumGltf::MeshPrimitive, ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler>(); - context.registerExtension< + options.registerExtension< CesiumGltf::MeshPrimitive, ExtensionKhrDracoMeshCompressionJsonHandler>(); - context.registerExtension< + options.registerExtension< CesiumGltf::MeshPrimitive, ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler>(); - context.registerExtension< + options.registerExtension< CesiumGltf::Node, ExtensionExtInstanceFeaturesJsonHandler>(); - context.registerExtension< + options.registerExtension< CesiumGltf::Node, ExtensionExtMeshGpuInstancingJsonHandler>(); - context.registerExtension< + options.registerExtension< CesiumGltf::Node, ExtensionNodeMaxarMeshVariantsJsonHandler>(); - context.registerExtension< + options.registerExtension< CesiumGltf::Buffer, ExtensionBufferExtMeshoptCompressionJsonHandler>(); - context.registerExtension< + options.registerExtension< CesiumGltf::BufferView, ExtensionBufferViewExtMeshoptCompressionJsonHandler>(); - context.registerExtension< + options.registerExtension< CesiumGltf::Material, ExtensionKhrMaterialsUnlitJsonHandler>(); - context.registerExtension< + options.registerExtension< CesiumGltf::Texture, ExtensionKhrTextureBasisuJsonHandler>(); - context.registerExtension< + options.registerExtension< CesiumGltf::Texture, ExtensionTextureWebpJsonHandler>(); - context.registerExtension< + options.registerExtension< CesiumGltf::TextureInfo, ExtensionKhrTextureTransformJsonHandler>(); } diff --git a/CesiumGltfReader/generated/src/registerExtensions.h b/CesiumGltfReader/generated/src/registerExtensions.h index a48a8d097..8ed07e616 100644 --- a/CesiumGltfReader/generated/src/registerExtensions.h +++ b/CesiumGltfReader/generated/src/registerExtensions.h @@ -3,9 +3,9 @@ #pragma once namespace CesiumJsonReader { -class ExtensionReaderContext; +class JsonReaderOptions; } // namespace CesiumJsonReader namespace CesiumGltfReader { -void registerExtensions(CesiumJsonReader::ExtensionReaderContext& context); +void registerExtensions(CesiumJsonReader::JsonReaderOptions& options); } diff --git a/CesiumGltfReader/include/CesiumGltfReader/GltfReader.h b/CesiumGltfReader/include/CesiumGltfReader/GltfReader.h index e2ef4282b..92aa5af56 100644 --- a/CesiumGltfReader/include/CesiumGltfReader/GltfReader.h +++ b/CesiumGltfReader/include/CesiumGltfReader/GltfReader.h @@ -9,8 +9,8 @@ #include #include #include -#include #include +#include #include @@ -139,16 +139,14 @@ class CESIUMGLTFREADER_API GltfReader { GltfReader(); /** - * @brief Gets the context used to control how extensions are loaded from glTF - * files. + * @brief Gets the options controlling how the JSON is read. */ - CesiumJsonReader::ExtensionReaderContext& getExtensions(); + CesiumJsonReader::JsonReaderOptions& getOptions(); /** - * @brief Gets the context used to control how extensions are loaded from glTF - * files. + * @brief Gets the options controlling how the JSON is read. */ - const CesiumJsonReader::ExtensionReaderContext& getExtensions() const; + const CesiumJsonReader::JsonReaderOptions& getExtensions() const; /** * @brief Reads a glTF or binary glTF (GLB) from a buffer. @@ -210,7 +208,7 @@ class CESIUMGLTFREADER_API GltfReader { generateMipMaps(CesiumGltf::ImageCesium& image); private: - CesiumJsonReader::ExtensionReaderContext _context; + CesiumJsonReader::JsonReaderOptions _context; }; } // namespace CesiumGltfReader diff --git a/CesiumGltfReader/src/GltfReader.cpp b/CesiumGltfReader/src/GltfReader.cpp index 9c336df95..9ed43479d 100644 --- a/CesiumGltfReader/src/GltfReader.cpp +++ b/CesiumGltfReader/src/GltfReader.cpp @@ -12,9 +12,9 @@ #include #include #include -#include #include #include +#include #include #include @@ -63,7 +63,7 @@ bool isBinaryGltf(const gsl::span& data) noexcept { } GltfReaderResult readJsonGltf( - const CesiumJsonReader::ExtensionReaderContext& context, + const CesiumJsonReader::JsonReaderOptions& context, const gsl::span& data) { CESIUM_TRACE("CesiumGltfReader::GltfReader::readJsonGltf"); @@ -99,7 +99,7 @@ std::string toMagicString(uint32_t i) { } GltfReaderResult readBinaryGltf( - const CesiumJsonReader::ExtensionReaderContext& context, + const CesiumJsonReader::JsonReaderOptions& context, const gsl::span& data) { CESIUM_TRACE("CesiumGltfReader::GltfReader::readBinaryGltf"); @@ -333,12 +333,11 @@ void postprocess( GltfReader::GltfReader() : _context() { registerExtensions(this->_context); } -CesiumJsonReader::ExtensionReaderContext& GltfReader::getExtensions() { +CesiumJsonReader::JsonReaderOptions& GltfReader::getOptions() { return this->_context; } -const CesiumJsonReader::ExtensionReaderContext& -GltfReader::getExtensions() const { +const CesiumJsonReader::JsonReaderOptions& GltfReader::getExtensions() const { return this->_context; } @@ -346,8 +345,7 @@ GltfReaderResult GltfReader::readGltf( const gsl::span& data, const GltfReaderOptions& options) const { - const CesiumJsonReader::ExtensionReaderContext& context = - this->getExtensions(); + const CesiumJsonReader::JsonReaderOptions& context = this->getExtensions(); GltfReaderResult result = isBinaryGltf(data) ? readBinaryGltf(context, data) : readJsonGltf(context, data); diff --git a/CesiumGltfReader/src/NamedObjectJsonHandler.cpp b/CesiumGltfReader/src/NamedObjectJsonHandler.cpp index 246fd2638..33e440667 100644 --- a/CesiumGltfReader/src/NamedObjectJsonHandler.cpp +++ b/CesiumGltfReader/src/NamedObjectJsonHandler.cpp @@ -7,7 +7,7 @@ namespace CesiumGltfReader { NamedObjectJsonHandler::NamedObjectJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept + const CesiumJsonReader::JsonReaderOptions& context) noexcept : CesiumJsonReader::ExtensibleObjectJsonHandler(context), _name() {} void NamedObjectJsonHandler::reset( diff --git a/CesiumGltfReader/src/NamedObjectJsonHandler.h b/CesiumGltfReader/src/NamedObjectJsonHandler.h index e091f76a6..2638432a0 100644 --- a/CesiumGltfReader/src/NamedObjectJsonHandler.h +++ b/CesiumGltfReader/src/NamedObjectJsonHandler.h @@ -14,7 +14,7 @@ class NamedObjectJsonHandler : public CesiumJsonReader::ExtensibleObjectJsonHandler { protected: NamedObjectJsonHandler( - const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + const CesiumJsonReader::JsonReaderOptions& context) noexcept; void reset(IJsonHandler* pParentReader, CesiumGltf::NamedObject* pObject); IJsonHandler* readObjectKeyNamedObject( const std::string& objectType, diff --git a/CesiumGltfReader/test/TestGltfReader.cpp b/CesiumGltfReader/test/TestGltfReader.cpp index eac6e3e21..56660f56b 100644 --- a/CesiumGltfReader/test/TestGltfReader.cpp +++ b/CesiumGltfReader/test/TestGltfReader.cpp @@ -332,7 +332,7 @@ TEST_CASE("Can deserialize KHR_draco_mesh_compression") { // Repeat test but this time the extension should be deserialized as a // JsonValue. - reader.getExtensions().setExtensionState( + reader.getOptions().setExtensionState( "KHR_draco_mesh_compression", CesiumJsonReader::ExtensionState::JsonOnly); @@ -367,7 +367,7 @@ TEST_CASE("Can deserialize KHR_draco_mesh_compression") { ->getSafeNumberOrDefault(1) == 0); // Repeat test but this time the extension should not be deserialized at all. - reader.getExtensions().setExtensionState( + reader.getOptions().setExtensionState( "KHR_draco_mesh_compression", CesiumJsonReader::ExtensionState::Disabled); @@ -432,10 +432,10 @@ TEST_CASE("Extensions deserialize to JsonVaue iff " "Goodbye World"); // Repeat test but this time the extension should be skipped. - reader.getExtensions().setExtensionState( + reader.getOptions().setExtensionState( "A", CesiumJsonReader::ExtensionState::Disabled); - reader.getExtensions().setExtensionState( + reader.getOptions().setExtensionState( "B", CesiumJsonReader::ExtensionState::Disabled); diff --git a/CesiumJsonReader/include/CesiumJsonReader/ExtensibleObjectJsonHandler.h b/CesiumJsonReader/include/CesiumJsonReader/ExtensibleObjectJsonHandler.h index d2fc34bc3..d99de7ed5 100644 --- a/CesiumJsonReader/include/CesiumJsonReader/ExtensibleObjectJsonHandler.h +++ b/CesiumJsonReader/include/CesiumJsonReader/ExtensibleObjectJsonHandler.h @@ -1,9 +1,9 @@ #pragma once #include "DictionaryJsonHandler.h" -#include "ExtensionReaderContext.h" #include "ExtensionsJsonHandler.h" #include "JsonObjectJsonHandler.h" +#include "JsonReaderOptions.h" #include "ObjectJsonHandler.h" #include @@ -14,7 +14,7 @@ namespace CesiumJsonReader { class ExtensibleObjectJsonHandler : public CesiumJsonReader::ObjectJsonHandler { public: explicit ExtensibleObjectJsonHandler( - const ExtensionReaderContext& context) noexcept; + const JsonReaderOptions& context) noexcept; protected: void reset(IJsonHandler* pParent, CesiumUtility::ExtensibleObject* pObject); diff --git a/CesiumJsonReader/include/CesiumJsonReader/ExtensionsJsonHandler.h b/CesiumJsonReader/include/CesiumJsonReader/ExtensionsJsonHandler.h index e8616b205..6534614ee 100644 --- a/CesiumJsonReader/include/CesiumJsonReader/ExtensionsJsonHandler.h +++ b/CesiumJsonReader/include/CesiumJsonReader/ExtensionsJsonHandler.h @@ -1,7 +1,7 @@ #pragma once -#include "ExtensionReaderContext.h" #include "IExtensionJsonHandler.h" +#include "JsonReaderOptions.h" #include "ObjectJsonHandler.h" #include @@ -12,7 +12,7 @@ namespace CesiumJsonReader { class ExtensionsJsonHandler : public CesiumJsonReader::ObjectJsonHandler { public: - explicit ExtensionsJsonHandler(const ExtensionReaderContext& context) noexcept + explicit ExtensionsJsonHandler(const JsonReaderOptions& context) noexcept : ObjectJsonHandler(), _context(context), _pObject(nullptr), @@ -26,7 +26,7 @@ class ExtensionsJsonHandler : public CesiumJsonReader::ObjectJsonHandler { virtual IJsonHandler* readObjectKey(const std::string_view& str) override; private: - const ExtensionReaderContext& _context; + const JsonReaderOptions& _context; CesiumUtility::ExtensibleObject* _pObject = nullptr; std::string _objectType; std::unique_ptr _currentExtensionHandler; diff --git a/CesiumJsonReader/include/CesiumJsonReader/ExtensionReaderContext.h b/CesiumJsonReader/include/CesiumJsonReader/JsonReaderOptions.h similarity index 93% rename from CesiumJsonReader/include/CesiumJsonReader/ExtensionReaderContext.h rename to CesiumJsonReader/include/CesiumJsonReader/JsonReaderOptions.h index 7c6830dba..a71cd71c2 100644 --- a/CesiumJsonReader/include/CesiumJsonReader/ExtensionReaderContext.h +++ b/CesiumJsonReader/include/CesiumJsonReader/JsonReaderOptions.h @@ -42,7 +42,10 @@ enum class ExtensionState { Disabled }; -class CESIUMJSONREADER_API ExtensionReaderContext { +/** + * @brief Holds options for reading statically-typed data structures from JSON. + */ +class CESIUMJSONREADER_API JsonReaderOptions { public: /** * @brief Gets a value indicating whether the values of unknown properties are @@ -78,7 +81,7 @@ class CESIUMJSONREADER_API ExtensionReaderContext { this->_extensions.emplace(extensionName, ObjectTypeToHandler()).first; it->second.insert_or_assign( TExtended::TypeName, - ExtensionReaderFactory([](const ExtensionReaderContext& context) { + ExtensionReaderFactory([](const JsonReaderOptions& context) { return std::make_unique(context); })); } @@ -100,7 +103,7 @@ class CESIUMJSONREADER_API ExtensionReaderContext { .first; it->second.insert_or_assign( TExtended::TypeName, - ExtensionHandlerFactory([](const ExtensionReaderContext& context) { + ExtensionHandlerFactory([](const JsonReaderOptions& context) { return std::make_unique(context); })); } @@ -133,7 +136,7 @@ class CESIUMJSONREADER_API ExtensionReaderContext { private: using ExtensionHandlerFactory = std::function( - const ExtensionReaderContext&)>; + const JsonReaderOptions&)>; using ObjectTypeToHandler = std::map; using ExtensionNameMap = std::map; diff --git a/CesiumJsonReader/src/ExtensibleObjectJsonHandler.cpp b/CesiumJsonReader/src/ExtensibleObjectJsonHandler.cpp index f98860461..246e19119 100644 --- a/CesiumJsonReader/src/ExtensibleObjectJsonHandler.cpp +++ b/CesiumJsonReader/src/ExtensibleObjectJsonHandler.cpp @@ -1,13 +1,13 @@ #include "CesiumJsonReader/ExtensibleObjectJsonHandler.h" -#include "CesiumJsonReader/ExtensionReaderContext.h" #include "CesiumJsonReader/ExtensionsJsonHandler.h" #include "CesiumJsonReader/JsonHandler.h" +#include "CesiumJsonReader/JsonReaderOptions.h" #include "CesiumJsonReader/ObjectJsonHandler.h" namespace CesiumJsonReader { ExtensibleObjectJsonHandler::ExtensibleObjectJsonHandler( - const ExtensionReaderContext& context) noexcept + const JsonReaderOptions& context) noexcept : ObjectJsonHandler(), _extras(), _extensions(context), diff --git a/CesiumJsonReader/src/ExtensionsJsonHandler.cpp b/CesiumJsonReader/src/ExtensionsJsonHandler.cpp index f33feb155..2b3ef0e28 100644 --- a/CesiumJsonReader/src/ExtensionsJsonHandler.cpp +++ b/CesiumJsonReader/src/ExtensionsJsonHandler.cpp @@ -1,6 +1,6 @@ #include "CesiumJsonReader/ExtensionsJsonHandler.h" -#include "CesiumJsonReader/ExtensionReaderContext.h" +#include "CesiumJsonReader/JsonReaderOptions.h" namespace CesiumJsonReader { void ExtensionsJsonHandler::reset( diff --git a/CesiumJsonReader/src/ExtensionReaderContext.cpp b/CesiumJsonReader/src/JsonReaderOptions.cpp similarity index 96% rename from CesiumJsonReader/src/ExtensionReaderContext.cpp rename to CesiumJsonReader/src/JsonReaderOptions.cpp index db23c14ac..ad894ba0c 100644 --- a/CesiumJsonReader/src/ExtensionReaderContext.cpp +++ b/CesiumJsonReader/src/JsonReaderOptions.cpp @@ -1,4 +1,4 @@ -#include "CesiumJsonReader/ExtensionReaderContext.h" +#include "CesiumJsonReader/JsonReaderOptions.h" #include "CesiumJsonReader/IExtensionJsonHandler.h" #include "CesiumJsonReader/JsonObjectJsonHandler.h" @@ -73,14 +73,14 @@ class AnyExtensionJsonHandler : public JsonObjectJsonHandler, } }; -void ExtensionReaderContext::setExtensionState( +void JsonReaderOptions::setExtensionState( const std::string& extensionName, ExtensionState newState) { this->_extensionStates[extensionName] = newState; } std::unique_ptr -ExtensionReaderContext::createExtensionHandler( +JsonReaderOptions::createExtensionHandler( const std::string_view& extensionName, const std::string& extendedObjectType) const { diff --git a/tools/generate-classes/generate.js b/tools/generate-classes/generate.js index 698dfc0f7..cce3b7720 100644 --- a/tools/generate-classes/generate.js +++ b/tools/generate-classes/generate.js @@ -150,7 +150,7 @@ function generate(options, schema, writers) { ${readerHeaders.map((header) => `#include ${header}`).join("\n")} namespace CesiumJsonReader { - class ExtensionReaderContext; + class JsonReaderOptions; } namespace ${readerNamespace} { @@ -160,7 +160,7 @@ function generate(options, schema, writers) { ${thisConfig.extensionName ? `static inline constexpr const char* ExtensionName = "${thisConfig.extensionName}";` : ""} - ${name}JsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept; + ${name}JsonHandler(const CesiumJsonReader::JsonReaderOptions& options) noexcept; void reset(IJsonHandler* pParentHandler, ${namespace}::${name}* pObject); virtual IJsonHandler* readObjectKey(const std::string_view& str) override; @@ -268,7 +268,7 @@ function generate(options, schema, writers) { namespace ${readerNamespace} { - ${name}JsonHandler::${name}JsonHandler(const CesiumJsonReader::ExtensionReaderContext& context) noexcept : ${baseReader}(context)${generateReaderOptionsInitializerList(properties, 'context')} {} + ${name}JsonHandler::${name}JsonHandler(const CesiumJsonReader::JsonReaderOptions& options) noexcept : ${baseReader}(options)${generateReaderOptionsInitializerList(properties, 'options')} {} void ${name}JsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, ${namespace}::${name}* pObject) { ${baseReader}::reset(pParentHandler, pObject); diff --git a/tools/generate-classes/generateRegisterExtensions.js b/tools/generate-classes/generateRegisterExtensions.js index 5dbdb69a2..6ee45c33c 100644 --- a/tools/generate-classes/generateRegisterExtensions.js +++ b/tools/generate-classes/generateRegisterExtensions.js @@ -43,43 +43,43 @@ function generateRegisterExtensions(options) { // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #pragma once - + namespace CesiumJsonReader { - class ExtensionReaderContext; + class JsonReaderOptions; } // namespace CesiumJsonReader - + namespace ${readerNamespace} { - void registerExtensions(CesiumJsonReader::ExtensionReaderContext& context); + void registerExtensions(CesiumJsonReader::JsonReaderOptions& options); } `; const readerImplementation = ` // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! - + #include "${filename}.h" - - #include + + #include ${classNames .map((className) => { return `#include <${namespace}/${className}.h>`; }) .join("\n")} - + ${extensionClassNames .map((extensionClassName) => { return `#include "${extensionClassName}JsonHandler.h"`; }) - .join("\n")} + .join("\n")} namespace ${readerNamespace} { - - void registerExtensions(CesiumJsonReader::ExtensionReaderContext& context) { - (void)context; + + void registerExtensions(CesiumJsonReader::JsonReaderOptions& options) { + (void)options; ${registrations .map((registration) => { - return `context.registerExtension<${namespace}::${registration.className}, ${registration.extensionClassName}JsonHandler>();`; + return `options.registerExtension<${namespace}::${registration.className}, ${registration.extensionClassName}JsonHandler>();`; }) .join("\n")} } @@ -90,11 +90,11 @@ function generateRegisterExtensions(options) { // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #pragma once - + namespace CesiumJsonWriter { class ExtensionWriterContext; } // namespace CesiumJsonWriter - + namespace ${writerNamespace} { void registerExtensions(CesiumJsonWriter::ExtensionWriterContext& context); } @@ -103,9 +103,9 @@ function generateRegisterExtensions(options) { const writerImplementation = ` // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! - + #include "${filename}.h" - + #include "${rootSchemaName}JsonWriter.h" #include @@ -115,15 +115,15 @@ function generateRegisterExtensions(options) { return `#include <${namespace}/${className}.h>`; }) .join("\n")} - + ${extensionClassNames .map((extensionClassName) => { return `#include <${namespace}/${extensionClassName}.h>`; }) - .join("\n")} - + .join("\n")} + namespace ${writerNamespace} { - + void registerExtensions(CesiumJsonWriter::ExtensionWriterContext& context) { (void)context; ${registrations From bfbb6945e58a65aa3e4fd34796d93b9716b389ab Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Wed, 16 Aug 2023 13:56:37 -0400 Subject: [PATCH 167/421] Add support for vecN --- .../include/CesiumGltf/PropertyArrayView.h | 104 +- CesiumGltf/include/CesiumGltf/PropertyView.h | 797 +- CesiumGltf/test/TestPropertyTableView.cpp | 6494 ++++++++--------- CesiumGltf/test/TestPropertyView.cpp | 322 +- 4 files changed, 3746 insertions(+), 3971 deletions(-) diff --git a/CesiumGltf/include/CesiumGltf/PropertyArrayView.h b/CesiumGltf/include/CesiumGltf/PropertyArrayView.h index f7978107c..119187f24 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyArrayView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyArrayView.h @@ -67,7 +67,7 @@ template <> class PropertyArrayView { /** * @brief Constructs an empty array view. */ - PropertyArrayView() : _values{}, _bitOffset{0}, _size{0} {} + PropertyArrayView() : _values{}, _size{0} {} /** * @brief Constructs an array view from a buffer. @@ -81,7 +81,7 @@ template <> class PropertyArrayView { const gsl::span& buffer, int64_t bitOffset, int64_t size) noexcept - : _values{buffer}, _bitOffset{bitOffset}, _size{size} {} + : _values{BooleanArrayView{buffer, bitOffset}}, _size{size} {} /** * @brief Constructs an array view from a vector of values. This is mainly @@ -90,33 +90,35 @@ template <> class PropertyArrayView { * @param values The vector containing the values. */ PropertyArrayView(const std::vector&& values) - : _values{std::move(values)}, _bitOffset{0}, _size{0} { - const auto vectorValues = std::get>(_values); - _size = static_cast(vectorValues.size()); + : _values{std::move(values)}, _size{0} { + size_t size = std::get>(_values).size(); + _size = static_cast(size); } bool operator[](int64_t index) const noexcept { - // There's no way to access the bitstream data in std::vector, so this - // implementation is very "either or". - if (std::holds_alternative>(_values)) { - const auto vectorValues = std::get>(_values); - return vectorValues[static_cast(index)]; - } - - const auto values = std::get>(_values); - index += _bitOffset; - const int64_t byteIndex = index / 8; - const int64_t bitIndex = index % 8; - const int bitValue = static_cast(values[byteIndex] >> bitIndex) & 1; - return bitValue == 1; + return std::visit( + [index](auto const& values) -> auto const { return values[index]; }, + _values); } int64_t size() const noexcept { return _size; } private: - using ArrayType = std::variant, std::vector>; + struct BooleanArrayView { + gsl::span values; + int64_t bitOffset = 0; + + bool operator[](int64_t index) const noexcept { + index += bitOffset; + const int64_t byteIndex = index / 8; + const int64_t bitIndex = index % 8; + const int bitValue = static_cast(values[byteIndex] >> bitIndex) & 1; + return bitValue == 1; + } + }; + + using ArrayType = std::variant>; ArrayType _values; - int64_t _bitOffset; int64_t _size; }; @@ -125,8 +127,7 @@ template <> class PropertyArrayView { /** * @brief Constructs an empty array view. */ - PropertyArrayView() - : _values{}, _stringOffsets{}, _stringOffsetType{}, _size{0} {} + PropertyArrayView() : _values{}, _size{0} {} /** * @brief Constructs an array view from buffers and their information. @@ -141,9 +142,7 @@ template <> class PropertyArrayView { const gsl::span& stringOffsets, PropertyComponentType stringOffsetType, int64_t size) noexcept - : _values{values}, - _stringOffsets{stringOffsets}, - _stringOffsetType{stringOffsetType}, + : _values{StringArrayView{values, stringOffsets, stringOffsetType}}, _size{size} {} /** @@ -152,42 +151,43 @@ template <> class PropertyArrayView { * * @param values The vector containing the values. */ - PropertyArrayView(const std::vector&& values) - : _values{std::move(values)}, - _stringOffsets{}, - _stringOffsetType{PropertyComponentType::None}, - _size{0} { - const auto vectorValues = std::get>(_values); - _size = static_cast(vectorValues.size()); + PropertyArrayView(const std::vector&& values) noexcept + : _values{std::move(values)}, _size{0} { + size_t size = std::get>(_values).size(); + _size = static_cast(size); } std::string_view operator[](int64_t index) const noexcept { - if (std::holds_alternative>(_values)) { - const auto vectorValues = std::get>(_values); - return vectorValues[static_cast(index)]; - } - - const auto values = std::get>(_values); - const size_t currentOffset = - getOffsetFromOffsetsBuffer(index, _stringOffsets, _stringOffsetType); - const size_t nextOffset = getOffsetFromOffsetsBuffer( - index + 1, - _stringOffsets, - _stringOffsetType); - return std::string_view( - reinterpret_cast(values.data() + currentOffset), - (nextOffset - currentOffset)); + return std::visit( + [index](auto const& values) -> auto const { + return std::string_view(values[index]); + }, + _values); } int64_t size() const noexcept { return _size; } private: - using ArrayType = - std::variant, std::vector>; - ArrayType _values; - gsl::span _stringOffsets; + struct StringArrayView { + gsl::span values; + gsl::span stringOffsets; + PropertyComponentType stringOffsetType = PropertyComponentType::None; + + std::string_view operator[](int64_t index) const noexcept { + const size_t currentOffset = + getOffsetFromOffsetsBuffer(index, stringOffsets, stringOffsetType); + const size_t nextOffset = getOffsetFromOffsetsBuffer( + index + 1, + stringOffsets, + stringOffsetType); + return std::string_view( + reinterpret_cast(values.data() + currentOffset), + (nextOffset - currentOffset)); + } + }; - PropertyComponentType _stringOffsetType; + using ArrayType = std::variant>; + ArrayType _values; int64_t _size; }; } // namespace CesiumGltf diff --git a/CesiumGltf/include/CesiumGltf/PropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyView.h index 26bec92ea..39e9f7949 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyView.h @@ -7,43 +7,6 @@ #include namespace CesiumGltf { -/** - * @brief Indicates the status of a property view. - * - * The {@link PropertyView} constructor always completes successfully. - * However, it may find fundamental errors within the property definition - * itself. This enumeration provides the reason. - */ -enum class PropertyViewStatus { - /** - * @brief This property view is valid and ready to use. - */ - Valid, - - /** - * @brief This property view is trying to view a property that does not exist. - */ - ErrorNonexistentProperty, - - /** - * @brief This property view's type does not match what is - * specified in {@link ClassProperty::type}. - */ - ErrorTypeMismatch, - - /** - * @brief This property view's component type does not match what - * is specified in {@link ClassProperty::componentType}. - */ - ErrorComponentTypeMismatch, - - /** - * @brief This property view differs from what is specified in - * {@link ClassProperty::array}. - */ - ErrorArrayTypeMismatch, -}; - /** * @brief An interface for generic metadata property in EXT_structural_metadata. * @@ -52,6 +15,15 @@ enum class PropertyViewStatus { * property values. Although they are typically defined via class property, they * may be overriden by individual instances of the property. The constructor is * responsible for resolving those differences. + * + * However, there are fundamental differences between property tables, property + * textures, and property attributes. Notably, the ways in which values are + * stored -- as well as what types of values are even supported -- vary between + * the three. Therefore, this interface has no "status" and does not validate + * ElementType against the input class property. Derived classes must do their + * own validation to ensure that ElementType matches the given class definition. + * + * @tparam ElementType The C++ type of the values in this property */ template class IPropertyView { public: @@ -130,13 +102,6 @@ template class IPropertyView { * effect of normalized, offset, and scale properties into account. */ virtual std::optional defaultValue() const noexcept = 0; - -protected: - /** - * @brief Gets the status of the property view. Derived classes will often - * have their own status enumerations, so this is only used internally. - */ - virtual PropertyViewStatus propertyViewStatus() const noexcept = 0; }; /** @@ -153,8 +118,7 @@ class PropertyView : IPropertyView { * @brief Constructs an empty property instance. */ PropertyView() - : _propertyViewStatus(PropertyViewStatus::ErrorNonexistentProperty), - _normalized(false), + : _normalized(false), _offset(std::nullopt), _scale(std::nullopt), _max(std::nullopt), @@ -167,8 +131,7 @@ class PropertyView : IPropertyView { * @brief Constructs a property instance from a class definition only. */ PropertyView(const ClassProperty& classProperty) - : _propertyViewStatus(PropertyViewStatus::Valid), - _normalized(classProperty.normalized), + : _normalized(classProperty.normalized), _offset(std::nullopt), _scale(std::nullopt), _max(std::nullopt), @@ -176,49 +139,21 @@ class PropertyView : IPropertyView { _required(classProperty.required), _noData(std::nullopt), _defaultValue(std::nullopt) { - if (convertStringToPropertyType(classProperty.type) != - TypeToPropertyType::value) { - _propertyViewStatus = PropertyViewStatus::ErrorTypeMismatch; - return; + if constexpr (IsMetadataNumeric::value) { + _offset = + classProperty.offset ? getValue(*classProperty.offset) : std::nullopt; + _scale = + classProperty.scale ? getValue(*classProperty.scale) : std::nullopt; + _max = classProperty.max ? getValue(*classProperty.max) : std::nullopt; + _min = classProperty.min ? getValue(*classProperty.min) : std::nullopt; } - if (!classProperty.componentType && - TypeToPropertyType::component != - PropertyComponentType::None) { - _propertyViewStatus = PropertyViewStatus::ErrorComponentTypeMismatch; - return; - } - - if (classProperty.componentType && - convertStringToPropertyComponentType(*classProperty.componentType) != - TypeToPropertyType::component) { - _propertyViewStatus = PropertyViewStatus::ErrorComponentTypeMismatch; - return; - } - - if (classProperty.array) { - _propertyViewStatus = PropertyViewStatus::ErrorArrayTypeMismatch; - return; - } - - _offset = classProperty.offset - ? getValue(*classProperty.offset) - : std::nullopt; - _scale = classProperty.scale ? getValue(*classProperty.scale) - : std::nullopt; - _max = classProperty.max ? getValue(*classProperty.max) - : std::nullopt; - _min = classProperty.min ? getValue(*classProperty.min) - : std::nullopt; - if (!_required) { - _noData = classProperty.noData - ? getValue(*classProperty.noData) - : std::nullopt; - _defaultValue = - classProperty.defaultProperty - ? getValue(*classProperty.defaultProperty) - : std::nullopt; + _noData = + classProperty.noData ? getValue(*classProperty.noData) : std::nullopt; + _defaultValue = classProperty.defaultProperty + ? getValue(*classProperty.defaultProperty) + : std::nullopt; } } @@ -231,25 +166,23 @@ class PropertyView : IPropertyView { const ClassProperty& classProperty, const PropertyTableProperty& property) : PropertyView(classProperty) { - if (_propertyViewStatus != PropertyViewStatus::Valid) { - return; - } - // If the property has its own values, override the class-provided values. - if (property.offset) { - _offset = getValue(*property.offset); - } + if constexpr (IsMetadataNumeric::value) { + if (property.offset) { + _offset = getValue(*property.offset); + } - if (property.scale) { - _scale = getValue(*property.scale); - } + if (property.scale) { + _scale = getValue(*property.scale); + } - if (property.max) { - _max = getValue(*property.max); - } + if (property.max) { + _max = getValue(*property.max); + } - if (property.min) { - _min = getValue(*property.min); + if (property.min) { + _min = getValue(*property.min); + } } } @@ -261,25 +194,23 @@ class PropertyView : IPropertyView { const ClassProperty& classProperty, const PropertyTextureProperty& property) : PropertyView(classProperty) { - if (_propertyViewStatus != PropertyViewStatus::Valid) { - return; - } - // If the property has its own values, override the class-provided values. - if (property.offset) { - _offset = getValue(*property.offset); - } + if constexpr (IsMetadataNumeric::value) { + if (property.offset) { + _offset = getValue(*property.offset); + } - if (property.scale) { - _scale = getValue(*property.scale); - } + if (property.scale) { + _scale = getValue(*property.scale); + } - if (property.max) { - _max = getValue(*property.max); - } + if (property.max) { + _max = getValue(*property.max); + } - if (property.min) { - _min = getValue(*property.min); + if (property.min) { + _min = getValue(*property.min); + } } } @@ -328,30 +259,20 @@ class PropertyView : IPropertyView { virtual bool required() const noexcept override { return _required; } /** - * @copydoc IPropertyView::arrayCount + * @copydoc IPropertyView::noData */ virtual std::optional noData() const noexcept override { return _noData; } /** - * @copydoc IPropertyView::arrayCount + * @copydoc IPropertyView::defaultValue */ virtual std::optional defaultValue() const noexcept override { return _defaultValue; } -protected: - /** - * @copydoc IPropertyView::propertyViewStatus - */ - virtual PropertyViewStatus propertyViewStatus() const noexcept override { - return _propertyViewStatus; - } - private: - PropertyViewStatus _propertyViewStatus; - bool _normalized; std::optional _offset; @@ -369,98 +290,39 @@ class PropertyView : IPropertyView { * If T is a type with multiple components, e.g. a VECN or MATN type, this * will return std::nullopt if one or more components could not be parsed. * - * If T is an array view, this will return std::nullopt if one or - * more entries could not be parsed. - * * @return The value as an instance of T, or std::nullopt if it could not be * parsed. */ - template - static std::optional getValue(const CesiumUtility::JsonValue& jsonValue) { - if constexpr (IsMetadataScalar::value) { - return getScalar(jsonValue); - } else - - if constexpr (IsMetadataVecN::value) { - return getVecN(jsonValue); + static std::optional + getValue(const CesiumUtility::JsonValue& jsonValue) { + if constexpr (IsMetadataScalar::value) { + return getScalar(jsonValue); + } else if constexpr (IsMetadataVecN::value) { + return getVecN(jsonValue); } else return std::nullopt; - // if constexpr (IsMetadataMatN::value) { //} } /** - * @brief Attempts to parse from the given json value. If it could not be - * parsed as an ElementType, this returns the default value. - * - * If ElementType is a type with multiple components, e.g. a VECN or MATN - * type. - * - * @return The value as an ElementType, or std::nullopt if it could not be - * parsed. - */ - template - static T getValueOrDefault( - const CesiumUtility::JsonValue& jsonValue, - const T& defaultValue) { - if constexpr (IsMetadataScalar::value) { - return jsonValue.getSafeNumberOrDefault(defaultValue); - } - - if constexpr (IsMetadataVecN::value) { - return getVecNOrDefault(jsonValue, defaultValue); - } - - if constexpr (IsMetadataMatN::value) { - } - } - - /** - * @brief Attempts to parse from the given json value. If it could not be - * parsed as an ElementType, this returns the default value. + * @brief Attempts to parse a scalar from the given JSON value. If the JSON + * value is a number of the wrong type, this will round it to the closest + * representation in the desired type, if possible. Otherwise, this returns + * std::nullopt. * - * If ElementType is a type with multiple components, e.g. a VECN or MATN - * type. - * - * @return The value as an ElementType, or std::nullopt if it could not be + * @return The value as a type T, or std::nullopt if it could not be * parsed. */ template static std::optional getScalar(const CesiumUtility::JsonValue& jsonValue) { try { - return jsonValue.getSafeNumber(); + return jsonValue.getSafeNumber(); } catch (const CesiumUtility::JsonValueNotRealValue& /*error*/) { return std::nullopt; } catch (const gsl::narrowing_error& /*error*/) { - // Continue below. - } - - // If the value is in range, but it loses precision, a narrowing_error will - // be returned. Try to round to a reasonable number anyways, even if it - // loses precision. - if (jsonValue.isInt64()) { - int64_t int64Value = jsonValue.getInt64(); - if (int64Value >= - static_cast(std::numeric_limits::lowest()) && - int64Value <= static_cast(std::numeric_limits::max())) { - return static_cast(int64Value); - } - } else if (jsonValue.isUint64()) { - uint64_t uint64Value = jsonValue.getUint64(); - if (uint64Value <= static_cast(std::numeric_limits::max())) { - return static_cast(uint64Value); - } - } else if (jsonValue.isDouble()) { - double doubleValue = jsonValue.getDouble(); - if (doubleValue >= - static_cast(std::numeric_limits::lowest()) && - doubleValue <= static_cast(std::numeric_limits::max())) { - return static_cast(doubleValue); - } + return std::nullopt; } - - return std::nullopt; } template @@ -470,81 +332,55 @@ class PropertyView : IPropertyView { return std::nullopt; } - return std::nullopt; - // const CesiumUtility::JsonValue::Array& array = value.getArray(); - // glm::length_t N = VecType::length(); - // if (array.size() != N) { - // return std::nullopt; - //} + const CesiumUtility::JsonValue::Array& array = jsonValue.getArray(); + glm::length_t N = VecType::length(); + if (array.size() != N) { + return std::nullopt; + } - // using T = VecType::type; + using T = VecType::value_type; - // glm::length result; - // for (glm::length_t i = 0; i < N; i++) { - // std::optional value = getValue(array[i]); - // if (!value) { - // return std::nullopt; - // } + VecType result; + for (glm::length_t i = 0; i < N; i++) { + std::optional value = getScalar(array[i]); + if (!value) { + return std::nullopt; + } - // result[i] = value; - //} + result[i] = *value; + } - // return result; + return result; } - template - static VecType getVecNOrDefault( - const CesiumUtility::JsonValue& value, - const VecType& defaultValue) { + template + static std::optional + getMatNFromJsonValue(const CesiumUtility::JsonValue& value) { if (!jsonValue.isArray()) { - return defaultValue; + return std::nullopt; } const CesiumUtility::JsonValue::Array& array = jsonValue.getArray(); glm::length_t N = VecType::length(); - if (array.size() != N) { - return defaultValue; + glm::length_t numValues = N * N; + if (array.size() != numValues) { + return std::nullopt; } - using T = VecType::type; + using T = MatType::value_type; - VecType result; - for (glm::length_t i = 0; i < N; i++) { - std::optional value = getValue(array[i]); + MatType result; + for (glm::length_t i = 0; i < numValues; i++) { + std::optional value = getScalar(array[i]); if (!value) { return std::nullopt; } - result[i] = value ? *value : defaultValue; + result[i] = value; } return result; } - - // template - // static std::optional - // getMatNFromJsonValue(const CesiumUtility::JsonValue& value) { - // if (!jsonValue.isArray()) { - // return std::nullopt; - // } - - // const CesiumUtility::JsonValue::Array& array = jsonValue.getArray(); - // if (array.size() != N) { - // return std::nullopt; - // } - - // glm::vec result; - // for (glm::length_t i = 0; i < N; i++) { - // std::optional value = getValue(array[i]); - // if (!value) { - // return std::nullopt; - // } - - // result[i] = value; - // } - - // return result; - //} }; template <> class PropertyView : IPropertyView { @@ -552,28 +388,13 @@ template <> class PropertyView : IPropertyView { /** * @brief Constructs an empty property instance. */ - PropertyView() - : _propertyViewStatus(PropertyViewStatus::ErrorNonexistentProperty), - _required(false), - _defaultValue(std::nullopt) {} + PropertyView() : _required(false), _defaultValue(std::nullopt) {} /** * @brief Constructs a property instance from a class definition only. */ PropertyView(const ClassProperty& classProperty) - : _propertyViewStatus(PropertyViewStatus::Valid), - _required(classProperty.required), - _defaultValue(std::nullopt) { - if (classProperty.type != ClassProperty::Type::BOOLEAN) { - _propertyViewStatus = PropertyViewStatus::ErrorTypeMismatch; - return; - } - - if (classProperty.array) { - _propertyViewStatus = PropertyViewStatus::ErrorArrayTypeMismatch; - return; - } - + : _required(classProperty.required), _defaultValue(std::nullopt) { if (!_required) { _defaultValue = classProperty.defaultProperty ? getBooleanValue(*classProperty.defaultProperty) @@ -581,6 +402,7 @@ template <> class PropertyView : IPropertyView { } } +protected: /** * @brief Constructs a property instance from a property table property and * its class definition. @@ -599,8 +421,7 @@ template <> class PropertyView : IPropertyView { const PropertyTextureProperty& /*property*/) : PropertyView(classProperty) {} - ~PropertyView() {} - +public: /** * @copydoc IPropertyView::arrayCount */ @@ -658,16 +479,7 @@ template <> class PropertyView : IPropertyView { return _defaultValue; } -protected: - /** - * @copydoc IPropertyView::propertyViewStatus - */ - virtual PropertyViewStatus propertyViewStatus() const noexcept override { - return _propertyViewStatus; - } - private: - PropertyViewStatus _propertyViewStatus; bool _required; std::optional _defaultValue; @@ -688,29 +500,15 @@ class PropertyView : IPropertyView { * @brief Constructs an empty property instance. */ PropertyView() - : _propertyViewStatus(PropertyViewStatus::ErrorNonexistentProperty), - _required(false), - _noData(std::nullopt), - _defaultValue(std::nullopt) {} + : _required(false), _noData(std::nullopt), _defaultValue(std::nullopt) {} /** * @brief Constructs a property instance from a class definition only. */ PropertyView(const ClassProperty& classProperty) - : _propertyViewStatus(PropertyViewStatus::Valid), - _required(classProperty.required), + : _required(classProperty.required), _noData(std::nullopt), _defaultValue(std::nullopt) { - if (classProperty.type != ClassProperty::Type::STRING) { - _propertyViewStatus = PropertyViewStatus::ErrorTypeMismatch; - return; - } - - if (classProperty.array) { - _propertyViewStatus = PropertyViewStatus::ErrorArrayTypeMismatch; - return; - } - if (!_required) { _noData = classProperty.noData ? getStringValue(*classProperty.noData) : std::nullopt; @@ -720,6 +518,7 @@ class PropertyView : IPropertyView { } } +protected: /** * @brief Constructs a property instance from a property table property and * its class definition. @@ -738,8 +537,7 @@ class PropertyView : IPropertyView { const PropertyTextureProperty& /*property*/) : PropertyView(classProperty) {} - ~PropertyView() {} - +public: /** * @copydoc IPropertyView::arrayCount */ @@ -804,16 +602,7 @@ class PropertyView : IPropertyView { return std::nullopt; } -protected: - /** - * @copydoc IPropertyView::propertyViewStatus - */ - virtual PropertyViewStatus propertyViewStatus() const noexcept override { - return _propertyViewStatus; - } - private: - PropertyViewStatus _propertyViewStatus; bool _required; std::optional _noData; std::optional _defaultValue; @@ -836,8 +625,7 @@ class PropertyView> * @brief Constructs an empty property instance. */ PropertyView() - : _propertyViewStatus(PropertyViewStatus::ErrorNonexistentProperty), - _count(0), + : _count(0), _normalized(false), _offset(std::nullopt), _scale(std::nullopt), @@ -851,8 +639,7 @@ class PropertyView> * @brief Constructs a property instance from a class definition only. */ PropertyView(const ClassProperty& classProperty) - : _propertyViewStatus(PropertyViewStatus::Valid), - _count(0), + : _count(_count = classProperty.count ? *classProperty.count : 0), _normalized(classProperty.normalized), _offset(std::nullopt), _scale(std::nullopt), @@ -861,33 +648,6 @@ class PropertyView> _required(classProperty.required), _noData(std::nullopt), _defaultValue(std::nullopt) { - if (convertStringToPropertyType(classProperty.type) != - TypeToPropertyType::value) { - _propertyViewStatus = PropertyViewStatus::ErrorTypeMismatch; - return; - } - - if (!classProperty.componentType && - TypeToPropertyType::component != - PropertyComponentType::None) { - _propertyViewStatus = PropertyViewStatus::ErrorComponentTypeMismatch; - return; - } - - if (classProperty.componentType && - convertStringToPropertyComponentType(*classProperty.componentType) != - TypeToPropertyType::component) { - _propertyViewStatus = PropertyViewStatus::ErrorComponentTypeMismatch; - return; - } - - if (!classProperty.array) { - _propertyViewStatus = PropertyViewStatus::ErrorArrayTypeMismatch; - return; - } - - _count = classProperty.count ? *classProperty.count : 0; - //_offset = classProperty.offset // ? getValue(*classProperty.offset) // : std::nullopt; @@ -898,7 +658,7 @@ class PropertyView> //_min = classProperty.min ? getValue(*classProperty.min) // : std::nullopt; - //if (!_required) { + // if (!_required) { // _noData = classProperty.noData // ? getValue(*classProperty.noData) // : std::nullopt; @@ -918,24 +678,20 @@ class PropertyView> const ClassProperty& classProperty, const PropertyTableProperty& /*property*/) : PropertyView(classProperty) { - if (_propertyViewStatus != PropertyViewStatus::Valid) { - return; - } - //// If the property has its own values, override the class-provided values. - //if (property.offset) { + // if (property.offset) { // _offset = getValue(*property.offset); //} - //if (property.scale) { + // if (property.scale) { // _scale = getValue(*property.scale); //} - //if (property.max) { + // if (property.max) { // _max = getValue(*property.max); //} - //if (property.min) { + // if (property.min) { // _min = getValue(*property.min); //} } @@ -948,24 +704,20 @@ class PropertyView> const ClassProperty& classProperty, const PropertyTextureProperty& /*property*/) : PropertyView(classProperty) { - if (_propertyViewStatus != PropertyViewStatus::Valid) { - return; - } - //// If the property has its own values, override the class-provided values. - //if (property.offset) { + // if (property.offset) { // _offset = getValue(*property.offset); //} - //if (property.scale) { + // if (property.scale) { // _scale = getValue(*property.scale); //} - //if (property.max) { + // if (property.max) { // _max = getValue(*property.max); //} - //if (property.min) { + // if (property.min) { // _min = getValue(*property.min); //} } @@ -1034,17 +786,7 @@ class PropertyView> return _defaultValue; } -protected: - /** - * @copydoc IPropertyView::propertyViewStatus - */ - virtual PropertyViewStatus propertyViewStatus() const noexcept override { - return _propertyViewStatus; - } - private: - PropertyViewStatus _propertyViewStatus; - int64_t _count; bool _normalized; @@ -1060,23 +802,19 @@ class PropertyView> /** * @brief Attempts to parse from the given json value. * - * If T is a type with multiple components, e.g. a VECN or MATN type, this - * will return std::nullopt if one or more components could not be parsed. - * - * If T is an array view, this will return std::nullopt if one or - * more entries could not be parsed. - * - * @return The value as an instance of T, or std::nullopt if it could not be + * If ElementType is a type with multiple components, e.g. a VECN or MATN + * type, this will return std::nullopt if one or more components could not be * parsed. - */ - template - static std::optional getValue(const CesiumUtility::JsonValue& jsonValue) { - if constexpr (IsMetadataScalar::value) { - return getScalar(jsonValue); - } else - - if constexpr (IsMetadataVecN::value) { - return getVecN(jsonValue); + * + * @return The value as an instance of ElementType, or std::nullopt if it + * could not be parsed. + */ + static std::optional + getValue(const CesiumUtility::JsonValue& jsonValue) { + if constexpr (IsMetadataScalar::value) { + return getScalar(jsonValue); + } else if constexpr (IsMetadataVecN::value) { + return getVecN(jsonValue); } else return std::nullopt; @@ -1084,43 +822,6 @@ class PropertyView> //} } - /** - * @brief Attempts to parse from the given json value. If it could not be - * parsed as an ElementType, this returns the default value. - * - * If ElementType is a type with multiple components, e.g. a VECN or MATN - * type. - * - * @return The value as an ElementType, or std::nullopt if it could not be - * parsed. - */ - template - static T getValueOrDefault( - const CesiumUtility::JsonValue& jsonValue, - const T& defaultValue) { - if constexpr (IsMetadataScalar::value) { - return jsonValue.getSafeNumberOrDefault(defaultValue); - } - - if constexpr (IsMetadataVecN::value) { - return getVecNOrDefault(jsonValue, defaultValue); - } - - if constexpr (IsMetadataMatN::value) { - } - - if constexpr (IsMetadataArray::value) { - return defaultValue; - // if (!jsonValue.isArray()) { - // return PropertyArrayView(std::vector()); - // } - - // return jsonValue.isArray() - // ? getArrayValueOrDefault(jsonValue.getArray(), - // defaultValue) : defaultValue; - } - } - /** * @brief Attempts to parse from the given json value. If it could not be * parsed as an ElementType, this returns the default value. @@ -1138,89 +839,30 @@ class PropertyView> } catch (const CesiumUtility::JsonValueNotRealValue& /*error*/) { return std::nullopt; } catch (const gsl::narrowing_error& /*error*/) { - // Continue below. - } - - // If the value is in range, but it loses precision, a narrowing_error will - // be returned. Try to round to a reasonable number anyways, even if it - // loses precision. - if (jsonValue.isInt64()) { - int64_t int64Value = jsonValue.getInt64(); - if (int64Value >= - static_cast(std::numeric_limits::lowest()) && - int64Value <= static_cast(std::numeric_limits::max())) { - return static_cast(int64Value); - } - } else if (jsonValue.isUint64()) { - uint64_t uint64Value = jsonValue.getUint64(); - if (uint64Value <= static_cast(std::numeric_limits::max())) { - return static_cast(uint64Value); - } - } else if (jsonValue.isDouble()) { - double doubleValue = jsonValue.getDouble(); - if (doubleValue >= - static_cast(std::numeric_limits::lowest()) && - doubleValue <= static_cast(std::numeric_limits::max())) { - return static_cast(doubleValue); - } + return std::nullopt; } - - return std::nullopt; } - template - static std::optional + template + static std::optional> getVecN(const CesiumUtility::JsonValue& jsonValue) { if (!jsonValue.isArray()) { return std::nullopt; } - return std::nullopt; - // const CesiumUtility::JsonValue::Array& array = value.getArray(); - // glm::length_t N = VecType::length(); - // if (array.size() != N) { - // return std::nullopt; - //} - - // using T = VecType::type; - - // glm::length result; - // for (glm::length_t i = 0; i < N; i++) { - // std::optional value = getValue(array[i]); - // if (!value) { - // return std::nullopt; - // } - - // result[i] = value; - //} - - // return result; - } - - template - static VecType getVecNOrDefault( - const CesiumUtility::JsonValue& value, - const VecType& defaultValue) { - if (!jsonValue.isArray()) { - return defaultValue; - } - - const CesiumUtility::JsonValue::Array& array = jsonValue.getArray(); - glm::length_t N = VecType::length(); + const CesiumUtility::JsonValue::Array& array = value.getArray(); if (array.size() != N) { - return defaultValue; + return std::nullopt; } - using T = VecType::type; - - VecType result; + glm::vec result; for (glm::length_t i = 0; i < N; i++) { - std::optional value = getValue(array[i]); + std::optional value = getValue(array[i]); if (!value) { return std::nullopt; } - result[i] = value ? *value : defaultValue; + result[i] = value; } return result; @@ -1250,35 +892,6 @@ class PropertyView> // return result; //} - - template - static std::optional> - getArrayValue(const CesiumUtility::JsonValue::Array& /*arrayValue*/) { - return std::nullopt; - // std::vector values(arrayValue.size()); - // for (size_t i = 0; i < arrayValue.size(); i++) { - // const CesiumUtility::JsonValue& value = arrayValue[i]; - // std::optional arrayElement = getValue(value); - // values[i] = arrayElement; - //} - - // return PropertyArrayView(values); - } - - template - static PropertyArrayView getArrayValueOrDefault( - const CesiumUtility::JsonValue::Array& arrayValue, - const ArrayType& defaultElementValue) { - - std::vector values(arrayValue.size()); - for (size_t i = 0; i < arrayValue.size(); i++) { - const CesiumUtility::JsonValue& value = arrayValue[i]; - std::optional arrayElement = getValue(value); - values[i] = arrayElement; - } - - return PropertyArrayView(values); - } }; template <> @@ -1288,40 +901,23 @@ class PropertyView> /** * @brief Constructs an empty property instance. */ - PropertyView() - : _propertyViewStatus(PropertyViewStatus::ErrorNonexistentProperty), - _count(0), - _required(false), - _defaultValue(std::nullopt) {} + PropertyView() : _count(0), _required(false), _defaultValue(std::nullopt) {} /** * @brief Constructs a property instance from a class definition only. */ PropertyView(const ClassProperty& classProperty) - : _propertyViewStatus(PropertyViewStatus::Valid), - _count(0), + : _count(classProperty.count ? *classProperty.count : 0), _required(classProperty.required), _defaultValue(std::nullopt) { - if (classProperty.type != ClassProperty::Type::BOOLEAN) { - _propertyViewStatus = PropertyViewStatus::ErrorTypeMismatch; - return; - } - - if (!classProperty.array) { - _propertyViewStatus = PropertyViewStatus::ErrorArrayTypeMismatch; - return; - } - - _count = classProperty.count ? *classProperty.count : 0; - if (!_required) { - _defaultValue = - classProperty.defaultProperty - ? getBooleanArrayValues(*classProperty.defaultProperty) - : std::nullopt; + _defaultValue = classProperty.defaultProperty + ? getBooleanArrayValue(*classProperty.defaultProperty) + : std::nullopt; } } +protected: /** * @brief Constructs a property instance from a property table property and * its class definition. @@ -1340,8 +936,7 @@ class PropertyView> const PropertyTextureProperty& /*property*/) : PropertyView(classProperty) {} - ~PropertyView() {} - +public: /** * @copydoc IPropertyView::arrayCount */ @@ -1400,25 +995,22 @@ class PropertyView> */ virtual std::optional> defaultValue() const noexcept override { - return _defaultValue; - } - -protected: - /** - * @copydoc IPropertyView::propertyViewStatus - */ - virtual PropertyViewStatus propertyViewStatus() const noexcept override { - return _propertyViewStatus; + if (!_defaultValue) { + return std::nullopt; + } + // Not as easy to construct a gsl::span for the boolean data (the .data() of + // a boolean vector is inaccessible). Just make a copy. + std::vector defaultValueCopy(*_defaultValue); + return PropertyArrayView(std::move(defaultValueCopy)); } private: - PropertyViewStatus _propertyViewStatus; int64_t _count; bool _required; - std::optional> _defaultValue; + std::optional> _defaultValue; - static std::optional> - getBooleanArrayValues(const CesiumUtility::JsonValue& value) { + static std::optional> + getBooleanArrayValue(const CesiumUtility::JsonValue& value) { if (!value.isArray()) { return std::nullopt; } @@ -1444,44 +1036,27 @@ class PropertyView> /** * @brief Constructs an empty property instance. */ - PropertyView() - : _propertyViewStatus(PropertyViewStatus::ErrorNonexistentProperty), - _count(0), - _required(false), - _noData(std::nullopt), - _defaultValue(std::nullopt) {} + PropertyView() : _count(0), _required(false), _noData(), _defaultValue() {} /** * @brief Constructs a property instance from a class definition only. */ PropertyView(const ClassProperty& classProperty) - : _propertyViewStatus(PropertyViewStatus::Valid), - _count(0), + : _count(classProperty.count ? *classProperty.count : 0), _required(classProperty.required), - _noData(std::nullopt), - _defaultValue(std::nullopt) { - if (classProperty.type != ClassProperty::Type::STRING) { - _propertyViewStatus = PropertyViewStatus::ErrorTypeMismatch; - return; - } - - if (!classProperty.array) { - _propertyViewStatus = PropertyViewStatus::ErrorArrayTypeMismatch; - return; + _noData(), + _defaultValue() { + if (!_required) { + _noData = classProperty.noData + ? getStringArrayValue(*classProperty.noData) + : std::nullopt; + _defaultValue = classProperty.defaultProperty + ? getStringArrayValue(*classProperty.defaultProperty) + : std::nullopt; } - - _count = classProperty.count ? *classProperty.count : 0; - - // if (!_required) { - // _noData = classProperty.noData ? - // getStringArrayData(*classProperty.noData) - // : std::nullopt; - // _defaultValue = classProperty.defaultProperty - // ? getStringArrayData(*classProperty.defaultProperty) - // : std::nullopt; - //} } +protected: /** * @brief Constructs a property instance from a property table property and * its class definition. @@ -1500,8 +1075,7 @@ class PropertyView> const PropertyTextureProperty& /*property*/) : PropertyView(classProperty) {} - ~PropertyView() {} - +public: /** * @copydoc IPropertyView::arrayCount */ @@ -1554,7 +1128,14 @@ class PropertyView> */ virtual std::optional> noData() const noexcept override { - return _noData; + if (!_noData) { + return std::nullopt; + } + + // Just copy the strings. Easier than iterating through all of the strings + // to generate an offsets buffer with a best-fitting offset type. + std::vector noDataCopy(*_noData); + return PropertyArrayView(std::move(noDataCopy)); } /** @@ -1562,31 +1143,43 @@ class PropertyView> */ virtual std::optional> defaultValue() const noexcept override { - return _defaultValue; - } + if (!_defaultValue) { + return std::nullopt; + } -protected: - /** - * @copydoc IPropertyView::propertyViewStatus - */ - virtual PropertyViewStatus propertyViewStatus() const noexcept override { - return _propertyViewStatus; + // Just copy the strings. Easier than iterating through all of the strings + // to generate an offsets buffer with a best-fitting offset type. + std::vector defaultValueCopy(*_defaultValue); + return PropertyArrayView(std::move(defaultValueCopy)); } private: - PropertyViewStatus _propertyViewStatus; int64_t _count; bool _required; - std::optional> _noData; - std::optional> _defaultValue; - static std::optional> - getStringArrayData(const CesiumUtility::JsonValue& value) { - if (!value.isArray()) { + std::optional> _noData; + std::optional> _defaultValue; + + static std::optional> + getStringArrayValue(const CesiumUtility::JsonValue& jsonValue) { + if (!jsonValue.isArray()) { return std::nullopt; } - std::vector strings; + std::vector result; + const auto array = jsonValue.getArray(); + result.reserve(array.size()); + + for (size_t i = 0; i < array.size(); i++) { + if (!array[i].isString()) { + // The entire array is invalidated; return. + return std::nullopt; + } + + result.push_back(array[i].getString()); + } + + return result; } }; diff --git a/CesiumGltf/test/TestPropertyTableView.cpp b/CesiumGltf/test/TestPropertyTableView.cpp index 434e30124..4a625a350 100644 --- a/CesiumGltf/test/TestPropertyTableView.cpp +++ b/CesiumGltf/test/TestPropertyTableView.cpp @@ -1,3247 +1,3247 @@ -//#include "CesiumGltf/PropertyTableView.h" -// -//#include -// -//#include -// -//using namespace CesiumGltf; -// -//template -//void addBufferToModel(Model& model, const std::vector& values) { -// Buffer& valueBuffer = model.buffers.emplace_back(); -// valueBuffer.cesium.data.resize(values.size() * sizeof(T)); -// valueBuffer.byteLength = static_cast(valueBuffer.cesium.data.size()); -// std::memcpy( -// valueBuffer.cesium.data.data(), -// values.data(), -// valueBuffer.cesium.data.size()); -// -// BufferView& valueBufferView = model.bufferViews.emplace_back(); -// valueBufferView.buffer = static_cast(model.buffers.size() - 1); -// valueBufferView.byteOffset = 0; -// valueBufferView.byteLength = valueBuffer.byteLength; -//} -// -//TEST_CASE("Test PropertyTableView on model without EXT_structural_metadata " -// "extension") { -// Model model; -// -// // Create an erroneously isolated property table. -// PropertyTable propertyTable; -// propertyTable.classProperty = "TestClass"; -// propertyTable.count = static_cast(10); -// -// PropertyTableProperty& propertyTableProperty = -// propertyTable.properties["TestClassProperty"]; -// propertyTableProperty.values = static_cast(0); -// -// PropertyTableView view(model, propertyTable); -// REQUIRE( -// view.status() == PropertyTableViewStatus::ErrorMissingMetadataExtension); -// REQUIRE(view.size() == 0); -// -// const ClassProperty* classProperty = -// view.getClassProperty("TestClassProperty"); -// REQUIRE(!classProperty); -//} -// -//TEST_CASE("Test PropertyTableView on model without metadata schema") { -// Model model; -// -// ExtensionModelExtStructuralMetadata& metadata = -// model.addExtension(); -// -// PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); -// propertyTable.classProperty = "TestClass"; -// propertyTable.count = static_cast(10); -// -// PropertyTableProperty& propertyTableProperty = -// propertyTable.properties["TestClassProperty"]; -// propertyTableProperty.values = static_cast(0); -// -// PropertyTableView view(model, propertyTable); -// REQUIRE(view.status() == PropertyTableViewStatus::ErrorMissingSchema); -// REQUIRE(view.size() == 0); -// -// const ClassProperty* classProperty = -// view.getClassProperty("TestClassProperty"); -// REQUIRE(!classProperty); -//} -// -//TEST_CASE("Test property table with nonexistent class") { -// Model model; -// -// ExtensionModelExtStructuralMetadata& metadata = -// model.addExtension(); -// -// Schema& schema = metadata.schema.emplace(); -// Class& testClass = schema.classes["TestClass"]; -// ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; -// testClassProperty.type = ClassProperty::Type::SCALAR; -// testClassProperty.componentType = ClassProperty::ComponentType::UINT32; -// -// PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); -// propertyTable.classProperty = "I Don't Exist"; -// propertyTable.count = static_cast(10); -// -// PropertyTableProperty& propertyTableProperty = -// propertyTable.properties["TestClassProperty"]; -// propertyTableProperty.values = static_cast(0); -// -// PropertyTableView view(model, propertyTable); -// REQUIRE(view.status() == PropertyTableViewStatus::ErrorClassNotFound); -// REQUIRE(view.size() == 0); -// -// const ClassProperty* classProperty = -// view.getClassProperty("TestClassProperty"); -// REQUIRE(!classProperty); -//} -// -//TEST_CASE("Test scalar PropertyTableProperty") { -// Model model; -// std::vector values = {12, 34, 30, 11, 34, 34, 11, 33, 122, 33}; -// -// addBufferToModel(model, values); -// size_t valueBufferIndex = model.buffers.size() - 1; -// size_t valueBufferViewIndex = model.bufferViews.size() - 1; -// -// ExtensionModelExtStructuralMetadata& metadata = -// model.addExtension(); -// -// Schema& schema = metadata.schema.emplace(); -// Class& testClass = schema.classes["TestClass"]; -// ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; -// testClassProperty.type = ClassProperty::Type::SCALAR; -// testClassProperty.componentType = ClassProperty::ComponentType::UINT32; -// -// PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); -// propertyTable.classProperty = "TestClass"; -// propertyTable.count = static_cast(values.size()); -// -// PropertyTableProperty& propertyTableProperty = -// propertyTable.properties["TestClassProperty"]; -// propertyTableProperty.values = static_cast(valueBufferViewIndex); -// -// PropertyTableView view(model, propertyTable); -// REQUIRE(view.status() == PropertyTableViewStatus::Valid); -// REQUIRE(view.size() == propertyTable.count); -// -// const ClassProperty* classProperty = -// view.getClassProperty("TestClassProperty"); -// REQUIRE(classProperty); -// REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); -// REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT32); -// REQUIRE(classProperty->count == std::nullopt); -// REQUIRE(!classProperty->array); -// -// SECTION("Access correct type") { -// PropertyTablePropertyView uint32Property = -// view.getPropertyView("TestClassProperty"); -// REQUIRE(uint32Property.status() == PropertyTablePropertyViewStatus::Valid); -// REQUIRE(uint32Property.size() > 0); -// -// for (int64_t i = 0; i < uint32Property.size(); ++i) { -// REQUIRE(uint32Property.get(i) == values[static_cast(i)]); -// } -// } -// -// SECTION("Access wrong type") { -// PropertyTablePropertyView uvec3Invalid = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// uvec3Invalid.status() == -// PropertyTablePropertyViewStatus::ErrorTypeMismatch); -// -// PropertyTablePropertyView u32mat3x3Invalid = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// u32mat3x3Invalid.status() == -// PropertyTablePropertyViewStatus::ErrorTypeMismatch); -// -// PropertyTablePropertyView boolInvalid = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// boolInvalid.status() == -// PropertyTablePropertyViewStatus::ErrorTypeMismatch); -// -// PropertyTablePropertyView stringInvalid = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// stringInvalid.status() == -// PropertyTablePropertyViewStatus::ErrorTypeMismatch); -// } -// -// SECTION("Access wrong component type") { -// PropertyTablePropertyView uint8Invalid = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// uint8Invalid.status() == -// PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); -// -// PropertyTablePropertyView int32Invalid = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// int32Invalid.status() == -// PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); -// -// PropertyTablePropertyView uint64Invalid = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// uint64Invalid.status() == -// PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); -// } -// -// SECTION("Access incorrectly as array") { -// PropertyTablePropertyView> uint32ArrayInvalid = -// view.getPropertyView>("TestClassProperty"); -// REQUIRE( -// uint32ArrayInvalid.status() == -// PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); -// } -// -// SECTION("Wrong buffer index") { -// model.bufferViews[valueBufferViewIndex].buffer = 2; -// PropertyTablePropertyView uint32Property = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// uint32Property.status() == -// PropertyTablePropertyViewStatus::ErrorInvalidValueBuffer); -// } -// -// SECTION("Wrong buffer view index") { -// propertyTableProperty.values = -1; -// PropertyTablePropertyView uint32Property = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// uint32Property.status() == -// PropertyTablePropertyViewStatus::ErrorInvalidValueBufferView); -// } -// -// SECTION("Buffer view points outside of the real buffer length") { -// model.buffers[valueBufferIndex].cesium.data.resize(12); -// PropertyTablePropertyView uint32Property = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// uint32Property.status() == -// PropertyTablePropertyViewStatus::ErrorBufferViewOutOfBounds); -// } -// -// SECTION("Buffer view length isn't multiple of sizeof(T)") { -// model.bufferViews[valueBufferViewIndex].byteLength = 13; -// PropertyTablePropertyView uint32Property = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// uint32Property.status() == -// PropertyTablePropertyViewStatus:: -// ErrorBufferViewSizeNotDivisibleByTypeSize); -// } -// -// SECTION("Buffer view length doesn't match with propertyTableCount") { -// model.bufferViews[valueBufferViewIndex].byteLength = 12; -// PropertyTablePropertyView uint32Property = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// uint32Property.status() == -// PropertyTablePropertyViewStatus:: -// ErrorBufferViewSizeDoesNotMatchPropertyTableCount); -// } -//} -// -//TEST_CASE("Test vecN PropertyTableProperty") { -// Model model; -// std::vector values = { -// glm::ivec3(-12, 34, 30), -// glm::ivec3(11, 73, 0), -// glm::ivec3(-2, 6, 12), -// glm::ivec3(-4, 8, -13)}; -// -// addBufferToModel(model, values); -// size_t valueBufferIndex = model.buffers.size() - 1; -// size_t valueBufferViewIndex = model.bufferViews.size() - 1; -// -// ExtensionModelExtStructuralMetadata& metadata = -// model.addExtension(); -// -// Schema& schema = metadata.schema.emplace(); -// Class& testClass = schema.classes["TestClass"]; -// ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; -// testClassProperty.type = ClassProperty::Type::VEC3; -// testClassProperty.componentType = ClassProperty::ComponentType::INT32; -// -// PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); -// propertyTable.classProperty = "TestClass"; -// propertyTable.count = static_cast(values.size()); -// -// PropertyTableProperty& propertyTableProperty = -// propertyTable.properties["TestClassProperty"]; -// propertyTableProperty.values = static_cast(valueBufferViewIndex); -// -// PropertyTableView view(model, propertyTable); -// REQUIRE(view.status() == PropertyTableViewStatus::Valid); -// REQUIRE(view.size() == propertyTable.count); -// -// const ClassProperty* classProperty = -// view.getClassProperty("TestClassProperty"); -// REQUIRE(classProperty); -// REQUIRE(classProperty->type == ClassProperty::Type::VEC3); -// REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT32); -// REQUIRE(classProperty->count == std::nullopt); -// REQUIRE(!classProperty->array); -// -// SECTION("Access correct type") { -// PropertyTablePropertyView ivec3Property = -// view.getPropertyView("TestClassProperty"); -// REQUIRE(ivec3Property.status() == PropertyTablePropertyViewStatus::Valid); -// REQUIRE(ivec3Property.size() > 0); -// -// for (int64_t i = 0; i < ivec3Property.size(); ++i) { -// REQUIRE(ivec3Property.get(i) == values[static_cast(i)]); -// } -// } -// -// SECTION("Access wrong type") { -// PropertyTablePropertyView int32Invalid = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// int32Invalid.status() == -// PropertyTablePropertyViewStatus::ErrorTypeMismatch); -// -// PropertyTablePropertyView ivec2Invalid = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// ivec2Invalid.status() == -// PropertyTablePropertyViewStatus::ErrorTypeMismatch); -// -// PropertyTablePropertyView i32mat3x3Invalid = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// i32mat3x3Invalid.status() == -// PropertyTablePropertyViewStatus::ErrorTypeMismatch); -// -// PropertyTablePropertyView boolInvalid = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// boolInvalid.status() == -// PropertyTablePropertyViewStatus::ErrorTypeMismatch); -// -// PropertyTablePropertyView stringInvalid = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// stringInvalid.status() == -// PropertyTablePropertyViewStatus::ErrorTypeMismatch); -// } -// -// SECTION("Access wrong component type") { -// PropertyTablePropertyView u8vec3Invalid = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// u8vec3Invalid.status() == -// PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); -// -// PropertyTablePropertyView i16vec3Invalid = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// i16vec3Invalid.status() == -// PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); -// -// PropertyTablePropertyView vec3Invalid = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// vec3Invalid.status() == -// PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); -// } -// -// SECTION("Access incorrectly as array") { -// PropertyTablePropertyView> ivec3ArrayInvalid = -// view.getPropertyView>( -// "TestClassProperty"); -// REQUIRE( -// ivec3ArrayInvalid.status() == -// PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); -// } -// -// SECTION("Wrong buffer index") { -// model.bufferViews[valueBufferViewIndex].buffer = 2; -// PropertyTablePropertyView ivec3Property = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// ivec3Property.status() == -// PropertyTablePropertyViewStatus::ErrorInvalidValueBuffer); -// } -// -// SECTION("Wrong buffer view index") { -// propertyTableProperty.values = -1; -// PropertyTablePropertyView ivec3Property = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// ivec3Property.status() == -// PropertyTablePropertyViewStatus::ErrorInvalidValueBufferView); -// } -// -// SECTION("Buffer view points outside of the real buffer length") { -// model.buffers[valueBufferIndex].cesium.data.resize(12); -// PropertyTablePropertyView ivec3Property = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// ivec3Property.status() == -// PropertyTablePropertyViewStatus::ErrorBufferViewOutOfBounds); -// } -// -// SECTION("Buffer view length isn't multiple of sizeof(T)") { -// model.bufferViews[valueBufferViewIndex].byteLength = 11; -// PropertyTablePropertyView ivec3Property = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// ivec3Property.status() == -// PropertyTablePropertyViewStatus:: -// ErrorBufferViewSizeNotDivisibleByTypeSize); -// } -// -// SECTION("Buffer view length doesn't match with propertyTableCount") { -// model.bufferViews[valueBufferViewIndex].byteLength = 12; -// PropertyTablePropertyView ivec3Property = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// ivec3Property.status() == -// PropertyTablePropertyViewStatus:: -// ErrorBufferViewSizeDoesNotMatchPropertyTableCount); -// } -//} -// -//TEST_CASE("Test matN PropertyTableProperty") { -// Model model; -// // clang-format off -// std::vector values = { -// glm::u32mat2x2( -// 12, 34, -// 30, 1), -// glm::u32mat2x2( -// 11, 8, -// 73, 102), -// glm::u32mat2x2( -// 1, 0, -// 63, 2), -// glm::u32mat2x2( -// 4, 8, -// 3, 23)}; -// // clang-format on -// -// addBufferToModel(model, values); -// size_t valueBufferIndex = model.buffers.size() - 1; -// size_t valueBufferViewIndex = model.bufferViews.size() - 1; -// -// ExtensionModelExtStructuralMetadata& metadata = -// model.addExtension(); -// -// Schema& schema = metadata.schema.emplace(); -// Class& testClass = schema.classes["TestClass"]; -// ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; -// testClassProperty.type = ClassProperty::Type::MAT2; -// testClassProperty.componentType = ClassProperty::ComponentType::UINT32; -// -// PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); -// propertyTable.classProperty = "TestClass"; -// propertyTable.count = static_cast(values.size()); -// -// PropertyTableProperty& propertyTableProperty = -// propertyTable.properties["TestClassProperty"]; -// propertyTableProperty.values = static_cast(valueBufferViewIndex); -// -// PropertyTableView view(model, propertyTable); -// REQUIRE(view.status() == PropertyTableViewStatus::Valid); -// REQUIRE(view.size() == propertyTable.count); -// -// const ClassProperty* classProperty = -// view.getClassProperty("TestClassProperty"); -// REQUIRE(classProperty); -// REQUIRE(classProperty->type == ClassProperty::Type::MAT2); -// REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT32); -// REQUIRE(classProperty->count == std::nullopt); -// REQUIRE(!classProperty->array); -// -// SECTION("Access correct type") { -// PropertyTablePropertyView u32mat2x2Property = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// u32mat2x2Property.status() == PropertyTablePropertyViewStatus::Valid); -// REQUIRE(u32mat2x2Property.size() > 0); -// -// for (int64_t i = 0; i < u32mat2x2Property.size(); ++i) { -// REQUIRE(u32mat2x2Property.get(i) == values[static_cast(i)]); -// } -// } -// -// SECTION("Access wrong type") { -// PropertyTablePropertyView uint32Invalid = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// uint32Invalid.status() == -// PropertyTablePropertyViewStatus::ErrorTypeMismatch); -// -// PropertyTablePropertyView uvec2Invalid = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// uvec2Invalid.status() == -// PropertyTablePropertyViewStatus::ErrorTypeMismatch); -// -// PropertyTablePropertyView u32mat4x4Invalid = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// u32mat4x4Invalid.status() == -// PropertyTablePropertyViewStatus::ErrorTypeMismatch); -// -// PropertyTablePropertyView boolInvalid = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// boolInvalid.status() == -// PropertyTablePropertyViewStatus::ErrorTypeMismatch); -// -// PropertyTablePropertyView stringInvalid = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// stringInvalid.status() == -// PropertyTablePropertyViewStatus::ErrorTypeMismatch); -// } -// -// SECTION("Access wrong component type") { -// PropertyTablePropertyView u8mat2x2Invalid = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// u8mat2x2Invalid.status() == -// PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); -// -// PropertyTablePropertyView i32mat2x2Invalid = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// i32mat2x2Invalid.status() == -// PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); -// -// PropertyTablePropertyView mat2Invalid = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// mat2Invalid.status() == -// PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); -// } -// -// SECTION("Access incorrectly as array") { -// PropertyTablePropertyView> -// u32mat2x2ArrayInvalid = -// view.getPropertyView>( -// "TestClassProperty"); -// REQUIRE( -// u32mat2x2ArrayInvalid.status() == -// PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); -// } -// -// SECTION("Wrong buffer index") { -// model.bufferViews[valueBufferViewIndex].buffer = 2; -// PropertyTablePropertyView u32mat2x2Property = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// u32mat2x2Property.status() == -// PropertyTablePropertyViewStatus::ErrorInvalidValueBuffer); -// } -// -// SECTION("Wrong buffer view index") { -// propertyTableProperty.values = -1; -// PropertyTablePropertyView u32mat2x2Property = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// u32mat2x2Property.status() == -// PropertyTablePropertyViewStatus::ErrorInvalidValueBufferView); -// } -// -// SECTION("Buffer view points outside of the real buffer length") { -// model.buffers[valueBufferIndex].cesium.data.resize(sizeof(glm::u32mat2x2)); -// PropertyTablePropertyView u32mat2x2Property = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// u32mat2x2Property.status() == -// PropertyTablePropertyViewStatus::ErrorBufferViewOutOfBounds); -// } -// -// SECTION("Buffer view length isn't multiple of sizeof(T)") { -// model.bufferViews[valueBufferViewIndex].byteLength = -// sizeof(glm::u32mat2x2) * 4 - 1; -// PropertyTablePropertyView u32mat2x2Property = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// u32mat2x2Property.status() == -// PropertyTablePropertyViewStatus:: -// ErrorBufferViewSizeNotDivisibleByTypeSize); -// } -// -// SECTION("Buffer view length doesn't match with propertyTableCount") { -// model.bufferViews[valueBufferViewIndex].byteLength = sizeof(glm::u32mat2x2); -// -// PropertyTablePropertyView u32mat2x2Property = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// u32mat2x2Property.status() == -// PropertyTablePropertyViewStatus:: -// ErrorBufferViewSizeDoesNotMatchPropertyTableCount); -// } -//} -// -//TEST_CASE("Test boolean PropertyTableProperty") { -// Model model; -// -// int64_t instanceCount = 21; -// std::vector expected; -// std::vector values; -// values.resize(3); -// for (int64_t i = 0; i < instanceCount; ++i) { -// if (i % 2 == 0) { -// expected.emplace_back(true); -// } else { -// expected.emplace_back(false); -// } -// -// uint8_t expectedValue = expected.back(); -// int64_t byteIndex = i / 8; -// int64_t bitIndex = i % 8; -// values[static_cast(byteIndex)] = static_cast( -// (expectedValue << bitIndex) | values[static_cast(byteIndex)]); -// } -// addBufferToModel(model, values); -// -// ExtensionModelExtStructuralMetadata& metadata = -// model.addExtension(); -// -// Schema& schema = metadata.schema.emplace(); -// Class& testClass = schema.classes["TestClass"]; -// ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; -// testClassProperty.type = ClassProperty::Type::BOOLEAN; -// -// PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); -// propertyTable.classProperty = "TestClass"; -// propertyTable.count = static_cast(instanceCount); -// -// PropertyTableProperty& propertyTableProperty = -// propertyTable.properties["TestClassProperty"]; -// propertyTableProperty.values = -// static_cast(model.bufferViews.size() - 1); -// -// PropertyTableView view(model, propertyTable); -// REQUIRE(view.status() == PropertyTableViewStatus::Valid); -// REQUIRE(view.size() == propertyTable.count); -// -// const ClassProperty* classProperty = -// view.getClassProperty("TestClassProperty"); -// REQUIRE(classProperty); -// REQUIRE(classProperty->type == ClassProperty::Type::BOOLEAN); -// REQUIRE(classProperty->componentType == std::nullopt); -// REQUIRE(classProperty->count == std::nullopt); -// REQUIRE(!classProperty->array); -// -// SECTION("Access correct type") { -// PropertyTablePropertyView boolProperty = -// view.getPropertyView("TestClassProperty"); -// REQUIRE(boolProperty.status() == PropertyTablePropertyViewStatus::Valid); -// REQUIRE(boolProperty.size() == instanceCount); -// for (int64_t i = 0; i < boolProperty.size(); ++i) { -// bool expectedValue = expected[static_cast(i)]; -// REQUIRE(boolProperty.get(i) == expectedValue); -// } -// } -// -// SECTION("Buffer size doesn't match with propertyTableCount") { -// propertyTable.count = 66; -// PropertyTablePropertyView boolProperty = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// boolProperty.status() == -// PropertyTablePropertyViewStatus:: -// ErrorBufferViewSizeDoesNotMatchPropertyTableCount); -// } -//} -// -//TEST_CASE("Test string PropertyTableProperty") { -// Model model; -// -// std::vector expected{"What's up", "Test_0", "Test_1", "", "Hi"}; -// size_t totalBytes = 0; -// for (const std::string& expectedValue : expected) { -// totalBytes += expectedValue.size(); -// } -// -// std::vector stringOffsets( -// (expected.size() + 1) * sizeof(uint32_t)); -// std::vector values(totalBytes); -// uint32_t* offsetValue = reinterpret_cast(stringOffsets.data()); -// for (size_t i = 0; i < expected.size(); ++i) { -// const std::string& expectedValue = expected[i]; -// std::memcpy( -// values.data() + offsetValue[i], -// expectedValue.c_str(), -// expectedValue.size()); -// offsetValue[i + 1] = -// offsetValue[i] + static_cast(expectedValue.size()); -// } -// -// addBufferToModel(model, values); -// size_t valueBufferIndex = model.buffers.size() - 1; -// size_t valueBufferViewIndex = model.bufferViews.size() - 1; -// -// addBufferToModel(model, stringOffsets); -// size_t offsetBufferIndex = model.buffers.size() - 1; -// size_t offsetBufferViewIndex = model.bufferViews.size() - 1; -// -// ExtensionModelExtStructuralMetadata& metadata = -// model.addExtension(); -// -// Schema& schema = metadata.schema.emplace(); -// Class& testClass = schema.classes["TestClass"]; -// ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; -// testClassProperty.type = ClassProperty::Type::STRING; -// -// PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); -// propertyTable.classProperty = "TestClass"; -// propertyTable.count = static_cast(expected.size()); -// -// PropertyTableProperty& propertyTableProperty = -// propertyTable.properties["TestClassProperty"]; -// propertyTableProperty.stringOffsetType = -// PropertyTableProperty::StringOffsetType::UINT32; -// propertyTableProperty.values = static_cast(valueBufferViewIndex); -// propertyTableProperty.stringOffsets = -// static_cast(offsetBufferViewIndex); -// -// PropertyTableView view(model, propertyTable); -// REQUIRE(view.status() == PropertyTableViewStatus::Valid); -// REQUIRE(view.size() == propertyTable.count); -// -// const ClassProperty* classProperty = -// view.getClassProperty("TestClassProperty"); -// REQUIRE(classProperty); -// REQUIRE(classProperty->type == ClassProperty::Type::STRING); -// REQUIRE(classProperty->componentType == std::nullopt); -// REQUIRE(classProperty->count == std::nullopt); -// REQUIRE(!classProperty->array); -// -// SECTION("Access correct type") { -// PropertyTablePropertyView stringProperty = -// view.getPropertyView("TestClassProperty"); -// REQUIRE(stringProperty.status() == PropertyTablePropertyViewStatus::Valid); -// for (size_t i = 0; i < expected.size(); ++i) { -// REQUIRE(stringProperty.get(static_cast(i)) == expected[i]); -// } -// } -// -// SECTION("Wrong array type") { -// PropertyTablePropertyView> -// stringArrayInvalid = -// view.getPropertyView>( -// "TestClassProperty"); -// REQUIRE( -// stringArrayInvalid.status() == -// PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); -// } -// -// SECTION("Wrong offset type") { -// propertyTableProperty.stringOffsetType = -// PropertyTableProperty::StringOffsetType::UINT8; -// PropertyTablePropertyView stringProperty = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// stringProperty.status() == -// PropertyTablePropertyViewStatus:: -// ErrorBufferViewSizeDoesNotMatchPropertyTableCount); -// -// propertyTableProperty.stringOffsetType = -// PropertyTableProperty::StringOffsetType::UINT64; -// stringProperty = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// stringProperty.status() == -// PropertyTablePropertyViewStatus:: -// ErrorBufferViewSizeDoesNotMatchPropertyTableCount); -// -// propertyTableProperty.stringOffsetType = "NONSENSE"; -// stringProperty = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// stringProperty.status() == -// PropertyTablePropertyViewStatus::ErrorInvalidStringOffsetType); -// -// propertyTableProperty.stringOffsetType = ""; -// propertyTableProperty.arrayOffsetType = -// PropertyTableProperty::StringOffsetType::UINT32; -// stringProperty = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// stringProperty.status() == -// PropertyTablePropertyViewStatus::ErrorInvalidStringOffsetType); -// } -// -// SECTION("Offset values are not sorted ascending") { -// uint32_t* offset = reinterpret_cast( -// model.buffers[offsetBufferIndex].cesium.data.data()); -// offset[2] = -// static_cast(model.buffers[valueBufferIndex].byteLength + 4); -// PropertyTablePropertyView stringProperty = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// stringProperty.status() == -// PropertyTablePropertyViewStatus::ErrorStringOffsetsNotSorted); -// } -// -// SECTION("Offset value points outside of value buffer") { -// uint32_t* offset = reinterpret_cast( -// model.buffers[offsetBufferIndex].cesium.data.data()); -// offset[propertyTable.count] = -// static_cast(model.buffers[valueBufferIndex].byteLength + 4); -// PropertyTablePropertyView stringProperty = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// stringProperty.status() == -// PropertyTablePropertyViewStatus::ErrorStringOffsetOutOfBounds); -// } -//} -// -//TEST_CASE("Test fixed-length scalar array") { -// Model model; -// std::vector values = -// {12, 34, 30, 11, 34, 34, 11, 33, 122, 33, 223, 11}; -// -// addBufferToModel(model, values); -// size_t valueBufferViewIndex = model.bufferViews.size() - 1; -// -// ExtensionModelExtStructuralMetadata& metadata = -// model.addExtension(); -// -// Schema& schema = metadata.schema.emplace(); -// Class& testClass = schema.classes["TestClass"]; -// ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; -// testClassProperty.type = ClassProperty::Type::SCALAR; -// testClassProperty.componentType = ClassProperty::ComponentType::UINT32; -// testClassProperty.array = true; -// testClassProperty.count = 3; -// -// PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); -// propertyTable.classProperty = "TestClass"; -// propertyTable.count = static_cast( -// values.size() / static_cast(testClassProperty.count.value())); -// -// PropertyTableProperty& propertyTableProperty = -// propertyTable.properties["TestClassProperty"]; -// propertyTableProperty.values = -// static_cast(model.bufferViews.size() - 1); -// -// PropertyTableView view(model, propertyTable); -// REQUIRE(view.status() == PropertyTableViewStatus::Valid); -// REQUIRE(view.size() == propertyTable.count); -// -// const ClassProperty* classProperty = -// view.getClassProperty("TestClassProperty"); -// REQUIRE(classProperty); -// REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); -// REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT32); -// REQUIRE(classProperty->array); -// REQUIRE(classProperty->count == 3); -// -// SECTION("Access the right type") { -// PropertyTablePropertyView> arrayProperty = -// view.getPropertyView>("TestClassProperty"); -// REQUIRE(arrayProperty.status() == PropertyTablePropertyViewStatus::Valid); -// -// for (int64_t i = 0; i < arrayProperty.size(); ++i) { -// PropertyArrayView member = arrayProperty.get(i); -// for (int64_t j = 0; j < member.size(); ++j) { -// REQUIRE(member[j] == values[static_cast(i * 3 + j)]); -// } -// } -// } -// -// SECTION("Wrong type") { -// PropertyTablePropertyView> boolArrayInvalid = -// view.getPropertyView>("TestClassProperty"); -// REQUIRE( -// boolArrayInvalid.status() == -// PropertyTablePropertyViewStatus::ErrorTypeMismatch); -// -// PropertyTablePropertyView> uvec2ArrayInvalid = -// view.getPropertyView>( -// "TestClassProperty"); -// REQUIRE( -// uvec2ArrayInvalid.status() == -// PropertyTablePropertyViewStatus::ErrorTypeMismatch); -// } -// -// SECTION("Wrong component type") { -// PropertyTablePropertyView> int32ArrayInvalid = -// view.getPropertyView>("TestClassProperty"); -// REQUIRE( -// int32ArrayInvalid.status() == -// PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); -// } -// -// SECTION("Not an array type") { -// PropertyTablePropertyView uint32Invalid = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// uint32Invalid.status() == -// PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); -// } -// -// SECTION("Buffer size is not a multiple of type size") { -// model.bufferViews[valueBufferViewIndex].byteLength = 13; -// PropertyTablePropertyView> arrayProperty = -// view.getPropertyView>("TestClassProperty"); -// REQUIRE( -// arrayProperty.status() == -// PropertyTablePropertyViewStatus:: -// ErrorBufferViewSizeNotDivisibleByTypeSize); -// } -// -// SECTION("Negative component count") { -// testClassProperty.count = -1; -// PropertyTablePropertyView> arrayProperty = -// view.getPropertyView>("TestClassProperty"); -// REQUIRE( -// arrayProperty.status() == PropertyTablePropertyViewStatus:: -// ErrorArrayCountAndOffsetBufferDontExist); -// } -// -// SECTION("Value buffer doesn't fit into property table count") { -// testClassProperty.count = 55; -// PropertyTablePropertyView> arrayProperty = -// view.getPropertyView>("TestClassProperty"); -// REQUIRE( -// arrayProperty.status() == -// PropertyTablePropertyViewStatus:: -// ErrorBufferViewSizeDoesNotMatchPropertyTableCount); -// } -//} -// -//TEST_CASE("Test variable-length scalar array") { -// Model model; -// -// std::vector> expected{ -// {12, 33, 11, 344, 112, 444, 1}, -// {}, -// {}, -// {122, 23, 333, 12}, -// {}, -// {333, 311, 22, 34}, -// {}, -// {33, 1888, 233, 33019}}; -// size_t numOfElements = 0; -// for (const auto& expectedMember : expected) { -// numOfElements += expectedMember.size(); -// } -// -// std::vector values(numOfElements * sizeof(uint16_t)); -// std::vector offsets((expected.size() + 1) * sizeof(uint64_t)); -// uint64_t* offsetValue = reinterpret_cast(offsets.data()); -// for (size_t i = 0; i < expected.size(); ++i) { -// std::memcpy( -// values.data() + offsetValue[i], -// expected[i].data(), -// expected[i].size() * sizeof(uint16_t)); -// offsetValue[i + 1] = offsetValue[i] + expected[i].size() * sizeof(uint16_t); -// } -// -// addBufferToModel(model, values); -// size_t valueBufferIndex = model.buffers.size() - 1; -// size_t valueBufferViewIndex = model.bufferViews.size() - 1; -// -// addBufferToModel(model, offsets); -// size_t offsetBufferIndex = model.buffers.size() - 1; -// size_t offsetBufferViewIndex = model.bufferViews.size() - 1; -// -// ExtensionModelExtStructuralMetadata& metadata = -// model.addExtension(); -// -// Schema& schema = metadata.schema.emplace(); -// Class& testClass = schema.classes["TestClass"]; -// ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; -// testClassProperty.type = ClassProperty::Type::SCALAR; -// testClassProperty.componentType = ClassProperty::ComponentType::UINT16; -// testClassProperty.array = true; -// -// PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); -// propertyTable.classProperty = "TestClass"; -// propertyTable.count = static_cast(expected.size()); -// -// PropertyTableProperty& propertyTableProperty = -// propertyTable.properties["TestClassProperty"]; -// propertyTableProperty.values = static_cast(valueBufferViewIndex); -// propertyTableProperty.arrayOffsets = -// static_cast(offsetBufferViewIndex); -// propertyTableProperty.arrayOffsetType = -// PropertyTableProperty::ArrayOffsetType::UINT64; -// -// PropertyTableView view(model, propertyTable); -// REQUIRE(view.status() == PropertyTableViewStatus::Valid); -// REQUIRE(view.size() == propertyTable.count); -// -// const ClassProperty* classProperty = -// view.getClassProperty("TestClassProperty"); -// REQUIRE(classProperty); -// REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); -// REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT16); -// REQUIRE(classProperty->array); -// REQUIRE(!classProperty->count); -// -// SECTION("Access the correct type") { -// PropertyTablePropertyView> property = -// view.getPropertyView>("TestClassProperty"); -// REQUIRE(property.status() == PropertyTablePropertyViewStatus::Valid); -// for (size_t i = 0; i < expected.size(); ++i) { -// PropertyArrayView valueMember = -// property.get(static_cast(i)); -// REQUIRE(valueMember.size() == static_cast(expected[i].size())); -// for (size_t j = 0; j < expected[i].size(); ++j) { -// REQUIRE(expected[i][j] == valueMember[static_cast(j)]); -// } -// } -// } -// SECTION("Wrong offset type") { -// propertyTableProperty.arrayOffsetType = -// PropertyTableProperty::ArrayOffsetType::UINT8; -// PropertyTablePropertyView> arrayProperty = -// view.getPropertyView>("TestClassProperty"); -// REQUIRE( -// arrayProperty.status() == -// PropertyTablePropertyViewStatus:: -// ErrorBufferViewSizeDoesNotMatchPropertyTableCount); -// -// propertyTableProperty.arrayOffsetType = -// PropertyTableProperty::ArrayOffsetType::UINT16; -// arrayProperty = -// view.getPropertyView>("TestClassProperty"); -// REQUIRE( -// arrayProperty.status() == -// PropertyTablePropertyViewStatus:: -// ErrorBufferViewSizeDoesNotMatchPropertyTableCount); -// -// propertyTableProperty.arrayOffsetType = "NONSENSE"; -// arrayProperty = -// view.getPropertyView>("TestClassProperty"); -// REQUIRE( -// arrayProperty.status() == -// PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); -// -// propertyTableProperty.arrayOffsetType = ""; -// propertyTableProperty.stringOffsetType = -// PropertyTableProperty::StringOffsetType::UINT64; -// arrayProperty = -// view.getPropertyView>("TestClassProperty"); -// REQUIRE( -// arrayProperty.status() == -// PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); -// } -// -// SECTION("Offset values are not sorted ascending") { -// propertyTableProperty.arrayOffsetType = -// PropertyTableProperty::ArrayOffsetType::UINT64; -// uint64_t* offset = reinterpret_cast( -// model.buffers[offsetBufferIndex].cesium.data.data()); -// offset[propertyTable.count] = 0; -// PropertyTablePropertyView> arrayProperty = -// view.getPropertyView>("TestClassProperty"); -// REQUIRE( -// arrayProperty.status() == -// PropertyTablePropertyViewStatus::ErrorArrayOffsetsNotSorted); -// } -// -// SECTION("Offset value points outside of value buffer") { -// uint64_t* offset = reinterpret_cast( -// model.buffers[offsetBufferIndex].cesium.data.data()); -// offset[propertyTable.count] = -// static_cast(model.buffers[valueBufferIndex].byteLength + 4); -// PropertyTablePropertyView> arrayProperty = -// view.getPropertyView>("TestClassProperty"); -// REQUIRE( -// arrayProperty.status() == -// PropertyTablePropertyViewStatus::ErrorArrayOffsetOutOfBounds); -// } -// -// SECTION("Count and offset buffer are both present") { -// testClassProperty.count = 3; -// PropertyTablePropertyView> property = -// view.getPropertyView>("TestClassProperty"); -// REQUIRE( -// property.status() == -// PropertyTablePropertyViewStatus::ErrorArrayCountAndOffsetBufferCoexist); -// } -//} -// -//TEST_CASE("Test fixed-length vecN array") { -// Model model; -// std::vector values = { -// glm::ivec3(12, 34, -30), -// glm::ivec3(-2, 0, 1), -// glm::ivec3(1, 2, 8), -// glm::ivec3(-100, 84, 6), -// glm::ivec3(2, -2, -2), -// glm::ivec3(40, 61, 3), -// }; -// -// addBufferToModel(model, values); -// size_t valueBufferViewIndex = model.bufferViews.size() - 1; -// -// ExtensionModelExtStructuralMetadata& metadata = -// model.addExtension(); -// -// Schema& schema = metadata.schema.emplace(); -// Class& testClass = schema.classes["TestClass"]; -// ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; -// testClassProperty.type = ClassProperty::Type::VEC3; -// testClassProperty.componentType = ClassProperty::ComponentType::INT32; -// testClassProperty.array = true; -// testClassProperty.count = 2; -// -// PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); -// propertyTable.classProperty = "TestClass"; -// propertyTable.count = static_cast( -// values.size() / static_cast(testClassProperty.count.value())); -// -// PropertyTableProperty& propertyTableProperty = -// propertyTable.properties["TestClassProperty"]; -// propertyTableProperty.values = -// static_cast(model.bufferViews.size() - 1); -// -// PropertyTableView view(model, propertyTable); -// REQUIRE(view.status() == PropertyTableViewStatus::Valid); -// REQUIRE(view.size() == propertyTable.count); -// -// const ClassProperty* classProperty = -// view.getClassProperty("TestClassProperty"); -// REQUIRE(classProperty); -// REQUIRE(classProperty->type == ClassProperty::Type::VEC3); -// REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT32); -// REQUIRE(classProperty->array); -// REQUIRE(classProperty->count == 2); -// -// SECTION("Access the right type") { -// PropertyTablePropertyView> arrayProperty = -// view.getPropertyView>( -// "TestClassProperty"); -// REQUIRE(arrayProperty.status() == PropertyTablePropertyViewStatus::Valid); -// -// for (int64_t i = 0; i < arrayProperty.size(); ++i) { -// PropertyArrayView member = arrayProperty.get(i); -// for (int64_t j = 0; j < member.size(); ++j) { -// REQUIRE(member[j] == values[static_cast(i * 2 + j)]); -// } -// } -// } -// -// SECTION("Wrong type") { -// PropertyTablePropertyView> int32ArrayInvalid = -// view.getPropertyView>("TestClassProperty"); -// REQUIRE( -// int32ArrayInvalid.status() == -// PropertyTablePropertyViewStatus::ErrorTypeMismatch); -// -// PropertyTablePropertyView> ivec2ArrayInvalid = -// view.getPropertyView>( -// "TestClassProperty"); -// REQUIRE( -// ivec2ArrayInvalid.status() == -// PropertyTablePropertyViewStatus::ErrorTypeMismatch); -// } -// -// SECTION("Wrong component type") { -// PropertyTablePropertyView> uvec3ArrayInvalid = -// view.getPropertyView>( -// "TestClassProperty"); -// REQUIRE( -// uvec3ArrayInvalid.status() == -// PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); -// } -// -// SECTION("Not an array type") { -// PropertyTablePropertyView ivec3Invalid = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// ivec3Invalid.status() == -// PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); -// } -// -// SECTION("Buffer size is not a multiple of type size") { -// model.bufferViews[valueBufferViewIndex].byteLength = 13; -// PropertyTablePropertyView> arrayProperty = -// view.getPropertyView>( -// "TestClassProperty"); -// REQUIRE( -// arrayProperty.status() == -// PropertyTablePropertyViewStatus:: -// ErrorBufferViewSizeNotDivisibleByTypeSize); -// } -// -// SECTION("Negative component count") { -// testClassProperty.count = -1; -// PropertyTablePropertyView> arrayProperty = -// view.getPropertyView>( -// "TestClassProperty"); -// REQUIRE( -// arrayProperty.status() == PropertyTablePropertyViewStatus:: -// ErrorArrayCountAndOffsetBufferDontExist); -// } -// -// SECTION("Value buffer doesn't fit into property table count") { -// testClassProperty.count = 55; -// PropertyTablePropertyView> arrayProperty = -// view.getPropertyView>( -// "TestClassProperty"); -// REQUIRE( -// arrayProperty.status() == -// PropertyTablePropertyViewStatus:: -// ErrorBufferViewSizeDoesNotMatchPropertyTableCount); -// } -//} -// -//TEST_CASE("Test variable-length vecN array") { -// Model model; -// // clang-format off -// std::vector> expected{ -// { glm::ivec3(12, 34, -30), glm::ivec3(-2, 0, 1) }, -// { glm::ivec3(1, 2, 8), }, -// {}, -// { glm::ivec3(-100, 84, 6), glm::ivec3(2, -2, -2), glm::ivec3(40, 61, 3) }, -// { glm::ivec3(-1, 4, -7) }, -// }; -// // clang-format on -// -// size_t numOfElements = 0; -// for (const auto& expectedMember : expected) { -// numOfElements += expectedMember.size(); -// } -// -// std::vector values(numOfElements * sizeof(glm::ivec3)); -// std::vector offsets((expected.size() + 1) * sizeof(uint64_t)); -// uint64_t* offsetValue = reinterpret_cast(offsets.data()); -// for (size_t i = 0; i < expected.size(); ++i) { -// std::memcpy( -// values.data() + offsetValue[i], -// expected[i].data(), -// expected[i].size() * sizeof(glm::ivec3)); -// offsetValue[i + 1] = -// offsetValue[i] + expected[i].size() * sizeof(glm::ivec3); -// } -// -// addBufferToModel(model, values); -// size_t valueBufferIndex = model.buffers.size() - 1; -// size_t valueBufferViewIndex = model.bufferViews.size() - 1; -// -// addBufferToModel(model, offsets); -// size_t offsetBufferIndex = model.buffers.size() - 1; -// size_t offsetBufferViewIndex = model.bufferViews.size() - 1; -// -// ExtensionModelExtStructuralMetadata& metadata = -// model.addExtension(); -// -// Schema& schema = metadata.schema.emplace(); -// Class& testClass = schema.classes["TestClass"]; -// ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; -// testClassProperty.type = ClassProperty::Type::VEC3; -// testClassProperty.componentType = ClassProperty::ComponentType::INT32; -// testClassProperty.array = true; -// -// PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); -// propertyTable.classProperty = "TestClass"; -// propertyTable.count = static_cast(expected.size()); -// -// PropertyTableProperty& propertyTableProperty = -// propertyTable.properties["TestClassProperty"]; -// propertyTableProperty.values = static_cast(valueBufferViewIndex); -// propertyTableProperty.arrayOffsets = -// static_cast(offsetBufferViewIndex); -// propertyTableProperty.arrayOffsetType = -// PropertyTableProperty::ArrayOffsetType::UINT64; -// -// PropertyTableView view(model, propertyTable); -// REQUIRE(view.status() == PropertyTableViewStatus::Valid); -// REQUIRE(view.size() == propertyTable.count); -// -// const ClassProperty* classProperty = -// view.getClassProperty("TestClassProperty"); -// REQUIRE(classProperty); -// REQUIRE(classProperty->type == ClassProperty::Type::VEC3); -// REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT32); -// REQUIRE(classProperty->array); -// REQUIRE(!classProperty->count); -// -// SECTION("Access the correct type") { -// PropertyTablePropertyView> property = -// view.getPropertyView>( -// "TestClassProperty"); -// REQUIRE(property.status() == PropertyTablePropertyViewStatus::Valid); -// for (size_t i = 0; i < expected.size(); ++i) { -// PropertyArrayView valueMember = -// property.get(static_cast(i)); -// REQUIRE(valueMember.size() == static_cast(expected[i].size())); -// for (size_t j = 0; j < expected[i].size(); ++j) { -// REQUIRE(expected[i][j] == valueMember[static_cast(j)]); -// } -// } -// } -// -// SECTION("Wrong offset type") { -// propertyTableProperty.arrayOffsetType = -// PropertyTableProperty::ArrayOffsetType::UINT8; -// PropertyTablePropertyView> arrayProperty = -// view.getPropertyView>( -// "TestClassProperty"); -// REQUIRE( -// arrayProperty.status() == -// PropertyTablePropertyViewStatus:: -// ErrorBufferViewSizeDoesNotMatchPropertyTableCount); -// -// propertyTableProperty.arrayOffsetType = -// PropertyTableProperty::ArrayOffsetType::UINT16; -// arrayProperty = view.getPropertyView>( -// "TestClassProperty"); -// REQUIRE( -// arrayProperty.status() == -// PropertyTablePropertyViewStatus:: -// ErrorBufferViewSizeDoesNotMatchPropertyTableCount); -// -// propertyTableProperty.arrayOffsetType = "NONSENSE"; -// arrayProperty = view.getPropertyView>( -// "TestClassProperty"); -// REQUIRE( -// arrayProperty.status() == -// PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); -// -// propertyTableProperty.arrayOffsetType = ""; -// propertyTableProperty.stringOffsetType = -// PropertyTableProperty::StringOffsetType::UINT64; -// arrayProperty = view.getPropertyView>( -// "TestClassProperty"); -// REQUIRE( -// arrayProperty.status() == -// PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); -// } -// -// SECTION("Offset values are not sorted ascending") { -// propertyTableProperty.arrayOffsetType = -// PropertyTableProperty::ArrayOffsetType::UINT64; -// uint64_t* offset = reinterpret_cast( -// model.buffers[offsetBufferIndex].cesium.data.data()); -// offset[propertyTable.count] = 0; -// PropertyTablePropertyView> arrayProperty = -// view.getPropertyView>( -// "TestClassProperty"); -// REQUIRE( -// arrayProperty.status() == -// PropertyTablePropertyViewStatus::ErrorArrayOffsetsNotSorted); -// } -// -// SECTION("Offset value points outside of value buffer") { -// uint64_t* offset = reinterpret_cast( -// model.buffers[offsetBufferIndex].cesium.data.data()); -// offset[propertyTable.count] = -// static_cast(model.buffers[valueBufferIndex].byteLength + 4); -// PropertyTablePropertyView> arrayProperty = -// view.getPropertyView>( -// "TestClassProperty"); -// REQUIRE( -// arrayProperty.status() == -// PropertyTablePropertyViewStatus::ErrorArrayOffsetOutOfBounds); -// } -// -// SECTION("Count and offset buffer are both present") { -// testClassProperty.count = 3; -// PropertyTablePropertyView> property = -// view.getPropertyView>( -// "TestClassProperty"); -// REQUIRE( -// property.status() == -// PropertyTablePropertyViewStatus::ErrorArrayCountAndOffsetBufferCoexist); -// } -//} -// -//TEST_CASE("Test fixed-length matN array") { -// Model model; -// // clang-format off -// std::vector values = { -// glm::i32mat2x2( -// 12, 34, -// -30, 20), -// glm::i32mat2x2( -// -2, -2, -// 0, 1), -// glm::i32mat2x2( -// 1, 2, -// 8, 5), -// glm::i32mat2x2( -// -100, 3, -// 84, 6), -// glm::i32mat2x2( -// 2, 12, -// -2, -2), -// glm::i32mat2x2( -// 40, 61, -// 7, -3), -// }; -// // clang-format on -// -// addBufferToModel(model, values); -// size_t valueBufferViewIndex = model.bufferViews.size() - 1; -// -// ExtensionModelExtStructuralMetadata& metadata = -// model.addExtension(); -// -// Schema& schema = metadata.schema.emplace(); -// Class& testClass = schema.classes["TestClass"]; -// ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; -// testClassProperty.type = ClassProperty::Type::MAT2; -// testClassProperty.componentType = ClassProperty::ComponentType::INT32; -// testClassProperty.array = true; -// testClassProperty.count = 2; -// -// PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); -// propertyTable.classProperty = "TestClass"; -// propertyTable.count = static_cast( -// values.size() / static_cast(testClassProperty.count.value())); -// -// PropertyTableProperty& propertyTableProperty = -// propertyTable.properties["TestClassProperty"]; -// propertyTableProperty.values = -// static_cast(model.bufferViews.size() - 1); -// -// PropertyTableView view(model, propertyTable); -// REQUIRE(view.status() == PropertyTableViewStatus::Valid); -// REQUIRE(view.size() == propertyTable.count); -// -// const ClassProperty* classProperty = -// view.getClassProperty("TestClassProperty"); -// REQUIRE(classProperty); -// REQUIRE(classProperty->type == ClassProperty::Type::MAT2); -// REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT32); -// REQUIRE(classProperty->array); -// REQUIRE(classProperty->count == 2); -// -// SECTION("Access the right type") { -// PropertyTablePropertyView> arrayProperty = -// view.getPropertyView>( -// "TestClassProperty"); -// REQUIRE(arrayProperty.status() == PropertyTablePropertyViewStatus::Valid); -// -// for (int64_t i = 0; i < arrayProperty.size(); ++i) { -// PropertyArrayView member = arrayProperty.get(i); -// for (int64_t j = 0; j < member.size(); ++j) { -// REQUIRE(member[j] == values[static_cast(i * 2 + j)]); -// } -// } -// } -// -// SECTION("Wrong type") { -// PropertyTablePropertyView> int32ArrayInvalid = -// view.getPropertyView>("TestClassProperty"); -// REQUIRE( -// int32ArrayInvalid.status() == -// PropertyTablePropertyViewStatus::ErrorTypeMismatch); -// -// PropertyTablePropertyView> ivec2ArrayInvalid = -// view.getPropertyView>( -// "TestClassProperty"); -// REQUIRE( -// ivec2ArrayInvalid.status() == -// PropertyTablePropertyViewStatus::ErrorTypeMismatch); -// } -// -// SECTION("Wrong component type") { -// PropertyTablePropertyView> -// u32mat2x2ArrayInvalid = -// view.getPropertyView>( -// "TestClassProperty"); -// REQUIRE( -// u32mat2x2ArrayInvalid.status() == -// PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); -// } -// -// SECTION("Not an array type") { -// PropertyTablePropertyView ivec3Invalid = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// ivec3Invalid.status() == -// PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); -// } -// -// SECTION("Buffer size is not a multiple of type size") { -// model.bufferViews[valueBufferViewIndex].byteLength = 13; -// PropertyTablePropertyView> arrayProperty = -// view.getPropertyView>( -// "TestClassProperty"); -// REQUIRE( -// arrayProperty.status() == -// PropertyTablePropertyViewStatus:: -// ErrorBufferViewSizeNotDivisibleByTypeSize); -// } -// -// SECTION("Negative component count") { -// testClassProperty.count = -1; -// PropertyTablePropertyView> arrayProperty = -// view.getPropertyView>( -// "TestClassProperty"); -// REQUIRE( -// arrayProperty.status() == PropertyTablePropertyViewStatus:: -// ErrorArrayCountAndOffsetBufferDontExist); -// } -// -// SECTION("Value buffer doesn't fit into property table count") { -// testClassProperty.count = 55; -// PropertyTablePropertyView> arrayProperty = -// view.getPropertyView>( -// "TestClassProperty"); -// REQUIRE( -// arrayProperty.status() == -// PropertyTablePropertyViewStatus:: -// ErrorBufferViewSizeDoesNotMatchPropertyTableCount); -// } -//} -// -//TEST_CASE("Test variable-length matN array") { -// Model model; -// // clang-format off -// std::vector data0{ -// glm::i32mat2x2( -// 3, -2, -// 1, 0), -// glm::i32mat2x2( -// 40, 3, -// 8, -9) -// }; -// std::vector data1{ -// glm::i32mat2x2( -// 1, 10, -// 7, 8), -// }; -// std::vector data2{ -// glm::i32mat2x2( -// 18, 0, -// 1, 17), -// glm::i32mat2x2( -// -4, -2, -// -9, 1), -// glm::i32mat2x2( -// 1, 8, -// -99, 3), -// }; -// // clang-format on -// -// std::vector> -// expected{data0, {}, data1, data2, {}}; -// -// size_t numOfElements = 0; -// for (const auto& expectedMember : expected) { -// numOfElements += expectedMember.size(); -// } -// -// std::vector values(numOfElements * sizeof(glm::i32mat2x2)); -// std::vector offsets((expected.size() + 1) * sizeof(uint64_t)); -// uint64_t* offsetValue = reinterpret_cast(offsets.data()); -// for (size_t i = 0; i < expected.size(); ++i) { -// std::memcpy( -// values.data() + offsetValue[i], -// expected[i].data(), -// expected[i].size() * sizeof(glm::i32mat2x2)); -// offsetValue[i + 1] = -// offsetValue[i] + expected[i].size() * sizeof(glm::i32mat2x2); -// } -// -// addBufferToModel(model, values); -// size_t valueBufferIndex = model.buffers.size() - 1; -// size_t valueBufferViewIndex = model.bufferViews.size() - 1; -// -// addBufferToModel(model, offsets); -// size_t offsetBufferIndex = model.buffers.size() - 1; -// size_t offsetBufferViewIndex = model.bufferViews.size() - 1; -// -// ExtensionModelExtStructuralMetadata& metadata = -// model.addExtension(); -// -// Schema& schema = metadata.schema.emplace(); -// Class& testClass = schema.classes["TestClass"]; -// ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; -// testClassProperty.type = ClassProperty::Type::MAT2; -// testClassProperty.componentType = ClassProperty::ComponentType::INT32; -// testClassProperty.array = true; -// -// PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); -// propertyTable.classProperty = "TestClass"; -// propertyTable.count = static_cast(expected.size()); -// -// PropertyTableProperty& propertyTableProperty = -// propertyTable.properties["TestClassProperty"]; -// propertyTableProperty.values = static_cast(valueBufferViewIndex); -// propertyTableProperty.arrayOffsets = -// static_cast(offsetBufferViewIndex); -// propertyTableProperty.arrayOffsetType = -// PropertyTableProperty::ArrayOffsetType::UINT64; -// -// PropertyTableView view(model, propertyTable); -// REQUIRE(view.status() == PropertyTableViewStatus::Valid); -// REQUIRE(view.size() == propertyTable.count); -// -// const ClassProperty* classProperty = -// view.getClassProperty("TestClassProperty"); -// REQUIRE(classProperty); -// REQUIRE(classProperty->type == ClassProperty::Type::MAT2); -// REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT32); -// REQUIRE(classProperty->array); -// REQUIRE(!classProperty->count); -// -// SECTION("Access the correct type") { -// PropertyTablePropertyView> property = -// view.getPropertyView>( -// "TestClassProperty"); -// REQUIRE(property.status() == PropertyTablePropertyViewStatus::Valid); -// for (size_t i = 0; i < expected.size(); ++i) { -// PropertyArrayView valueMember = -// property.get(static_cast(i)); -// REQUIRE(valueMember.size() == static_cast(expected[i].size())); -// for (size_t j = 0; j < expected[i].size(); ++j) { -// REQUIRE(expected[i][j] == valueMember[static_cast(j)]); -// } -// } -// } -// -// SECTION("Wrong offset type") { -// propertyTableProperty.arrayOffsetType = -// PropertyTableProperty::ArrayOffsetType::UINT8; -// PropertyTablePropertyView> arrayProperty = -// view.getPropertyView>( -// "TestClassProperty"); -// REQUIRE( -// arrayProperty.status() == -// PropertyTablePropertyViewStatus:: -// ErrorBufferViewSizeDoesNotMatchPropertyTableCount); -// -// propertyTableProperty.arrayOffsetType = -// PropertyTableProperty::ArrayOffsetType::UINT16; -// arrayProperty = view.getPropertyView>( -// "TestClassProperty"); -// REQUIRE( -// arrayProperty.status() == -// PropertyTablePropertyViewStatus:: -// ErrorBufferViewSizeDoesNotMatchPropertyTableCount); -// -// propertyTableProperty.arrayOffsetType = "NONSENSE"; -// arrayProperty = view.getPropertyView>( -// "TestClassProperty"); -// REQUIRE( -// arrayProperty.status() == -// PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); -// -// propertyTableProperty.arrayOffsetType = ""; -// propertyTableProperty.stringOffsetType = -// PropertyTableProperty::StringOffsetType::UINT64; -// arrayProperty = view.getPropertyView>( -// "TestClassProperty"); -// REQUIRE( -// arrayProperty.status() == -// PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); -// } -// -// SECTION("Offset values are not sorted ascending") { -// propertyTableProperty.arrayOffsetType = -// PropertyTableProperty::ArrayOffsetType::UINT64; -// uint64_t* offset = reinterpret_cast( -// model.buffers[offsetBufferIndex].cesium.data.data()); -// offset[propertyTable.count] = 0; -// PropertyTablePropertyView> arrayProperty = -// view.getPropertyView>( -// "TestClassProperty"); -// REQUIRE( -// arrayProperty.status() == -// PropertyTablePropertyViewStatus::ErrorArrayOffsetsNotSorted); -// } -// -// SECTION("Offset value points outside of value buffer") { -// uint64_t* offset = reinterpret_cast( -// model.buffers[offsetBufferIndex].cesium.data.data()); -// offset[propertyTable.count] = -// static_cast(model.buffers[valueBufferIndex].byteLength + 4); -// PropertyTablePropertyView> arrayProperty = -// view.getPropertyView>( -// "TestClassProperty"); -// REQUIRE( -// arrayProperty.status() == -// PropertyTablePropertyViewStatus::ErrorArrayOffsetOutOfBounds); -// } -// -// SECTION("Count and offset buffer are both present") { -// testClassProperty.count = 3; -// PropertyTablePropertyView> property = -// view.getPropertyView>( -// "TestClassProperty"); -// REQUIRE( -// property.status() == -// PropertyTablePropertyViewStatus::ErrorArrayCountAndOffsetBufferCoexist); -// } -//} -// -//TEST_CASE("Test fixed-length boolean array") { -// Model model; -// -// std::vector expected = { -// true, -// false, -// false, -// true, -// false, -// false, -// true, -// true, -// true, -// false, -// false, -// true}; -// std::vector values; -// size_t requiredBytesSize = static_cast( -// glm::ceil(static_cast(expected.size()) / 8.0)); -// values.resize(requiredBytesSize); -// for (size_t i = 0; i < expected.size(); ++i) { -// uint8_t expectedValue = expected[i]; -// size_t byteIndex = i / 8; -// size_t bitIndex = i % 8; -// values[byteIndex] = -// static_cast((expectedValue << bitIndex) | values[byteIndex]); -// } -// -// addBufferToModel(model, values); -// -// ExtensionModelExtStructuralMetadata& metadata = -// model.addExtension(); -// -// Schema& schema = metadata.schema.emplace(); -// Class& testClass = schema.classes["TestClass"]; -// ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; -// testClassProperty.type = ClassProperty::Type::BOOLEAN; -// testClassProperty.array = true; -// testClassProperty.count = 3; -// -// PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); -// propertyTable.classProperty = "TestClass"; -// propertyTable.count = static_cast( -// expected.size() / static_cast(testClassProperty.count.value())); -// -// PropertyTableProperty& propertyTableProperty = -// propertyTable.properties["TestClassProperty"]; -// propertyTableProperty.values = -// static_cast(model.bufferViews.size() - 1); -// -// PropertyTableView view(model, propertyTable); -// REQUIRE(view.status() == PropertyTableViewStatus::Valid); -// REQUIRE(view.size() == propertyTable.count); -// -// const ClassProperty* classProperty = -// view.getClassProperty("TestClassProperty"); -// REQUIRE(classProperty); -// REQUIRE(classProperty->type == ClassProperty::Type::BOOLEAN); -// REQUIRE(classProperty->array); -// REQUIRE(classProperty->count == 3); -// -// SECTION("Access correct type") { -// PropertyTablePropertyView> boolArrayProperty = -// view.getPropertyView>("TestClassProperty"); -// REQUIRE( -// boolArrayProperty.status() == PropertyTablePropertyViewStatus::Valid); -// REQUIRE(boolArrayProperty.size() == propertyTable.count); -// REQUIRE(boolArrayProperty.size() > 0); -// for (int64_t i = 0; i < boolArrayProperty.size(); ++i) { -// PropertyArrayView valueMember = boolArrayProperty.get(i); -// for (int64_t j = 0; j < valueMember.size(); ++j) { -// REQUIRE(valueMember[j] == expected[static_cast(i * 3 + j)]); -// } -// } -// } -// -// SECTION("Wrong type") { -// PropertyTablePropertyView> uint8ArrayInvalid = -// view.getPropertyView>("TestClassProperty"); -// REQUIRE( -// uint8ArrayInvalid.status() == -// PropertyTablePropertyViewStatus::ErrorTypeMismatch); -// } -// -// SECTION("View is not array type") { -// PropertyTablePropertyView boolInvalid = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// boolInvalid.status() == -// PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); -// } -// -// SECTION("Value buffer doesn't have enough required bytes") { -// testClassProperty.count = 11; -// PropertyTablePropertyView> boolArrayProperty = -// view.getPropertyView>("TestClassProperty"); -// REQUIRE( -// boolArrayProperty.status() == -// PropertyTablePropertyViewStatus:: -// ErrorBufferViewSizeDoesNotMatchPropertyTableCount); -// } -// -// SECTION("Count is negative") { -// testClassProperty.count = -1; -// PropertyTablePropertyView> boolArrayProperty = -// view.getPropertyView>("TestClassProperty"); -// REQUIRE( -// boolArrayProperty.status() == -// PropertyTablePropertyViewStatus:: -// ErrorArrayCountAndOffsetBufferDontExist); -// } -//} -// -//TEST_CASE("Test variable-length boolean array") { -// Model model; -// -// std::vector> expected{ -// {true, false, true, true, false, true, true}, -// {}, -// {}, -// {}, -// {false, false, false, false}, -// {true, false, true}, -// {false}, -// {true, true, true, true, true, false, false}}; -// size_t numOfElements = 0; -// for (const auto& expectedMember : expected) { -// numOfElements += expectedMember.size(); -// } -// -// size_t requiredBytesSize = -// static_cast(glm::ceil(static_cast(numOfElements) / 8.0)); -// std::vector values(requiredBytesSize); -// std::vector offsets((expected.size() + 1) * sizeof(uint64_t)); -// uint64_t* offsetValue = reinterpret_cast(offsets.data()); -// size_t indexSoFar = 0; -// for (size_t i = 0; i < expected.size(); ++i) { -// for (size_t j = 0; j < expected[i].size(); ++j) { -// uint8_t expectedValue = expected[i][j]; -// size_t byteIndex = indexSoFar / 8; -// size_t bitIndex = indexSoFar % 8; -// values[byteIndex] = static_cast( -// (expectedValue << bitIndex) | static_cast(values[byteIndex])); -// ++indexSoFar; -// } -// offsetValue[i + 1] = offsetValue[i] + expected[i].size(); -// } -// -// addBufferToModel(model, values); -// size_t valueBufferIndex = model.buffers.size() - 1; -// size_t valueBufferViewIndex = model.bufferViews.size() - 1; -// -// addBufferToModel(model, offsets); -// size_t offsetBufferIndex = model.buffers.size() - 1; -// size_t offsetBufferViewIndex = model.bufferViews.size() - 1; -// -// ExtensionModelExtStructuralMetadata& metadata = -// model.addExtension(); -// -// Schema& schema = metadata.schema.emplace(); -// Class& testClass = schema.classes["TestClass"]; -// ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; -// testClassProperty.type = ClassProperty::Type::BOOLEAN; -// testClassProperty.array = true; -// -// PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); -// propertyTable.classProperty = "TestClass"; -// propertyTable.count = static_cast(expected.size()); -// -// PropertyTableProperty& propertyTableProperty = -// propertyTable.properties["TestClassProperty"]; -// propertyTableProperty.values = static_cast(valueBufferViewIndex); -// propertyTableProperty.arrayOffsets = -// static_cast(offsetBufferViewIndex); -// propertyTableProperty.arrayOffsetType = -// PropertyTableProperty::ArrayOffsetType::UINT64; -// -// PropertyTableView view(model, propertyTable); -// REQUIRE(view.status() == PropertyTableViewStatus::Valid); -// REQUIRE(view.size() == propertyTable.count); -// -// const ClassProperty* classProperty = -// view.getClassProperty("TestClassProperty"); -// REQUIRE(classProperty); -// REQUIRE(classProperty->type == ClassProperty::Type::BOOLEAN); -// REQUIRE(classProperty->array); -// REQUIRE(!classProperty->count); -// -// SECTION("Access correct type") { -// PropertyTablePropertyView> boolArrayProperty = -// view.getPropertyView>("TestClassProperty"); -// REQUIRE( -// boolArrayProperty.status() == PropertyTablePropertyViewStatus::Valid); -// for (size_t i = 0; i < expected.size(); ++i) { -// PropertyArrayView arrayMember = -// boolArrayProperty.get(static_cast(i)); -// REQUIRE(arrayMember.size() == static_cast(expected[i].size())); -// for (size_t j = 0; j < expected[i].size(); ++j) { -// REQUIRE(expected[i][j] == arrayMember[static_cast(j)]); -// } -// } -// } -// -// SECTION("Wrong offset type") { -// propertyTableProperty.arrayOffsetType = -// PropertyTableProperty::ArrayOffsetType::UINT8; -// PropertyTablePropertyView> arrayProperty = -// view.getPropertyView>("TestClassProperty"); -// REQUIRE( -// arrayProperty.status() == -// PropertyTablePropertyViewStatus:: -// ErrorBufferViewSizeDoesNotMatchPropertyTableCount); -// -// propertyTableProperty.arrayOffsetType = -// PropertyTableProperty::ArrayOffsetType::UINT16; -// arrayProperty = -// view.getPropertyView>("TestClassProperty"); -// REQUIRE( -// arrayProperty.status() == -// PropertyTablePropertyViewStatus:: -// ErrorBufferViewSizeDoesNotMatchPropertyTableCount); -// -// propertyTableProperty.arrayOffsetType = "NONSENSE"; -// arrayProperty = -// view.getPropertyView>("TestClassProperty"); -// REQUIRE( -// arrayProperty.status() == -// PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); -// -// propertyTableProperty.arrayOffsetType = ""; -// propertyTableProperty.stringOffsetType = -// PropertyTableProperty::StringOffsetType::UINT64; -// arrayProperty = -// view.getPropertyView>("TestClassProperty"); -// REQUIRE( -// arrayProperty.status() == -// PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); -// } -// -// SECTION("Offset values are not sorted ascending") { -// propertyTableProperty.arrayOffsetType = -// PropertyTableProperty::ArrayOffsetType::UINT64; -// uint64_t* offset = reinterpret_cast( -// model.buffers[offsetBufferIndex].cesium.data.data()); -// offset[propertyTable.count] = 0; -// PropertyTablePropertyView> arrayProperty = -// view.getPropertyView>("TestClassProperty"); -// REQUIRE( -// arrayProperty.status() == -// PropertyTablePropertyViewStatus::ErrorArrayOffsetsNotSorted); -// } -// -// SECTION("Offset value points outside of value buffer") { -// uint64_t* offset = reinterpret_cast( -// model.buffers[offsetBufferIndex].cesium.data.data()); -// offset[propertyTable.count] = static_cast( -// model.buffers[valueBufferIndex].byteLength * 8 + 20); -// PropertyTablePropertyView> arrayProperty = -// view.getPropertyView>("TestClassProperty"); -// REQUIRE( -// arrayProperty.status() == -// PropertyTablePropertyViewStatus::ErrorArrayOffsetOutOfBounds); -// } -// -// SECTION("Count and offset buffer both present") { -// testClassProperty.count = 3; -// PropertyTablePropertyView> boolArrayProperty = -// view.getPropertyView>("TestClassProperty"); -// REQUIRE( -// boolArrayProperty.status() == -// PropertyTablePropertyViewStatus::ErrorArrayCountAndOffsetBufferCoexist); -// } -//} -// -//TEST_CASE("Test fixed-length arrays of strings") { -// Model model; -// -// std::vector expected{ -// "What's up", -// "Breaking news!!! Aliens no longer attacks the US first", -// "But they still abduct my cows! Those milk thiefs! 👽 🐮", -// "I'm not crazy. My mother had me tested 🤪", -// "I love you, meat bags! ❤️", -// "Book in the freezer"}; -// -// size_t totalBytes = 0; -// for (const std::string& expectedValue : expected) { -// totalBytes += expectedValue.size(); -// } -// -// std::vector offsets((expected.size() + 1) * sizeof(uint32_t)); -// std::vector values(totalBytes); -// uint32_t* offsetValue = reinterpret_cast(offsets.data()); -// for (size_t i = 0; i < expected.size(); ++i) { -// const std::string& expectedValue = expected[i]; -// std::memcpy( -// values.data() + offsetValue[i], -// expectedValue.c_str(), -// expectedValue.size()); -// offsetValue[i + 1] = -// offsetValue[i] + static_cast(expectedValue.size()); -// } -// -// addBufferToModel(model, values); -// size_t valueBufferViewIndex = model.bufferViews.size() - 1; -// -// addBufferToModel(model, offsets); -// size_t offsetBufferViewIndex = model.bufferViews.size() - 1; -// -// ExtensionModelExtStructuralMetadata& metadata = -// model.addExtension(); -// -// Schema& schema = metadata.schema.emplace(); -// Class& testClass = schema.classes["TestClass"]; -// ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; -// testClassProperty.type = ClassProperty::Type::STRING; -// testClassProperty.array = true; -// testClassProperty.count = 2; -// -// PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); -// propertyTable.classProperty = "TestClass"; -// propertyTable.count = static_cast( -// expected.size() / static_cast(testClassProperty.count.value())); -// -// PropertyTableProperty& propertyTableProperty = -// propertyTable.properties["TestClassProperty"]; -// propertyTableProperty.stringOffsetType = -// PropertyTableProperty::StringOffsetType::UINT32; -// propertyTableProperty.values = static_cast(valueBufferViewIndex); -// propertyTableProperty.stringOffsets = -// static_cast(offsetBufferViewIndex); -// -// PropertyTableView view(model, propertyTable); -// REQUIRE(view.status() == PropertyTableViewStatus::Valid); -// REQUIRE(view.size() == propertyTable.count); -// -// const ClassProperty* classProperty = -// view.getClassProperty("TestClassProperty"); -// REQUIRE(classProperty); -// REQUIRE(classProperty->type == ClassProperty::Type::STRING); -// REQUIRE(classProperty->array); -// REQUIRE(classProperty->count == 2); -// -// SECTION("Access correct type") { -// PropertyTablePropertyView> -// stringProperty = -// view.getPropertyView>( -// "TestClassProperty"); -// REQUIRE(stringProperty.status() == PropertyTablePropertyViewStatus::Valid); -// REQUIRE(stringProperty.size() == 3); -// -// PropertyArrayView v0 = stringProperty.get(0); -// REQUIRE(v0.size() == 2); -// REQUIRE(v0[0] == "What's up"); -// REQUIRE(v0[1] == "Breaking news!!! Aliens no longer attacks the US first"); -// -// PropertyArrayView v1 = stringProperty.get(1); -// REQUIRE(v1.size() == 2); -// REQUIRE(v1[0] == "But they still abduct my cows! Those milk thiefs! 👽 🐮"); -// REQUIRE(v1[1] == "I'm not crazy. My mother had me tested 🤪"); -// -// PropertyArrayView v2 = stringProperty.get(2); -// REQUIRE(v2.size() == 2); -// REQUIRE(v2[0] == "I love you, meat bags! ❤️"); -// REQUIRE(v2[1] == "Book in the freezer"); -// } -// -// SECTION("Array type mismatch") { -// PropertyTablePropertyView stringProperty = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// stringProperty.status() == -// PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); -// } -// -// SECTION("Count is negative") { -// testClassProperty.count = -1; -// PropertyTablePropertyView> -// stringProperty = -// view.getPropertyView>( -// "TestClassProperty"); -// REQUIRE( -// stringProperty.status() == PropertyTablePropertyViewStatus:: -// ErrorArrayCountAndOffsetBufferDontExist); -// } -// -// SECTION("Offset type is unknown") { -// propertyTableProperty.stringOffsetType = "NONSENSE"; -// PropertyTablePropertyView> -// stringProperty = -// view.getPropertyView>( -// "TestClassProperty"); -// REQUIRE( -// stringProperty.status() == -// PropertyTablePropertyViewStatus::ErrorInvalidStringOffsetType); -// -// propertyTableProperty.stringOffsetType = ""; -// propertyTableProperty.arrayOffsetType = -// PropertyTableProperty::ArrayOffsetType::UINT32; -// stringProperty = view.getPropertyView>( -// "TestClassProperty"); -// REQUIRE( -// stringProperty.status() == -// PropertyTablePropertyViewStatus::ErrorInvalidStringOffsetType); -// } -// -// SECTION("String offsets don't exist") { -// propertyTableProperty.stringOffsets = -1; -// PropertyTablePropertyView> -// stringProperty = -// view.getPropertyView>( -// "TestClassProperty"); -// REQUIRE( -// stringProperty.status() == -// PropertyTablePropertyViewStatus::ErrorInvalidStringOffsetBufferView); -// } -//} -// -//TEST_CASE("Test variable-length arrays of strings") { -// Model model; -// -// std::vector> expected{ -// {"What's up"}, -// {"Breaking news!!! Aliens no longer attacks the US first", -// "But they still abduct my cows! Those milk thiefs! 👽 🐮"}, -// {"I'm not crazy. My mother had me tested 🤪", -// "I love you, meat bags! ❤️", -// "Book in the freezer"}}; -// -// size_t totalBytes = 0; -// size_t numOfElements = 0; -// for (const auto& expectedValues : expected) { -// for (const auto& value : expectedValues) { -// totalBytes += value.size(); -// } -// -// numOfElements += expectedValues.size(); -// } -// -// std::vector offsets((expected.size() + 1) * sizeof(uint32_t)); -// std::vector stringOffsets((numOfElements + 1) * sizeof(uint32_t)); -// std::vector values(totalBytes); -// uint32_t* offsetValue = reinterpret_cast(offsets.data()); -// uint32_t* stringOffsetValue = -// reinterpret_cast(stringOffsets.data()); -// size_t strOffsetIdx = 0; -// for (size_t i = 0; i < expected.size(); ++i) { -// for (size_t j = 0; j < expected[i].size(); ++j) { -// const std::string& expectedValue = expected[i][j]; -// std::memcpy( -// values.data() + stringOffsetValue[strOffsetIdx], -// expectedValue.c_str(), -// expectedValue.size()); -// -// stringOffsetValue[strOffsetIdx + 1] = -// stringOffsetValue[strOffsetIdx] + -// static_cast(expectedValue.size()); -// ++strOffsetIdx; -// } -// -// offsetValue[i + 1] = -// offsetValue[i] + -// static_cast(expected[i].size() * sizeof(uint32_t)); -// } -// -// addBufferToModel(model, values); -// size_t valueBufferViewIndex = model.bufferViews.size() - 1; -// -// addBufferToModel(model, offsets); -// size_t arrayOffsetBuffer = model.buffers.size() - 1; -// size_t arrayOffsetBufferView = model.bufferViews.size() - 1; -// -// addBufferToModel(model, stringOffsets); -// size_t stringOffsetBuffer = model.buffers.size() - 1; -// size_t stringOffsetBufferView = model.bufferViews.size() - 1; -// -// ExtensionModelExtStructuralMetadata& metadata = -// model.addExtension(); -// -// Schema& schema = metadata.schema.emplace(); -// Class& testClass = schema.classes["TestClass"]; -// ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; -// testClassProperty.type = ClassProperty::Type::STRING; -// testClassProperty.array = true; -// -// PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); -// propertyTable.classProperty = "TestClass"; -// propertyTable.count = static_cast(expected.size()); -// -// PropertyTableProperty& propertyTableProperty = -// propertyTable.properties["TestClassProperty"]; -// propertyTableProperty.arrayOffsetType = -// PropertyTableProperty::ArrayOffsetType::UINT32; -// propertyTableProperty.stringOffsetType = -// PropertyTableProperty::StringOffsetType::UINT32; -// propertyTableProperty.values = static_cast(valueBufferViewIndex); -// propertyTableProperty.arrayOffsets = -// static_cast(arrayOffsetBufferView); -// propertyTableProperty.stringOffsets = -// static_cast(stringOffsetBufferView); -// -// PropertyTableView view(model, propertyTable); -// REQUIRE(view.status() == PropertyTableViewStatus::Valid); -// REQUIRE(view.size() == propertyTable.count); -// -// const ClassProperty* classProperty = -// view.getClassProperty("TestClassProperty"); -// REQUIRE(classProperty); -// REQUIRE(classProperty->type == ClassProperty::Type::STRING); -// REQUIRE(classProperty->array); -// REQUIRE(!classProperty->componentType); -// REQUIRE(!classProperty->count); -// -// SECTION("Access correct type") { -// PropertyTablePropertyView> -// stringProperty = -// view.getPropertyView>( -// "TestClassProperty"); -// REQUIRE(stringProperty.status() == PropertyTablePropertyViewStatus::Valid); -// for (size_t i = 0; i < expected.size(); ++i) { -// PropertyArrayView stringArray = -// stringProperty.get(static_cast(i)); -// for (size_t j = 0; j < expected[i].size(); ++j) { -// REQUIRE(stringArray[static_cast(j)] == expected[i][j]); -// } -// } -// } -// -// SECTION("Wrong array offset type") { -// propertyTableProperty.arrayOffsetType = -// PropertyTableProperty::ArrayOffsetType::UINT8; -// PropertyTablePropertyView> -// arrayProperty = -// view.getPropertyView>( -// "TestClassProperty"); -// REQUIRE( -// arrayProperty.status() == -// PropertyTablePropertyViewStatus:: -// ErrorBufferViewSizeDoesNotMatchPropertyTableCount); -// -// propertyTableProperty.arrayOffsetType = -// PropertyTableProperty::ArrayOffsetType::UINT16; -// arrayProperty = view.getPropertyView>( -// "TestClassProperty"); -// REQUIRE( -// arrayProperty.status() == -// PropertyTablePropertyViewStatus:: -// ErrorBufferViewSizeDoesNotMatchPropertyTableCount); -// -// propertyTableProperty.arrayOffsetType = "NONSENSE"; -// arrayProperty = view.getPropertyView>( -// "TestClassProperty"); -// REQUIRE( -// arrayProperty.status() == -// PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); -// propertyTableProperty.arrayOffsetType = -// PropertyTableProperty::ArrayOffsetType::UINT32; -// } -// -// SECTION("Wrong string offset type") { -// propertyTableProperty.stringOffsetType = -// PropertyTableProperty::StringOffsetType::UINT8; -// PropertyTablePropertyView> -// arrayProperty = -// view.getPropertyView>( -// "TestClassProperty"); -// REQUIRE( -// arrayProperty.status() == -// PropertyTablePropertyViewStatus:: -// ErrorBufferViewSizeDoesNotMatchPropertyTableCount); -// -// propertyTableProperty.stringOffsetType = -// PropertyTableProperty::StringOffsetType::UINT16; -// arrayProperty = view.getPropertyView>( -// "TestClassProperty"); -// REQUIRE( -// arrayProperty.status() == -// PropertyTablePropertyViewStatus:: -// ErrorBufferViewSizeDoesNotMatchPropertyTableCount); -// -// propertyTableProperty.stringOffsetType = "NONSENSE"; -// arrayProperty = view.getPropertyView>( -// "TestClassProperty"); -// REQUIRE( -// arrayProperty.status() == -// PropertyTablePropertyViewStatus::ErrorInvalidStringOffsetType); -// propertyTableProperty.stringOffsetType = -// PropertyTableProperty::StringOffsetType::UINT32; -// } -// -// SECTION("Array offset values are not sorted ascending") { -// uint32_t* offset = reinterpret_cast( -// model.buffers[arrayOffsetBuffer].cesium.data.data()); -// offset[0] = static_cast(1000); -// PropertyTablePropertyView> -// arrayProperty = -// view.getPropertyView>( -// "TestClassProperty"); -// REQUIRE( -// arrayProperty.status() == -// PropertyTablePropertyViewStatus::ErrorArrayOffsetsNotSorted); -// offset[0] = 0; -// } -// -// SECTION("String offset values are not sorted ascending") { -// uint32_t* offset = reinterpret_cast( -// model.buffers[stringOffsetBuffer].cesium.data.data()); -// offset[0] = static_cast(1000); -// PropertyTablePropertyView> -// arrayProperty = -// view.getPropertyView>( -// "TestClassProperty"); -// REQUIRE( -// arrayProperty.status() == -// PropertyTablePropertyViewStatus::ErrorStringOffsetsNotSorted); -// offset[0] = 0; -// } -// -// SECTION("Array offset value points outside of value buffer") { -// uint32_t* offset = reinterpret_cast( -// model.buffers[arrayOffsetBuffer].cesium.data.data()); -// uint32_t previousValue = offset[propertyTable.count]; -// offset[propertyTable.count] = static_cast(100000); -// PropertyTablePropertyView> -// arrayProperty = -// view.getPropertyView>( -// "TestClassProperty"); -// REQUIRE( -// arrayProperty.status() == -// PropertyTablePropertyViewStatus::ErrorArrayOffsetOutOfBounds); -// offset[propertyTable.count] = previousValue; -// } -// -// SECTION("String offset value points outside of value buffer") { -// uint32_t* offset = reinterpret_cast( -// model.buffers[stringOffsetBuffer].cesium.data.data()); -// uint32_t previousValue = offset[6]; -// offset[6] = static_cast(100000); -// PropertyTablePropertyView> -// arrayProperty = -// view.getPropertyView>( -// "TestClassProperty"); -// REQUIRE( -// arrayProperty.status() == -// PropertyTablePropertyViewStatus::ErrorStringOffsetOutOfBounds); -// offset[6] = previousValue; -// } -// -// SECTION("Count and offset buffer both present") { -// testClassProperty.count = 3; -// PropertyTablePropertyView> -// boolArrayProperty = -// view.getPropertyView>( -// "TestClassProperty"); -// REQUIRE( -// boolArrayProperty.status() == -// PropertyTablePropertyViewStatus::ErrorArrayCountAndOffsetBufferCoexist); -// } -//} -// -//TEST_CASE("Test callback on invalid property table view") { -// Model model; -// ExtensionModelExtStructuralMetadata& metadata = -// model.addExtension(); -// metadata.schema.emplace(); -// -// // Property table has a nonexistent class. -// PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); -// propertyTable.classProperty = "TestClass"; -// propertyTable.count = static_cast(5); -// -// PropertyTableProperty& propertyTableProperty = -// propertyTable.properties["TestClassProperty"]; -// propertyTableProperty.values = static_cast(-1); -// -// PropertyTableView view(model, propertyTable); -// REQUIRE(view.status() == PropertyTableViewStatus::ErrorClassNotFound); -// REQUIRE(view.size() == 0); -// -// const ClassProperty* classProperty = -// view.getClassProperty("TestClassProperty"); -// REQUIRE(!classProperty); -// -// uint32_t invokedCallbackCount = 0; -// view.getPropertyView( -// "TestClassProperty", -// [&invokedCallbackCount]( -// const std::string& /*propertyName*/, -// auto propertyValue) mutable { -// invokedCallbackCount++; -// REQUIRE( -// propertyValue.status() == -// PropertyTablePropertyViewStatus::ErrorInvalidPropertyTable); -// REQUIRE(propertyValue.size() == 0); -// }); -// -// REQUIRE(invokedCallbackCount == 1); -//} -// -//TEST_CASE("Test callback for invalid PropertyTableProperty") { -// Model model; -// ExtensionModelExtStructuralMetadata& metadata = -// model.addExtension(); -// -// Schema& schema = metadata.schema.emplace(); -// Class& testClass = schema.classes["TestClass"]; -// ClassProperty& testClassProperty = testClass.properties["InvalidProperty"]; -// testClassProperty.type = ClassProperty::Type::SCALAR; -// testClassProperty.componentType = ClassProperty::ComponentType::UINT32; -// -// PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); -// propertyTable.classProperty = "TestClass"; -// propertyTable.count = static_cast(5); -// -// PropertyTableProperty& propertyTableProperty = -// propertyTable.properties["InvalidProperty"]; -// propertyTableProperty.values = static_cast(-1); -// -// PropertyTableView view(model, propertyTable); -// REQUIRE(view.status() == PropertyTableViewStatus::Valid); -// REQUIRE(view.size() == propertyTable.count); -// -// const ClassProperty* classProperty = view.getClassProperty("InvalidProperty"); -// REQUIRE(classProperty); -// -// classProperty = view.getClassProperty("NonexistentProperty"); -// REQUIRE(!classProperty); -// -// uint32_t invokedCallbackCount = 0; -// auto testCallback = [&invokedCallbackCount]( -// const std::string& /*propertyName*/, -// auto propertyValue) mutable { -// invokedCallbackCount++; -// REQUIRE(propertyValue.status() != PropertyTablePropertyViewStatus::Valid); -// REQUIRE(propertyValue.size() == 0); -// }; -// -// view.getPropertyView("InvalidProperty", testCallback); -// view.getPropertyView("NonexistentProperty", testCallback); -// -// REQUIRE(invokedCallbackCount == 2); -//} -// -//TEST_CASE("Test callback for scalar PropertyTableProperty") { -// Model model; -// std::vector values = {12, 34, 30, 11, 34, 34, 11, 33, 122, 33}; -// -// addBufferToModel(model, values); -// size_t valueBufferViewIndex = model.bufferViews.size() - 1; -// -// ExtensionModelExtStructuralMetadata& metadata = -// model.addExtension(); -// -// Schema& schema = metadata.schema.emplace(); -// Class& testClass = schema.classes["TestClass"]; -// ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; -// testClassProperty.type = ClassProperty::Type::SCALAR; -// testClassProperty.componentType = ClassProperty::ComponentType::UINT32; -// -// PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); -// propertyTable.classProperty = "TestClass"; -// propertyTable.count = static_cast(values.size()); -// -// PropertyTableProperty& propertyTableProperty = -// propertyTable.properties["TestClassProperty"]; -// propertyTableProperty.values = static_cast(valueBufferViewIndex); -// -// PropertyTableView view(model, propertyTable); -// REQUIRE(view.status() == PropertyTableViewStatus::Valid); -// REQUIRE(view.size() == propertyTable.count); -// -// const ClassProperty* classProperty = -// view.getClassProperty("TestClassProperty"); -// REQUIRE(classProperty); -// REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); -// REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT32); -// REQUIRE(!classProperty->array); -// REQUIRE(classProperty->count == std::nullopt); -// -// uint32_t invokedCallbackCount = 0; -// view.getPropertyView( -// "TestClassProperty", -// [&values, &invokedCallbackCount]( -// const std::string& /*propertyName*/, -// auto propertyValue) mutable { -// invokedCallbackCount++; -// if constexpr (std::is_same_v< -// PropertyTablePropertyView, -// decltype(propertyValue)>) { -// REQUIRE( -// propertyValue.status() == PropertyTablePropertyViewStatus::Valid); -// REQUIRE(propertyValue.size() > 0); -// -// for (int64_t i = 0; i < propertyValue.size(); ++i) { -// REQUIRE( -// static_cast(propertyValue.get(i)) == -// values[static_cast(i)]); -// } -// } else { -// FAIL( -// "getPropertyView returned PropertyTablePropertyView of incorrect " -// "type for TestClassProperty."); -// } -// }); -// -// REQUIRE(invokedCallbackCount == 1); -//} -// -//TEST_CASE("Test callback for vecN PropertyTableProperty") { -// Model model; -// std::vector values = { -// glm::ivec3(-12, 34, 30), -// glm::ivec3(11, 73, 0), -// glm::ivec3(-2, 6, 12), -// glm::ivec3(-4, 8, -13)}; -// -// addBufferToModel(model, values); -// size_t valueBufferViewIndex = model.bufferViews.size() - 1; -// -// ExtensionModelExtStructuralMetadata& metadata = -// model.addExtension(); -// -// Schema& schema = metadata.schema.emplace(); -// Class& testClass = schema.classes["TestClass"]; -// ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; -// testClassProperty.type = ClassProperty::Type::VEC3; -// testClassProperty.componentType = ClassProperty::ComponentType::INT32; -// -// PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); -// propertyTable.classProperty = "TestClass"; -// propertyTable.count = static_cast(values.size()); -// -// PropertyTableProperty& propertyTableProperty = -// propertyTable.properties["TestClassProperty"]; -// propertyTableProperty.values = static_cast(valueBufferViewIndex); -// -// PropertyTableView view(model, propertyTable); -// REQUIRE(view.status() == PropertyTableViewStatus::Valid); -// REQUIRE(view.size() == propertyTable.count); -// -// const ClassProperty* classProperty = -// view.getClassProperty("TestClassProperty"); -// REQUIRE(classProperty); -// REQUIRE(classProperty->type == ClassProperty::Type::VEC3); -// REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT32); -// REQUIRE(classProperty->count == std::nullopt); -// REQUIRE(!classProperty->array); -// -// uint32_t invokedCallbackCount = 0; -// view.getPropertyView( -// "TestClassProperty", -// [&values, &invokedCallbackCount]( -// const std::string& /*propertyName*/, -// auto propertyValue) mutable { -// invokedCallbackCount++; -// REQUIRE( -// propertyValue.status() == PropertyTablePropertyViewStatus::Valid); -// REQUIRE(propertyValue.size() > 0); -// -// if constexpr (std::is_same_v< -// PropertyTablePropertyView, -// decltype(propertyValue)>) { -// for (int64_t i = 0; i < propertyValue.size(); ++i) { -// REQUIRE( -// static_cast(propertyValue.get(i)) == -// values[static_cast(i)]); -// } -// } else { -// FAIL( -// "getPropertyView returned PropertyTablePropertyView of incorrect " -// "type for TestClassProperty."); -// } -// }); -// -// REQUIRE(invokedCallbackCount == 1); -//} -// -//TEST_CASE("Test callback for matN PropertyTableProperty") { -// Model model; -// // clang-format off -// std::vector values = { -// glm::u32mat2x2( -// 12, 34, -// 30, 1), -// glm::u32mat2x2( -// 11, 8, -// 73, 102), -// glm::u32mat2x2( -// 1, 0, -// 63, 2), -// glm::u32mat2x2( -// 4, 8, -// 3, 23)}; -// // clang-format on -// -// addBufferToModel(model, values); -// size_t valueBufferViewIndex = model.bufferViews.size() - 1; -// -// ExtensionModelExtStructuralMetadata& metadata = -// model.addExtension(); -// -// Schema& schema = metadata.schema.emplace(); -// Class& testClass = schema.classes["TestClass"]; -// ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; -// testClassProperty.type = ClassProperty::Type::MAT2; -// testClassProperty.componentType = ClassProperty::ComponentType::UINT32; -// -// PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); -// propertyTable.classProperty = "TestClass"; -// propertyTable.count = static_cast(values.size()); -// -// PropertyTableProperty& propertyTableProperty = -// propertyTable.properties["TestClassProperty"]; -// propertyTableProperty.values = static_cast(valueBufferViewIndex); -// -// PropertyTableView view(model, propertyTable); -// REQUIRE(view.status() == PropertyTableViewStatus::Valid); -// REQUIRE(view.size() == propertyTable.count); -// -// const ClassProperty* classProperty = -// view.getClassProperty("TestClassProperty"); -// REQUIRE(classProperty); -// REQUIRE(classProperty->type == ClassProperty::Type::MAT2); -// REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT32); -// REQUIRE(classProperty->count == std::nullopt); -// REQUIRE(!classProperty->array); -// -// uint32_t invokedCallbackCount = 0; -// view.getPropertyView( -// "TestClassProperty", -// [&values, &invokedCallbackCount]( -// const std::string& /*propertyName*/, -// auto propertyValue) mutable { -// REQUIRE( -// propertyValue.status() == PropertyTablePropertyViewStatus::Valid); -// REQUIRE(propertyValue.size() > 0); -// -// if constexpr (std::is_same_v< -// PropertyTablePropertyView, -// decltype(propertyValue)>) { -// for (int64_t i = 0; i < propertyValue.size(); ++i) { -// REQUIRE( -// static_cast(propertyValue.get(i)) == -// values[static_cast(i)]); -// } -// } else { -// FAIL( -// "getPropertyView returned PropertyTablePropertyView of incorrect " -// "type for TestClassProperty."); -// } -// invokedCallbackCount++; -// }); -// -// REQUIRE(invokedCallbackCount == 1); -//} -// -//TEST_CASE("Test callback for boolean PropertyTableProperty") { -// Model model; -// -// int64_t instanceCount = 21; -// std::vector expected; -// std::vector values; -// values.resize(3); -// for (int64_t i = 0; i < instanceCount; ++i) { -// if (i % 2 == 0) { -// expected.emplace_back(true); -// } else { -// expected.emplace_back(false); -// } -// -// uint8_t expectedValue = expected.back(); -// int64_t byteIndex = i / 8; -// int64_t bitIndex = i % 8; -// values[static_cast(byteIndex)] = static_cast( -// (expectedValue << bitIndex) | values[static_cast(byteIndex)]); -// } -// -// addBufferToModel(model, values); -// -// ExtensionModelExtStructuralMetadata& metadata = -// model.addExtension(); -// -// Schema& schema = metadata.schema.emplace(); -// Class& testClass = schema.classes["TestClass"]; -// ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; -// testClassProperty.type = ClassProperty::Type::BOOLEAN; -// -// PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); -// propertyTable.classProperty = "TestClass"; -// propertyTable.count = static_cast(instanceCount); -// -// PropertyTableProperty& propertyTableProperty = -// propertyTable.properties["TestClassProperty"]; -// propertyTableProperty.values = -// static_cast(model.bufferViews.size() - 1); -// -// PropertyTableView view(model, propertyTable); -// REQUIRE(view.status() == PropertyTableViewStatus::Valid); -// REQUIRE(view.size() == propertyTable.count); -// -// const ClassProperty* classProperty = -// view.getClassProperty("TestClassProperty"); -// REQUIRE(classProperty); -// REQUIRE(classProperty->type == ClassProperty::Type::BOOLEAN); -// REQUIRE(classProperty->componentType == std::nullopt); -// REQUIRE(classProperty->count == std::nullopt); -// REQUIRE(!classProperty->array); -// -// uint32_t invokedCallbackCount = 0; -// view.getPropertyView( -// "TestClassProperty", -// [&expected, &invokedCallbackCount]( -// const std::string& /*propertyName*/, -// auto propertyValue) mutable { -// invokedCallbackCount++; -// -// REQUIRE( -// propertyValue.status() == PropertyTablePropertyViewStatus::Valid); -// REQUIRE(propertyValue.size() > 0); -// -// if constexpr (std::is_same_v< -// PropertyTablePropertyView, -// decltype(propertyValue)>) { -// for (int64_t i = 0; i < propertyValue.size(); ++i) { -// REQUIRE( -// static_cast(propertyValue.get(i)) == -// expected[static_cast(i)]); -// } -// } else { -// FAIL( -// "getPropertyView returned PropertyTablePropertyView of incorrect " -// "type for TestClassProperty."); -// } -// }); -// -// REQUIRE(invokedCallbackCount == 1); -//} -// -//TEST_CASE("Test callback for string PropertyTableProperty") { -// Model model; -// -// std::vector expected{"What's up", "Test_0", "Test_1", "", "Hi"}; -// size_t totalBytes = 0; -// for (const std::string& expectedValue : expected) { -// totalBytes += expectedValue.size(); -// } -// -// std::vector stringOffsets( -// (expected.size() + 1) * sizeof(uint32_t)); -// std::vector values(totalBytes); -// uint32_t* offsetValue = reinterpret_cast(stringOffsets.data()); -// for (size_t i = 0; i < expected.size(); ++i) { -// const std::string& expectedValue = expected[i]; -// std::memcpy( -// values.data() + offsetValue[i], -// expectedValue.c_str(), -// expectedValue.size()); -// offsetValue[i + 1] = -// offsetValue[i] + static_cast(expectedValue.size()); -// } -// -// addBufferToModel(model, values); -// size_t valueBufferViewIndex = model.bufferViews.size() - 1; -// -// addBufferToModel(model, stringOffsets); -// size_t offsetBufferViewIndex = model.bufferViews.size() - 1; -// -// ExtensionModelExtStructuralMetadata& metadata = -// model.addExtension(); -// -// Schema& schema = metadata.schema.emplace(); -// Class& testClass = schema.classes["TestClass"]; -// ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; -// testClassProperty.type = ClassProperty::Type::STRING; -// -// PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); -// propertyTable.classProperty = "TestClass"; -// propertyTable.count = static_cast(expected.size()); -// -// PropertyTableProperty& propertyTableProperty = -// propertyTable.properties["TestClassProperty"]; -// propertyTableProperty.stringOffsetType = -// PropertyTableProperty::StringOffsetType::UINT32; -// propertyTableProperty.values = static_cast(valueBufferViewIndex); -// propertyTableProperty.stringOffsets = -// static_cast(offsetBufferViewIndex); -// -// PropertyTableView view(model, propertyTable); -// REQUIRE(view.status() == PropertyTableViewStatus::Valid); -// REQUIRE(view.size() == propertyTable.count); -// -// const ClassProperty* classProperty = -// view.getClassProperty("TestClassProperty"); -// REQUIRE(classProperty); -// REQUIRE(classProperty->type == ClassProperty::Type::STRING); -// REQUIRE(classProperty->componentType == std::nullopt); -// REQUIRE(classProperty->count == std::nullopt); -// REQUIRE(!classProperty->array); -// -// uint32_t invokedCallbackCount = 0; -// view.getPropertyView( -// "TestClassProperty", -// [&expected, &invokedCallbackCount]( -// const std::string& /*propertyName*/, -// auto propertyValue) mutable { -// invokedCallbackCount++; -// REQUIRE( -// propertyValue.status() == PropertyTablePropertyViewStatus::Valid); -// REQUIRE(propertyValue.size() > 0); -// -// if constexpr (std::is_same_v< -// PropertyTablePropertyView, -// decltype(propertyValue)>) { -// for (int64_t i = 0; i < propertyValue.size(); ++i) { -// REQUIRE( -// static_cast(propertyValue.get(i)) == -// expected[static_cast(i)]); -// } -// } else { -// FAIL( -// "getPropertyView returned PropertyTablePropertyView of incorrect " -// "type for TestClassProperty."); -// } -// }); -// -// REQUIRE(invokedCallbackCount == 1); -//} -// -//TEST_CASE("Test callback for scalar array PropertyTableProperty") { -// Model model; -// std::vector values = -// {12, 34, 30, 11, 34, 34, 11, 33, 122, 33, 223, 11}; -// -// addBufferToModel(model, values); -// -// ExtensionModelExtStructuralMetadata& metadata = -// model.addExtension(); -// -// Schema& schema = metadata.schema.emplace(); -// Class& testClass = schema.classes["TestClass"]; -// ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; -// testClassProperty.type = ClassProperty::Type::SCALAR; -// testClassProperty.componentType = ClassProperty::ComponentType::UINT32; -// testClassProperty.array = true; -// testClassProperty.count = 3; -// -// PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); -// propertyTable.classProperty = "TestClass"; -// propertyTable.count = static_cast( -// values.size() / static_cast(testClassProperty.count.value())); -// -// PropertyTableProperty& propertyTableProperty = -// propertyTable.properties["TestClassProperty"]; -// propertyTableProperty.values = -// static_cast(model.bufferViews.size() - 1); -// -// PropertyTableView view(model, propertyTable); -// REQUIRE(view.status() == PropertyTableViewStatus::Valid); -// REQUIRE(view.size() == propertyTable.count); -// -// const ClassProperty* classProperty = -// view.getClassProperty("TestClassProperty"); -// REQUIRE(classProperty); -// REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); -// REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT32); -// REQUIRE(classProperty->array); -// REQUIRE(classProperty->count == 3); -// -// uint32_t invokedCallbackCount = 0; -// view.getPropertyView( -// "TestClassProperty", -// [&values, &invokedCallbackCount]( -// const std::string& /*propertyName*/, -// auto propertyValue) { -// invokedCallbackCount++; -// REQUIRE( -// propertyValue.status() == PropertyTablePropertyViewStatus::Valid); -// REQUIRE(propertyValue.size() > 0); -// -// if constexpr (std::is_same_v< -// PropertyTablePropertyView< -// PropertyArrayView>, -// decltype(propertyValue)>) { -// for (int64_t i = 0; i < propertyValue.size(); ++i) { -// PropertyArrayView member = propertyValue.get(i); -// for (int64_t j = 0; j < member.size(); ++j) { -// REQUIRE(member[j] == values[static_cast(i * 3 + j)]); -// } -// } -// } else { -// FAIL( -// "getPropertyView returned PropertyTablePropertyView of incorrect " -// "type for TestClassProperty."); -// } -// }); -// -// REQUIRE(invokedCallbackCount == 1); -//} -// -//TEST_CASE("Test callback for vecN array PropertyTableProperty") { -// Model model; -// std::vector values = { -// glm::ivec3(12, 34, -30), -// glm::ivec3(-2, 0, 1), -// glm::ivec3(1, 2, 8), -// glm::ivec3(-100, 84, 6), -// glm::ivec3(2, -2, -2), -// glm::ivec3(40, 61, 3), -// }; -// -// addBufferToModel(model, values); -// -// ExtensionModelExtStructuralMetadata& metadata = -// model.addExtension(); -// -// Schema& schema = metadata.schema.emplace(); -// Class& testClass = schema.classes["TestClass"]; -// ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; -// testClassProperty.type = ClassProperty::Type::VEC3; -// testClassProperty.componentType = ClassProperty::ComponentType::INT32; -// testClassProperty.array = true; -// testClassProperty.count = 2; -// -// PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); -// propertyTable.classProperty = "TestClass"; -// propertyTable.count = static_cast( -// values.size() / static_cast(testClassProperty.count.value())); -// -// PropertyTableProperty& propertyTableProperty = -// propertyTable.properties["TestClassProperty"]; -// propertyTableProperty.values = -// static_cast(model.bufferViews.size() - 1); -// -// PropertyTableView view(model, propertyTable); -// REQUIRE(view.status() == PropertyTableViewStatus::Valid); -// REQUIRE(view.size() == propertyTable.count); -// -// const ClassProperty* classProperty = -// view.getClassProperty("TestClassProperty"); -// REQUIRE(classProperty); -// REQUIRE(classProperty->type == ClassProperty::Type::VEC3); -// REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT32); -// REQUIRE(classProperty->array); -// REQUIRE(classProperty->count == 2); -// -// uint32_t invokedCallbackCount = 0; -// view.getPropertyView( -// "TestClassProperty", -// [&values, &invokedCallbackCount]( -// const std::string& /*propertyName*/, -// auto propertyValue) { -// invokedCallbackCount++; -// REQUIRE( -// propertyValue.status() == PropertyTablePropertyViewStatus::Valid); -// REQUIRE(propertyValue.size() > 0); -// -// if constexpr (std::is_same_v< -// PropertyTablePropertyView< -// PropertyArrayView>, -// decltype(propertyValue)>) { -// for (int64_t i = 0; i < propertyValue.size(); ++i) { -// PropertyArrayView member = propertyValue.get(i); -// for (int64_t j = 0; j < member.size(); ++j) { -// REQUIRE(member[j] == values[static_cast(i * 2 + j)]); -// } -// } -// } else { -// FAIL( -// "getPropertyView returned PropertyTablePropertyView of incorrect " -// "type for TestClassProperty."); -// } -// }); -// -// REQUIRE(invokedCallbackCount == 1); -//} -// -//TEST_CASE("Test callback for matN array PropertyTableProperty") { -// Model model; -// // clang-format off -// std::vector values = { -// glm::i32mat2x2( -// 12, 34, -// -30, 20), -// glm::i32mat2x2( -// -2, -2, -// 0, 1), -// glm::i32mat2x2( -// 1, 2, -// 8, 5), -// glm::i32mat2x2( -// -100, 3, -// 84, 6), -// glm::i32mat2x2( -// 2, 12, -// -2, -2), -// glm::i32mat2x2( -// 40, 61, -// 7, -3), -// }; -// // clang-format on -// -// addBufferToModel(model, values); -// -// ExtensionModelExtStructuralMetadata& metadata = -// model.addExtension(); -// -// Schema& schema = metadata.schema.emplace(); -// Class& testClass = schema.classes["TestClass"]; -// ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; -// testClassProperty.type = ClassProperty::Type::MAT2; -// testClassProperty.componentType = ClassProperty::ComponentType::INT32; -// testClassProperty.array = true; -// testClassProperty.count = 2; -// -// PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); -// propertyTable.classProperty = "TestClass"; -// propertyTable.count = static_cast( -// values.size() / static_cast(testClassProperty.count.value())); -// -// PropertyTableProperty& propertyTableProperty = -// propertyTable.properties["TestClassProperty"]; -// propertyTableProperty.values = -// static_cast(model.bufferViews.size() - 1); -// -// PropertyTableView view(model, propertyTable); -// REQUIRE(view.status() == PropertyTableViewStatus::Valid); -// REQUIRE(view.size() == propertyTable.count); -// -// const ClassProperty* classProperty = -// view.getClassProperty("TestClassProperty"); -// REQUIRE(classProperty); -// REQUIRE(classProperty->type == ClassProperty::Type::MAT2); -// REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT32); -// REQUIRE(classProperty->array); -// REQUIRE(classProperty->count == 2); -// -// uint32_t invokedCallbackCount = 0; -// view.getPropertyView( -// "TestClassProperty", -// [&values, &invokedCallbackCount]( -// const std::string& /*propertyName*/, -// auto propertyValue) { -// invokedCallbackCount++; -// REQUIRE( -// propertyValue.status() == PropertyTablePropertyViewStatus::Valid); -// REQUIRE(propertyValue.size() > 0); -// -// if constexpr (std::is_same_v< -// PropertyTablePropertyView< -// PropertyArrayView>, -// decltype(propertyValue)>) { -// for (int64_t i = 0; i < propertyValue.size(); ++i) { -// PropertyArrayView member = propertyValue.get(i); -// for (int64_t j = 0; j < member.size(); ++j) { -// REQUIRE(member[j] == values[static_cast(i * 2 + j)]); -// } -// } -// } else { -// FAIL( -// "getPropertyView returned PropertyTablePropertyView of incorrect " -// "type for TestClassProperty."); -// } -// }); -// -// REQUIRE(invokedCallbackCount == 1); -//} -// -//TEST_CASE("Test callback for boolean array PropertyTableProperty") { -// Model model; -// -// std::vector expected = { -// true, -// false, -// false, -// true, -// false, -// false, -// true, -// true, -// true, -// false, -// false, -// true}; -// std::vector values; -// size_t requiredBytesSize = static_cast( -// glm::ceil(static_cast(expected.size()) / 8.0)); -// values.resize(requiredBytesSize); -// for (size_t i = 0; i < expected.size(); ++i) { -// uint8_t expectedValue = expected[i]; -// size_t byteIndex = i / 8; -// size_t bitIndex = i % 8; -// values[byteIndex] = -// static_cast((expectedValue << bitIndex) | values[byteIndex]); -// } -// -// addBufferToModel(model, values); -// -// ExtensionModelExtStructuralMetadata& metadata = -// model.addExtension(); -// -// Schema& schema = metadata.schema.emplace(); -// Class& testClass = schema.classes["TestClass"]; -// ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; -// testClassProperty.type = ClassProperty::Type::BOOLEAN; -// testClassProperty.array = true; -// testClassProperty.count = 3; -// -// PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); -// propertyTable.classProperty = "TestClass"; -// propertyTable.count = static_cast( -// expected.size() / static_cast(testClassProperty.count.value())); -// -// PropertyTableProperty& propertyTableProperty = -// propertyTable.properties["TestClassProperty"]; -// propertyTableProperty.values = -// static_cast(model.bufferViews.size() - 1); -// -// PropertyTableView view(model, propertyTable); -// REQUIRE(view.status() == PropertyTableViewStatus::Valid); -// REQUIRE(view.size() == propertyTable.count); -// -// const ClassProperty* classProperty = -// view.getClassProperty("TestClassProperty"); -// REQUIRE(classProperty); -// REQUIRE(classProperty->type == ClassProperty::Type::BOOLEAN); -// REQUIRE(classProperty->array); -// REQUIRE(classProperty->count == 3); -// -// uint32_t invokedCallbackCount = 0; -// view.getPropertyView( -// "TestClassProperty", -// [&expected, &invokedCallbackCount]( -// const std::string& /*propertyName*/, -// auto propertyValue) { -// invokedCallbackCount++; -// REQUIRE( -// propertyValue.status() == PropertyTablePropertyViewStatus::Valid); -// REQUIRE(propertyValue.size() > 0); -// -// if constexpr (std::is_same_v< -// PropertyTablePropertyView>, -// decltype(propertyValue)>) { -// for (int64_t i = 0; i < propertyValue.size(); ++i) { -// PropertyArrayView member = propertyValue.get(i); -// for (int64_t j = 0; j < member.size(); ++j) { -// REQUIRE(member[j] == expected[static_cast(i * 3 + j)]); -// } -// } -// } else { -// FAIL( -// "getPropertyView returned PropertyTablePropertyView of incorrect " -// "type for TestClassProperty."); -// } -// }); -// -// REQUIRE(invokedCallbackCount == 1); -//} -// -//TEST_CASE("Test callback for string array PropertyTableProperty") { -// Model model; -// -// std::vector expected{ -// "What's up", -// "Breaking news!!! Aliens no longer attacks the US first", -// "But they still abduct my cows! Those milk thiefs! 👽 🐮", -// "I'm not crazy. My mother had me tested 🤪", -// "I love you, meat bags! ❤️", -// "Book in the freezer"}; -// -// size_t totalBytes = 0; -// for (const std::string& expectedValue : expected) { -// totalBytes += expectedValue.size(); -// } -// -// std::vector offsets((expected.size() + 1) * sizeof(uint32_t)); -// std::vector values(totalBytes); -// uint32_t* offsetValue = reinterpret_cast(offsets.data()); -// for (size_t i = 0; i < expected.size(); ++i) { -// const std::string& expectedValue = expected[i]; -// std::memcpy( -// values.data() + offsetValue[i], -// expectedValue.c_str(), -// expectedValue.size()); -// offsetValue[i + 1] = -// offsetValue[i] + static_cast(expectedValue.size()); -// } -// -// addBufferToModel(model, values); -// size_t valueBufferViewIndex = model.bufferViews.size() - 1; -// -// addBufferToModel(model, offsets); -// size_t offsetBufferViewIndex = model.bufferViews.size() - 1; -// -// ExtensionModelExtStructuralMetadata& metadata = -// model.addExtension(); -// -// Schema& schema = metadata.schema.emplace(); -// Class& testClass = schema.classes["TestClass"]; -// ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; -// testClassProperty.type = ClassProperty::Type::STRING; -// testClassProperty.array = true; -// testClassProperty.count = 2; -// -// PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); -// propertyTable.classProperty = "TestClass"; -// propertyTable.count = static_cast( -// expected.size() / static_cast(testClassProperty.count.value())); -// -// PropertyTableProperty& propertyTableProperty = -// propertyTable.properties["TestClassProperty"]; -// propertyTableProperty.stringOffsetType = -// PropertyTableProperty::StringOffsetType::UINT32; -// propertyTableProperty.values = static_cast(valueBufferViewIndex); -// propertyTableProperty.stringOffsets = -// static_cast(offsetBufferViewIndex); -// -// PropertyTableView view(model, propertyTable); -// REQUIRE(view.status() == PropertyTableViewStatus::Valid); -// REQUIRE(view.size() == propertyTable.count); -// -// const ClassProperty* classProperty = -// view.getClassProperty("TestClassProperty"); -// REQUIRE(classProperty); -// REQUIRE(classProperty->type == ClassProperty::Type::STRING); -// REQUIRE(classProperty->array); -// REQUIRE(classProperty->count == 2); -// -// uint32_t invokedCallbackCount = 0; -// view.getPropertyView( -// "TestClassProperty", -// [&invokedCallbackCount]( -// const std::string& /*propertyName*/, -// auto propertyValue) { -// invokedCallbackCount++; -// REQUIRE( -// propertyValue.status() == PropertyTablePropertyViewStatus::Valid); -// REQUIRE(propertyValue.size() == 3); -// -// if constexpr (std::is_same_v< -// PropertyTablePropertyView< -// PropertyArrayView>, -// decltype(propertyValue)>) { -// PropertyArrayView v0 = propertyValue.get(0); -// REQUIRE(v0.size() == 2); -// REQUIRE(v0[0] == "What's up"); -// REQUIRE( -// v0[1] == -// "Breaking news!!! Aliens no longer attacks the US first"); -// -// PropertyArrayView v1 = propertyValue.get(1); -// REQUIRE(v1.size() == 2); -// REQUIRE( -// v1[0] == "But they still abduct my cows! Those milk thiefs! 👽 🐮"); -// REQUIRE(v1[1] == "I'm not crazy. My mother had me tested 🤪"); -// -// PropertyArrayView v2 = propertyValue.get(2); -// REQUIRE(v2.size() == 2); -// REQUIRE(v2[0] == "I love you, meat bags! ❤️"); -// REQUIRE(v2[1] == "Book in the freezer"); -// } else { -// FAIL( -// "getPropertyView returned PropertyTablePropertyView of incorrect " -// "type for TestClassProperty."); -// } -// }); -// -// REQUIRE(invokedCallbackCount == 1); -//} +#include "CesiumGltf/PropertyTableView.h" + +#include + +#include + +using namespace CesiumGltf; + +template +void addBufferToModel(Model& model, const std::vector& values) { + Buffer& valueBuffer = model.buffers.emplace_back(); + valueBuffer.cesium.data.resize(values.size() * sizeof(T)); + valueBuffer.byteLength = static_cast(valueBuffer.cesium.data.size()); + std::memcpy( + valueBuffer.cesium.data.data(), + values.data(), + valueBuffer.cesium.data.size()); + + BufferView& valueBufferView = model.bufferViews.emplace_back(); + valueBufferView.buffer = static_cast(model.buffers.size() - 1); + valueBufferView.byteOffset = 0; + valueBufferView.byteLength = valueBuffer.byteLength; +} + +TEST_CASE("Test PropertyTableView on model without EXT_structural_metadata " + "extension") { + Model model; + + // Create an erroneously isolated property table. + PropertyTable propertyTable; + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast(10); + + PropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = static_cast(0); + + PropertyTableView view(model, propertyTable); + REQUIRE( + view.status() == PropertyTableViewStatus::ErrorMissingMetadataExtension); + REQUIRE(view.size() == 0); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(!classProperty); +} + +TEST_CASE("Test PropertyTableView on model without metadata schema") { + Model model; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast(10); + + PropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = static_cast(0); + + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::ErrorMissingSchema); + REQUIRE(view.size() == 0); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(!classProperty); +} + +TEST_CASE("Test property table with nonexistent class") { + Model model; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::UINT32; + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "I Don't Exist"; + propertyTable.count = static_cast(10); + + PropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = static_cast(0); + + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::ErrorClassNotFound); + REQUIRE(view.size() == 0); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(!classProperty); +} + +TEST_CASE("Test scalar PropertyTableProperty") { + Model model; + std::vector values = {12, 34, 30, 11, 34, 34, 11, 33, 122, 33}; + + addBufferToModel(model, values); + size_t valueBufferIndex = model.buffers.size() - 1; + size_t valueBufferViewIndex = model.bufferViews.size() - 1; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::UINT32; + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast(values.size()); + + PropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = static_cast(valueBufferViewIndex); + + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT32); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->array); + + SECTION("Access correct type") { + PropertyTablePropertyView uint32Property = + view.getPropertyView("TestClassProperty"); + REQUIRE(uint32Property.status() == PropertyTablePropertyViewStatus::Valid); + REQUIRE(uint32Property.size() > 0); + + for (int64_t i = 0; i < uint32Property.size(); ++i) { + REQUIRE(uint32Property.get(i) == values[static_cast(i)]); + } + } + + SECTION("Access wrong type") { + PropertyTablePropertyView uvec3Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uvec3Invalid.status() == + PropertyTablePropertyViewStatus::ErrorTypeMismatch); + + PropertyTablePropertyView u32mat3x3Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + u32mat3x3Invalid.status() == + PropertyTablePropertyViewStatus::ErrorTypeMismatch); + + PropertyTablePropertyView boolInvalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + boolInvalid.status() == + PropertyTablePropertyViewStatus::ErrorTypeMismatch); + + PropertyTablePropertyView stringInvalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + stringInvalid.status() == + PropertyTablePropertyViewStatus::ErrorTypeMismatch); + } + + SECTION("Access wrong component type") { + PropertyTablePropertyView uint8Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uint8Invalid.status() == + PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); + + PropertyTablePropertyView int32Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + int32Invalid.status() == + PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); + + PropertyTablePropertyView uint64Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uint64Invalid.status() == + PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); + } + + SECTION("Access incorrectly as array") { + PropertyTablePropertyView> uint32ArrayInvalid = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + uint32ArrayInvalid.status() == + PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); + } + + SECTION("Wrong buffer index") { + model.bufferViews[valueBufferViewIndex].buffer = 2; + PropertyTablePropertyView uint32Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uint32Property.status() == + PropertyTablePropertyViewStatus::ErrorInvalidValueBuffer); + } + + SECTION("Wrong buffer view index") { + propertyTableProperty.values = -1; + PropertyTablePropertyView uint32Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uint32Property.status() == + PropertyTablePropertyViewStatus::ErrorInvalidValueBufferView); + } + + SECTION("Buffer view points outside of the real buffer length") { + model.buffers[valueBufferIndex].cesium.data.resize(12); + PropertyTablePropertyView uint32Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uint32Property.status() == + PropertyTablePropertyViewStatus::ErrorBufferViewOutOfBounds); + } + + SECTION("Buffer view length isn't multiple of sizeof(T)") { + model.bufferViews[valueBufferViewIndex].byteLength = 13; + PropertyTablePropertyView uint32Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uint32Property.status() == + PropertyTablePropertyViewStatus:: + ErrorBufferViewSizeNotDivisibleByTypeSize); + } + + SECTION("Buffer view length doesn't match with propertyTableCount") { + model.bufferViews[valueBufferViewIndex].byteLength = 12; + PropertyTablePropertyView uint32Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uint32Property.status() == + PropertyTablePropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + } +} + +TEST_CASE("Test vecN PropertyTableProperty") { + Model model; + std::vector values = { + glm::ivec3(-12, 34, 30), + glm::ivec3(11, 73, 0), + glm::ivec3(-2, 6, 12), + glm::ivec3(-4, 8, -13)}; + + addBufferToModel(model, values); + size_t valueBufferIndex = model.buffers.size() - 1; + size_t valueBufferViewIndex = model.bufferViews.size() - 1; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::VEC3; + testClassProperty.componentType = ClassProperty::ComponentType::INT32; + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast(values.size()); + + PropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = static_cast(valueBufferViewIndex); + + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::VEC3); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT32); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->array); + + SECTION("Access correct type") { + PropertyTablePropertyView ivec3Property = + view.getPropertyView("TestClassProperty"); + REQUIRE(ivec3Property.status() == PropertyTablePropertyViewStatus::Valid); + REQUIRE(ivec3Property.size() > 0); + + for (int64_t i = 0; i < ivec3Property.size(); ++i) { + REQUIRE(ivec3Property.get(i) == values[static_cast(i)]); + } + } + + SECTION("Access wrong type") { + PropertyTablePropertyView int32Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + int32Invalid.status() == + PropertyTablePropertyViewStatus::ErrorTypeMismatch); + + PropertyTablePropertyView ivec2Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + ivec2Invalid.status() == + PropertyTablePropertyViewStatus::ErrorTypeMismatch); + + PropertyTablePropertyView i32mat3x3Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + i32mat3x3Invalid.status() == + PropertyTablePropertyViewStatus::ErrorTypeMismatch); + + PropertyTablePropertyView boolInvalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + boolInvalid.status() == + PropertyTablePropertyViewStatus::ErrorTypeMismatch); + + PropertyTablePropertyView stringInvalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + stringInvalid.status() == + PropertyTablePropertyViewStatus::ErrorTypeMismatch); + } + + SECTION("Access wrong component type") { + PropertyTablePropertyView u8vec3Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + u8vec3Invalid.status() == + PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); + + PropertyTablePropertyView i16vec3Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + i16vec3Invalid.status() == + PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); + + PropertyTablePropertyView vec3Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + vec3Invalid.status() == + PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); + } + + SECTION("Access incorrectly as array") { + PropertyTablePropertyView> ivec3ArrayInvalid = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + ivec3ArrayInvalid.status() == + PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); + } + + SECTION("Wrong buffer index") { + model.bufferViews[valueBufferViewIndex].buffer = 2; + PropertyTablePropertyView ivec3Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + ivec3Property.status() == + PropertyTablePropertyViewStatus::ErrorInvalidValueBuffer); + } + + SECTION("Wrong buffer view index") { + propertyTableProperty.values = -1; + PropertyTablePropertyView ivec3Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + ivec3Property.status() == + PropertyTablePropertyViewStatus::ErrorInvalidValueBufferView); + } + + SECTION("Buffer view points outside of the real buffer length") { + model.buffers[valueBufferIndex].cesium.data.resize(12); + PropertyTablePropertyView ivec3Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + ivec3Property.status() == + PropertyTablePropertyViewStatus::ErrorBufferViewOutOfBounds); + } + + SECTION("Buffer view length isn't multiple of sizeof(T)") { + model.bufferViews[valueBufferViewIndex].byteLength = 11; + PropertyTablePropertyView ivec3Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + ivec3Property.status() == + PropertyTablePropertyViewStatus:: + ErrorBufferViewSizeNotDivisibleByTypeSize); + } + + SECTION("Buffer view length doesn't match with propertyTableCount") { + model.bufferViews[valueBufferViewIndex].byteLength = 12; + PropertyTablePropertyView ivec3Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + ivec3Property.status() == + PropertyTablePropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + } +} + +TEST_CASE("Test matN PropertyTableProperty") { + Model model; + // clang-format off + std::vector values = { + glm::u32mat2x2( + 12, 34, + 30, 1), + glm::u32mat2x2( + 11, 8, + 73, 102), + glm::u32mat2x2( + 1, 0, + 63, 2), + glm::u32mat2x2( + 4, 8, + 3, 23)}; + // clang-format on + + addBufferToModel(model, values); + size_t valueBufferIndex = model.buffers.size() - 1; + size_t valueBufferViewIndex = model.bufferViews.size() - 1; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::MAT2; + testClassProperty.componentType = ClassProperty::ComponentType::UINT32; + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast(values.size()); + + PropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = static_cast(valueBufferViewIndex); + + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::MAT2); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT32); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->array); + + SECTION("Access correct type") { + PropertyTablePropertyView u32mat2x2Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + u32mat2x2Property.status() == PropertyTablePropertyViewStatus::Valid); + REQUIRE(u32mat2x2Property.size() > 0); + + for (int64_t i = 0; i < u32mat2x2Property.size(); ++i) { + REQUIRE(u32mat2x2Property.get(i) == values[static_cast(i)]); + } + } + + SECTION("Access wrong type") { + PropertyTablePropertyView uint32Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uint32Invalid.status() == + PropertyTablePropertyViewStatus::ErrorTypeMismatch); + + PropertyTablePropertyView uvec2Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uvec2Invalid.status() == + PropertyTablePropertyViewStatus::ErrorTypeMismatch); + + PropertyTablePropertyView u32mat4x4Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + u32mat4x4Invalid.status() == + PropertyTablePropertyViewStatus::ErrorTypeMismatch); + + PropertyTablePropertyView boolInvalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + boolInvalid.status() == + PropertyTablePropertyViewStatus::ErrorTypeMismatch); + + PropertyTablePropertyView stringInvalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + stringInvalid.status() == + PropertyTablePropertyViewStatus::ErrorTypeMismatch); + } + + SECTION("Access wrong component type") { + PropertyTablePropertyView u8mat2x2Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + u8mat2x2Invalid.status() == + PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); + + PropertyTablePropertyView i32mat2x2Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + i32mat2x2Invalid.status() == + PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); + + PropertyTablePropertyView mat2Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + mat2Invalid.status() == + PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); + } + + SECTION("Access incorrectly as array") { + PropertyTablePropertyView> + u32mat2x2ArrayInvalid = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + u32mat2x2ArrayInvalid.status() == + PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); + } + + SECTION("Wrong buffer index") { + model.bufferViews[valueBufferViewIndex].buffer = 2; + PropertyTablePropertyView u32mat2x2Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + u32mat2x2Property.status() == + PropertyTablePropertyViewStatus::ErrorInvalidValueBuffer); + } + + SECTION("Wrong buffer view index") { + propertyTableProperty.values = -1; + PropertyTablePropertyView u32mat2x2Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + u32mat2x2Property.status() == + PropertyTablePropertyViewStatus::ErrorInvalidValueBufferView); + } + + SECTION("Buffer view points outside of the real buffer length") { + model.buffers[valueBufferIndex].cesium.data.resize(sizeof(glm::u32mat2x2)); + PropertyTablePropertyView u32mat2x2Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + u32mat2x2Property.status() == + PropertyTablePropertyViewStatus::ErrorBufferViewOutOfBounds); + } + + SECTION("Buffer view length isn't multiple of sizeof(T)") { + model.bufferViews[valueBufferViewIndex].byteLength = + sizeof(glm::u32mat2x2) * 4 - 1; + PropertyTablePropertyView u32mat2x2Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + u32mat2x2Property.status() == + PropertyTablePropertyViewStatus:: + ErrorBufferViewSizeNotDivisibleByTypeSize); + } + + SECTION("Buffer view length doesn't match with propertyTableCount") { + model.bufferViews[valueBufferViewIndex].byteLength = sizeof(glm::u32mat2x2); + + PropertyTablePropertyView u32mat2x2Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + u32mat2x2Property.status() == + PropertyTablePropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + } +} + +TEST_CASE("Test boolean PropertyTableProperty") { + Model model; + + int64_t instanceCount = 21; + std::vector expected; + std::vector values; + values.resize(3); + for (int64_t i = 0; i < instanceCount; ++i) { + if (i % 2 == 0) { + expected.emplace_back(true); + } else { + expected.emplace_back(false); + } + + uint8_t expectedValue = expected.back(); + int64_t byteIndex = i / 8; + int64_t bitIndex = i % 8; + values[static_cast(byteIndex)] = static_cast( + (expectedValue << bitIndex) | values[static_cast(byteIndex)]); + } + addBufferToModel(model, values); + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::BOOLEAN; + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast(instanceCount); + + PropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = + static_cast(model.bufferViews.size() - 1); + + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::BOOLEAN); + REQUIRE(classProperty->componentType == std::nullopt); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->array); + + SECTION("Access correct type") { + PropertyTablePropertyView boolProperty = + view.getPropertyView("TestClassProperty"); + REQUIRE(boolProperty.status() == PropertyTablePropertyViewStatus::Valid); + REQUIRE(boolProperty.size() == instanceCount); + for (int64_t i = 0; i < boolProperty.size(); ++i) { + bool expectedValue = expected[static_cast(i)]; + REQUIRE(boolProperty.get(i) == expectedValue); + } + } + + SECTION("Buffer size doesn't match with propertyTableCount") { + propertyTable.count = 66; + PropertyTablePropertyView boolProperty = + view.getPropertyView("TestClassProperty"); + REQUIRE( + boolProperty.status() == + PropertyTablePropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + } +} + +TEST_CASE("Test string PropertyTableProperty") { + Model model; + + std::vector expected{"What's up", "Test_0", "Test_1", "", "Hi"}; + size_t totalBytes = 0; + for (const std::string& expectedValue : expected) { + totalBytes += expectedValue.size(); + } + + std::vector stringOffsets( + (expected.size() + 1) * sizeof(uint32_t)); + std::vector values(totalBytes); + uint32_t* offsetValue = reinterpret_cast(stringOffsets.data()); + for (size_t i = 0; i < expected.size(); ++i) { + const std::string& expectedValue = expected[i]; + std::memcpy( + values.data() + offsetValue[i], + expectedValue.c_str(), + expectedValue.size()); + offsetValue[i + 1] = + offsetValue[i] + static_cast(expectedValue.size()); + } + + addBufferToModel(model, values); + size_t valueBufferIndex = model.buffers.size() - 1; + size_t valueBufferViewIndex = model.bufferViews.size() - 1; + + addBufferToModel(model, stringOffsets); + size_t offsetBufferIndex = model.buffers.size() - 1; + size_t offsetBufferViewIndex = model.bufferViews.size() - 1; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::STRING; + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast(expected.size()); + + PropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.stringOffsetType = + PropertyTableProperty::StringOffsetType::UINT32; + propertyTableProperty.values = static_cast(valueBufferViewIndex); + propertyTableProperty.stringOffsets = + static_cast(offsetBufferViewIndex); + + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::STRING); + REQUIRE(classProperty->componentType == std::nullopt); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->array); + + SECTION("Access correct type") { + PropertyTablePropertyView stringProperty = + view.getPropertyView("TestClassProperty"); + REQUIRE(stringProperty.status() == PropertyTablePropertyViewStatus::Valid); + for (size_t i = 0; i < expected.size(); ++i) { + REQUIRE(stringProperty.get(static_cast(i)) == expected[i]); + } + } + + SECTION("Wrong array type") { + PropertyTablePropertyView> + stringArrayInvalid = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + stringArrayInvalid.status() == + PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); + } + + SECTION("Wrong offset type") { + propertyTableProperty.stringOffsetType = + PropertyTableProperty::StringOffsetType::UINT8; + PropertyTablePropertyView stringProperty = + view.getPropertyView("TestClassProperty"); + REQUIRE( + stringProperty.status() == + PropertyTablePropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + + propertyTableProperty.stringOffsetType = + PropertyTableProperty::StringOffsetType::UINT64; + stringProperty = + view.getPropertyView("TestClassProperty"); + REQUIRE( + stringProperty.status() == + PropertyTablePropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + + propertyTableProperty.stringOffsetType = "NONSENSE"; + stringProperty = + view.getPropertyView("TestClassProperty"); + REQUIRE( + stringProperty.status() == + PropertyTablePropertyViewStatus::ErrorInvalidStringOffsetType); + + propertyTableProperty.stringOffsetType = ""; + propertyTableProperty.arrayOffsetType = + PropertyTableProperty::StringOffsetType::UINT32; + stringProperty = + view.getPropertyView("TestClassProperty"); + REQUIRE( + stringProperty.status() == + PropertyTablePropertyViewStatus::ErrorInvalidStringOffsetType); + } + + SECTION("Offset values are not sorted ascending") { + uint32_t* offset = reinterpret_cast( + model.buffers[offsetBufferIndex].cesium.data.data()); + offset[2] = + static_cast(model.buffers[valueBufferIndex].byteLength + 4); + PropertyTablePropertyView stringProperty = + view.getPropertyView("TestClassProperty"); + REQUIRE( + stringProperty.status() == + PropertyTablePropertyViewStatus::ErrorStringOffsetsNotSorted); + } + + SECTION("Offset value points outside of value buffer") { + uint32_t* offset = reinterpret_cast( + model.buffers[offsetBufferIndex].cesium.data.data()); + offset[propertyTable.count] = + static_cast(model.buffers[valueBufferIndex].byteLength + 4); + PropertyTablePropertyView stringProperty = + view.getPropertyView("TestClassProperty"); + REQUIRE( + stringProperty.status() == + PropertyTablePropertyViewStatus::ErrorStringOffsetOutOfBounds); + } +} + +TEST_CASE("Test fixed-length scalar array") { + Model model; + std::vector values = + {12, 34, 30, 11, 34, 34, 11, 33, 122, 33, 223, 11}; + + addBufferToModel(model, values); + size_t valueBufferViewIndex = model.bufferViews.size() - 1; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::UINT32; + testClassProperty.array = true; + testClassProperty.count = 3; + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast( + values.size() / static_cast(testClassProperty.count.value())); + + PropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = + static_cast(model.bufferViews.size() - 1); + + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT32); + REQUIRE(classProperty->array); + REQUIRE(classProperty->count == 3); + + SECTION("Access the right type") { + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE(arrayProperty.status() == PropertyTablePropertyViewStatus::Valid); + + for (int64_t i = 0; i < arrayProperty.size(); ++i) { + PropertyArrayView member = arrayProperty.get(i); + for (int64_t j = 0; j < member.size(); ++j) { + REQUIRE(member[j] == values[static_cast(i * 3 + j)]); + } + } + } + + SECTION("Wrong type") { + PropertyTablePropertyView> boolArrayInvalid = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + boolArrayInvalid.status() == + PropertyTablePropertyViewStatus::ErrorTypeMismatch); + + PropertyTablePropertyView> uvec2ArrayInvalid = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + uvec2ArrayInvalid.status() == + PropertyTablePropertyViewStatus::ErrorTypeMismatch); + } + + SECTION("Wrong component type") { + PropertyTablePropertyView> int32ArrayInvalid = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + int32ArrayInvalid.status() == + PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); + } + + SECTION("Not an array type") { + PropertyTablePropertyView uint32Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uint32Invalid.status() == + PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); + } + + SECTION("Buffer size is not a multiple of type size") { + model.bufferViews[valueBufferViewIndex].byteLength = 13; + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus:: + ErrorBufferViewSizeNotDivisibleByTypeSize); + } + + SECTION("Negative component count") { + testClassProperty.count = -1; + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + arrayProperty.status() == PropertyTablePropertyViewStatus:: + ErrorArrayCountAndOffsetBufferDontExist); + } + + SECTION("Value buffer doesn't fit into property table count") { + testClassProperty.count = 55; + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + } +} + +TEST_CASE("Test variable-length scalar array") { + Model model; + + std::vector> expected{ + {12, 33, 11, 344, 112, 444, 1}, + {}, + {}, + {122, 23, 333, 12}, + {}, + {333, 311, 22, 34}, + {}, + {33, 1888, 233, 33019}}; + size_t numOfElements = 0; + for (const auto& expectedMember : expected) { + numOfElements += expectedMember.size(); + } + + std::vector values(numOfElements * sizeof(uint16_t)); + std::vector offsets((expected.size() + 1) * sizeof(uint64_t)); + uint64_t* offsetValue = reinterpret_cast(offsets.data()); + for (size_t i = 0; i < expected.size(); ++i) { + std::memcpy( + values.data() + offsetValue[i], + expected[i].data(), + expected[i].size() * sizeof(uint16_t)); + offsetValue[i + 1] = offsetValue[i] + expected[i].size() * sizeof(uint16_t); + } + + addBufferToModel(model, values); + size_t valueBufferIndex = model.buffers.size() - 1; + size_t valueBufferViewIndex = model.bufferViews.size() - 1; + + addBufferToModel(model, offsets); + size_t offsetBufferIndex = model.buffers.size() - 1; + size_t offsetBufferViewIndex = model.bufferViews.size() - 1; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::UINT16; + testClassProperty.array = true; + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast(expected.size()); + + PropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = static_cast(valueBufferViewIndex); + propertyTableProperty.arrayOffsets = + static_cast(offsetBufferViewIndex); + propertyTableProperty.arrayOffsetType = + PropertyTableProperty::ArrayOffsetType::UINT64; + + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT16); + REQUIRE(classProperty->array); + REQUIRE(!classProperty->count); + + SECTION("Access the correct type") { + PropertyTablePropertyView> property = + view.getPropertyView>("TestClassProperty"); + REQUIRE(property.status() == PropertyTablePropertyViewStatus::Valid); + for (size_t i = 0; i < expected.size(); ++i) { + PropertyArrayView valueMember = + property.get(static_cast(i)); + REQUIRE(valueMember.size() == static_cast(expected[i].size())); + for (size_t j = 0; j < expected[i].size(); ++j) { + REQUIRE(expected[i][j] == valueMember[static_cast(j)]); + } + } + } + SECTION("Wrong offset type") { + propertyTableProperty.arrayOffsetType = + PropertyTableProperty::ArrayOffsetType::UINT8; + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + + propertyTableProperty.arrayOffsetType = + PropertyTableProperty::ArrayOffsetType::UINT16; + arrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + + propertyTableProperty.arrayOffsetType = "NONSENSE"; + arrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); + + propertyTableProperty.arrayOffsetType = ""; + propertyTableProperty.stringOffsetType = + PropertyTableProperty::StringOffsetType::UINT64; + arrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); + } + + SECTION("Offset values are not sorted ascending") { + propertyTableProperty.arrayOffsetType = + PropertyTableProperty::ArrayOffsetType::UINT64; + uint64_t* offset = reinterpret_cast( + model.buffers[offsetBufferIndex].cesium.data.data()); + offset[propertyTable.count] = 0; + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus::ErrorArrayOffsetsNotSorted); + } + + SECTION("Offset value points outside of value buffer") { + uint64_t* offset = reinterpret_cast( + model.buffers[offsetBufferIndex].cesium.data.data()); + offset[propertyTable.count] = + static_cast(model.buffers[valueBufferIndex].byteLength + 4); + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus::ErrorArrayOffsetOutOfBounds); + } + + SECTION("Count and offset buffer are both present") { + testClassProperty.count = 3; + PropertyTablePropertyView> property = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + property.status() == + PropertyTablePropertyViewStatus::ErrorArrayCountAndOffsetBufferCoexist); + } +} + +TEST_CASE("Test fixed-length vecN array") { + Model model; + std::vector values = { + glm::ivec3(12, 34, -30), + glm::ivec3(-2, 0, 1), + glm::ivec3(1, 2, 8), + glm::ivec3(-100, 84, 6), + glm::ivec3(2, -2, -2), + glm::ivec3(40, 61, 3), + }; + + addBufferToModel(model, values); + size_t valueBufferViewIndex = model.bufferViews.size() - 1; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::VEC3; + testClassProperty.componentType = ClassProperty::ComponentType::INT32; + testClassProperty.array = true; + testClassProperty.count = 2; + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast( + values.size() / static_cast(testClassProperty.count.value())); + + PropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = + static_cast(model.bufferViews.size() - 1); + + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::VEC3); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT32); + REQUIRE(classProperty->array); + REQUIRE(classProperty->count == 2); + + SECTION("Access the right type") { + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE(arrayProperty.status() == PropertyTablePropertyViewStatus::Valid); + + for (int64_t i = 0; i < arrayProperty.size(); ++i) { + PropertyArrayView member = arrayProperty.get(i); + for (int64_t j = 0; j < member.size(); ++j) { + REQUIRE(member[j] == values[static_cast(i * 2 + j)]); + } + } + } + + SECTION("Wrong type") { + PropertyTablePropertyView> int32ArrayInvalid = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + int32ArrayInvalid.status() == + PropertyTablePropertyViewStatus::ErrorTypeMismatch); + + PropertyTablePropertyView> ivec2ArrayInvalid = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + ivec2ArrayInvalid.status() == + PropertyTablePropertyViewStatus::ErrorTypeMismatch); + } + + SECTION("Wrong component type") { + PropertyTablePropertyView> uvec3ArrayInvalid = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + uvec3ArrayInvalid.status() == + PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); + } + + SECTION("Not an array type") { + PropertyTablePropertyView ivec3Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + ivec3Invalid.status() == + PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); + } + + SECTION("Buffer size is not a multiple of type size") { + model.bufferViews[valueBufferViewIndex].byteLength = 13; + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus:: + ErrorBufferViewSizeNotDivisibleByTypeSize); + } + + SECTION("Negative component count") { + testClassProperty.count = -1; + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == PropertyTablePropertyViewStatus:: + ErrorArrayCountAndOffsetBufferDontExist); + } + + SECTION("Value buffer doesn't fit into property table count") { + testClassProperty.count = 55; + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + } +} + +TEST_CASE("Test variable-length vecN array") { + Model model; + // clang-format off + std::vector> expected{ + { glm::ivec3(12, 34, -30), glm::ivec3(-2, 0, 1) }, + { glm::ivec3(1, 2, 8), }, + {}, + { glm::ivec3(-100, 84, 6), glm::ivec3(2, -2, -2), glm::ivec3(40, 61, 3) }, + { glm::ivec3(-1, 4, -7) }, + }; + // clang-format on + + size_t numOfElements = 0; + for (const auto& expectedMember : expected) { + numOfElements += expectedMember.size(); + } + + std::vector values(numOfElements * sizeof(glm::ivec3)); + std::vector offsets((expected.size() + 1) * sizeof(uint64_t)); + uint64_t* offsetValue = reinterpret_cast(offsets.data()); + for (size_t i = 0; i < expected.size(); ++i) { + std::memcpy( + values.data() + offsetValue[i], + expected[i].data(), + expected[i].size() * sizeof(glm::ivec3)); + offsetValue[i + 1] = + offsetValue[i] + expected[i].size() * sizeof(glm::ivec3); + } + + addBufferToModel(model, values); + size_t valueBufferIndex = model.buffers.size() - 1; + size_t valueBufferViewIndex = model.bufferViews.size() - 1; + + addBufferToModel(model, offsets); + size_t offsetBufferIndex = model.buffers.size() - 1; + size_t offsetBufferViewIndex = model.bufferViews.size() - 1; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::VEC3; + testClassProperty.componentType = ClassProperty::ComponentType::INT32; + testClassProperty.array = true; + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast(expected.size()); + + PropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = static_cast(valueBufferViewIndex); + propertyTableProperty.arrayOffsets = + static_cast(offsetBufferViewIndex); + propertyTableProperty.arrayOffsetType = + PropertyTableProperty::ArrayOffsetType::UINT64; + + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::VEC3); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT32); + REQUIRE(classProperty->array); + REQUIRE(!classProperty->count); + + SECTION("Access the correct type") { + PropertyTablePropertyView> property = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE(property.status() == PropertyTablePropertyViewStatus::Valid); + for (size_t i = 0; i < expected.size(); ++i) { + PropertyArrayView valueMember = + property.get(static_cast(i)); + REQUIRE(valueMember.size() == static_cast(expected[i].size())); + for (size_t j = 0; j < expected[i].size(); ++j) { + REQUIRE(expected[i][j] == valueMember[static_cast(j)]); + } + } + } + + SECTION("Wrong offset type") { + propertyTableProperty.arrayOffsetType = + PropertyTableProperty::ArrayOffsetType::UINT8; + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + + propertyTableProperty.arrayOffsetType = + PropertyTableProperty::ArrayOffsetType::UINT16; + arrayProperty = view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + + propertyTableProperty.arrayOffsetType = "NONSENSE"; + arrayProperty = view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); + + propertyTableProperty.arrayOffsetType = ""; + propertyTableProperty.stringOffsetType = + PropertyTableProperty::StringOffsetType::UINT64; + arrayProperty = view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); + } + + SECTION("Offset values are not sorted ascending") { + propertyTableProperty.arrayOffsetType = + PropertyTableProperty::ArrayOffsetType::UINT64; + uint64_t* offset = reinterpret_cast( + model.buffers[offsetBufferIndex].cesium.data.data()); + offset[propertyTable.count] = 0; + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus::ErrorArrayOffsetsNotSorted); + } + + SECTION("Offset value points outside of value buffer") { + uint64_t* offset = reinterpret_cast( + model.buffers[offsetBufferIndex].cesium.data.data()); + offset[propertyTable.count] = + static_cast(model.buffers[valueBufferIndex].byteLength + 4); + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus::ErrorArrayOffsetOutOfBounds); + } + + SECTION("Count and offset buffer are both present") { + testClassProperty.count = 3; + PropertyTablePropertyView> property = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + property.status() == + PropertyTablePropertyViewStatus::ErrorArrayCountAndOffsetBufferCoexist); + } +} + +TEST_CASE("Test fixed-length matN array") { + Model model; + // clang-format off + std::vector values = { + glm::i32mat2x2( + 12, 34, + -30, 20), + glm::i32mat2x2( + -2, -2, + 0, 1), + glm::i32mat2x2( + 1, 2, + 8, 5), + glm::i32mat2x2( + -100, 3, + 84, 6), + glm::i32mat2x2( + 2, 12, + -2, -2), + glm::i32mat2x2( + 40, 61, + 7, -3), + }; + // clang-format on + + addBufferToModel(model, values); + size_t valueBufferViewIndex = model.bufferViews.size() - 1; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::MAT2; + testClassProperty.componentType = ClassProperty::ComponentType::INT32; + testClassProperty.array = true; + testClassProperty.count = 2; + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast( + values.size() / static_cast(testClassProperty.count.value())); + + PropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = + static_cast(model.bufferViews.size() - 1); + + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::MAT2); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT32); + REQUIRE(classProperty->array); + REQUIRE(classProperty->count == 2); + + SECTION("Access the right type") { + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE(arrayProperty.status() == PropertyTablePropertyViewStatus::Valid); + + for (int64_t i = 0; i < arrayProperty.size(); ++i) { + PropertyArrayView member = arrayProperty.get(i); + for (int64_t j = 0; j < member.size(); ++j) { + REQUIRE(member[j] == values[static_cast(i * 2 + j)]); + } + } + } + + SECTION("Wrong type") { + PropertyTablePropertyView> int32ArrayInvalid = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + int32ArrayInvalid.status() == + PropertyTablePropertyViewStatus::ErrorTypeMismatch); + + PropertyTablePropertyView> ivec2ArrayInvalid = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + ivec2ArrayInvalid.status() == + PropertyTablePropertyViewStatus::ErrorTypeMismatch); + } + + SECTION("Wrong component type") { + PropertyTablePropertyView> + u32mat2x2ArrayInvalid = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + u32mat2x2ArrayInvalid.status() == + PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); + } + + SECTION("Not an array type") { + PropertyTablePropertyView ivec3Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + ivec3Invalid.status() == + PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); + } + + SECTION("Buffer size is not a multiple of type size") { + model.bufferViews[valueBufferViewIndex].byteLength = 13; + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus:: + ErrorBufferViewSizeNotDivisibleByTypeSize); + } + + SECTION("Negative component count") { + testClassProperty.count = -1; + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == PropertyTablePropertyViewStatus:: + ErrorArrayCountAndOffsetBufferDontExist); + } + + SECTION("Value buffer doesn't fit into property table count") { + testClassProperty.count = 55; + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + } +} + +TEST_CASE("Test variable-length matN array") { + Model model; + // clang-format off + std::vector data0{ + glm::i32mat2x2( + 3, -2, + 1, 0), + glm::i32mat2x2( + 40, 3, + 8, -9) + }; + std::vector data1{ + glm::i32mat2x2( + 1, 10, + 7, 8), + }; + std::vector data2{ + glm::i32mat2x2( + 18, 0, + 1, 17), + glm::i32mat2x2( + -4, -2, + -9, 1), + glm::i32mat2x2( + 1, 8, + -99, 3), + }; + // clang-format on + + std::vector> + expected{data0, {}, data1, data2, {}}; + + size_t numOfElements = 0; + for (const auto& expectedMember : expected) { + numOfElements += expectedMember.size(); + } + + std::vector values(numOfElements * sizeof(glm::i32mat2x2)); + std::vector offsets((expected.size() + 1) * sizeof(uint64_t)); + uint64_t* offsetValue = reinterpret_cast(offsets.data()); + for (size_t i = 0; i < expected.size(); ++i) { + std::memcpy( + values.data() + offsetValue[i], + expected[i].data(), + expected[i].size() * sizeof(glm::i32mat2x2)); + offsetValue[i + 1] = + offsetValue[i] + expected[i].size() * sizeof(glm::i32mat2x2); + } + + addBufferToModel(model, values); + size_t valueBufferIndex = model.buffers.size() - 1; + size_t valueBufferViewIndex = model.bufferViews.size() - 1; + + addBufferToModel(model, offsets); + size_t offsetBufferIndex = model.buffers.size() - 1; + size_t offsetBufferViewIndex = model.bufferViews.size() - 1; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::MAT2; + testClassProperty.componentType = ClassProperty::ComponentType::INT32; + testClassProperty.array = true; + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast(expected.size()); + + PropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = static_cast(valueBufferViewIndex); + propertyTableProperty.arrayOffsets = + static_cast(offsetBufferViewIndex); + propertyTableProperty.arrayOffsetType = + PropertyTableProperty::ArrayOffsetType::UINT64; + + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::MAT2); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT32); + REQUIRE(classProperty->array); + REQUIRE(!classProperty->count); + + SECTION("Access the correct type") { + PropertyTablePropertyView> property = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE(property.status() == PropertyTablePropertyViewStatus::Valid); + for (size_t i = 0; i < expected.size(); ++i) { + PropertyArrayView valueMember = + property.get(static_cast(i)); + REQUIRE(valueMember.size() == static_cast(expected[i].size())); + for (size_t j = 0; j < expected[i].size(); ++j) { + REQUIRE(expected[i][j] == valueMember[static_cast(j)]); + } + } + } + + SECTION("Wrong offset type") { + propertyTableProperty.arrayOffsetType = + PropertyTableProperty::ArrayOffsetType::UINT8; + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + + propertyTableProperty.arrayOffsetType = + PropertyTableProperty::ArrayOffsetType::UINT16; + arrayProperty = view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + + propertyTableProperty.arrayOffsetType = "NONSENSE"; + arrayProperty = view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); + + propertyTableProperty.arrayOffsetType = ""; + propertyTableProperty.stringOffsetType = + PropertyTableProperty::StringOffsetType::UINT64; + arrayProperty = view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); + } + + SECTION("Offset values are not sorted ascending") { + propertyTableProperty.arrayOffsetType = + PropertyTableProperty::ArrayOffsetType::UINT64; + uint64_t* offset = reinterpret_cast( + model.buffers[offsetBufferIndex].cesium.data.data()); + offset[propertyTable.count] = 0; + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus::ErrorArrayOffsetsNotSorted); + } + + SECTION("Offset value points outside of value buffer") { + uint64_t* offset = reinterpret_cast( + model.buffers[offsetBufferIndex].cesium.data.data()); + offset[propertyTable.count] = + static_cast(model.buffers[valueBufferIndex].byteLength + 4); + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus::ErrorArrayOffsetOutOfBounds); + } + + SECTION("Count and offset buffer are both present") { + testClassProperty.count = 3; + PropertyTablePropertyView> property = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + property.status() == + PropertyTablePropertyViewStatus::ErrorArrayCountAndOffsetBufferCoexist); + } +} + +TEST_CASE("Test fixed-length boolean array") { + Model model; + + std::vector expected = { + true, + false, + false, + true, + false, + false, + true, + true, + true, + false, + false, + true}; + std::vector values; + size_t requiredBytesSize = static_cast( + glm::ceil(static_cast(expected.size()) / 8.0)); + values.resize(requiredBytesSize); + for (size_t i = 0; i < expected.size(); ++i) { + uint8_t expectedValue = expected[i]; + size_t byteIndex = i / 8; + size_t bitIndex = i % 8; + values[byteIndex] = + static_cast((expectedValue << bitIndex) | values[byteIndex]); + } + + addBufferToModel(model, values); + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::BOOLEAN; + testClassProperty.array = true; + testClassProperty.count = 3; + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast( + expected.size() / static_cast(testClassProperty.count.value())); + + PropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = + static_cast(model.bufferViews.size() - 1); + + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::BOOLEAN); + REQUIRE(classProperty->array); + REQUIRE(classProperty->count == 3); + + SECTION("Access correct type") { + PropertyTablePropertyView> boolArrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + boolArrayProperty.status() == PropertyTablePropertyViewStatus::Valid); + REQUIRE(boolArrayProperty.size() == propertyTable.count); + REQUIRE(boolArrayProperty.size() > 0); + for (int64_t i = 0; i < boolArrayProperty.size(); ++i) { + PropertyArrayView valueMember = boolArrayProperty.get(i); + for (int64_t j = 0; j < valueMember.size(); ++j) { + REQUIRE(valueMember[j] == expected[static_cast(i * 3 + j)]); + } + } + } + + SECTION("Wrong type") { + PropertyTablePropertyView> uint8ArrayInvalid = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + uint8ArrayInvalid.status() == + PropertyTablePropertyViewStatus::ErrorTypeMismatch); + } + + SECTION("View is not array type") { + PropertyTablePropertyView boolInvalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + boolInvalid.status() == + PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); + } + + SECTION("Value buffer doesn't have enough required bytes") { + testClassProperty.count = 11; + PropertyTablePropertyView> boolArrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + boolArrayProperty.status() == + PropertyTablePropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + } + + SECTION("Count is negative") { + testClassProperty.count = -1; + PropertyTablePropertyView> boolArrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + boolArrayProperty.status() == + PropertyTablePropertyViewStatus:: + ErrorArrayCountAndOffsetBufferDontExist); + } +} + +TEST_CASE("Test variable-length boolean array") { + Model model; + + std::vector> expected{ + {true, false, true, true, false, true, true}, + {}, + {}, + {}, + {false, false, false, false}, + {true, false, true}, + {false}, + {true, true, true, true, true, false, false}}; + size_t numOfElements = 0; + for (const auto& expectedMember : expected) { + numOfElements += expectedMember.size(); + } + + size_t requiredBytesSize = + static_cast(glm::ceil(static_cast(numOfElements) / 8.0)); + std::vector values(requiredBytesSize); + std::vector offsets((expected.size() + 1) * sizeof(uint64_t)); + uint64_t* offsetValue = reinterpret_cast(offsets.data()); + size_t indexSoFar = 0; + for (size_t i = 0; i < expected.size(); ++i) { + for (size_t j = 0; j < expected[i].size(); ++j) { + uint8_t expectedValue = expected[i][j]; + size_t byteIndex = indexSoFar / 8; + size_t bitIndex = indexSoFar % 8; + values[byteIndex] = static_cast( + (expectedValue << bitIndex) | static_cast(values[byteIndex])); + ++indexSoFar; + } + offsetValue[i + 1] = offsetValue[i] + expected[i].size(); + } + + addBufferToModel(model, values); + size_t valueBufferIndex = model.buffers.size() - 1; + size_t valueBufferViewIndex = model.bufferViews.size() - 1; + + addBufferToModel(model, offsets); + size_t offsetBufferIndex = model.buffers.size() - 1; + size_t offsetBufferViewIndex = model.bufferViews.size() - 1; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::BOOLEAN; + testClassProperty.array = true; + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast(expected.size()); + + PropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = static_cast(valueBufferViewIndex); + propertyTableProperty.arrayOffsets = + static_cast(offsetBufferViewIndex); + propertyTableProperty.arrayOffsetType = + PropertyTableProperty::ArrayOffsetType::UINT64; + + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::BOOLEAN); + REQUIRE(classProperty->array); + REQUIRE(!classProperty->count); + + SECTION("Access correct type") { + PropertyTablePropertyView> boolArrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + boolArrayProperty.status() == PropertyTablePropertyViewStatus::Valid); + for (size_t i = 0; i < expected.size(); ++i) { + PropertyArrayView arrayMember = + boolArrayProperty.get(static_cast(i)); + REQUIRE(arrayMember.size() == static_cast(expected[i].size())); + for (size_t j = 0; j < expected[i].size(); ++j) { + REQUIRE(expected[i][j] == arrayMember[static_cast(j)]); + } + } + } + + SECTION("Wrong offset type") { + propertyTableProperty.arrayOffsetType = + PropertyTableProperty::ArrayOffsetType::UINT8; + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + + propertyTableProperty.arrayOffsetType = + PropertyTableProperty::ArrayOffsetType::UINT16; + arrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + + propertyTableProperty.arrayOffsetType = "NONSENSE"; + arrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); + + propertyTableProperty.arrayOffsetType = ""; + propertyTableProperty.stringOffsetType = + PropertyTableProperty::StringOffsetType::UINT64; + arrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); + } + + SECTION("Offset values are not sorted ascending") { + propertyTableProperty.arrayOffsetType = + PropertyTableProperty::ArrayOffsetType::UINT64; + uint64_t* offset = reinterpret_cast( + model.buffers[offsetBufferIndex].cesium.data.data()); + offset[propertyTable.count] = 0; + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus::ErrorArrayOffsetsNotSorted); + } + + SECTION("Offset value points outside of value buffer") { + uint64_t* offset = reinterpret_cast( + model.buffers[offsetBufferIndex].cesium.data.data()); + offset[propertyTable.count] = static_cast( + model.buffers[valueBufferIndex].byteLength * 8 + 20); + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus::ErrorArrayOffsetOutOfBounds); + } + + SECTION("Count and offset buffer both present") { + testClassProperty.count = 3; + PropertyTablePropertyView> boolArrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + boolArrayProperty.status() == + PropertyTablePropertyViewStatus::ErrorArrayCountAndOffsetBufferCoexist); + } +} + +TEST_CASE("Test fixed-length arrays of strings") { + Model model; + + std::vector expected{ + "What's up", + "Breaking news!!! Aliens no longer attacks the US first", + "But they still abduct my cows! Those milk thiefs! 👽 🐮", + "I'm not crazy. My mother had me tested 🤪", + "I love you, meat bags! ❤️", + "Book in the freezer"}; + + size_t totalBytes = 0; + for (const std::string& expectedValue : expected) { + totalBytes += expectedValue.size(); + } + + std::vector offsets((expected.size() + 1) * sizeof(uint32_t)); + std::vector values(totalBytes); + uint32_t* offsetValue = reinterpret_cast(offsets.data()); + for (size_t i = 0; i < expected.size(); ++i) { + const std::string& expectedValue = expected[i]; + std::memcpy( + values.data() + offsetValue[i], + expectedValue.c_str(), + expectedValue.size()); + offsetValue[i + 1] = + offsetValue[i] + static_cast(expectedValue.size()); + } + + addBufferToModel(model, values); + size_t valueBufferViewIndex = model.bufferViews.size() - 1; + + addBufferToModel(model, offsets); + size_t offsetBufferViewIndex = model.bufferViews.size() - 1; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::STRING; + testClassProperty.array = true; + testClassProperty.count = 2; + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast( + expected.size() / static_cast(testClassProperty.count.value())); + + PropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.stringOffsetType = + PropertyTableProperty::StringOffsetType::UINT32; + propertyTableProperty.values = static_cast(valueBufferViewIndex); + propertyTableProperty.stringOffsets = + static_cast(offsetBufferViewIndex); + + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::STRING); + REQUIRE(classProperty->array); + REQUIRE(classProperty->count == 2); + + SECTION("Access correct type") { + PropertyTablePropertyView> + stringProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE(stringProperty.status() == PropertyTablePropertyViewStatus::Valid); + REQUIRE(stringProperty.size() == 3); + + PropertyArrayView v0 = stringProperty.get(0); + REQUIRE(v0.size() == 2); + REQUIRE(v0[0] == "What's up"); + REQUIRE(v0[1] == "Breaking news!!! Aliens no longer attacks the US first"); + + PropertyArrayView v1 = stringProperty.get(1); + REQUIRE(v1.size() == 2); + REQUIRE(v1[0] == "But they still abduct my cows! Those milk thiefs! 👽 🐮"); + REQUIRE(v1[1] == "I'm not crazy. My mother had me tested 🤪"); + + PropertyArrayView v2 = stringProperty.get(2); + REQUIRE(v2.size() == 2); + REQUIRE(v2[0] == "I love you, meat bags! ❤️"); + REQUIRE(v2[1] == "Book in the freezer"); + } + + SECTION("Array type mismatch") { + PropertyTablePropertyView stringProperty = + view.getPropertyView("TestClassProperty"); + REQUIRE( + stringProperty.status() == + PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); + } + + SECTION("Count is negative") { + testClassProperty.count = -1; + PropertyTablePropertyView> + stringProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + stringProperty.status() == PropertyTablePropertyViewStatus:: + ErrorArrayCountAndOffsetBufferDontExist); + } + + SECTION("Offset type is unknown") { + propertyTableProperty.stringOffsetType = "NONSENSE"; + PropertyTablePropertyView> + stringProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + stringProperty.status() == + PropertyTablePropertyViewStatus::ErrorInvalidStringOffsetType); + + propertyTableProperty.stringOffsetType = ""; + propertyTableProperty.arrayOffsetType = + PropertyTableProperty::ArrayOffsetType::UINT32; + stringProperty = view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + stringProperty.status() == + PropertyTablePropertyViewStatus::ErrorInvalidStringOffsetType); + } + + SECTION("String offsets don't exist") { + propertyTableProperty.stringOffsets = -1; + PropertyTablePropertyView> + stringProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + stringProperty.status() == + PropertyTablePropertyViewStatus::ErrorInvalidStringOffsetBufferView); + } +} + +TEST_CASE("Test variable-length arrays of strings") { + Model model; + + std::vector> expected{ + {"What's up"}, + {"Breaking news!!! Aliens no longer attacks the US first", + "But they still abduct my cows! Those milk thiefs! 👽 🐮"}, + {"I'm not crazy. My mother had me tested 🤪", + "I love you, meat bags! ❤️", + "Book in the freezer"}}; + + size_t totalBytes = 0; + size_t numOfElements = 0; + for (const auto& expectedValues : expected) { + for (const auto& value : expectedValues) { + totalBytes += value.size(); + } + + numOfElements += expectedValues.size(); + } + + std::vector offsets((expected.size() + 1) * sizeof(uint32_t)); + std::vector stringOffsets((numOfElements + 1) * sizeof(uint32_t)); + std::vector values(totalBytes); + uint32_t* offsetValue = reinterpret_cast(offsets.data()); + uint32_t* stringOffsetValue = + reinterpret_cast(stringOffsets.data()); + size_t strOffsetIdx = 0; + for (size_t i = 0; i < expected.size(); ++i) { + for (size_t j = 0; j < expected[i].size(); ++j) { + const std::string& expectedValue = expected[i][j]; + std::memcpy( + values.data() + stringOffsetValue[strOffsetIdx], + expectedValue.c_str(), + expectedValue.size()); + + stringOffsetValue[strOffsetIdx + 1] = + stringOffsetValue[strOffsetIdx] + + static_cast(expectedValue.size()); + ++strOffsetIdx; + } + + offsetValue[i + 1] = + offsetValue[i] + + static_cast(expected[i].size() * sizeof(uint32_t)); + } + + addBufferToModel(model, values); + size_t valueBufferViewIndex = model.bufferViews.size() - 1; + + addBufferToModel(model, offsets); + size_t arrayOffsetBuffer = model.buffers.size() - 1; + size_t arrayOffsetBufferView = model.bufferViews.size() - 1; + + addBufferToModel(model, stringOffsets); + size_t stringOffsetBuffer = model.buffers.size() - 1; + size_t stringOffsetBufferView = model.bufferViews.size() - 1; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::STRING; + testClassProperty.array = true; + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast(expected.size()); + + PropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.arrayOffsetType = + PropertyTableProperty::ArrayOffsetType::UINT32; + propertyTableProperty.stringOffsetType = + PropertyTableProperty::StringOffsetType::UINT32; + propertyTableProperty.values = static_cast(valueBufferViewIndex); + propertyTableProperty.arrayOffsets = + static_cast(arrayOffsetBufferView); + propertyTableProperty.stringOffsets = + static_cast(stringOffsetBufferView); + + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::STRING); + REQUIRE(classProperty->array); + REQUIRE(!classProperty->componentType); + REQUIRE(!classProperty->count); + + SECTION("Access correct type") { + PropertyTablePropertyView> + stringProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE(stringProperty.status() == PropertyTablePropertyViewStatus::Valid); + for (size_t i = 0; i < expected.size(); ++i) { + PropertyArrayView stringArray = + stringProperty.get(static_cast(i)); + for (size_t j = 0; j < expected[i].size(); ++j) { + REQUIRE(stringArray[static_cast(j)] == expected[i][j]); + } + } + } + + SECTION("Wrong array offset type") { + propertyTableProperty.arrayOffsetType = + PropertyTableProperty::ArrayOffsetType::UINT8; + PropertyTablePropertyView> + arrayProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + + propertyTableProperty.arrayOffsetType = + PropertyTableProperty::ArrayOffsetType::UINT16; + arrayProperty = view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + + propertyTableProperty.arrayOffsetType = "NONSENSE"; + arrayProperty = view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); + propertyTableProperty.arrayOffsetType = + PropertyTableProperty::ArrayOffsetType::UINT32; + } + + SECTION("Wrong string offset type") { + propertyTableProperty.stringOffsetType = + PropertyTableProperty::StringOffsetType::UINT8; + PropertyTablePropertyView> + arrayProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + + propertyTableProperty.stringOffsetType = + PropertyTableProperty::StringOffsetType::UINT16; + arrayProperty = view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + + propertyTableProperty.stringOffsetType = "NONSENSE"; + arrayProperty = view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus::ErrorInvalidStringOffsetType); + propertyTableProperty.stringOffsetType = + PropertyTableProperty::StringOffsetType::UINT32; + } + + SECTION("Array offset values are not sorted ascending") { + uint32_t* offset = reinterpret_cast( + model.buffers[arrayOffsetBuffer].cesium.data.data()); + offset[0] = static_cast(1000); + PropertyTablePropertyView> + arrayProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus::ErrorArrayOffsetsNotSorted); + offset[0] = 0; + } + + SECTION("String offset values are not sorted ascending") { + uint32_t* offset = reinterpret_cast( + model.buffers[stringOffsetBuffer].cesium.data.data()); + offset[0] = static_cast(1000); + PropertyTablePropertyView> + arrayProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus::ErrorStringOffsetsNotSorted); + offset[0] = 0; + } + + SECTION("Array offset value points outside of value buffer") { + uint32_t* offset = reinterpret_cast( + model.buffers[arrayOffsetBuffer].cesium.data.data()); + uint32_t previousValue = offset[propertyTable.count]; + offset[propertyTable.count] = static_cast(100000); + PropertyTablePropertyView> + arrayProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus::ErrorArrayOffsetOutOfBounds); + offset[propertyTable.count] = previousValue; + } + + SECTION("String offset value points outside of value buffer") { + uint32_t* offset = reinterpret_cast( + model.buffers[stringOffsetBuffer].cesium.data.data()); + uint32_t previousValue = offset[6]; + offset[6] = static_cast(100000); + PropertyTablePropertyView> + arrayProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus::ErrorStringOffsetOutOfBounds); + offset[6] = previousValue; + } + + SECTION("Count and offset buffer both present") { + testClassProperty.count = 3; + PropertyTablePropertyView> + boolArrayProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + boolArrayProperty.status() == + PropertyTablePropertyViewStatus::ErrorArrayCountAndOffsetBufferCoexist); + } +} + +TEST_CASE("Test callback on invalid property table view") { + Model model; + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + metadata.schema.emplace(); + + // Property table has a nonexistent class. + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast(5); + + PropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = static_cast(-1); + + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::ErrorClassNotFound); + REQUIRE(view.size() == 0); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(!classProperty); + + uint32_t invokedCallbackCount = 0; + view.getPropertyView( + "TestClassProperty", + [&invokedCallbackCount]( + const std::string& /*propertyName*/, + auto propertyValue) mutable { + invokedCallbackCount++; + REQUIRE( + propertyValue.status() == + PropertyTablePropertyViewStatus::ErrorInvalidPropertyTable); + REQUIRE(propertyValue.size() == 0); + }); + + REQUIRE(invokedCallbackCount == 1); +} + +TEST_CASE("Test callback for invalid PropertyTableProperty") { + Model model; + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["InvalidProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::UINT32; + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast(5); + + PropertyTableProperty& propertyTableProperty = + propertyTable.properties["InvalidProperty"]; + propertyTableProperty.values = static_cast(-1); + + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + + const ClassProperty* classProperty = view.getClassProperty("InvalidProperty"); + REQUIRE(classProperty); + + classProperty = view.getClassProperty("NonexistentProperty"); + REQUIRE(!classProperty); + + uint32_t invokedCallbackCount = 0; + auto testCallback = [&invokedCallbackCount]( + const std::string& /*propertyName*/, + auto propertyValue) mutable { + invokedCallbackCount++; + REQUIRE(propertyValue.status() != PropertyTablePropertyViewStatus::Valid); + REQUIRE(propertyValue.size() == 0); + }; + + view.getPropertyView("InvalidProperty", testCallback); + view.getPropertyView("NonexistentProperty", testCallback); + + REQUIRE(invokedCallbackCount == 2); +} + +TEST_CASE("Test callback for scalar PropertyTableProperty") { + Model model; + std::vector values = {12, 34, 30, 11, 34, 34, 11, 33, 122, 33}; + + addBufferToModel(model, values); + size_t valueBufferViewIndex = model.bufferViews.size() - 1; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::UINT32; + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast(values.size()); + + PropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = static_cast(valueBufferViewIndex); + + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT32); + REQUIRE(!classProperty->array); + REQUIRE(classProperty->count == std::nullopt); + + uint32_t invokedCallbackCount = 0; + view.getPropertyView( + "TestClassProperty", + [&values, &invokedCallbackCount]( + const std::string& /*propertyName*/, + auto propertyValue) mutable { + invokedCallbackCount++; + if constexpr (std::is_same_v< + PropertyTablePropertyView, + decltype(propertyValue)>) { + REQUIRE( + propertyValue.status() == PropertyTablePropertyViewStatus::Valid); + REQUIRE(propertyValue.size() > 0); + + for (int64_t i = 0; i < propertyValue.size(); ++i) { + REQUIRE( + static_cast(propertyValue.get(i)) == + values[static_cast(i)]); + } + } else { + FAIL( + "getPropertyView returned PropertyTablePropertyView of incorrect " + "type for TestClassProperty."); + } + }); + + REQUIRE(invokedCallbackCount == 1); +} + +TEST_CASE("Test callback for vecN PropertyTableProperty") { + Model model; + std::vector values = { + glm::ivec3(-12, 34, 30), + glm::ivec3(11, 73, 0), + glm::ivec3(-2, 6, 12), + glm::ivec3(-4, 8, -13)}; + + addBufferToModel(model, values); + size_t valueBufferViewIndex = model.bufferViews.size() - 1; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::VEC3; + testClassProperty.componentType = ClassProperty::ComponentType::INT32; + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast(values.size()); + + PropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = static_cast(valueBufferViewIndex); + + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::VEC3); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT32); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->array); + + uint32_t invokedCallbackCount = 0; + view.getPropertyView( + "TestClassProperty", + [&values, &invokedCallbackCount]( + const std::string& /*propertyName*/, + auto propertyValue) mutable { + invokedCallbackCount++; + REQUIRE( + propertyValue.status() == PropertyTablePropertyViewStatus::Valid); + REQUIRE(propertyValue.size() > 0); + + if constexpr (std::is_same_v< + PropertyTablePropertyView, + decltype(propertyValue)>) { + for (int64_t i = 0; i < propertyValue.size(); ++i) { + REQUIRE( + static_cast(propertyValue.get(i)) == + values[static_cast(i)]); + } + } else { + FAIL( + "getPropertyView returned PropertyTablePropertyView of incorrect " + "type for TestClassProperty."); + } + }); + + REQUIRE(invokedCallbackCount == 1); +} + +TEST_CASE("Test callback for matN PropertyTableProperty") { + Model model; + // clang-format off + std::vector values = { + glm::u32mat2x2( + 12, 34, + 30, 1), + glm::u32mat2x2( + 11, 8, + 73, 102), + glm::u32mat2x2( + 1, 0, + 63, 2), + glm::u32mat2x2( + 4, 8, + 3, 23)}; + // clang-format on + + addBufferToModel(model, values); + size_t valueBufferViewIndex = model.bufferViews.size() - 1; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::MAT2; + testClassProperty.componentType = ClassProperty::ComponentType::UINT32; + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast(values.size()); + + PropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = static_cast(valueBufferViewIndex); + + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::MAT2); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT32); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->array); + + uint32_t invokedCallbackCount = 0; + view.getPropertyView( + "TestClassProperty", + [&values, &invokedCallbackCount]( + const std::string& /*propertyName*/, + auto propertyValue) mutable { + REQUIRE( + propertyValue.status() == PropertyTablePropertyViewStatus::Valid); + REQUIRE(propertyValue.size() > 0); + + if constexpr (std::is_same_v< + PropertyTablePropertyView, + decltype(propertyValue)>) { + for (int64_t i = 0; i < propertyValue.size(); ++i) { + REQUIRE( + static_cast(propertyValue.get(i)) == + values[static_cast(i)]); + } + } else { + FAIL( + "getPropertyView returned PropertyTablePropertyView of incorrect " + "type for TestClassProperty."); + } + invokedCallbackCount++; + }); + + REQUIRE(invokedCallbackCount == 1); +} + +TEST_CASE("Test callback for boolean PropertyTableProperty") { + Model model; + + int64_t instanceCount = 21; + std::vector expected; + std::vector values; + values.resize(3); + for (int64_t i = 0; i < instanceCount; ++i) { + if (i % 2 == 0) { + expected.emplace_back(true); + } else { + expected.emplace_back(false); + } + + uint8_t expectedValue = expected.back(); + int64_t byteIndex = i / 8; + int64_t bitIndex = i % 8; + values[static_cast(byteIndex)] = static_cast( + (expectedValue << bitIndex) | values[static_cast(byteIndex)]); + } + + addBufferToModel(model, values); + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::BOOLEAN; + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast(instanceCount); + + PropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = + static_cast(model.bufferViews.size() - 1); + + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::BOOLEAN); + REQUIRE(classProperty->componentType == std::nullopt); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->array); + + uint32_t invokedCallbackCount = 0; + view.getPropertyView( + "TestClassProperty", + [&expected, &invokedCallbackCount]( + const std::string& /*propertyName*/, + auto propertyValue) mutable { + invokedCallbackCount++; + + REQUIRE( + propertyValue.status() == PropertyTablePropertyViewStatus::Valid); + REQUIRE(propertyValue.size() > 0); + + if constexpr (std::is_same_v< + PropertyTablePropertyView, + decltype(propertyValue)>) { + for (int64_t i = 0; i < propertyValue.size(); ++i) { + REQUIRE( + static_cast(propertyValue.get(i)) == + expected[static_cast(i)]); + } + } else { + FAIL( + "getPropertyView returned PropertyTablePropertyView of incorrect " + "type for TestClassProperty."); + } + }); + + REQUIRE(invokedCallbackCount == 1); +} + +TEST_CASE("Test callback for string PropertyTableProperty") { + Model model; + + std::vector expected{"What's up", "Test_0", "Test_1", "", "Hi"}; + size_t totalBytes = 0; + for (const std::string& expectedValue : expected) { + totalBytes += expectedValue.size(); + } + + std::vector stringOffsets( + (expected.size() + 1) * sizeof(uint32_t)); + std::vector values(totalBytes); + uint32_t* offsetValue = reinterpret_cast(stringOffsets.data()); + for (size_t i = 0; i < expected.size(); ++i) { + const std::string& expectedValue = expected[i]; + std::memcpy( + values.data() + offsetValue[i], + expectedValue.c_str(), + expectedValue.size()); + offsetValue[i + 1] = + offsetValue[i] + static_cast(expectedValue.size()); + } + + addBufferToModel(model, values); + size_t valueBufferViewIndex = model.bufferViews.size() - 1; + + addBufferToModel(model, stringOffsets); + size_t offsetBufferViewIndex = model.bufferViews.size() - 1; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::STRING; + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast(expected.size()); + + PropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.stringOffsetType = + PropertyTableProperty::StringOffsetType::UINT32; + propertyTableProperty.values = static_cast(valueBufferViewIndex); + propertyTableProperty.stringOffsets = + static_cast(offsetBufferViewIndex); + + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::STRING); + REQUIRE(classProperty->componentType == std::nullopt); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->array); + + uint32_t invokedCallbackCount = 0; + view.getPropertyView( + "TestClassProperty", + [&expected, &invokedCallbackCount]( + const std::string& /*propertyName*/, + auto propertyValue) mutable { + invokedCallbackCount++; + REQUIRE( + propertyValue.status() == PropertyTablePropertyViewStatus::Valid); + REQUIRE(propertyValue.size() > 0); + + if constexpr (std::is_same_v< + PropertyTablePropertyView, + decltype(propertyValue)>) { + for (int64_t i = 0; i < propertyValue.size(); ++i) { + REQUIRE( + static_cast(propertyValue.get(i)) == + expected[static_cast(i)]); + } + } else { + FAIL( + "getPropertyView returned PropertyTablePropertyView of incorrect " + "type for TestClassProperty."); + } + }); + + REQUIRE(invokedCallbackCount == 1); +} + +TEST_CASE("Test callback for scalar array PropertyTableProperty") { + Model model; + std::vector values = + {12, 34, 30, 11, 34, 34, 11, 33, 122, 33, 223, 11}; + + addBufferToModel(model, values); + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::UINT32; + testClassProperty.array = true; + testClassProperty.count = 3; + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast( + values.size() / static_cast(testClassProperty.count.value())); + + PropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = + static_cast(model.bufferViews.size() - 1); + + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT32); + REQUIRE(classProperty->array); + REQUIRE(classProperty->count == 3); + + uint32_t invokedCallbackCount = 0; + view.getPropertyView( + "TestClassProperty", + [&values, &invokedCallbackCount]( + const std::string& /*propertyName*/, + auto propertyValue) { + invokedCallbackCount++; + REQUIRE( + propertyValue.status() == PropertyTablePropertyViewStatus::Valid); + REQUIRE(propertyValue.size() > 0); + + if constexpr (std::is_same_v< + PropertyTablePropertyView< + PropertyArrayView>, + decltype(propertyValue)>) { + for (int64_t i = 0; i < propertyValue.size(); ++i) { + PropertyArrayView member = propertyValue.get(i); + for (int64_t j = 0; j < member.size(); ++j) { + REQUIRE(member[j] == values[static_cast(i * 3 + j)]); + } + } + } else { + FAIL( + "getPropertyView returned PropertyTablePropertyView of incorrect " + "type for TestClassProperty."); + } + }); + + REQUIRE(invokedCallbackCount == 1); +} + +TEST_CASE("Test callback for vecN array PropertyTableProperty") { + Model model; + std::vector values = { + glm::ivec3(12, 34, -30), + glm::ivec3(-2, 0, 1), + glm::ivec3(1, 2, 8), + glm::ivec3(-100, 84, 6), + glm::ivec3(2, -2, -2), + glm::ivec3(40, 61, 3), + }; + + addBufferToModel(model, values); + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::VEC3; + testClassProperty.componentType = ClassProperty::ComponentType::INT32; + testClassProperty.array = true; + testClassProperty.count = 2; + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast( + values.size() / static_cast(testClassProperty.count.value())); + + PropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = + static_cast(model.bufferViews.size() - 1); + + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::VEC3); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT32); + REQUIRE(classProperty->array); + REQUIRE(classProperty->count == 2); + + uint32_t invokedCallbackCount = 0; + view.getPropertyView( + "TestClassProperty", + [&values, &invokedCallbackCount]( + const std::string& /*propertyName*/, + auto propertyValue) { + invokedCallbackCount++; + REQUIRE( + propertyValue.status() == PropertyTablePropertyViewStatus::Valid); + REQUIRE(propertyValue.size() > 0); + + if constexpr (std::is_same_v< + PropertyTablePropertyView< + PropertyArrayView>, + decltype(propertyValue)>) { + for (int64_t i = 0; i < propertyValue.size(); ++i) { + PropertyArrayView member = propertyValue.get(i); + for (int64_t j = 0; j < member.size(); ++j) { + REQUIRE(member[j] == values[static_cast(i * 2 + j)]); + } + } + } else { + FAIL( + "getPropertyView returned PropertyTablePropertyView of incorrect " + "type for TestClassProperty."); + } + }); + + REQUIRE(invokedCallbackCount == 1); +} + +TEST_CASE("Test callback for matN array PropertyTableProperty") { + Model model; + // clang-format off + std::vector values = { + glm::i32mat2x2( + 12, 34, + -30, 20), + glm::i32mat2x2( + -2, -2, + 0, 1), + glm::i32mat2x2( + 1, 2, + 8, 5), + glm::i32mat2x2( + -100, 3, + 84, 6), + glm::i32mat2x2( + 2, 12, + -2, -2), + glm::i32mat2x2( + 40, 61, + 7, -3), + }; + // clang-format on + + addBufferToModel(model, values); + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::MAT2; + testClassProperty.componentType = ClassProperty::ComponentType::INT32; + testClassProperty.array = true; + testClassProperty.count = 2; + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast( + values.size() / static_cast(testClassProperty.count.value())); + + PropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = + static_cast(model.bufferViews.size() - 1); + + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::MAT2); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT32); + REQUIRE(classProperty->array); + REQUIRE(classProperty->count == 2); + + uint32_t invokedCallbackCount = 0; + view.getPropertyView( + "TestClassProperty", + [&values, &invokedCallbackCount]( + const std::string& /*propertyName*/, + auto propertyValue) { + invokedCallbackCount++; + REQUIRE( + propertyValue.status() == PropertyTablePropertyViewStatus::Valid); + REQUIRE(propertyValue.size() > 0); + + if constexpr (std::is_same_v< + PropertyTablePropertyView< + PropertyArrayView>, + decltype(propertyValue)>) { + for (int64_t i = 0; i < propertyValue.size(); ++i) { + PropertyArrayView member = propertyValue.get(i); + for (int64_t j = 0; j < member.size(); ++j) { + REQUIRE(member[j] == values[static_cast(i * 2 + j)]); + } + } + } else { + FAIL( + "getPropertyView returned PropertyTablePropertyView of incorrect " + "type for TestClassProperty."); + } + }); + + REQUIRE(invokedCallbackCount == 1); +} + +TEST_CASE("Test callback for boolean array PropertyTableProperty") { + Model model; + + std::vector expected = { + true, + false, + false, + true, + false, + false, + true, + true, + true, + false, + false, + true}; + std::vector values; + size_t requiredBytesSize = static_cast( + glm::ceil(static_cast(expected.size()) / 8.0)); + values.resize(requiredBytesSize); + for (size_t i = 0; i < expected.size(); ++i) { + uint8_t expectedValue = expected[i]; + size_t byteIndex = i / 8; + size_t bitIndex = i % 8; + values[byteIndex] = + static_cast((expectedValue << bitIndex) | values[byteIndex]); + } + + addBufferToModel(model, values); + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::BOOLEAN; + testClassProperty.array = true; + testClassProperty.count = 3; + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast( + expected.size() / static_cast(testClassProperty.count.value())); + + PropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = + static_cast(model.bufferViews.size() - 1); + + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::BOOLEAN); + REQUIRE(classProperty->array); + REQUIRE(classProperty->count == 3); + + uint32_t invokedCallbackCount = 0; + view.getPropertyView( + "TestClassProperty", + [&expected, &invokedCallbackCount]( + const std::string& /*propertyName*/, + auto propertyValue) { + invokedCallbackCount++; + REQUIRE( + propertyValue.status() == PropertyTablePropertyViewStatus::Valid); + REQUIRE(propertyValue.size() > 0); + + if constexpr (std::is_same_v< + PropertyTablePropertyView>, + decltype(propertyValue)>) { + for (int64_t i = 0; i < propertyValue.size(); ++i) { + PropertyArrayView member = propertyValue.get(i); + for (int64_t j = 0; j < member.size(); ++j) { + REQUIRE(member[j] == expected[static_cast(i * 3 + j)]); + } + } + } else { + FAIL( + "getPropertyView returned PropertyTablePropertyView of incorrect " + "type for TestClassProperty."); + } + }); + + REQUIRE(invokedCallbackCount == 1); +} + +TEST_CASE("Test callback for string array PropertyTableProperty") { + Model model; + + std::vector expected{ + "What's up", + "Breaking news!!! Aliens no longer attacks the US first", + "But they still abduct my cows! Those milk thiefs! 👽 🐮", + "I'm not crazy. My mother had me tested 🤪", + "I love you, meat bags! ❤️", + "Book in the freezer"}; + + size_t totalBytes = 0; + for (const std::string& expectedValue : expected) { + totalBytes += expectedValue.size(); + } + + std::vector offsets((expected.size() + 1) * sizeof(uint32_t)); + std::vector values(totalBytes); + uint32_t* offsetValue = reinterpret_cast(offsets.data()); + for (size_t i = 0; i < expected.size(); ++i) { + const std::string& expectedValue = expected[i]; + std::memcpy( + values.data() + offsetValue[i], + expectedValue.c_str(), + expectedValue.size()); + offsetValue[i + 1] = + offsetValue[i] + static_cast(expectedValue.size()); + } + + addBufferToModel(model, values); + size_t valueBufferViewIndex = model.bufferViews.size() - 1; + + addBufferToModel(model, offsets); + size_t offsetBufferViewIndex = model.bufferViews.size() - 1; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::STRING; + testClassProperty.array = true; + testClassProperty.count = 2; + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast( + expected.size() / static_cast(testClassProperty.count.value())); + + PropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.stringOffsetType = + PropertyTableProperty::StringOffsetType::UINT32; + propertyTableProperty.values = static_cast(valueBufferViewIndex); + propertyTableProperty.stringOffsets = + static_cast(offsetBufferViewIndex); + + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::STRING); + REQUIRE(classProperty->array); + REQUIRE(classProperty->count == 2); + + uint32_t invokedCallbackCount = 0; + view.getPropertyView( + "TestClassProperty", + [&invokedCallbackCount]( + const std::string& /*propertyName*/, + auto propertyValue) { + invokedCallbackCount++; + REQUIRE( + propertyValue.status() == PropertyTablePropertyViewStatus::Valid); + REQUIRE(propertyValue.size() == 3); + + if constexpr (std::is_same_v< + PropertyTablePropertyView< + PropertyArrayView>, + decltype(propertyValue)>) { + PropertyArrayView v0 = propertyValue.get(0); + REQUIRE(v0.size() == 2); + REQUIRE(v0[0] == "What's up"); + REQUIRE( + v0[1] == + "Breaking news!!! Aliens no longer attacks the US first"); + + PropertyArrayView v1 = propertyValue.get(1); + REQUIRE(v1.size() == 2); + REQUIRE( + v1[0] == "But they still abduct my cows! Those milk thiefs! 👽 🐮"); + REQUIRE(v1[1] == "I'm not crazy. My mother had me tested 🤪"); + + PropertyArrayView v2 = propertyValue.get(2); + REQUIRE(v2.size() == 2); + REQUIRE(v2[0] == "I love you, meat bags! ❤️"); + REQUIRE(v2[1] == "Book in the freezer"); + } else { + FAIL( + "getPropertyView returned PropertyTablePropertyView of incorrect " + "type for TestClassProperty."); + } + }); + + REQUIRE(invokedCallbackCount == 1); +} diff --git a/CesiumGltf/test/TestPropertyView.cpp b/CesiumGltf/test/TestPropertyView.cpp index ee3bf466b..27106da09 100644 --- a/CesiumGltf/test/TestPropertyView.cpp +++ b/CesiumGltf/test/TestPropertyView.cpp @@ -23,11 +23,11 @@ TEST_CASE("Boolean PropertyView") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::BOOLEAN; classProperty.normalized = true; - classProperty.offset = JsonValue(true); - classProperty.scale = JsonValue(true); - classProperty.max = JsonValue(true); - classProperty.min = JsonValue(true); - classProperty.noData = JsonValue(true); + classProperty.offset = true; + classProperty.scale = true; + classProperty.max = true; + classProperty.min = true; + classProperty.noData = true; PropertyView view(classProperty); REQUIRE(!view.normalized()); @@ -38,7 +38,7 @@ TEST_CASE("Boolean PropertyView") { REQUIRE(!view.noData()); } - SECTION("Ignores count (array is false)") { + SECTION("Ignores count") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::BOOLEAN; classProperty.count = 5; @@ -51,7 +51,7 @@ TEST_CASE("Boolean PropertyView") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::BOOLEAN; classProperty.required = false; - classProperty.defaultProperty = JsonValue(false); + classProperty.defaultProperty = false; PropertyView view(classProperty); REQUIRE(!view.required()); @@ -63,7 +63,7 @@ TEST_CASE("Boolean PropertyView") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::BOOLEAN; classProperty.required = true; - classProperty.defaultProperty = JsonValue(false); + classProperty.defaultProperty = false; PropertyView view(classProperty); REQUIRE(view.required()); @@ -73,7 +73,7 @@ TEST_CASE("Boolean PropertyView") { SECTION("Returns nullopt for invalid types") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::BOOLEAN; - classProperty.defaultProperty = JsonValue(1); + classProperty.defaultProperty = 1; PropertyView view(classProperty); REQUIRE(!view.required()); @@ -102,7 +102,7 @@ TEST_CASE("Scalar PropertyView") { REQUIRE(!view.defaultValue()); } - SECTION("Ignores count (array is false)") { + SECTION("Ignores count") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::SCALAR; classProperty.componentType = ClassProperty::ComponentType::UINT8; @@ -116,10 +116,10 @@ TEST_CASE("Scalar PropertyView") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::SCALAR; classProperty.componentType = ClassProperty::ComponentType::INT16; - classProperty.offset = JsonValue(5); - classProperty.scale = JsonValue(2); - classProperty.max = JsonValue(10); - classProperty.min = JsonValue(-10); + classProperty.offset = 5; + classProperty.scale = 2; + classProperty.max = 10; + classProperty.min = -10; PropertyView view(classProperty); REQUIRE(view.offset()); @@ -133,49 +133,34 @@ TEST_CASE("Scalar PropertyView") { REQUIRE(*view.min() == -10); } - SECTION("Returns nullopt for out-of-bounds offset, scale, max, and min") { + SECTION("Returns nullopt for out-of-bounds values") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::SCALAR; classProperty.componentType = ClassProperty::ComponentType::INT8; - classProperty.offset = JsonValue(129); - classProperty.scale = JsonValue(255); - classProperty.max = JsonValue(1000); - classProperty.min = JsonValue(-1000); + classProperty.offset = 20; + classProperty.scale = 255; + classProperty.max = 127; + classProperty.min = -1000; PropertyView view(classProperty); - REQUIRE(!view.offset()); - REQUIRE(!view.scale()); - REQUIRE(!view.max()); - REQUIRE(!view.min()); - } + REQUIRE(view.offset()); + REQUIRE(view.max()); - SECTION("Rounds numbers where possible") { - ClassProperty classProperty; - classProperty.type = ClassProperty::Type::SCALAR; - classProperty.componentType = ClassProperty::ComponentType::INT8; - classProperty.offset = JsonValue(-5.5); - classProperty.scale = JsonValue(2.4); - classProperty.max = JsonValue(150); - classProperty.min = JsonValue(-150); + REQUIRE(*view.offset() == 20); + REQUIRE(*view.max() == 127); - PropertyView view(classProperty); - REQUIRE(view.offset()); - REQUIRE(view.scale()); - REQUIRE(!view.max()); + REQUIRE(!view.scale()); REQUIRE(!view.min()); - - REQUIRE(*view.offset() == -5); - REQUIRE(*view.scale() == 2); } SECTION("Returns nullopt for invalid types") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::SCALAR; classProperty.componentType = ClassProperty::ComponentType::INT8; - classProperty.offset = JsonValue("10"); - classProperty.scale = JsonValue(false); - classProperty.max = JsonValue(JsonValue::Array()); - classProperty.min = JsonValue(JsonValue::Array{10}); + classProperty.offset = "10"; + classProperty.scale = false; + classProperty.max = 5.5; + classProperty.min = JsonValue::Array{1}; PropertyView view(classProperty); REQUIRE(!view.offset()); @@ -189,8 +174,8 @@ TEST_CASE("Scalar PropertyView") { classProperty.type = ClassProperty::Type::SCALAR; classProperty.componentType = ClassProperty::ComponentType::UINT8; classProperty.required = false; - classProperty.noData = JsonValue(0); - classProperty.defaultProperty = JsonValue(1); + classProperty.noData = 0; + classProperty.defaultProperty = 1; PropertyView view(classProperty); REQUIRE(!view.required()); @@ -206,8 +191,8 @@ TEST_CASE("Scalar PropertyView") { classProperty.type = ClassProperty::Type::SCALAR; classProperty.componentType = ClassProperty::ComponentType::UINT8; classProperty.required = true; - classProperty.noData = JsonValue(0); - classProperty.defaultProperty = JsonValue(1); + classProperty.noData = 0; + classProperty.defaultProperty = 1; PropertyView view(classProperty); REQUIRE(view.required()); @@ -216,15 +201,135 @@ TEST_CASE("Scalar PropertyView") { } } +TEST_CASE("VecN PropertyView") { + SECTION("Constructs with non-optional properties") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::VEC3; + classProperty.componentType = ClassProperty::ComponentType::INT16; + classProperty.normalized = true; + classProperty.required = true; + + PropertyView view(classProperty); + REQUIRE(view.normalized()); + REQUIRE(view.required()); + + REQUIRE(view.arrayCount() == 0); + REQUIRE(!view.offset()); + REQUIRE(!view.scale()); + REQUIRE(!view.max()); + REQUIRE(!view.min()); + REQUIRE(!view.noData()); + REQUIRE(!view.defaultValue()); + } + + SECTION("Ignores count") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::VEC3; + classProperty.componentType = ClassProperty::ComponentType::INT16; + classProperty.count = 5; + + PropertyView view(classProperty); + REQUIRE(view.arrayCount() == 0); + } + + SECTION("Constructs with offset, scale, max, and min") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::VEC3; + classProperty.componentType = ClassProperty::ComponentType::INT16; + classProperty.offset = JsonValue::Array{-1, 1, 2}; + classProperty.scale = JsonValue::Array{2, 1, 3}; + classProperty.max = JsonValue::Array{10, 5, 6}; + classProperty.min = JsonValue::Array{-11, -12, -13}; + + PropertyView view(classProperty); + REQUIRE(view.offset()); + REQUIRE(view.scale()); + REQUIRE(view.max()); + REQUIRE(view.min()); + + REQUIRE(*view.offset() == glm::i16vec3(-1, 1, 2)); + REQUIRE(*view.scale() == glm::i16vec3(2, 1, 3)); + REQUIRE(*view.max() == glm::i16vec3(10, 5, 6)); + REQUIRE(*view.min() == glm::i16vec3(-11, -12, -13)); + } + + SECTION("Returns nullopt for out-of-bounds values") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::VEC2; + classProperty.componentType = ClassProperty::ComponentType::INT8; + classProperty.offset = JsonValue::Array{-1, 2}; + classProperty.scale = JsonValue::Array{1, 128}; + classProperty.max = JsonValue::Array{10, 5}; + classProperty.min = JsonValue::Array{-200, 0}; + + PropertyView view(classProperty); + REQUIRE(view.offset()); + REQUIRE(view.max()); + + REQUIRE(*view.offset() == glm::i8vec2(-1, 2)); + REQUIRE(*view.max() == glm::i8vec2(10, 5)); + + REQUIRE(!view.scale()); + REQUIRE(!view.min()); + } + + SECTION("Returns nullopt for invalid types") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::VEC3; + classProperty.componentType = ClassProperty::ComponentType::INT8; + classProperty.offset = "(1, 2, 3)"; + classProperty.scale = 1; + classProperty.max = JsonValue::Array{10, 20, 30, 40}; + classProperty.min = JsonValue::Array{-10, -1}; + + PropertyView view(classProperty); + REQUIRE(!view.offset()); + REQUIRE(!view.scale()); + REQUIRE(!view.max()); + REQUIRE(!view.min()); + } + + SECTION("Constructs with noData and defaultProperty") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::VEC4; + classProperty.componentType = ClassProperty::ComponentType::FLOAT32; + classProperty.required = false; + classProperty.noData = JsonValue::Array{0.0f, 0.0f, 0.0f, 0.0f}; + classProperty.defaultProperty = JsonValue::Array{1.0f, 2.0f, 3.0f, 4.0f}; + + PropertyView view(classProperty); + REQUIRE(!view.required()); + REQUIRE(view.noData()); + REQUIRE(view.defaultValue()); + + REQUIRE(*view.noData() == glm::vec4(0.0f)); + REQUIRE(*view.defaultValue() == glm::vec4(1.0f, 2.0f, 3.0f, 4.0f)); + } + + SECTION("Ignores noData and defaultProperty when required is true") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::VEC4; + classProperty.componentType = ClassProperty::ComponentType::FLOAT32; + classProperty.required = true; + classProperty.noData = JsonValue::Array{0.0f, 0.0f, 0.0f, 0.0f}; + classProperty.defaultProperty = JsonValue::Array{1.0f, 2.0f, 3.0f, 4.0f}; + + PropertyView view(classProperty); + REQUIRE(view.required()); + REQUIRE(!view.noData()); + REQUIRE(!view.defaultValue()); + } +} + TEST_CASE("String PropertyView") { SECTION("Ignores non-applicable properties") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::STRING; classProperty.normalized = true; - classProperty.offset = JsonValue("test"); - classProperty.scale = JsonValue("test"); - classProperty.max = JsonValue("test"); - classProperty.min = JsonValue("test"); + classProperty.offset = "offset"; + classProperty.scale = "scale"; + classProperty.max = "max"; + classProperty.min = "min"; PropertyView view(classProperty); REQUIRE(!view.normalized()); @@ -234,7 +339,7 @@ TEST_CASE("String PropertyView") { REQUIRE(!view.min()); } - SECTION("Ignores count (array is false)") { + SECTION("Ignores count") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::STRING; classProperty.count = 5; @@ -247,8 +352,8 @@ TEST_CASE("String PropertyView") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::STRING; classProperty.required = false; - classProperty.noData = JsonValue("null"); - classProperty.defaultProperty = JsonValue("default"); + classProperty.noData = "null"; + classProperty.defaultProperty = "default"; PropertyView view(classProperty); REQUIRE(!view.required()); @@ -263,8 +368,8 @@ TEST_CASE("String PropertyView") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::STRING; classProperty.required = true; - classProperty.noData = JsonValue("null"); - classProperty.defaultProperty = JsonValue("default"); + classProperty.noData = "null"; + classProperty.defaultProperty = "default"; PropertyView view(classProperty); REQUIRE(view.required()); @@ -275,8 +380,8 @@ TEST_CASE("String PropertyView") { SECTION("Returns nullopt for invalid types") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::STRING; - classProperty.noData = JsonValue::Array{JsonValue("null")}; - classProperty.defaultProperty = JsonValue(true); + classProperty.noData = JsonValue::Array{"null"}; + classProperty.defaultProperty = true; PropertyView view(classProperty); REQUIRE(!view.required()); @@ -290,11 +395,11 @@ TEST_CASE("Boolean Array PropertyView") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::BOOLEAN; classProperty.normalized = true; - classProperty.offset = JsonValue::Array{JsonValue(true)}; - classProperty.scale = JsonValue::Array{JsonValue(true)}; - classProperty.max = JsonValue::Array{JsonValue(true)}; - classProperty.min = JsonValue::Array{JsonValue(true)}; - classProperty.noData = JsonValue::Array{JsonValue(true)}; + classProperty.offset = JsonValue::Array{true}; + classProperty.scale = JsonValue::Array{true}; + classProperty.max = JsonValue::Array{true}; + classProperty.min = JsonValue::Array{true}; + classProperty.noData = JsonValue::Array{true}; PropertyView> view(classProperty); REQUIRE(!view.normalized()); @@ -320,8 +425,7 @@ TEST_CASE("Boolean Array PropertyView") { classProperty.type = ClassProperty::Type::BOOLEAN; classProperty.array = true; classProperty.required = false; - classProperty.defaultProperty = - JsonValue::Array{JsonValue(false), JsonValue(true)}; + classProperty.defaultProperty = JsonValue::Array{false, true}; PropertyView> view(classProperty); REQUIRE(!view.required()); @@ -338,10 +442,9 @@ TEST_CASE("Boolean Array PropertyView") { classProperty.type = ClassProperty::Type::BOOLEAN; classProperty.array = true; classProperty.required = true; - classProperty.defaultProperty = - JsonValue::Array{JsonValue(false), JsonValue(true)}; + classProperty.defaultProperty = JsonValue::Array{false, true}; - PropertyView view(classProperty); + PropertyView> view(classProperty); REQUIRE(view.required()); REQUIRE(!view.defaultValue()); } @@ -350,10 +453,89 @@ TEST_CASE("Boolean Array PropertyView") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::BOOLEAN; classProperty.array = true; - classProperty.defaultProperty = JsonValue(true); + classProperty.defaultProperty = true; - PropertyView view(classProperty); + PropertyView> view(classProperty); REQUIRE(!view.required()); REQUIRE(!view.defaultValue()); } } + +TEST_CASE("String Array PropertyView") { + SECTION("Ignores non-applicable properties") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::STRING; + classProperty.array = true; + classProperty.normalized = true; + classProperty.offset = {"offset"}; + classProperty.scale = {"scale"}; + classProperty.max = {"max"}; + classProperty.min = {"min"}; + + PropertyView> view(classProperty); + REQUIRE(!view.normalized()); + REQUIRE(!view.offset()); + REQUIRE(!view.scale()); + REQUIRE(!view.max()); + REQUIRE(!view.min()); + } + + SECTION("Constructs with count") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::STRING; + classProperty.array = true; + classProperty.count = 5; + + PropertyView> view(classProperty); + REQUIRE(view.arrayCount() == classProperty.count); + } + + SECTION("Constructs with noData and defaultProperty") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::STRING; + classProperty.array = true; + classProperty.required = false; + classProperty.noData = JsonValue::Array{"null", "0"}; + classProperty.defaultProperty = JsonValue::Array{"default1", "default2"}; + + PropertyView> view(classProperty); + REQUIRE(!view.required()); + REQUIRE(view.noData()); + REQUIRE(view.defaultValue()); + + const auto noData = *view.noData(); + REQUIRE(noData.size() == 2); + REQUIRE(noData[0] == "null"); + REQUIRE(noData[1] == "0"); + + const auto defaultValue = *view.defaultValue(); + REQUIRE(defaultValue.size() == 2); + REQUIRE(defaultValue[0] == "default1"); + REQUIRE(defaultValue[1] == "default2"); + } + + SECTION("Ignores noData and defaultProperty when required is true") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::STRING; + classProperty.required = true; + classProperty.noData = JsonValue::Array{"null", "0"}; + classProperty.defaultProperty = JsonValue::Array{"default1", "default2"}; + + PropertyView> view(classProperty); + REQUIRE(view.required()); + REQUIRE(!view.noData()); + REQUIRE(!view.defaultValue()); + } + + SECTION("Returns nullopt for invalid types") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::STRING; + classProperty.noData = JsonValue::Array{"null"}; + classProperty.defaultProperty = true; + + PropertyView view(classProperty); + REQUIRE(!view.required()); + REQUIRE(!view.noData()); + REQUIRE(!view.defaultValue()); + } +} From ba779b7c415d181ac9f211112229adc35051a219 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Wed, 16 Aug 2023 14:56:02 -0400 Subject: [PATCH 168/421] Add support for matN values --- .../include/CesiumGltf/PropertyArrayView.h | 62 +++--- CesiumGltf/include/CesiumGltf/PropertyView.h | 37 ++-- CesiumGltf/test/TestPropertyView.cpp | 194 ++++++++++++++++++ 3 files changed, 249 insertions(+), 44 deletions(-) diff --git a/CesiumGltf/include/CesiumGltf/PropertyArrayView.h b/CesiumGltf/include/CesiumGltf/PropertyArrayView.h index 119187f24..a6db9667f 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyArrayView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyArrayView.h @@ -67,7 +67,7 @@ template <> class PropertyArrayView { /** * @brief Constructs an empty array view. */ - PropertyArrayView() : _values{}, _size{0} {} + PropertyArrayView() : _values{} {} /** * @brief Constructs an array view from a buffer. @@ -81,7 +81,7 @@ template <> class PropertyArrayView { const gsl::span& buffer, int64_t bitOffset, int64_t size) noexcept - : _values{BooleanArrayView{buffer, bitOffset}}, _size{size} {} + : _values{BooleanArrayView{buffer, bitOffset, size}} {} /** * @brief Constructs an array view from a vector of values. This is mainly @@ -90,10 +90,7 @@ template <> class PropertyArrayView { * @param values The vector containing the values. */ PropertyArrayView(const std::vector&& values) - : _values{std::move(values)}, _size{0} { - size_t size = std::get>(_values).size(); - _size = static_cast(size); - } + : _values{std::move(values)} {} bool operator[](int64_t index) const noexcept { return std::visit( @@ -101,25 +98,31 @@ template <> class PropertyArrayView { _values); } - int64_t size() const noexcept { return _size; } + int64_t size() const noexcept { + return std::visit( + [](auto const& values) { return static_cast(values.size()); }, + _values); + } private: struct BooleanArrayView { - gsl::span values; - int64_t bitOffset = 0; + gsl::span _values; + int64_t _bitOffset = 0; + int64_t _size = 0; bool operator[](int64_t index) const noexcept { - index += bitOffset; + index += _bitOffset; const int64_t byteIndex = index / 8; const int64_t bitIndex = index % 8; - const int bitValue = static_cast(values[byteIndex] >> bitIndex) & 1; + const int bitValue = static_cast(_values[byteIndex] >> bitIndex) & 1; return bitValue == 1; } + + int64_t size() const noexcept { return _size; } }; using ArrayType = std::variant>; ArrayType _values; - int64_t _size; }; template <> class PropertyArrayView { @@ -127,7 +130,7 @@ template <> class PropertyArrayView { /** * @brief Constructs an empty array view. */ - PropertyArrayView() : _values{}, _size{0} {} + PropertyArrayView() : _values{} {} /** * @brief Constructs an array view from buffers and their information. @@ -142,8 +145,8 @@ template <> class PropertyArrayView { const gsl::span& stringOffsets, PropertyComponentType stringOffsetType, int64_t size) noexcept - : _values{StringArrayView{values, stringOffsets, stringOffsetType}}, - _size{size} {} + : _values{ + StringArrayView{values, stringOffsets, stringOffsetType, size}} {} /** * @brief Constructs an array view from a vector of values. This is mainly @@ -152,10 +155,7 @@ template <> class PropertyArrayView { * @param values The vector containing the values. */ PropertyArrayView(const std::vector&& values) noexcept - : _values{std::move(values)}, _size{0} { - size_t size = std::get>(_values).size(); - _size = static_cast(size); - } + : _values{std::move(values)} {} std::string_view operator[](int64_t index) const noexcept { return std::visit( @@ -165,29 +165,35 @@ template <> class PropertyArrayView { _values); } - int64_t size() const noexcept { return _size; } + int64_t size() const noexcept { + return std::visit( + [](auto const& values) { return static_cast(values.size()); }, + _values); + } private: struct StringArrayView { - gsl::span values; - gsl::span stringOffsets; - PropertyComponentType stringOffsetType = PropertyComponentType::None; + gsl::span _values; + gsl::span _stringOffsets; + PropertyComponentType _stringOffsetType = PropertyComponentType::None; + int64_t _size = 0; std::string_view operator[](int64_t index) const noexcept { const size_t currentOffset = - getOffsetFromOffsetsBuffer(index, stringOffsets, stringOffsetType); + getOffsetFromOffsetsBuffer(index, _stringOffsets, _stringOffsetType); const size_t nextOffset = getOffsetFromOffsetsBuffer( index + 1, - stringOffsets, - stringOffsetType); + _stringOffsets, + _stringOffsetType); return std::string_view( - reinterpret_cast(values.data() + currentOffset), + reinterpret_cast(_values.data() + currentOffset), (nextOffset - currentOffset)); } + + int64_t size() const noexcept { return _size; } }; using ArrayType = std::variant>; ArrayType _values; - int64_t _size; }; } // namespace CesiumGltf diff --git a/CesiumGltf/include/CesiumGltf/PropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyView.h index 39e9f7949..d56c15e88 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyView.h @@ -297,12 +297,15 @@ class PropertyView : IPropertyView { getValue(const CesiumUtility::JsonValue& jsonValue) { if constexpr (IsMetadataScalar::value) { return getScalar(jsonValue); - } else if constexpr (IsMetadataVecN::value) { + } + + if constexpr (IsMetadataVecN::value) { return getVecN(jsonValue); - } else - return std::nullopt; - // if constexpr (IsMetadataMatN::value) { - //} + } + + if constexpr (IsMetadataMatN::value) { + return getMatN(jsonValue); + } } /** @@ -333,7 +336,7 @@ class PropertyView : IPropertyView { } const CesiumUtility::JsonValue::Array& array = jsonValue.getArray(); - glm::length_t N = VecType::length(); + constexpr glm::length_t N = VecType::length(); if (array.size() != N) { return std::nullopt; } @@ -355,28 +358,30 @@ class PropertyView : IPropertyView { template static std::optional - getMatNFromJsonValue(const CesiumUtility::JsonValue& value) { + getMatN(const CesiumUtility::JsonValue& jsonValue) { if (!jsonValue.isArray()) { return std::nullopt; } const CesiumUtility::JsonValue::Array& array = jsonValue.getArray(); - glm::length_t N = VecType::length(); - glm::length_t numValues = N * N; - if (array.size() != numValues) { + constexpr glm::length_t N = MatType::length(); + if (array.size() != N * N) { return std::nullopt; } using T = MatType::value_type; MatType result; - for (glm::length_t i = 0; i < numValues; i++) { - std::optional value = getScalar(array[i]); - if (!value) { - return std::nullopt; + for (glm::length_t i = 0; i < N; i++) { + // Try to parse each value in the column. + for (glm::length_t j = 0; j < N; j++) { + std::optional value = getScalar(array[i * N + j]); + if (!value) { + return std::nullopt; + } + + result[i][j] = *value; } - - result[i] = value; } return result; diff --git a/CesiumGltf/test/TestPropertyView.cpp b/CesiumGltf/test/TestPropertyView.cpp index 27106da09..52b49d9d3 100644 --- a/CesiumGltf/test/TestPropertyView.cpp +++ b/CesiumGltf/test/TestPropertyView.cpp @@ -321,6 +321,200 @@ TEST_CASE("VecN PropertyView") { } } +TEST_CASE("MatN PropertyView") { + SECTION("Constructs with non-optional properties") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::MAT3; + classProperty.componentType = ClassProperty::ComponentType::INT32; + classProperty.normalized = true; + classProperty.required = true; + + PropertyView view(classProperty); + REQUIRE(view.normalized()); + REQUIRE(view.required()); + + REQUIRE(view.arrayCount() == 0); + REQUIRE(!view.offset()); + REQUIRE(!view.scale()); + REQUIRE(!view.max()); + REQUIRE(!view.min()); + REQUIRE(!view.noData()); + REQUIRE(!view.defaultValue()); + } + + SECTION("Ignores count") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::MAT3; + classProperty.componentType = ClassProperty::ComponentType::INT32; + classProperty.count = 5; + + PropertyView view(classProperty); + REQUIRE(view.arrayCount() == 0); + } + + SECTION("Constructs with offset, scale, max, and min") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::MAT3; + classProperty.componentType = ClassProperty::ComponentType::INT32; + // clang-format off + classProperty.offset = JsonValue::Array{ + -1, 1, 2, + 3, -1, 4, + -5, -5, 0}; + classProperty.scale = JsonValue::Array{ + 1, 1, 1, + 2, 2, 3, + 3, 4, 5}; + classProperty.max = JsonValue::Array{ + 20, 5, 20, + 30, 22, 43, + 37, 1, 8}; + classProperty.min = JsonValue::Array{ + -10, -2, -3, + 0, 20, 4, + 9, 4, 5}; + // clang-format on + + PropertyView view(classProperty); + REQUIRE(view.offset()); + REQUIRE(view.scale()); + REQUIRE(view.max()); + REQUIRE(view.min()); + + // clang-format off + glm::imat3x3 expectedOffset( + -1, 1, 2, + 3, -1, 4, + -5, -5, 0); + REQUIRE(*view.offset() == expectedOffset); + + glm::imat3x3 expectedScale( + 1, 1, 1, + 2, 2, 3, + 3, 4, 5); + REQUIRE(*view.scale() == expectedScale); + + glm::imat3x3 expectedMax( + 20, 5, 20, + 30, 22, 43, + 37, 1, 8); + REQUIRE(*view.max() == expectedMax); + + glm::imat3x3 expectedMin( + -10, -2, -3, + 0, 20, 4, + 9, 4, 5); + REQUIRE(*view.min() == expectedMin); + // clang-format on + } + + SECTION("Returns nullopt for out-of-bounds values") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::MAT2; + classProperty.componentType = ClassProperty::ComponentType::INT8; + // clang-format off + classProperty.offset = JsonValue::Array{ + -1, 2, + 129, -2}; + classProperty.scale = JsonValue::Array{ + 1, 7, + 4, 6}; + classProperty.max = JsonValue::Array{ + 10, 240, + 1, 8}; + classProperty.min = JsonValue::Array{ + -29, -40, + -55, -43}; + // clang-format on + + PropertyView view(classProperty); + REQUIRE(view.scale()); + REQUIRE(view.min()); + + // clang-format off + glm::i8mat2x2 expectedScale( + 1, 7, + 4, 6); + REQUIRE(*view.scale() == expectedScale); + + glm::i8mat2x2 expectedMin( + -29, -40, + -55, -43); + REQUIRE(*view.min() == expectedMin); + // clang-format on + + REQUIRE(!view.offset()); + REQUIRE(!view.max()); + } + + SECTION("Returns nullopt for invalid types") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::MAT2; + classProperty.componentType = ClassProperty::ComponentType::UINT8; + classProperty.offset = "(1, 2, 3, 4)"; + classProperty.scale = 1; + classProperty.max = JsonValue::Array{10, 20, 30, 40, 50}; + classProperty.min = JsonValue::Array{0, 0}; + + PropertyView view(classProperty); + REQUIRE(!view.offset()); + REQUIRE(!view.scale()); + REQUIRE(!view.max()); + REQUIRE(!view.min()); + } + + SECTION("Constructs with noData and defaultProperty") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::MAT2; + classProperty.componentType = ClassProperty::ComponentType::FLOAT32; + classProperty.required = false; + // clang-format off + classProperty.noData = JsonValue::Array{ + 0.0f, 0.0f, + 0.0f, 0.0f}; + classProperty.defaultProperty = JsonValue::Array{ + 1.0f, 2.0f, + 3.0f, 4.0f}; + // clang-format on + + PropertyView view(classProperty); + REQUIRE(!view.required()); + REQUIRE(view.noData()); + REQUIRE(view.defaultValue()); + + // clang-format off + glm::mat2 expectedNoData( + 0.0f, 0.0f, + 0.0f, 0.0f); + REQUIRE(*view.noData() == expectedNoData); + + glm::mat2 expectedDefaultValue( + 1.0f, 2.0f, + 3.0f, 4.0f); + REQUIRE(*view.defaultValue() == expectedDefaultValue); + } + + SECTION("Ignores noData and defaultProperty when required is true") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::MAT2; + classProperty.componentType = ClassProperty::ComponentType::FLOAT32; + classProperty.required = true; + // clang-format off + classProperty.noData = JsonValue::Array{ + 0.0f, 0.0f, + 0.0f, 0.0f}; + classProperty.defaultProperty = JsonValue::Array{ + 1.0f, 2.0f, + 3.0f, 4.0f}; + // clang-format on + + PropertyView view(classProperty); + REQUIRE(view.required()); + REQUIRE(!view.noData()); + REQUIRE(!view.defaultValue()); + } +} + TEST_CASE("String PropertyView") { SECTION("Ignores non-applicable properties") { ClassProperty classProperty; From e7743ea61c0aba4d0649050298e09df4c5bb13f4 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Thu, 17 Aug 2023 13:55:54 -0400 Subject: [PATCH 169/421] Change status enum to static consts --- .../include/CesiumGltf/PropertyArrayView.h | 114 +- .../CesiumGltf/PropertyTablePropertyView.h | 119 +- .../include/CesiumGltf/PropertyTableView.h | 6 +- .../CesiumGltf/PropertyTexturePropertyView.h | 1 + CesiumGltf/include/CesiumGltf/PropertyView.h | 1069 +++++++++++++---- CesiumGltf/src/PropertyTableView.cpp | 18 +- CesiumGltf/test/TestPropertyView.cpp | 861 ++++++++++--- 7 files changed, 1640 insertions(+), 548 deletions(-) diff --git a/CesiumGltf/include/CesiumGltf/PropertyArrayView.h b/CesiumGltf/include/CesiumGltf/PropertyArrayView.h index a6db9667f..987908925 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyArrayView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyArrayView.h @@ -81,48 +81,22 @@ template <> class PropertyArrayView { const gsl::span& buffer, int64_t bitOffset, int64_t size) noexcept - : _values{BooleanArrayView{buffer, bitOffset, size}} {} - - /** - * @brief Constructs an array view from a vector of values. This is mainly - * used when the values cannot be viewed in place. - * - * @param values The vector containing the values. - */ - PropertyArrayView(const std::vector&& values) - : _values{std::move(values)} {} + : _values{buffer}, _bitOffset{bitOffset}, _size{size} {} bool operator[](int64_t index) const noexcept { - return std::visit( - [index](auto const& values) -> auto const { return values[index]; }, - _values); + index += _bitOffset; + const int64_t byteIndex = index / 8; + const int64_t bitIndex = index % 8; + const int bitValue = static_cast(_values[byteIndex] >> bitIndex) & 1; + return bitValue == 1; } - int64_t size() const noexcept { - return std::visit( - [](auto const& values) { return static_cast(values.size()); }, - _values); - } + int64_t size() const noexcept { return _size; } private: - struct BooleanArrayView { - gsl::span _values; - int64_t _bitOffset = 0; - int64_t _size = 0; - - bool operator[](int64_t index) const noexcept { - index += _bitOffset; - const int64_t byteIndex = index / 8; - const int64_t bitIndex = index % 8; - const int bitValue = static_cast(_values[byteIndex] >> bitIndex) & 1; - return bitValue == 1; - } - - int64_t size() const noexcept { return _size; } - }; - - using ArrayType = std::variant>; - ArrayType _values; + gsl::span _values; + int64_t _bitOffset = 0; + int64_t _size = 0; }; template <> class PropertyArrayView { @@ -130,7 +104,11 @@ template <> class PropertyArrayView { /** * @brief Constructs an empty array view. */ - PropertyArrayView() : _values{} {} + PropertyArrayView() + : _values{}, + _stringOffsets{}, + _stringOffsetType{PropertyComponentType::None}, + _size{0} {} /** * @brief Constructs an array view from buffers and their information. @@ -145,55 +123,29 @@ template <> class PropertyArrayView { const gsl::span& stringOffsets, PropertyComponentType stringOffsetType, int64_t size) noexcept - : _values{ - StringArrayView{values, stringOffsets, stringOffsetType, size}} {} - - /** - * @brief Constructs an array view from a vector of values. This is mainly - * used when the values cannot be viewed in place. - * - * @param values The vector containing the values. - */ - PropertyArrayView(const std::vector&& values) noexcept - : _values{std::move(values)} {} + : _values{values}, + _stringOffsets{stringOffsets}, + _stringOffsetType{stringOffsetType}, + _size(size) {} std::string_view operator[](int64_t index) const noexcept { - return std::visit( - [index](auto const& values) -> auto const { - return std::string_view(values[index]); - }, - _values); + const size_t currentOffset = + getOffsetFromOffsetsBuffer(index, _stringOffsets, _stringOffsetType); + const size_t nextOffset = getOffsetFromOffsetsBuffer( + index + 1, + _stringOffsets, + _stringOffsetType); + return std::string_view( + reinterpret_cast(_values.data() + currentOffset), + (nextOffset - currentOffset)); } - int64_t size() const noexcept { - return std::visit( - [](auto const& values) { return static_cast(values.size()); }, - _values); - } + int64_t size() const noexcept { return _size; } private: - struct StringArrayView { - gsl::span _values; - gsl::span _stringOffsets; - PropertyComponentType _stringOffsetType = PropertyComponentType::None; - int64_t _size = 0; - - std::string_view operator[](int64_t index) const noexcept { - const size_t currentOffset = - getOffsetFromOffsetsBuffer(index, _stringOffsets, _stringOffsetType); - const size_t nextOffset = getOffsetFromOffsetsBuffer( - index + 1, - _stringOffsets, - _stringOffsetType); - return std::string_view( - reinterpret_cast(_values.data() + currentOffset), - (nextOffset - currentOffset)); - } - - int64_t size() const noexcept { return _size; } - }; - - using ArrayType = std::variant>; - ArrayType _values; + gsl::span _values; + gsl::span _stringOffsets; + PropertyComponentType _stringOffsetType; + int64_t _size; }; } // namespace CesiumGltf diff --git a/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h index 3edc7b0a8..6c0d269b1 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h @@ -21,138 +21,113 @@ namespace CesiumGltf { * but instead indicate that its {@link PropertyTablePropertyView::size} is 0. * This enumeration provides the reason. */ -enum class PropertyTablePropertyViewStatus { - /** - * @brief This property view is valid and ready to use. - */ - Valid, - - /** - * @brief This property view was initialized from an invalid - * {@link PropertyTable}. - */ - ErrorInvalidPropertyTable, - - /** - * @brief This property view is trying to view a property that does not exist - * in the {@link PropertyTable}. - */ - ErrorNonexistentProperty, - - /** - * @brief This property view's type does not match what is - * specified in {@link ClassProperty::type}. - */ - ErrorTypeMismatch, - - /** - * @brief This property view's component type does not match what - * is specified in {@link ClassProperty::componentType}. - */ - ErrorComponentTypeMismatch, - +class PropertyTablePropertyViewStatus : public PropertyViewStatus { +public: /** - * @brief This property view differs from what is specified in - * {@link ClassProperty::array}. + * @brief This property view was initialized from an invalid {@link PropertyTable}. */ - ErrorArrayTypeMismatch, + static const PropertyViewStatusType ErrorInvalidPropertyTable = 12; /** * @brief This property view does not have a valid value buffer view index. */ - ErrorInvalidValueBufferView, + static const PropertyViewStatusType ErrorInvalidValueBufferView = 13; /** * @brief This array property view does not have a valid array offset buffer * view index. */ - ErrorInvalidArrayOffsetBufferView, + static const PropertyViewStatusType ErrorInvalidArrayOffsetBufferView = 14; /** * @brief This string property view does not have a valid string offset buffer * view index. */ - ErrorInvalidStringOffsetBufferView, + static const PropertyViewStatusType ErrorInvalidStringOffsetBufferView = 15; /** * @brief This property view has a valid value buffer view, but the buffer * view specifies an invalid buffer index. */ - ErrorInvalidValueBuffer, + static const PropertyViewStatusType ErrorInvalidValueBuffer = 16; /** * @brief This property view has a valid array string buffer view, but the * buffer view specifies an invalid buffer index. */ - ErrorInvalidArrayOffsetBuffer, + static const PropertyViewStatusType ErrorInvalidArrayOffsetBuffer = 17; /** * @brief This property view has a valid string offset buffer view, but the * buffer view specifies an invalid buffer index. */ - ErrorInvalidStringOffsetBuffer, + static const PropertyViewStatusType ErrorInvalidStringOffsetBuffer = 18; /** * @brief This property view has a buffer view that points outside the bounds * of its target buffer. */ - ErrorBufferViewOutOfBounds, + static const PropertyViewStatusType ErrorBufferViewOutOfBounds = 19; /** * @brief This property view has an invalid buffer view; its length is not * a multiple of the size of its type / offset type. */ - ErrorBufferViewSizeNotDivisibleByTypeSize, + static const PropertyViewStatusType + ErrorBufferViewSizeNotDivisibleByTypeSize = 20; /** * @brief This property view has an invalid buffer view; its length does not * match the size of the property table. */ - ErrorBufferViewSizeDoesNotMatchPropertyTableCount, + static const PropertyViewStatusType + ErrorBufferViewSizeDoesNotMatchPropertyTableCount = 21; /** * @brief This array property view has both a fixed length and an offset * buffer view defined. */ - ErrorArrayCountAndOffsetBufferCoexist, + static const PropertyViewStatusType ErrorArrayCountAndOffsetBufferCoexist = + 22; /** * @brief This array property view has neither a fixed length nor an offset * buffer view defined. */ - ErrorArrayCountAndOffsetBufferDontExist, + static const PropertyViewStatusType ErrorArrayCountAndOffsetBufferDontExist = + 23; /** * @brief This property view has an unknown array offset type. */ - ErrorInvalidArrayOffsetType, + static const PropertyViewStatusType ErrorInvalidArrayOffsetType = 24; /** * @brief This property view has an unknown string offset type. */ - ErrorInvalidStringOffsetType, + static const PropertyViewStatusType ErrorInvalidStringOffsetType = 25; /** * @brief This property view's array offset values are not sorted in ascending * order. */ - ErrorArrayOffsetsNotSorted, + static const PropertyViewStatusType ErrorArrayOffsetsNotSorted = 26; /** * @brief This property view's string offset values are not sorted in * ascending order. */ - ErrorStringOffsetsNotSorted, + static const PropertyViewStatusType ErrorStringOffsetsNotSorted = 27; /** * @brief This property view has an array offset that is out of bounds. */ - ErrorArrayOffsetOutOfBounds, + static const PropertyViewStatusType ErrorArrayOffsetOutOfBounds = 28; /** * @brief This property view has a string offset that is out of bounds. */ - ErrorStringOffsetOutOfBounds + static const PropertyViewStatusType ErrorStringOffsetOutOfBounds = 29; }; /** @@ -179,15 +154,32 @@ class PropertyTablePropertyView : public PropertyView { : PropertyView(), _status{PropertyTablePropertyViewStatus::ErrorNonexistentProperty}, _values, - _size{} {} + _size{0}, + _arrayOffsets{}, + _arrayOffsetType{PropertyComponentType::None}, + _arrayOffsetTypeSize{0}, + _stringOffsets{}, + _stringOffsetType{PropertyComponentType::None}, + _stringOffsetTypeSize{0}, + {} /** * @brief Constructs an invalid instance for an erroneous property. * - * @param status The {@link PropertyTablePropertyViewStatus} indicating the error with the property. + * @param status The value of {@link PropertyTablePropertyViewStatus} indicating the error with the property. */ - PropertyTablePropertyView(PropertyTablePropertyViewStatus status) - : PropertyView(), _status{status}, _values{}, _size{} { + PropertyTablePropertyView(PropertyViewStatusType status) + : PropertyView(), + _status{status}, + _values{}, + _size{0}, + _arrayOffsets{}, + _arrayOffsetType{PropertyComponentType::None}, + _arrayOffsetTypeSize{0}, + _stringOffsets{}, + _stringOffsetType{PropertyComponentType::None}, + _stringOffsetTypeSize{0} + { assert( _status != PropertyTablePropertyViewStatus::Valid && "An empty property view should not be constructed with a valid status"); @@ -208,13 +200,14 @@ class PropertyTablePropertyView : public PropertyView { : PropertyView(classProperty, property), _status{PropertyTablePropertyViewStatus::Valid}, _values{values}, + _size{size}, _arrayOffsets{}, _arrayOffsetType{PropertyComponentType::None}, _arrayOffsetTypeSize{0}, _stringOffsets{}, _stringOffsetType{PropertyComponentType::None}, - _stringOffsetTypeSize{0}, - _size{size} {} + _stringOffsetTypeSize{0} + {} /** * @brief Construct a valid instance pointing to the data specified by @@ -250,14 +243,9 @@ class PropertyTablePropertyView : public PropertyView { _size{size} {} /** - * @brief Gets the status of this property table property view. - * - * Indicates whether the view accurately reflects the property's data, or - * whether an error occurred. - * - * @return The status of this property view. + * @copydoc IPropertyView::status */ - PropertyTablePropertyViewStatus status() const noexcept { return _status; } + virtual int32_t status() const noexcept override { return _status; } /** * @brief Get the value of an element of the {@link PropertyTable}. @@ -430,8 +418,9 @@ class PropertyTablePropertyView : public PropertyView { } } - PropertyTablePropertyViewStatus _status; + PropertyViewStatusType _status; gsl::span _values; + int64_t _size; gsl::span _arrayOffsets; PropertyComponentType _arrayOffsetType; @@ -440,7 +429,5 @@ class PropertyTablePropertyView : public PropertyView { gsl::span _stringOffsets; PropertyComponentType _stringOffsetType; int64_t _stringOffsetTypeSize; - - int64_t _size; }; } // namespace CesiumGltf diff --git a/CesiumGltf/include/CesiumGltf/PropertyTableView.h b/CesiumGltf/include/CesiumGltf/PropertyTableView.h index c43c26512..1f5fb9a31 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTableView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTableView.h @@ -1152,11 +1152,11 @@ class PropertyTableView { const ClassProperty& classProperty, const PropertyTableProperty& propertyTableProperty) const; - PropertyTablePropertyViewStatus getBufferSafe( + PropertyViewStatusType getBufferSafe( int32_t bufferView, gsl::span& buffer) const noexcept; - PropertyTablePropertyViewStatus getArrayOffsetsBufferSafe( + PropertyViewStatusType getArrayOffsetsBufferSafe( int32_t arrayOffsetsBufferView, PropertyComponentType arrayOffsetType, size_t valuesBufferSize, @@ -1164,7 +1164,7 @@ class PropertyTableView { bool checkBitsSize, gsl::span& arrayOffsetsBuffer) const noexcept; - PropertyTablePropertyViewStatus getStringOffsetsBufferSafe( + PropertyViewStatusType getStringOffsetsBufferSafe( int32_t stringOffsetsBufferView, PropertyComponentType stringOffsetType, size_t valuesBufferSize, diff --git a/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h index 709c25e10..95417462a 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h @@ -4,6 +4,7 @@ #include "CesiumGltf/PropertyTextureProperty.h" #include "CesiumGltf/PropertyTypeTraits.h" #include "CesiumGltf/Sampler.h" +#include "CesiumGltf/PropertyView.h" #include #include diff --git a/CesiumGltf/include/CesiumGltf/PropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyView.h index d56c15e88..e897be9b3 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyView.h @@ -7,26 +7,98 @@ #include namespace CesiumGltf { + +typedef int32_t PropertyViewStatusType; + +class PropertyViewStatus { +public: + /** + * @brief This property view is valid and ready to use. + */ + static const PropertyViewStatusType Valid = 0; + + /** + * @brief This property view is trying to view a property that does not + * exist. + */ + static const PropertyViewStatusType ErrorNonexistentProperty = 1; + + /** + * @brief This property view's type does not match what is + * specified in {@link ClassProperty::type}. + */ + static const PropertyViewStatusType ErrorTypeMismatch = 2; + + /** + * @brief This property view's component type does not match what + * is specified in {@link ClassProperty::componentType}. + */ + static const PropertyViewStatusType ErrorComponentTypeMismatch = 3; + + /** + * @brief This property view differs from what is specified in + * {@link ClassProperty::array}. + */ + static const PropertyViewStatusType ErrorArrayTypeMismatch = 4; + + /** + * @brief This property says it is normalized, but is not of an integer + * component type. + */ + static const PropertyViewStatusType ErrorInvalidNormalization = 5; + + /** + * @brief The property provided an invalid offset value. + */ + static const PropertyViewStatusType ErrorInvalidOffset = 6; + + /** + * @brief The property provided an invalid scale value. + */ + static const PropertyViewStatusType ErrorInvalidScale = 7; + + /** + * @brief The property provided an invalid maximum value. + */ + static const PropertyViewStatusType ErrorInvalidMax = 8; + + /** + * @brief The property provided an invalid minimum value. + */ + static const PropertyViewStatusType ErrorInvalidMin = 9; + + /** + * @brief The property provided an invalid "no data" value. + */ + static const PropertyViewStatusType ErrorInvalidNoDataValue = 10; + + /** + * @brief The property provided an invalid default value. + */ + static const PropertyViewStatusType ErrorInvalidDefaultValue = 11; +}; + /** * @brief An interface for generic metadata property in EXT_structural_metadata. * * Whether they belong to property tables, property textures, or property * attributes, properties have their own sub-properties affecting the actual * property values. Although they are typically defined via class property, they - * may be overriden by individual instances of the property. The constructor is + * may be overridden by individual instances of the property. The constructor is * responsible for resolving those differences. * - * However, there are fundamental differences between property tables, property - * textures, and property attributes. Notably, the ways in which values are - * stored -- as well as what types of values are even supported -- vary between - * the three. Therefore, this interface has no "status" and does not validate - * ElementType against the input class property. Derived classes must do their - * own validation to ensure that ElementType matches the given class definition. - * * @tparam ElementType The C++ type of the values in this property */ template class IPropertyView { public: + /** + * @brief Gets the status of this property view, indicating whether an error + * occurred. + * + * @return The status of this property view. + */ + virtual PropertyViewStatusType status() const noexcept = 0; + /** * @brief Get the element count of the fixed-length arrays in this property. * Only applicable when the property is an array type. @@ -118,7 +190,8 @@ class PropertyView : IPropertyView { * @brief Constructs an empty property instance. */ PropertyView() - : _normalized(false), + : _status(PropertyViewStatus::ErrorNonexistentProperty), + _normalized(false), _offset(std::nullopt), _scale(std::nullopt), _max(std::nullopt), @@ -131,7 +204,8 @@ class PropertyView : IPropertyView { * @brief Constructs a property instance from a class definition only. */ PropertyView(const ClassProperty& classProperty) - : _normalized(classProperty.normalized), + : _status(PropertyViewStatus::Valid), + _normalized(classProperty.normalized), _offset(std::nullopt), _scale(std::nullopt), _max(std::nullopt), @@ -139,21 +213,107 @@ class PropertyView : IPropertyView { _required(classProperty.required), _noData(std::nullopt), _defaultValue(std::nullopt) { + if (convertPropertyTypeToString(TypeToPropertyType::value) != + classProperty.type) { + _status = PropertyViewStatus::ErrorTypeMismatch; + return; + } + + if (!classProperty.componentType && + TypeToPropertyType::component != + PropertyComponentType::None) { + _status = PropertyViewStatus::ErrorComponentTypeMismatch; + return; + } + + if (classProperty.componentType && + convertPropertyComponentTypeToString( + TypeToPropertyType::component) != + *classProperty.componentType) { + _status = PropertyViewStatus::ErrorComponentTypeMismatch; + return; + } + + if (classProperty.array) { + _status = PropertyViewStatus::ErrorArrayTypeMismatch; + return; + } + + if (classProperty.normalized) { + PropertyComponentType componentType = + TypeToPropertyType::component; + switch (componentType) { + case PropertyComponentType::Uint8: + case PropertyComponentType::Int8: + case PropertyComponentType::Uint16: + case PropertyComponentType::Int16: + case PropertyComponentType::Uint32: + case PropertyComponentType::Int32: + case PropertyComponentType::Uint64: + case PropertyComponentType::Int64: + break; + default: + _status = PropertyViewStatus::ErrorInvalidNormalization; + return; + } + } + if constexpr (IsMetadataNumeric::value) { - _offset = - classProperty.offset ? getValue(*classProperty.offset) : std::nullopt; - _scale = - classProperty.scale ? getValue(*classProperty.scale) : std::nullopt; - _max = classProperty.max ? getValue(*classProperty.max) : std::nullopt; - _min = classProperty.min ? getValue(*classProperty.min) : std::nullopt; + if (classProperty.offset) { + _offset = getValue(*classProperty.offset); + if (!_offset) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidOffset; + return; + } + } + + if (classProperty.scale) { + _scale = getValue(*classProperty.scale); + if (!_scale) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidScale; + return; + } + } + + if (classProperty.max) { + _max = getValue(*classProperty.max); + if (!_scale) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidMax; + return; + } + } + + if (classProperty.min) { + _min = getValue(*classProperty.min); + if (!_scale) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidMin; + return; + } + } } if (!_required) { - _noData = - classProperty.noData ? getValue(*classProperty.noData) : std::nullopt; - _defaultValue = classProperty.defaultProperty - ? getValue(*classProperty.defaultProperty) - : std::nullopt; + if (classProperty.noData) { + _noData = getValue(*classProperty.noData); + if (!_noData) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidNoDataValue; + return; + } + } + + if (classProperty.defaultProperty) { + _defaultValue = getValue(*classProperty.defaultProperty); + if (!_defaultValue) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidDefaultValue; + return; + } + } } } @@ -166,22 +326,46 @@ class PropertyView : IPropertyView { const ClassProperty& classProperty, const PropertyTableProperty& property) : PropertyView(classProperty) { + if (_status != PropertyViewStatus::Valid) { + return; + } + // If the property has its own values, override the class-provided values. if constexpr (IsMetadataNumeric::value) { if (property.offset) { _offset = getValue(*property.offset); + if (!_offset) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidOffset; + return; + } } if (property.scale) { _scale = getValue(*property.scale); + if (!_scale) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidScale; + return; + } } if (property.max) { _max = getValue(*property.max); + if (!_scale) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidMax; + return; + } } if (property.min) { _min = getValue(*property.min); + if (!_scale) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidMin; + return; + } } } } @@ -194,27 +378,58 @@ class PropertyView : IPropertyView { const ClassProperty& classProperty, const PropertyTextureProperty& property) : PropertyView(classProperty) { + if (_status != PropertyViewStatus::Valid) { + return; + } + // If the property has its own values, override the class-provided values. if constexpr (IsMetadataNumeric::value) { if (property.offset) { _offset = getValue(*property.offset); + if (!_offset) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidOffset; + return; + } } if (property.scale) { _scale = getValue(*property.scale); + if (!_scale) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidScale; + return; + } } if (property.max) { _max = getValue(*property.max); + if (!_scale) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidMax; + return; + } } if (property.min) { _min = getValue(*property.min); + if (!_scale) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidMin; + return; + } } } } public: + /** + * @copydoc IPropertyView::status + */ + virtual PropertyViewStatusType status() const noexcept override { + return _status; + } + /** * @copydoc IPropertyView::arrayCount */ @@ -273,6 +488,7 @@ class PropertyView : IPropertyView { } private: + PropertyViewStatusType _status; bool _normalized; std::optional _offset; @@ -308,15 +524,6 @@ class PropertyView : IPropertyView { } } - /** - * @brief Attempts to parse a scalar from the given JSON value. If the JSON - * value is a number of the wrong type, this will round it to the closest - * representation in the desired type, if possible. Otherwise, this returns - * std::nullopt. - * - * @return The value as a type T, or std::nullopt if it could not be - * parsed. - */ template static std::optional getScalar(const CesiumUtility::JsonValue& jsonValue) { try { @@ -341,7 +548,7 @@ class PropertyView : IPropertyView { return std::nullopt; } - using T = VecType::value_type; + using T = typename VecType::value_type; VecType result; for (glm::length_t i = 0; i < N; i++) { @@ -369,7 +576,7 @@ class PropertyView : IPropertyView { return std::nullopt; } - using T = MatType::value_type; + using T = typename MatType::value_type; MatType result; for (glm::length_t i = 0; i < N; i++) { @@ -393,17 +600,35 @@ template <> class PropertyView : IPropertyView { /** * @brief Constructs an empty property instance. */ - PropertyView() : _required(false), _defaultValue(std::nullopt) {} + PropertyView() + : _status(PropertyViewStatus::ErrorNonexistentProperty), + _required(false), + _defaultValue(std::nullopt) {} /** * @brief Constructs a property instance from a class definition only. */ PropertyView(const ClassProperty& classProperty) - : _required(classProperty.required), _defaultValue(std::nullopt) { - if (!_required) { - _defaultValue = classProperty.defaultProperty - ? getBooleanValue(*classProperty.defaultProperty) - : std::nullopt; + : _status(PropertyViewStatus::Valid), + _required(classProperty.required), + _defaultValue(std::nullopt) { + if (classProperty.type != ClassProperty::Type::BOOLEAN) { + _status = PropertyViewStatus::ErrorTypeMismatch; + return; + } + + if (classProperty.array) { + _status = PropertyViewStatus::ErrorArrayTypeMismatch; + return; + } + + if (!_required && classProperty.defaultProperty) { + _defaultValue = getBooleanValue(*classProperty.defaultProperty); + if (!_defaultValue) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidDefaultValue; + return; + } } } @@ -427,6 +652,13 @@ template <> class PropertyView : IPropertyView { : PropertyView(classProperty) {} public: + /** + * @copydoc IPropertyView::status + */ + virtual PropertyViewStatusType status() const noexcept override { + return _status; + } + /** * @copydoc IPropertyView::arrayCount */ @@ -485,6 +717,7 @@ template <> class PropertyView : IPropertyView { } private: + PropertyViewStatusType _status; bool _required; std::optional _defaultValue; @@ -505,21 +738,46 @@ class PropertyView : IPropertyView { * @brief Constructs an empty property instance. */ PropertyView() - : _required(false), _noData(std::nullopt), _defaultValue(std::nullopt) {} + : _status(PropertyViewStatus::ErrorNonexistentProperty), + _required(false), + _noData(std::nullopt), + _defaultValue(std::nullopt) {} /** * @brief Constructs a property instance from a class definition only. */ PropertyView(const ClassProperty& classProperty) - : _required(classProperty.required), + : _status(PropertyViewStatus::Valid), + _required(classProperty.required), _noData(std::nullopt), _defaultValue(std::nullopt) { + if (classProperty.type != ClassProperty::Type::STRING) { + _status = PropertyViewStatus::ErrorTypeMismatch; + return; + } + + if (classProperty.array) { + _status = PropertyViewStatus::ErrorArrayTypeMismatch; + return; + } + if (!_required) { - _noData = classProperty.noData ? getStringValue(*classProperty.noData) - : std::nullopt; - _defaultValue = classProperty.defaultProperty - ? getStringValue(*classProperty.defaultProperty) - : std::nullopt; + if (classProperty.noData) { + _noData = getStringValue(*classProperty.noData); + if (!_noData) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidNoDataValue; + return; + } + } + if (classProperty.defaultProperty) { + _defaultValue = getStringValue(*classProperty.defaultProperty); + if (!_defaultValue) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidDefaultValue; + return; + } + } } } @@ -543,6 +801,13 @@ class PropertyView : IPropertyView { : PropertyView(classProperty) {} public: + /** + * @copydoc IPropertyView::status + */ + virtual PropertyViewStatusType status() const noexcept override { + return _status; + } + /** * @copydoc IPropertyView::arrayCount */ @@ -608,6 +873,7 @@ class PropertyView : IPropertyView { } private: + PropertyViewStatusType _status; bool _required; std::optional _noData; std::optional _defaultValue; @@ -630,7 +896,8 @@ class PropertyView> * @brief Constructs an empty property instance. */ PropertyView() - : _count(0), + : _status(PropertyViewStatus::ErrorNonexistentProperty), + _count(0), _normalized(false), _offset(std::nullopt), _scale(std::nullopt), @@ -644,7 +911,8 @@ class PropertyView> * @brief Constructs a property instance from a class definition only. */ PropertyView(const ClassProperty& classProperty) - : _count(_count = classProperty.count ? *classProperty.count : 0), + : _status(PropertyViewStatus::Valid), + _count(_count = classProperty.count ? *classProperty.count : 0), _normalized(classProperty.normalized), _offset(std::nullopt), _scale(std::nullopt), @@ -653,25 +921,99 @@ class PropertyView> _required(classProperty.required), _noData(std::nullopt), _defaultValue(std::nullopt) { - //_offset = classProperty.offset - // ? getValue(*classProperty.offset) - // : std::nullopt; - //_scale = classProperty.scale ? getValue(*classProperty.scale) - // : std::nullopt; - //_max = classProperty.max ? getValue(*classProperty.max) - // : std::nullopt; - //_min = classProperty.min ? getValue(*classProperty.min) - // : std::nullopt; - - // if (!_required) { - // _noData = classProperty.noData - // ? getValue(*classProperty.noData) - // : std::nullopt; - // _defaultValue = - // classProperty.defaultProperty - // ? getValue(*classProperty.defaultProperty) - // : std::nullopt; - //} + if (convertPropertyTypeToString(TypeToPropertyType::value) != + classProperty.type) { + _status = PropertyViewStatus::ErrorTypeMismatch; + return; + } + + if (!classProperty.componentType && + TypeToPropertyType::component != + PropertyComponentType::None) { + _status = PropertyViewStatus::ErrorComponentTypeMismatch; + return; + } + + if (classProperty.componentType && + convertPropertyComponentTypeToString( + TypeToPropertyType::component) != + *classProperty.componentType) { + _status = PropertyViewStatus::ErrorComponentTypeMismatch; + return; + } + + if (!classProperty.array) { + _status = PropertyViewStatus::ErrorArrayTypeMismatch; + return; + } + + if (classProperty.normalized && !IsMetadataInteger::value) { + _status = PropertyViewStatus::ErrorInvalidNormalization; + return; + } + + // If the property has its own values, override the class-provided values. + if constexpr (IsMetadataNumeric::value) { + if (classProperty.offset) { + _offset = getArrayValue(*classProperty.offset); + if (!_offset || + (_count > 0 && _offset->size() != static_cast(_count))) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidOffset; + return; + } + } + + if (classProperty.scale) { + _scale = getArrayValue(*classProperty.scale); + if (!_scale || + (_count > 0 && _scale->size() != static_cast(_count))) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidScale; + return; + } + } + + if (classProperty.max) { + _max = getArrayValue(*classProperty.max); + if (!_max || + (_count > 0 && _max->size() != static_cast(_count))) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidMax; + return; + } + } + + if (classProperty.min) { + _min = getArrayValue(*classProperty.min); + if (!_min || + (_count > 0 && _min->size() != static_cast(_count))) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidMin; + return; + } + } + } + + if (!_required) { + if (classProperty.noData) { + _noData = getArrayValue(*classProperty.noData); + if (!_noData) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidNoDataValue; + return; + } + } + + if (classProperty.defaultProperty) { + _defaultValue = getArrayValue(*classProperty.defaultProperty); + if (!_defaultValue) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidDefaultValue; + return; + } + } + } } protected: @@ -681,24 +1023,50 @@ class PropertyView> */ PropertyView( const ClassProperty& classProperty, - const PropertyTableProperty& /*property*/) + const PropertyTableProperty& property) : PropertyView(classProperty) { - //// If the property has its own values, override the class-provided values. - // if (property.offset) { - // _offset = getValue(*property.offset); - //} + if (_status != PropertyViewStatus::Valid) { + return; + } + + // If the property has its own values, override the class-provided values. + if constexpr (IsMetadataNumeric::value) { + if (property.offset) { + _offset = getArrayValue(*property.offset); + if (!_offset) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidOffset; + return; + } + } - // if (property.scale) { - // _scale = getValue(*property.scale); - //} + if (property.scale) { + _scale = getArrayValue(*property.scale); + if (!_scale) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidScale; + return; + } + } - // if (property.max) { - // _max = getValue(*property.max); - //} + if (property.max) { + _max = getArrayValue(*property.max); + if (!_scale) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidMax; + return; + } + } - // if (property.min) { - // _min = getValue(*property.min); - //} + if (property.min) { + _min = getArrayValue(*property.min); + if (!_scale) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidMin; + return; + } + } + } } /** @@ -707,27 +1075,60 @@ class PropertyView> */ PropertyView( const ClassProperty& classProperty, - const PropertyTextureProperty& /*property*/) + const PropertyTextureProperty& property) : PropertyView(classProperty) { - //// If the property has its own values, override the class-provided values. - // if (property.offset) { - // _offset = getValue(*property.offset); - //} + if (_status != PropertyViewStatus::Valid) { + return; + } - // if (property.scale) { - // _scale = getValue(*property.scale); - //} + // If the property has its own values, override the class-provided values. + if constexpr (IsMetadataNumeric::value) { + if (property.offset) { + _offset = getArrayValue(*property.offset); + if (!_offset) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidOffset; + return; + } + } - // if (property.max) { - // _max = getValue(*property.max); - //} + if (property.scale) { + _scale = getArrayValue(*property.scale); + if (!_scale) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidScale; + return; + } + } + + if (property.max) { + _max = getArrayValue(*property.max); + if (!_scale) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidMax; + return; + } + } - // if (property.min) { - // _min = getValue(*property.min); - //} + if (property.min) { + _min = getArrayValue(*property.min); + if (!_scale) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidMin; + return; + } + } + } } public: + /** + * @copydoc IPropertyView::status + */ + virtual PropertyViewStatusType status() const noexcept override { + return _status; + } + /** * @copydoc IPropertyView::arrayCount */ @@ -743,7 +1144,12 @@ class PropertyView> */ virtual std::optional> offset() const noexcept override { - return _offset; + if (!_offset) { + return std::nullopt; + } + + return PropertyArrayView( + gsl::span(_offset->data(), _offset->size())); } /** @@ -751,7 +1157,12 @@ class PropertyView> */ virtual std::optional> scale() const noexcept override { - return _scale; + if (!_scale) { + return std::nullopt; + } + + return PropertyArrayView( + gsl::span(_scale->data(), _scale->size())); } /** @@ -759,7 +1170,12 @@ class PropertyView> */ virtual std::optional> max() const noexcept override { - return _max; + if (!_max) { + return std::nullopt; + } + + return PropertyArrayView( + gsl::span(_max->data(), _max->size())); } /** @@ -767,7 +1183,12 @@ class PropertyView> */ virtual std::optional> min() const noexcept override { - return _min; + if (!_min) { + return std::nullopt; + } + + return PropertyArrayView( + gsl::span(_min->data(), _min->size())); } /** @@ -780,7 +1201,12 @@ class PropertyView> */ virtual std::optional> noData() const noexcept override { - return _noData; + if (!_noData) { + return std::nullopt; + } + + return PropertyArrayView( + gsl::span(_noData->data(), _noData->size())); } /** @@ -788,59 +1214,81 @@ class PropertyView> */ virtual std::optional> defaultValue() const noexcept override { - return _defaultValue; + if (!_defaultValue) { + return std::nullopt; + } + + return PropertyArrayView(gsl::span( + _defaultValue->data(), + _defaultValue->size())); } private: + PropertyViewStatusType _status; int64_t _count; bool _normalized; - std::optional> _offset; - std::optional> _scale; - std::optional> _max; - std::optional> _min; + std::optional> _offset; + std::optional> _scale; + std::optional> _max; + std::optional> _min; bool _required; - std::optional> _noData; - std::optional> _defaultValue; + std::optional> _noData; + std::optional> _defaultValue; - /** - * @brief Attempts to parse from the given json value. - * - * If ElementType is a type with multiple components, e.g. a VECN or MATN - * type, this will return std::nullopt if one or more components could not be - * parsed. - * - * @return The value as an instance of ElementType, or std::nullopt if it - * could not be parsed. - */ - static std::optional - getValue(const CesiumUtility::JsonValue& jsonValue) { - if constexpr (IsMetadataScalar::value) { - return getScalar(jsonValue); - } else if constexpr (IsMetadataVecN::value) { - return getVecN(jsonValue); - } else + static std::optional> + getArrayValue(const CesiumUtility::JsonValue& jsonValue) { + if (!jsonValue.isArray()) { return std::nullopt; + } + + const CesiumUtility::JsonValue::Array& array = jsonValue.getArray(); + std::vector values; + values.reserve(array.size()); + + if constexpr (IsMetadataScalar::value) { + for (size_t i = 0; i < array.size(); i++) { + std::optional element = getScalar(array[i]); + if (!element) { + return std::nullopt; + } + + values.push_back(*element); + } + } + if constexpr (IsMetadataVecN::value) { + for (size_t i = 0; i < array.size(); i++) { + std::optional element = getVecN(array[i]); + if (!element) { + return std::nullopt; + } + + values.push_back(*element); + } + } + + if constexpr (IsMetadataMatN::value) { + for (size_t i = 0; i < array.size(); i++) { + std::optional element = getMatN(array[i]); + if (!element) { + return std::nullopt; + } + + values.push_back(*element); + } + } - // if constexpr (IsMetadataMatN::value) { - //} + std::vector result(values.size() * sizeof(ElementType)); + std::memcpy(result.data(), values.data(), result.size()); + + return result; } - /** - * @brief Attempts to parse from the given json value. If it could not be - * parsed as an ElementType, this returns the default value. - * - * If ElementType is a type with multiple components, e.g. a VECN or MATN - * type. - * - * @return The value as an ElementType, or std::nullopt if it could not be - * parsed. - */ template static std::optional getScalar(const CesiumUtility::JsonValue& jsonValue) { try { - return jsonValue.getSafeNumber(); + return jsonValue.getSafeNumber(); } catch (const CesiumUtility::JsonValueNotRealValue& /*error*/) { return std::nullopt; } catch (const gsl::narrowing_error& /*error*/) { @@ -848,55 +1296,64 @@ class PropertyView> } } - template - static std::optional> + template + static std::optional getVecN(const CesiumUtility::JsonValue& jsonValue) { if (!jsonValue.isArray()) { return std::nullopt; } - const CesiumUtility::JsonValue::Array& array = value.getArray(); + const CesiumUtility::JsonValue::Array& array = jsonValue.getArray(); + constexpr glm::length_t N = VecType::length(); if (array.size() != N) { return std::nullopt; } - glm::vec result; + using T = typename VecType::value_type; + + VecType result; for (glm::length_t i = 0; i < N; i++) { - std::optional value = getValue(array[i]); + std::optional value = getScalar(array[i]); if (!value) { return std::nullopt; } - result[i] = value; + result[i] = *value; } return result; } - // template - // static std::optional - // getMatNFromJsonValue(const CesiumUtility::JsonValue& value) { - // if (!jsonValue.isArray()) { - // return std::nullopt; - // } + template + static std::optional + getMatN(const CesiumUtility::JsonValue& jsonValue) { + if (!jsonValue.isArray()) { + return std::nullopt; + } + + const CesiumUtility::JsonValue::Array& array = jsonValue.getArray(); + constexpr glm::length_t N = MatType::length(); + if (array.size() != N * N) { + return std::nullopt; + } - // const CesiumUtility::JsonValue::Array& array = jsonValue.getArray(); - // if (array.size() != N) { - // return std::nullopt; - // } + using T = typename MatType::value_type; - // glm::vec result; - // for (glm::length_t i = 0; i < N; i++) { - // std::optional value = getValue(array[i]); - // if (!value) { - // return std::nullopt; - // } + MatType result; + for (glm::length_t i = 0; i < N; i++) { + // Try to parse each value in the column. + for (glm::length_t j = 0; j < N; j++) { + std::optional value = getScalar(array[i * N + j]); + if (!value) { + return std::nullopt; + } - // result[i] = value; - // } + result[i][j] = *value; + } + } - // return result; - //} + return result; + } }; template <> @@ -906,19 +1363,40 @@ class PropertyView> /** * @brief Constructs an empty property instance. */ - PropertyView() : _count(0), _required(false), _defaultValue(std::nullopt) {} + PropertyView() + : _status(PropertyViewStatus::ErrorNonexistentProperty), + _count(0), + _required(false), + _defaultValue(), + _size(0) {} /** * @brief Constructs a property instance from a class definition only. */ PropertyView(const ClassProperty& classProperty) - : _count(classProperty.count ? *classProperty.count : 0), + : _status(PropertyViewStatus::Valid), + _count(classProperty.count ? *classProperty.count : 0), _required(classProperty.required), - _defaultValue(std::nullopt) { - if (!_required) { - _defaultValue = classProperty.defaultProperty - ? getBooleanArrayValue(*classProperty.defaultProperty) - : std::nullopt; + _defaultValue(), + _size(0) { + if (classProperty.type != ClassProperty::Type::BOOLEAN) { + _status = PropertyViewStatus::ErrorTypeMismatch; + return; + } + + if (!classProperty.array) { + _status = PropertyViewStatus::ErrorArrayTypeMismatch; + return; + } + + if (!_required && classProperty.defaultProperty) { + _defaultValue = + getBooleanArrayValue(*classProperty.defaultProperty, _size); + if (_size == 0 || (_count > 0 && _size != _count)) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidDefaultValue; + return; + } } } @@ -933,8 +1411,8 @@ class PropertyView> : PropertyView(classProperty) {} /** - * @brief Constructs a property instance from a property texture property and - * its class definition. + * @brief Constructs a property instance from a property texture property + * and its class definition. */ PropertyView( const ClassProperty& classProperty, @@ -942,6 +1420,13 @@ class PropertyView> : PropertyView(classProperty) {} public: + /** + * @copydoc IPropertyView::status + */ + virtual PropertyViewStatusType status() const noexcept override { + return _status; + } + /** * @copydoc IPropertyView::arrayCount */ @@ -1000,34 +1485,60 @@ class PropertyView> */ virtual std::optional> defaultValue() const noexcept override { - if (!_defaultValue) { - return std::nullopt; + if (_size > 0) { + return PropertyArrayView( + gsl::span( + _defaultValue.data(), + _defaultValue.size()), + /* bitOffset = */ 0, + _size); } - // Not as easy to construct a gsl::span for the boolean data (the .data() of - // a boolean vector is inaccessible). Just make a copy. - std::vector defaultValueCopy(*_defaultValue); - return PropertyArrayView(std::move(defaultValueCopy)); + + return std::nullopt; } private: + PropertyViewStatusType _status; int64_t _count; bool _required; - std::optional> _defaultValue; - static std::optional> - getBooleanArrayValue(const CesiumUtility::JsonValue& value) { - if (!value.isArray()) { - return std::nullopt; + std::vector _defaultValue; + int64_t _size; + + static std::vector getBooleanArrayValue( + const CesiumUtility::JsonValue& jsonValue, + int64_t& size) { + if (!jsonValue.isArray()) { + return std::vector(); } - const CesiumUtility::JsonValue::Array& array = value.getArray(); - std::vector values(array.size()); - for (size_t i = 0; i < values.size(); i++) { + const CesiumUtility::JsonValue::Array& array = jsonValue.getArray(); + std::vector values; + values.reserve((array.size() / 8) + 1); + + size_t byteIndex = 0; + uint8_t bitIndex = 0; + + for (size_t i = 0; i < array.size(); i++, size++) { if (!array[i].isBool()) { - return std::nullopt; + size = 0; + return values; + } + + if (values.size() < byteIndex - 1) { + values.push_back(std::byte(0)); } - values[i] = array[i].getBool(); + std::byte value = std::byte(array[i].getBool() ? 1 : 0); + value = value << bitIndex; + + values[byteIndex] |= value; + + bitIndex++; + if (bitIndex > 7) { + byteIndex++; + bitIndex = 0; + } } return values; @@ -1041,23 +1552,68 @@ class PropertyView> /** * @brief Constructs an empty property instance. */ - PropertyView() : _count(0), _required(false), _noData(), _defaultValue() {} + PropertyView() + : _status(PropertyViewStatus::ErrorNonexistentProperty), + _count(0), + _required(false), + _noData(), + _noDataOffsets(), + _noDataOffsetType(PropertyComponentType::None), + _noDataSize(0), + _defaultValue(), + _defaultValueOffsets(), + _defaultValueOffsetType(PropertyComponentType::None), + _defaultValueSize(0) {} /** * @brief Constructs a property instance from a class definition only. */ PropertyView(const ClassProperty& classProperty) - : _count(classProperty.count ? *classProperty.count : 0), + : _status(PropertyViewStatus::Valid), + _count(classProperty.count ? *classProperty.count : 0), _required(classProperty.required), _noData(), - _defaultValue() { - if (!_required) { - _noData = classProperty.noData - ? getStringArrayValue(*classProperty.noData) - : std::nullopt; - _defaultValue = classProperty.defaultProperty - ? getStringArrayValue(*classProperty.defaultProperty) - : std::nullopt; + _noDataOffsets(), + _noDataOffsetType(PropertyComponentType::None), + _noDataSize(0), + _defaultValue(), + _defaultValueOffsets(), + _defaultValueOffsetType(PropertyComponentType::None), + _defaultValueSize(0) { + if (classProperty.type != ClassProperty::Type::STRING) { + _status = PropertyViewStatus::ErrorTypeMismatch; + return; + } + + if (!classProperty.array) { + _status = PropertyViewStatus::ErrorArrayTypeMismatch; + return; + } + + if (!_required && classProperty.noData) { + _noData = getStringArrayValue( + *classProperty.noData, + _noDataOffsets, + _noDataOffsetType, + _noDataSize); + if (_noDataSize == 0 || (_count > 0 && _noDataSize != _count)) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidNoDataValue; + return; + } + } + + if (!_required && classProperty.defaultProperty) { + _defaultValue = getStringArrayValue( + *classProperty.defaultProperty, + _defaultValueOffsets, + _defaultValueOffsetType, + _defaultValueSize); + if (_defaultValueSize == 0 || (_count > 0 && _noDataSize != _count)) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidDefaultValue; + return; + } } } @@ -1072,8 +1628,8 @@ class PropertyView> : PropertyView(classProperty) {} /** - * @brief Constructs a property instance from a property texture property and - * its class definition. + * @brief Constructs a property instance from a property texture property + * and its class definition. */ PropertyView( const ClassProperty& classProperty, @@ -1081,6 +1637,13 @@ class PropertyView> : PropertyView(classProperty) {} public: + /** + * @copydoc IPropertyView::status + */ + virtual PropertyViewStatusType status() const noexcept override { + return _status; + } + /** * @copydoc IPropertyView::arrayCount */ @@ -1133,14 +1696,17 @@ class PropertyView> */ virtual std::optional> noData() const noexcept override { - if (!_noData) { - return std::nullopt; + if (_noDataSize > 0) { + return PropertyArrayView( + gsl::span(_noData.data(), _noData.size()), + gsl::span( + _noDataOffsets.data(), + _noDataOffsets.size()), + _noDataOffsetType, + _noDataSize); } - // Just copy the strings. Easier than iterating through all of the strings - // to generate an offsets buffer with a best-fitting offset type. - std::vector noDataCopy(*_noData); - return PropertyArrayView(std::move(noDataCopy)); + return std::nullopt; } /** @@ -1148,40 +1714,101 @@ class PropertyView> */ virtual std::optional> defaultValue() const noexcept override { - if (!_defaultValue) { - return std::nullopt; + if (_noDataSize > 0) { + return PropertyArrayView( + gsl::span( + _defaultValue.data(), + _defaultValue.size()), + gsl::span( + _defaultValueOffsets.data(), + _defaultValueOffsets.size()), + _defaultValueOffsetType, + _defaultValueSize); } - // Just copy the strings. Easier than iterating through all of the strings - // to generate an offsets buffer with a best-fitting offset type. - std::vector defaultValueCopy(*_defaultValue); - return PropertyArrayView(std::move(defaultValueCopy)); + return std::nullopt; } private: + PropertyViewStatusType _status; int64_t _count; bool _required; - std::optional> _noData; - std::optional> _defaultValue; - - static std::optional> - getStringArrayValue(const CesiumUtility::JsonValue& jsonValue) { + std::vector _noData; + std::vector _noDataOffsets; + PropertyComponentType _noDataOffsetType; + int64_t _noDataSize; + + std::vector _defaultValue; + std::vector _defaultValueOffsets; + PropertyComponentType _defaultValueOffsetType; + int64_t _defaultValueSize; + + static std::vector getStringArrayValue( + const CesiumUtility::JsonValue& jsonValue, + std::vector& offsets, + PropertyComponentType& offsetType, + int64_t& size) { if (!jsonValue.isArray()) { - return std::nullopt; + return std::vector(); } - std::vector result; + std::vector strings; + std::vector stringOffsets; + const auto array = jsonValue.getArray(); - result.reserve(array.size()); + strings.reserve(array.size()); + stringOffsets.reserve(array.size() + 1); + stringOffsets.push_back(static_cast(0)); for (size_t i = 0; i < array.size(); i++) { if (!array[i].isString()) { // The entire array is invalidated; return. - return std::nullopt; + return std::vector(); } - result.push_back(array[i].getString()); + const std::string& string = array[i].getString(); + strings.push_back(string); + stringOffsets.push_back(stringOffsets[i] + string.size()); + } + + uint64_t totalLength = stringOffsets.back(); + std::vector values(totalLength); + for (size_t i = 0; i < strings.size(); ++i) { + std::memcpy( + values.data() + stringOffsets[i], + strings[i].data(), + strings[i].size()); + }; + + if (totalLength <= std::numeric_limits::max()) { + offsets = narrowOffsetsBuffer(stringOffsets); + offsetType = PropertyComponentType::Uint8; + } else if (totalLength <= std::numeric_limits::max()) { + offsets = narrowOffsetsBuffer(stringOffsets); + offsetType = PropertyComponentType::Uint16; + } else if (totalLength <= std::numeric_limits::max()) { + offsets = narrowOffsetsBuffer(stringOffsets); + offsetType = PropertyComponentType::Uint32; + } else { + offsets.resize(stringOffsets.size() * sizeof(uint64_t)); + std::memcpy(offsets.data(), stringOffsets.data(), offsets.size()); + offsetType = PropertyComponentType::Uint64; + } + + size = static_cast(strings.size()); + + return values; + } + + template + static std::vector + narrowOffsetsBuffer(std::vector offsets) { + std::vector result(offsets.size() * sizeof(T)); + size_t bufferOffset = 0; + for (size_t i = 0; i < offsets.size(); i++, bufferOffset += sizeof(T)) { + T offset = static_cast(offsets[i]); + std::memcpy(result.data() + bufferOffset, &offset, sizeof(T)); } return result; diff --git a/CesiumGltf/src/PropertyTableView.cpp b/CesiumGltf/src/PropertyTableView.cpp index 77d137233..5ba0af589 100644 --- a/CesiumGltf/src/PropertyTableView.cpp +++ b/CesiumGltf/src/PropertyTableView.cpp @@ -2,13 +2,13 @@ namespace CesiumGltf { template -static PropertyTablePropertyViewStatus checkOffsetsBuffer( +static PropertyViewStatusType checkOffsetsBuffer( const gsl::span& offsetBuffer, size_t valueBufferSize, size_t instanceCount, bool checkBitSize, - PropertyTablePropertyViewStatus offsetsNotSortedError, - PropertyTablePropertyViewStatus offsetOutOfBoundsError) noexcept { + PropertyViewStatusType offsetsNotSortedError, + PropertyViewStatusType offsetOutOfBoundsError) noexcept { if (offsetBuffer.size() % sizeof(T) != 0) { return PropertyTablePropertyViewStatus:: ErrorBufferViewSizeNotDivisibleByTypeSize; @@ -46,7 +46,7 @@ static PropertyTablePropertyViewStatus checkOffsetsBuffer( } template -static PropertyTablePropertyViewStatus checkStringAndArrayOffsetsBuffers( +static PropertyViewStatusType checkStringAndArrayOffsetsBuffers( const gsl::span& arrayOffsets, const gsl::span& stringOffsets, size_t valueBufferSize, @@ -147,7 +147,7 @@ PropertyTableView::getClassProperty(const std::string& propertyName) const { return &propertyIter->second; } -PropertyTablePropertyViewStatus PropertyTableView::getBufferSafe( +PropertyViewStatusType PropertyTableView::getBufferSafe( int32_t bufferViewIdx, gsl::span& buffer) const noexcept { buffer = {}; @@ -175,14 +175,14 @@ PropertyTablePropertyViewStatus PropertyTableView::getBufferSafe( return PropertyTablePropertyViewStatus::Valid; } -PropertyTablePropertyViewStatus PropertyTableView::getArrayOffsetsBufferSafe( +PropertyViewStatusType PropertyTableView::getArrayOffsetsBufferSafe( int32_t arrayOffsetsBufferView, PropertyComponentType arrayOffsetType, size_t valueBufferSize, size_t propertyTableCount, bool checkBitsSize, gsl::span& arrayOffsetsBuffer) const noexcept { - const PropertyTablePropertyViewStatus bufferStatus = + const PropertyViewStatusType bufferStatus = getBufferSafe(arrayOffsetsBufferView, arrayOffsetsBuffer); if (bufferStatus != PropertyTablePropertyViewStatus::Valid) { return bufferStatus; @@ -226,13 +226,13 @@ PropertyTablePropertyViewStatus PropertyTableView::getArrayOffsetsBufferSafe( } } -PropertyTablePropertyViewStatus PropertyTableView::getStringOffsetsBufferSafe( +PropertyViewStatusType PropertyTableView::getStringOffsetsBufferSafe( int32_t stringOffsetsBufferView, PropertyComponentType stringOffsetType, size_t valueBufferSize, size_t propertyTableCount, gsl::span& stringOffsetsBuffer) const noexcept { - const PropertyTablePropertyViewStatus bufferStatus = + const PropertyViewStatusType bufferStatus = getBufferSafe(stringOffsetsBufferView, stringOffsetsBuffer); if (bufferStatus != PropertyTablePropertyViewStatus::Valid) { return bufferStatus; diff --git a/CesiumGltf/test/TestPropertyView.cpp b/CesiumGltf/test/TestPropertyView.cpp index 52b49d9d3..3bc1b9ec7 100644 --- a/CesiumGltf/test/TestPropertyView.cpp +++ b/CesiumGltf/test/TestPropertyView.cpp @@ -5,20 +5,38 @@ using namespace CesiumGltf; using namespace CesiumUtility; -TEST_CASE("Constructs empty PropertyView") { - PropertyView view; - REQUIRE(view.arrayCount() == 0); - REQUIRE(!view.normalized()); - REQUIRE(!view.offset()); - REQUIRE(!view.scale()); - REQUIRE(!view.max()); - REQUIRE(!view.min()); - REQUIRE(!view.required()); - REQUIRE(!view.noData()); - REQUIRE(!view.defaultValue()); -} - TEST_CASE("Boolean PropertyView") { + SECTION("Constructs empty PropertyView") { + PropertyView view; + REQUIRE(view.status() == PropertyViewStatus::ErrorNonexistentProperty); + REQUIRE(view.arrayCount() == 0); + REQUIRE(!view.normalized()); + REQUIRE(!view.offset()); + REQUIRE(!view.scale()); + REQUIRE(!view.max()); + REQUIRE(!view.min()); + REQUIRE(!view.required()); + REQUIRE(!view.noData()); + REQUIRE(!view.defaultValue()); + } + + SECTION("Reports type mismatch") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::STRING; + + PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorTypeMismatch); + } + + SECTION("Reports array type mismatch") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::BOOLEAN; + classProperty.array = true; + + PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorArrayTypeMismatch); + } + SECTION("Ignores non-applicable properties") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::BOOLEAN; @@ -30,6 +48,7 @@ TEST_CASE("Boolean PropertyView") { classProperty.noData = true; PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::Valid); REQUIRE(!view.normalized()); REQUIRE(!view.offset()); REQUIRE(!view.scale()); @@ -44,6 +63,7 @@ TEST_CASE("Boolean PropertyView") { classProperty.count = 5; PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::Valid); REQUIRE(view.arrayCount() == 0); } @@ -54,6 +74,7 @@ TEST_CASE("Boolean PropertyView") { classProperty.defaultProperty = false; PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::Valid); REQUIRE(!view.required()); REQUIRE(view.defaultValue()); REQUIRE(!*view.defaultValue()); @@ -66,22 +87,73 @@ TEST_CASE("Boolean PropertyView") { classProperty.defaultProperty = false; PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::Valid); REQUIRE(view.required()); REQUIRE(!view.defaultValue()); } - SECTION("Returns nullopt for invalid types") { + SECTION("Reports default value invalid type") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::BOOLEAN; classProperty.defaultProperty = 1; PropertyView view(classProperty); - REQUIRE(!view.required()); - REQUIRE(!view.defaultValue()); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidDefaultValue); } } TEST_CASE("Scalar PropertyView") { + SECTION("Constructs empty PropertyView") { + PropertyView view; + REQUIRE(view.status() == PropertyViewStatus::ErrorNonexistentProperty); + REQUIRE(view.arrayCount() == 0); + REQUIRE(!view.normalized()); + REQUIRE(!view.offset()); + REQUIRE(!view.scale()); + REQUIRE(!view.max()); + REQUIRE(!view.min()); + REQUIRE(!view.required()); + REQUIRE(!view.noData()); + REQUIRE(!view.defaultValue()); + } + + SECTION("Reports type mismatch") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::STRING; + + PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorTypeMismatch); + } + + SECTION("Reports component type mismatch") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::SCALAR; + classProperty.componentType = ClassProperty::ComponentType::INT8; + + PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorComponentTypeMismatch); + } + + SECTION("Reports array type mismatch") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::SCALAR; + classProperty.componentType = ClassProperty::ComponentType::UINT8; + classProperty.array = true; + + PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorArrayTypeMismatch); + } + + SECTION("Reports invalid normalization") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::SCALAR; + classProperty.componentType = ClassProperty::ComponentType::FLOAT32; + classProperty.normalized = true; + + PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNormalization); + } + SECTION("Constructs with non-optional properties") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::SCALAR; @@ -90,6 +162,7 @@ TEST_CASE("Scalar PropertyView") { classProperty.required = true; PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::Valid); REQUIRE(view.normalized()); REQUIRE(view.required()); @@ -109,6 +182,7 @@ TEST_CASE("Scalar PropertyView") { classProperty.count = 5; PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::Valid); REQUIRE(view.arrayCount() == 0); } @@ -122,6 +196,7 @@ TEST_CASE("Scalar PropertyView") { classProperty.min = -10; PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::Valid); REQUIRE(view.offset()); REQUIRE(view.scale()); REQUIRE(view.max()); @@ -133,42 +208,6 @@ TEST_CASE("Scalar PropertyView") { REQUIRE(*view.min() == -10); } - SECTION("Returns nullopt for out-of-bounds values") { - ClassProperty classProperty; - classProperty.type = ClassProperty::Type::SCALAR; - classProperty.componentType = ClassProperty::ComponentType::INT8; - classProperty.offset = 20; - classProperty.scale = 255; - classProperty.max = 127; - classProperty.min = -1000; - - PropertyView view(classProperty); - REQUIRE(view.offset()); - REQUIRE(view.max()); - - REQUIRE(*view.offset() == 20); - REQUIRE(*view.max() == 127); - - REQUIRE(!view.scale()); - REQUIRE(!view.min()); - } - - SECTION("Returns nullopt for invalid types") { - ClassProperty classProperty; - classProperty.type = ClassProperty::Type::SCALAR; - classProperty.componentType = ClassProperty::ComponentType::INT8; - classProperty.offset = "10"; - classProperty.scale = false; - classProperty.max = 5.5; - classProperty.min = JsonValue::Array{1}; - - PropertyView view(classProperty); - REQUIRE(!view.offset()); - REQUIRE(!view.scale()); - REQUIRE(!view.max()); - REQUIRE(!view.min()); - } - SECTION("Constructs with noData and defaultProperty") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::SCALAR; @@ -199,9 +238,120 @@ TEST_CASE("Scalar PropertyView") { REQUIRE(!view.noData()); REQUIRE(!view.defaultValue()); } + + SECTION("Reports errors for out-of-bounds values") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::SCALAR; + classProperty.componentType = ClassProperty::ComponentType::INT8; + classProperty.defaultProperty = 2000; + + PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidDefaultValue); + + classProperty.noData = -129; + view = PropertyView(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNoDataValue); + + classProperty.min = -1000; + view = PropertyView(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMin); + + classProperty.max = 1000; + view = PropertyView(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMax); + + classProperty.scale = 200; + view = PropertyView(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidScale); + + classProperty.offset = 1234; + view = PropertyView(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidOffset); + } + + SECTION("Reports errors for invalid types") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::SCALAR; + classProperty.componentType = ClassProperty::ComponentType::INT8; + classProperty.defaultProperty = JsonValue::Array{1}; + + PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidDefaultValue); + + classProperty.noData = "0"; + view = PropertyView(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNoDataValue); + + classProperty.min = -12.7f; + view = PropertyView(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMin); + + classProperty.max = 5.5; + view = PropertyView(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMax); + + classProperty.scale = false; + view = PropertyView(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidScale); + + classProperty.offset = JsonValue::Array{}; + view = PropertyView(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidOffset); + } } TEST_CASE("VecN PropertyView") { + SECTION("Constructs empty PropertyView") { + PropertyView view; + REQUIRE(view.status() == PropertyViewStatus::ErrorNonexistentProperty); + REQUIRE(view.arrayCount() == 0); + REQUIRE(!view.normalized()); + REQUIRE(!view.offset()); + REQUIRE(!view.scale()); + REQUIRE(!view.max()); + REQUIRE(!view.min()); + REQUIRE(!view.required()); + REQUIRE(!view.noData()); + REQUIRE(!view.defaultValue()); + } + + SECTION("Reports type mismatch") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::VEC2; + + PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorTypeMismatch); + } + + SECTION("Reports component type mismatch") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::VEC3; + classProperty.componentType = ClassProperty::ComponentType::INT8; + + PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorComponentTypeMismatch); + } + + SECTION("Reports array type mismatch") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::VEC3; + classProperty.componentType = ClassProperty::ComponentType::FLOAT32; + classProperty.array = true; + + PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorArrayTypeMismatch); + } + + SECTION("Reports invalid normalization") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::VEC3; + classProperty.componentType = ClassProperty::ComponentType::FLOAT32; + classProperty.normalized = true; + + PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNormalization); + } + SECTION("Constructs with non-optional properties") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::VEC3; @@ -253,42 +403,6 @@ TEST_CASE("VecN PropertyView") { REQUIRE(*view.min() == glm::i16vec3(-11, -12, -13)); } - SECTION("Returns nullopt for out-of-bounds values") { - ClassProperty classProperty; - classProperty.type = ClassProperty::Type::VEC2; - classProperty.componentType = ClassProperty::ComponentType::INT8; - classProperty.offset = JsonValue::Array{-1, 2}; - classProperty.scale = JsonValue::Array{1, 128}; - classProperty.max = JsonValue::Array{10, 5}; - classProperty.min = JsonValue::Array{-200, 0}; - - PropertyView view(classProperty); - REQUIRE(view.offset()); - REQUIRE(view.max()); - - REQUIRE(*view.offset() == glm::i8vec2(-1, 2)); - REQUIRE(*view.max() == glm::i8vec2(10, 5)); - - REQUIRE(!view.scale()); - REQUIRE(!view.min()); - } - - SECTION("Returns nullopt for invalid types") { - ClassProperty classProperty; - classProperty.type = ClassProperty::Type::VEC3; - classProperty.componentType = ClassProperty::ComponentType::INT8; - classProperty.offset = "(1, 2, 3)"; - classProperty.scale = 1; - classProperty.max = JsonValue::Array{10, 20, 30, 40}; - classProperty.min = JsonValue::Array{-10, -1}; - - PropertyView view(classProperty); - REQUIRE(!view.offset()); - REQUIRE(!view.scale()); - REQUIRE(!view.max()); - REQUIRE(!view.min()); - } - SECTION("Constructs with noData and defaultProperty") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::VEC4; @@ -319,48 +433,161 @@ TEST_CASE("VecN PropertyView") { REQUIRE(!view.noData()); REQUIRE(!view.defaultValue()); } -} -TEST_CASE("MatN PropertyView") { - SECTION("Constructs with non-optional properties") { + SECTION("Reports errors for out-of-bounds values") { ClassProperty classProperty; - classProperty.type = ClassProperty::Type::MAT3; - classProperty.componentType = ClassProperty::ComponentType::INT32; - classProperty.normalized = true; - classProperty.required = true; + classProperty.type = ClassProperty::Type::VEC2; + classProperty.componentType = ClassProperty::ComponentType::INT8; + classProperty.defaultProperty = JsonValue::Array{128, 129}; - PropertyView view(classProperty); - REQUIRE(view.normalized()); - REQUIRE(view.required()); + PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidDefaultValue); + + classProperty.noData = JsonValue::Array{-128, -129}; + view = PropertyView(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNoDataValue); + + classProperty.min = JsonValue::Array{-200, 0}; + view = PropertyView(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMin); + + classProperty.max = JsonValue::Array{10, 5}; + view = PropertyView(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMax); + + classProperty.scale = JsonValue::Array{1, 128}; + view = PropertyView(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidScale); + + classProperty.offset = JsonValue::Array{-1, -222}; + view = PropertyView(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidOffset); + } + + SECTION("Reports errors for invalid types") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::VEC2; + classProperty.componentType = ClassProperty::ComponentType::INT8; + classProperty.defaultProperty = JsonValue::Array{1.0f, 20.9f}; + + PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidDefaultValue); + + classProperty.noData = "0"; + view = PropertyView(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNoDataValue); + + classProperty.min = JsonValue::Array{-10, -1}; + view = PropertyView(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMin); + + classProperty.max = JsonValue::Array{10, 20, 30, 40}; + view = PropertyView(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMax); + classProperty.scale = 1; + view = PropertyView(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidScale); + + classProperty.offset = "(1, 2, 3)"; + view = PropertyView(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidOffset); + } +} + +TEST_CASE("MatN PropertyView") { + SECTION("Constructs empty PropertyView") { + PropertyView view; + REQUIRE(view.status() == PropertyViewStatus::ErrorNonexistentProperty); REQUIRE(view.arrayCount() == 0); + REQUIRE(!view.normalized()); REQUIRE(!view.offset()); REQUIRE(!view.scale()); REQUIRE(!view.max()); REQUIRE(!view.min()); + REQUIRE(!view.required()); REQUIRE(!view.noData()); REQUIRE(!view.defaultValue()); } - SECTION("Ignores count") { + SECTION("Reports type mismatch") { ClassProperty classProperty; - classProperty.type = ClassProperty::Type::MAT3; - classProperty.componentType = ClassProperty::ComponentType::INT32; - classProperty.count = 5; + classProperty.type = ClassProperty::Type::MAT4; - PropertyView view(classProperty); - REQUIRE(view.arrayCount() == 0); + PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorTypeMismatch); } - SECTION("Constructs with offset, scale, max, and min") { + SECTION("Reports component type mismatch") { ClassProperty classProperty; - classProperty.type = ClassProperty::Type::MAT3; - classProperty.componentType = ClassProperty::ComponentType::INT32; - // clang-format off - classProperty.offset = JsonValue::Array{ - -1, 1, 2, - 3, -1, 4, - -5, -5, 0}; + classProperty.type = ClassProperty::Type::MAT2; + classProperty.componentType = ClassProperty::ComponentType::INT8; + + PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorComponentTypeMismatch); + } + + SECTION("Reports array type mismatch") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::MAT2; + classProperty.componentType = ClassProperty::ComponentType::FLOAT32; + classProperty.array = true; + + PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorArrayTypeMismatch); + } + + SECTION("Reports invalid normalization") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::MAT2; + classProperty.componentType = ClassProperty::ComponentType::FLOAT32; + classProperty.normalized = true; + + PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNormalization); + } + + SECTION("Constructs with non-optional properties") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::MAT3; + classProperty.componentType = ClassProperty::ComponentType::INT32; + classProperty.normalized = true; + classProperty.required = true; + + PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::Valid); + REQUIRE(view.normalized()); + REQUIRE(view.required()); + + REQUIRE(view.arrayCount() == 0); + REQUIRE(!view.offset()); + REQUIRE(!view.scale()); + REQUIRE(!view.max()); + REQUIRE(!view.min()); + REQUIRE(!view.noData()); + REQUIRE(!view.defaultValue()); + } + + SECTION("Ignores count") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::MAT3; + classProperty.componentType = ClassProperty::ComponentType::INT32; + classProperty.count = 5; + + PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::Valid); + REQUIRE(view.arrayCount() == 0); + } + + SECTION("Constructs with offset, scale, max, and min") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::MAT3; + classProperty.componentType = ClassProperty::ComponentType::INT32; + // clang-format off + classProperty.offset = JsonValue::Array{ + -1, 1, 2, + 3, -1, 4, + -5, -5, 0}; classProperty.scale = JsonValue::Array{ 1, 1, 1, 2, 2, 3, @@ -376,6 +603,7 @@ TEST_CASE("MatN PropertyView") { // clang-format on PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::Valid); REQUIRE(view.offset()); REQUIRE(view.scale()); REQUIRE(view.max()); @@ -408,61 +636,6 @@ TEST_CASE("MatN PropertyView") { // clang-format on } - SECTION("Returns nullopt for out-of-bounds values") { - ClassProperty classProperty; - classProperty.type = ClassProperty::Type::MAT2; - classProperty.componentType = ClassProperty::ComponentType::INT8; - // clang-format off - classProperty.offset = JsonValue::Array{ - -1, 2, - 129, -2}; - classProperty.scale = JsonValue::Array{ - 1, 7, - 4, 6}; - classProperty.max = JsonValue::Array{ - 10, 240, - 1, 8}; - classProperty.min = JsonValue::Array{ - -29, -40, - -55, -43}; - // clang-format on - - PropertyView view(classProperty); - REQUIRE(view.scale()); - REQUIRE(view.min()); - - // clang-format off - glm::i8mat2x2 expectedScale( - 1, 7, - 4, 6); - REQUIRE(*view.scale() == expectedScale); - - glm::i8mat2x2 expectedMin( - -29, -40, - -55, -43); - REQUIRE(*view.min() == expectedMin); - // clang-format on - - REQUIRE(!view.offset()); - REQUIRE(!view.max()); - } - - SECTION("Returns nullopt for invalid types") { - ClassProperty classProperty; - classProperty.type = ClassProperty::Type::MAT2; - classProperty.componentType = ClassProperty::ComponentType::UINT8; - classProperty.offset = "(1, 2, 3, 4)"; - classProperty.scale = 1; - classProperty.max = JsonValue::Array{10, 20, 30, 40, 50}; - classProperty.min = JsonValue::Array{0, 0}; - - PropertyView view(classProperty); - REQUIRE(!view.offset()); - REQUIRE(!view.scale()); - REQUIRE(!view.max()); - REQUIRE(!view.min()); - } - SECTION("Constructs with noData and defaultProperty") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::MAT2; @@ -478,6 +651,7 @@ TEST_CASE("MatN PropertyView") { // clang-format on PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::Valid); REQUIRE(!view.required()); REQUIRE(view.noData()); REQUIRE(view.defaultValue()); @@ -509,13 +683,139 @@ TEST_CASE("MatN PropertyView") { // clang-format on PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::Valid); REQUIRE(view.required()); REQUIRE(!view.noData()); REQUIRE(!view.defaultValue()); } + + SECTION("Reports errors for out-of-bounds values") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::MAT2; + classProperty.componentType = ClassProperty::ComponentType::INT8; + + // clang-format off + classProperty.defaultProperty = JsonValue::Array{ + 999, 1, + 2, 0}; + // clang-format on + + PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidDefaultValue); + + // clang-format off + classProperty.noData = JsonValue::Array{ + 0, 0, + 1, -129}; + // clang-format on + view = PropertyView(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNoDataValue); + + // clang-format off + classProperty.min = JsonValue::Array{ + -29, -40, + -55, -43}; + // clang-format on + view = PropertyView(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMin); + + // clang-format off + classProperty.max = JsonValue::Array{ + 10, 240, + 1, 8}; + // clang-format on + view = PropertyView(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMax); + + //clang-format off + classProperty.scale = JsonValue::Array{1, 197, 4, 6}; + // clang-format on + view = PropertyView(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidScale); + + // clang-format off + classProperty.offset = JsonValue::Array{ + -1, 2, + 129, -2}; + // clang-format on + view = PropertyView(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidOffset); + } + + SECTION("Reports errors for invalid types") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::MAT2; + classProperty.componentType = ClassProperty::ComponentType::INT8; + + // clang-format off + classProperty.defaultProperty = JsonValue::Array{ + JsonValue::Array{ + 999, 1, + 2, 0} + }; + // clang-format on + + PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidDefaultValue); + + // clang-format off + classProperty.noData = JsonValue::Array{ + 0.45, 0.0, + 1.0, -1.4}; + // clang-format on + view = PropertyView(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNoDataValue); + + classProperty.min = JsonValue::Array{0, 0}; + view = PropertyView(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMin); + + classProperty.max = JsonValue::Array{10, 20, 30, 40, 50}; + view = PropertyView(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMax); + + classProperty.scale = 1; + view = PropertyView(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidScale); + + classProperty.offset = "(1, 2, 3, 4)"; + view = PropertyView(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidOffset); + } } TEST_CASE("String PropertyView") { + SECTION("Constructs empty PropertyView") { + PropertyView view; + REQUIRE(view.status() == PropertyViewStatus::ErrorNonexistentProperty); + REQUIRE(view.arrayCount() == 0); + REQUIRE(!view.normalized()); + REQUIRE(!view.offset()); + REQUIRE(!view.scale()); + REQUIRE(!view.max()); + REQUIRE(!view.min()); + REQUIRE(!view.required()); + REQUIRE(!view.noData()); + REQUIRE(!view.defaultValue()); + } + + SECTION("Reports type mismatch") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::BOOLEAN; + + PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorTypeMismatch); + } + + SECTION("Reports array type mismatch") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::STRING; + classProperty.array = true; + + PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorArrayTypeMismatch); + } + SECTION("Ignores non-applicable properties") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::STRING; @@ -585,9 +885,42 @@ TEST_CASE("String PropertyView") { } TEST_CASE("Boolean Array PropertyView") { + SECTION("Constructs empty PropertyView") { + PropertyView> view; + REQUIRE(view.status() == PropertyViewStatus::ErrorNonexistentProperty); + REQUIRE(view.arrayCount() == 0); + REQUIRE(!view.normalized()); + REQUIRE(!view.offset()); + REQUIRE(!view.scale()); + REQUIRE(!view.max()); + REQUIRE(!view.min()); + REQUIRE(!view.required()); + REQUIRE(!view.noData()); + REQUIRE(!view.defaultValue()); + } + + SECTION("Reports type mismatch") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::STRING; + classProperty.array = true; + + PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorTypeMismatch); + } + + SECTION("Reports array type mismatch") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::STRING; + classProperty.array = false; + + PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorArrayTypeMismatch); + } + SECTION("Ignores non-applicable properties") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::BOOLEAN; + classProperty.array = true; classProperty.normalized = true; classProperty.offset = JsonValue::Array{true}; classProperty.scale = JsonValue::Array{true}; @@ -643,7 +976,7 @@ TEST_CASE("Boolean Array PropertyView") { REQUIRE(!view.defaultValue()); } - SECTION("Returns nullopt for invalid types") { + SECTION("Reports errors for invalid types") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::BOOLEAN; classProperty.array = true; @@ -655,6 +988,198 @@ TEST_CASE("Boolean Array PropertyView") { } } +TEST_CASE("Scalar Array PropertyView") { + SECTION("Constructs empty PropertyView") { + PropertyView> view; + REQUIRE(view.status() == PropertyViewStatus::ErrorNonexistentProperty); + REQUIRE(view.arrayCount() == 0); + REQUIRE(!view.normalized()); + REQUIRE(!view.offset()); + REQUIRE(!view.scale()); + REQUIRE(!view.max()); + REQUIRE(!view.min()); + REQUIRE(!view.required()); + REQUIRE(!view.noData()); + REQUIRE(!view.defaultValue()); + } + + SECTION("Reports type mismatch") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::STRING; + classProperty.array = true; + + PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorTypeMismatch); + } + + SECTION("Reports component type mismatch") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::SCALAR; + classProperty.componentType = ClassProperty::ComponentType::INT8; + classProperty.array = true; + + PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorComponentTypeMismatch); + } + + SECTION("Reports array type mismatch") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::SCALAR; + classProperty.componentType = ClassProperty::ComponentType::UINT8; + classProperty.array = false; + + PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorArrayTypeMismatch); + } + + SECTION("Reports invalid normalization") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::SCALAR; + classProperty.componentType = ClassProperty::ComponentType::FLOAT32; + classProperty.array = true; + classProperty.normalized = true; + + PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNormalization); + } + + SECTION("Constructs with non-optional properties") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::SCALAR; + classProperty.componentType = ClassProperty::ComponentType::UINT8; + classProperty.array = true; + classProperty.normalized = true; + classProperty.required = true; + + PropertyView> view(classProperty); + REQUIRE(view.normalized()); + REQUIRE(view.required()); + + REQUIRE(view.arrayCount() == 0); + REQUIRE(!view.offset()); + REQUIRE(!view.scale()); + REQUIRE(!view.max()); + REQUIRE(!view.min()); + REQUIRE(!view.noData()); + REQUIRE(!view.defaultValue()); + } + + SECTION("Constructs with count") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::SCALAR; + classProperty.componentType = ClassProperty::ComponentType::UINT8; + classProperty.array = true; + classProperty.count = 5; + + PropertyView> view(classProperty); + REQUIRE(view.arrayCount() == *classProperty.count); + } + + SECTION("Constructs with offset, scale, max, and min") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::SCALAR; + classProperty.componentType = ClassProperty::ComponentType::INT16; + classProperty.array = true; + classProperty.offset = JsonValue::Array{5, 10}; + classProperty.scale = JsonValue::Array{2, 1}; + classProperty.max = JsonValue::Array{10, 20}; + classProperty.min = JsonValue::Array{-10, -1}; + + PropertyView> view(classProperty); + REQUIRE(view.offset()); + REQUIRE(view.scale()); + REQUIRE(view.max()); + REQUIRE(view.min()); + + PropertyArrayView offset = *view.offset(); + REQUIRE(offset.size() == 2); + REQUIRE(offset[0] == 5); + REQUIRE(offset[1] == 10); + + PropertyArrayView scale = *view.scale(); + REQUIRE(scale.size() == 2); + REQUIRE(scale[0] == 2); + REQUIRE(scale[1] == 1); + + PropertyArrayView max = *view.max(); + REQUIRE(max.size() == 2); + REQUIRE(max[0] == 10); + REQUIRE(max[1] == 20); + + PropertyArrayView min = *view.min(); + REQUIRE(min.size() == 2); + REQUIRE(min[0] == -10); + REQUIRE(min[1] == -1); + } + + SECTION("Returns nullopt for out-of-bounds values") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::SCALAR; + classProperty.componentType = ClassProperty::ComponentType::INT8; + classProperty.offset = 20; + classProperty.scale = 255; + classProperty.max = 127; + classProperty.min = -1000; + + PropertyView view(classProperty); + REQUIRE(view.offset()); + REQUIRE(view.max()); + + REQUIRE(*view.offset() == 20); + REQUIRE(*view.max() == 127); + + REQUIRE(!view.scale()); + REQUIRE(!view.min()); + } + + SECTION("Returns nullopt for invalid types") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::SCALAR; + classProperty.componentType = ClassProperty::ComponentType::INT8; + classProperty.offset = "10"; + classProperty.scale = false; + classProperty.max = 5.5; + classProperty.min = JsonValue::Array{1}; + + PropertyView view(classProperty); + REQUIRE(!view.offset()); + REQUIRE(!view.scale()); + REQUIRE(!view.max()); + REQUIRE(!view.min()); + } + + SECTION("Constructs with noData and defaultProperty") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::SCALAR; + classProperty.componentType = ClassProperty::ComponentType::UINT8; + classProperty.required = false; + classProperty.noData = 0; + classProperty.defaultProperty = 1; + + PropertyView view(classProperty); + REQUIRE(!view.required()); + REQUIRE(view.noData()); + REQUIRE(view.defaultValue()); + + REQUIRE(*view.noData() == 0); + REQUIRE(*view.defaultValue() == 1); + } + + SECTION("Ignores noData and defaultProperty when required is true") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::SCALAR; + classProperty.componentType = ClassProperty::ComponentType::UINT8; + classProperty.required = true; + classProperty.noData = 0; + classProperty.defaultProperty = 1; + + PropertyView view(classProperty); + REQUIRE(view.required()); + REQUIRE(!view.noData()); + REQUIRE(!view.defaultValue()); + } +} + TEST_CASE("String Array PropertyView") { SECTION("Ignores non-applicable properties") { ClassProperty classProperty; From 38a595537d985e2202ff0097639125734ca44c89 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Thu, 17 Aug 2023 15:38:17 -0400 Subject: [PATCH 170/421] Finish unit tests for PropertyView --- .../CesiumGltf/PropertyTablePropertyView.h | 6 +- CesiumGltf/include/CesiumGltf/PropertyView.h | 28 +- CesiumGltf/test/TestPropertyView.cpp | 848 +++++++++++++++--- 3 files changed, 770 insertions(+), 112 deletions(-) diff --git a/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h index 6c0d269b1..065fb0162 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h @@ -178,8 +178,7 @@ class PropertyTablePropertyView : public PropertyView { _arrayOffsetTypeSize{0}, _stringOffsets{}, _stringOffsetType{PropertyComponentType::None}, - _stringOffsetTypeSize{0} - { + _stringOffsetTypeSize{0} { assert( _status != PropertyTablePropertyViewStatus::Valid && "An empty property view should not be constructed with a valid status"); @@ -206,8 +205,7 @@ class PropertyTablePropertyView : public PropertyView { _arrayOffsetTypeSize{0}, _stringOffsets{}, _stringOffsetType{PropertyComponentType::None}, - _stringOffsetTypeSize{0} - {} + _stringOffsetTypeSize{0} {} /** * @brief Construct a valid instance pointing to the data specified by diff --git a/CesiumGltf/include/CesiumGltf/PropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyView.h index e897be9b3..56fa1a6ac 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyView.h @@ -243,14 +243,14 @@ class PropertyView : IPropertyView { PropertyComponentType componentType = TypeToPropertyType::component; switch (componentType) { - case PropertyComponentType::Uint8: case PropertyComponentType::Int8: - case PropertyComponentType::Uint16: + case PropertyComponentType::Uint8: case PropertyComponentType::Int16: - case PropertyComponentType::Uint32: + case PropertyComponentType::Uint16: case PropertyComponentType::Int32: - case PropertyComponentType::Uint64: + case PropertyComponentType::Uint32: case PropertyComponentType::Int64: + case PropertyComponentType::Uint64: break; default: _status = PropertyViewStatus::ErrorInvalidNormalization; @@ -947,9 +947,23 @@ class PropertyView> return; } - if (classProperty.normalized && !IsMetadataInteger::value) { - _status = PropertyViewStatus::ErrorInvalidNormalization; - return; + if (classProperty.normalized) { + PropertyComponentType componentType = + TypeToPropertyType::component; + switch (componentType) { + case PropertyComponentType::Int8: + case PropertyComponentType::Uint8: + case PropertyComponentType::Int16: + case PropertyComponentType::Uint16: + case PropertyComponentType::Int32: + case PropertyComponentType::Uint32: + case PropertyComponentType::Int64: + case PropertyComponentType::Uint64: + break; + default: + _status = PropertyViewStatus::ErrorInvalidNormalization; + return; + } } // If the property has its own values, override the class-provided values. diff --git a/CesiumGltf/test/TestPropertyView.cpp b/CesiumGltf/test/TestPropertyView.cpp index 3bc1b9ec7..f6fc099c2 100644 --- a/CesiumGltf/test/TestPropertyView.cpp +++ b/CesiumGltf/test/TestPropertyView.cpp @@ -217,6 +217,7 @@ TEST_CASE("Scalar PropertyView") { classProperty.defaultProperty = 1; PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::Valid); REQUIRE(!view.required()); REQUIRE(view.noData()); REQUIRE(view.defaultValue()); @@ -234,6 +235,7 @@ TEST_CASE("Scalar PropertyView") { classProperty.defaultProperty = 1; PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::Valid); REQUIRE(view.required()); REQUIRE(!view.noData()); REQUIRE(!view.defaultValue()); @@ -360,6 +362,7 @@ TEST_CASE("VecN PropertyView") { classProperty.required = true; PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::Valid); REQUIRE(view.normalized()); REQUIRE(view.required()); @@ -379,6 +382,7 @@ TEST_CASE("VecN PropertyView") { classProperty.count = 5; PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::Valid); REQUIRE(view.arrayCount() == 0); } @@ -386,12 +390,13 @@ TEST_CASE("VecN PropertyView") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::VEC3; classProperty.componentType = ClassProperty::ComponentType::INT16; - classProperty.offset = JsonValue::Array{-1, 1, 2}; - classProperty.scale = JsonValue::Array{2, 1, 3}; - classProperty.max = JsonValue::Array{10, 5, 6}; - classProperty.min = JsonValue::Array{-11, -12, -13}; + classProperty.offset = {-1, 1, 2}; + classProperty.scale = {2, 1, 3}; + classProperty.max = {10, 5, 6}; + classProperty.min = {-11, -12, -13}; PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::Valid); REQUIRE(view.offset()); REQUIRE(view.scale()); REQUIRE(view.max()); @@ -408,10 +413,11 @@ TEST_CASE("VecN PropertyView") { classProperty.type = ClassProperty::Type::VEC4; classProperty.componentType = ClassProperty::ComponentType::FLOAT32; classProperty.required = false; - classProperty.noData = JsonValue::Array{0.0f, 0.0f, 0.0f, 0.0f}; - classProperty.defaultProperty = JsonValue::Array{1.0f, 2.0f, 3.0f, 4.0f}; + classProperty.noData = {0.0f, 0.0f, 0.0f, 0.0f}; + classProperty.defaultProperty = {1.0f, 2.0f, 3.0f, 4.0f}; PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::Valid); REQUIRE(!view.required()); REQUIRE(view.noData()); REQUIRE(view.defaultValue()); @@ -425,10 +431,11 @@ TEST_CASE("VecN PropertyView") { classProperty.type = ClassProperty::Type::VEC4; classProperty.componentType = ClassProperty::ComponentType::FLOAT32; classProperty.required = true; - classProperty.noData = JsonValue::Array{0.0f, 0.0f, 0.0f, 0.0f}; - classProperty.defaultProperty = JsonValue::Array{1.0f, 2.0f, 3.0f, 4.0f}; + classProperty.noData = {0.0f, 0.0f, 0.0f, 0.0f}; + classProperty.defaultProperty = {1.0f, 2.0f, 3.0f, 4.0f}; PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::Valid); REQUIRE(view.required()); REQUIRE(!view.noData()); REQUIRE(!view.defaultValue()); @@ -438,28 +445,28 @@ TEST_CASE("VecN PropertyView") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::VEC2; classProperty.componentType = ClassProperty::ComponentType::INT8; - classProperty.defaultProperty = JsonValue::Array{128, 129}; + classProperty.defaultProperty = {128, 129}; PropertyView view(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidDefaultValue); - classProperty.noData = JsonValue::Array{-128, -129}; + classProperty.noData = {-128, -129}; view = PropertyView(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNoDataValue); - classProperty.min = JsonValue::Array{-200, 0}; + classProperty.min = {-200, 0}; view = PropertyView(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMin); - classProperty.max = JsonValue::Array{10, 5}; + classProperty.max = {10, 5}; view = PropertyView(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMax); - classProperty.scale = JsonValue::Array{1, 128}; + classProperty.scale = {1, 128}; view = PropertyView(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidScale); - classProperty.offset = JsonValue::Array{-1, -222}; + classProperty.offset = {-1, -222}; view = PropertyView(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidOffset); } @@ -468,7 +475,7 @@ TEST_CASE("VecN PropertyView") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::VEC2; classProperty.componentType = ClassProperty::ComponentType::INT8; - classProperty.defaultProperty = JsonValue::Array{1.0f, 20.9f}; + classProperty.defaultProperty = {1.0f, 20.9f}; PropertyView view(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidDefaultValue); @@ -477,11 +484,11 @@ TEST_CASE("VecN PropertyView") { view = PropertyView(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNoDataValue); - classProperty.min = JsonValue::Array{-10, -1}; + classProperty.min = {-10, -1}; view = PropertyView(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMin); - classProperty.max = JsonValue::Array{10, 20, 30, 40}; + classProperty.max = {10, 20, 30, 40}; view = PropertyView(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMax); @@ -584,19 +591,19 @@ TEST_CASE("MatN PropertyView") { classProperty.type = ClassProperty::Type::MAT3; classProperty.componentType = ClassProperty::ComponentType::INT32; // clang-format off - classProperty.offset = JsonValue::Array{ + classProperty.offset = { -1, 1, 2, 3, -1, 4, -5, -5, 0}; - classProperty.scale = JsonValue::Array{ + classProperty.scale = { 1, 1, 1, 2, 2, 3, 3, 4, 5}; - classProperty.max = JsonValue::Array{ + classProperty.max = { 20, 5, 20, 30, 22, 43, 37, 1, 8}; - classProperty.min = JsonValue::Array{ + classProperty.min = { -10, -2, -3, 0, 20, 4, 9, 4, 5}; @@ -642,10 +649,10 @@ TEST_CASE("MatN PropertyView") { classProperty.componentType = ClassProperty::ComponentType::FLOAT32; classProperty.required = false; // clang-format off - classProperty.noData = JsonValue::Array{ + classProperty.noData = { 0.0f, 0.0f, 0.0f, 0.0f}; - classProperty.defaultProperty = JsonValue::Array{ + classProperty.defaultProperty = { 1.0f, 2.0f, 3.0f, 4.0f}; // clang-format on @@ -674,10 +681,10 @@ TEST_CASE("MatN PropertyView") { classProperty.componentType = ClassProperty::ComponentType::FLOAT32; classProperty.required = true; // clang-format off - classProperty.noData = JsonValue::Array{ + classProperty.noData = { 0.0f, 0.0f, 0.0f, 0.0f}; - classProperty.defaultProperty = JsonValue::Array{ + classProperty.defaultProperty = { 1.0f, 2.0f, 3.0f, 4.0f}; // clang-format on @@ -695,7 +702,7 @@ TEST_CASE("MatN PropertyView") { classProperty.componentType = ClassProperty::ComponentType::INT8; // clang-format off - classProperty.defaultProperty = JsonValue::Array{ + classProperty.defaultProperty = { 999, 1, 2, 0}; // clang-format on @@ -704,7 +711,7 @@ TEST_CASE("MatN PropertyView") { REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidDefaultValue); // clang-format off - classProperty.noData = JsonValue::Array{ + classProperty.noData = { 0, 0, 1, -129}; // clang-format on @@ -712,7 +719,7 @@ TEST_CASE("MatN PropertyView") { REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNoDataValue); // clang-format off - classProperty.min = JsonValue::Array{ + classProperty.min = { -29, -40, -55, -43}; // clang-format on @@ -720,7 +727,7 @@ TEST_CASE("MatN PropertyView") { REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMin); // clang-format off - classProperty.max = JsonValue::Array{ + classProperty.max = { 10, 240, 1, 8}; // clang-format on @@ -728,13 +735,13 @@ TEST_CASE("MatN PropertyView") { REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMax); //clang-format off - classProperty.scale = JsonValue::Array{1, 197, 4, 6}; + classProperty.scale = {1, 197, 4, 6}; // clang-format on view = PropertyView(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidScale); // clang-format off - classProperty.offset = JsonValue::Array{ + classProperty.offset = { -1, 2, 129, -2}; // clang-format on @@ -748,29 +755,26 @@ TEST_CASE("MatN PropertyView") { classProperty.componentType = ClassProperty::ComponentType::INT8; // clang-format off - classProperty.defaultProperty = JsonValue::Array{ - JsonValue::Array{ - 999, 1, - 2, 0} - }; + classProperty.defaultProperty = + JsonValue::Array{JsonValue::Array{999, 1, 2, 0}}; // clang-format on PropertyView view(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidDefaultValue); // clang-format off - classProperty.noData = JsonValue::Array{ + classProperty.noData = { 0.45, 0.0, 1.0, -1.4}; // clang-format on view = PropertyView(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNoDataValue); - classProperty.min = JsonValue::Array{0, 0}; + classProperty.min = {0, 0}; view = PropertyView(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMin); - classProperty.max = JsonValue::Array{10, 20, 30, 40, 50}; + classProperty.max = {10, 20, 30, 40, 50}; view = PropertyView(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMax); @@ -826,6 +830,7 @@ TEST_CASE("String PropertyView") { classProperty.min = "min"; PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::Valid); REQUIRE(!view.normalized()); REQUIRE(!view.offset()); REQUIRE(!view.scale()); @@ -839,6 +844,7 @@ TEST_CASE("String PropertyView") { classProperty.count = 5; PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::Valid); REQUIRE(view.arrayCount() == 0); } @@ -850,6 +856,7 @@ TEST_CASE("String PropertyView") { classProperty.defaultProperty = "default"; PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::Valid); REQUIRE(!view.required()); REQUIRE(view.noData()); REQUIRE(view.defaultValue()); @@ -866,21 +873,23 @@ TEST_CASE("String PropertyView") { classProperty.defaultProperty = "default"; PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::Valid); REQUIRE(view.required()); REQUIRE(!view.noData()); REQUIRE(!view.defaultValue()); } - SECTION("Returns nullopt for invalid types") { + SECTION("Reports errors for invalid types") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::STRING; - classProperty.noData = JsonValue::Array{"null"}; classProperty.defaultProperty = true; PropertyView view(classProperty); - REQUIRE(!view.required()); - REQUIRE(!view.noData()); - REQUIRE(!view.defaultValue()); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidDefaultValue); + + classProperty.noData = JsonValue::Array{"null"}; + view = PropertyView(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNoDataValue); } } @@ -910,7 +919,7 @@ TEST_CASE("Boolean Array PropertyView") { SECTION("Reports array type mismatch") { ClassProperty classProperty; - classProperty.type = ClassProperty::Type::STRING; + classProperty.type = ClassProperty::Type::BOOLEAN; classProperty.array = false; PropertyView> view(classProperty); @@ -929,6 +938,7 @@ TEST_CASE("Boolean Array PropertyView") { classProperty.noData = JsonValue::Array{true}; PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::Valid); REQUIRE(!view.normalized()); REQUIRE(!view.offset()); REQUIRE(!view.scale()); @@ -944,6 +954,7 @@ TEST_CASE("Boolean Array PropertyView") { classProperty.count = 5; PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::Valid); REQUIRE(view.arrayCount() == 5); } @@ -955,10 +966,11 @@ TEST_CASE("Boolean Array PropertyView") { classProperty.defaultProperty = JsonValue::Array{false, true}; PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::Valid); REQUIRE(!view.required()); REQUIRE(view.defaultValue()); - const auto defaultValue = *view.defaultValue(); + PropertyArrayView defaultValue = *view.defaultValue(); REQUIRE(defaultValue.size() == 2); REQUIRE(!defaultValue[0]); REQUIRE(defaultValue[1]); @@ -972,6 +984,7 @@ TEST_CASE("Boolean Array PropertyView") { classProperty.defaultProperty = JsonValue::Array{false, true}; PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::Valid); REQUIRE(view.required()); REQUIRE(!view.defaultValue()); } @@ -983,8 +996,7 @@ TEST_CASE("Boolean Array PropertyView") { classProperty.defaultProperty = true; PropertyView> view(classProperty); - REQUIRE(!view.required()); - REQUIRE(!view.defaultValue()); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidDefaultValue); } } @@ -1052,6 +1064,7 @@ TEST_CASE("Scalar Array PropertyView") { classProperty.required = true; PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::Valid); REQUIRE(view.normalized()); REQUIRE(view.required()); @@ -1072,6 +1085,7 @@ TEST_CASE("Scalar Array PropertyView") { classProperty.count = 5; PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::Valid); REQUIRE(view.arrayCount() == *classProperty.count); } @@ -1086,101 +1100,726 @@ TEST_CASE("Scalar Array PropertyView") { classProperty.min = JsonValue::Array{-10, -1}; PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::Valid); REQUIRE(view.offset()); REQUIRE(view.scale()); REQUIRE(view.max()); REQUIRE(view.min()); - PropertyArrayView offset = *view.offset(); - REQUIRE(offset.size() == 2); - REQUIRE(offset[0] == 5); - REQUIRE(offset[1] == 10); + PropertyArrayView value = *view.offset(); + REQUIRE(value.size() == 2); + REQUIRE(value[0] == 5); + REQUIRE(value[1] == 10); - PropertyArrayView scale = *view.scale(); - REQUIRE(scale.size() == 2); - REQUIRE(scale[0] == 2); - REQUIRE(scale[1] == 1); + value = *view.scale(); + REQUIRE(value.size() == 2); + REQUIRE(value[0] == 2); + REQUIRE(value[1] == 1); - PropertyArrayView max = *view.max(); - REQUIRE(max.size() == 2); - REQUIRE(max[0] == 10); - REQUIRE(max[1] == 20); + value = *view.max(); + REQUIRE(value.size() == 2); + REQUIRE(value[0] == 10); + REQUIRE(value[1] == 20); - PropertyArrayView min = *view.min(); - REQUIRE(min.size() == 2); - REQUIRE(min[0] == -10); - REQUIRE(min[1] == -1); + value = *view.min(); + REQUIRE(value.size() == 2); + REQUIRE(value[0] == -10); + REQUIRE(value[1] == -1); } - SECTION("Returns nullopt for out-of-bounds values") { + SECTION("Constructs with noData and defaultProperty") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::SCALAR; + classProperty.componentType = ClassProperty::ComponentType::UINT8; + classProperty.array = true; + classProperty.required = false; + classProperty.noData = {0, 1}; + classProperty.defaultProperty = {2, 3}; + + PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::Valid); + REQUIRE(!view.required()); + REQUIRE(view.noData()); + REQUIRE(view.defaultValue()); + + PropertyArrayView value = view.noData().value(); + REQUIRE(value.size() == 2); + REQUIRE(value[0] == 0); + REQUIRE(value[1] == 1); + + value = *view.defaultValue(); + REQUIRE(value.size() == 2); + REQUIRE(value[0] == 2); + REQUIRE(value[1] == 3); + } + + SECTION("Ignores noData and defaultProperty when required is true") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::SCALAR; + classProperty.componentType = ClassProperty::ComponentType::UINT8; + classProperty.array = true; + classProperty.required = true; + classProperty.noData = {0, 1}; + classProperty.defaultProperty = {2, 3}; + + PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::Valid); + REQUIRE(view.required()); + REQUIRE(!view.noData()); + REQUIRE(!view.defaultValue()); + } + + SECTION("Reports errors for out-of-bounds values") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::SCALAR; + classProperty.componentType = ClassProperty::ComponentType::UINT8; + classProperty.array = true; + classProperty.defaultProperty = {256, 256}; + + PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidDefaultValue); + + classProperty.noData = {-1, 0}; + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNoDataValue); + + classProperty.min = {0, -1}; + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMin); + + classProperty.max = {256, 255}; + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMax); + + classProperty.scale = {20, 300}; + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidScale); + + classProperty.offset = {2, -100}; + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidOffset); + } + + SECTION("Reports errors for invalid types") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::SCALAR; + classProperty.componentType = ClassProperty::ComponentType::UINT8; + classProperty.array = true; + classProperty.defaultProperty = "[256, 256]"; + + PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidDefaultValue); + + classProperty.noData = 0; + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNoDataValue); + + classProperty.min = false; + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMin); + + classProperty.max = JsonValue::Array{10.4, 30.0}; + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMax); + + classProperty.scale = JsonValue::Array{JsonValue::Array{2.3, 3.04}}; + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidScale); + + classProperty.offset = "10"; + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidOffset); + } +} + +TEST_CASE("VecN Array PropertyView") { + SECTION("Constructs empty PropertyView") { + PropertyView> view; + REQUIRE(view.status() == PropertyViewStatus::ErrorNonexistentProperty); + REQUIRE(view.arrayCount() == 0); + REQUIRE(!view.normalized()); + REQUIRE(!view.offset()); + REQUIRE(!view.scale()); + REQUIRE(!view.max()); + REQUIRE(!view.min()); + REQUIRE(!view.required()); + REQUIRE(!view.noData()); + REQUIRE(!view.defaultValue()); + } + + SECTION("Reports type mismatch") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::VEC2; + classProperty.array = true; + + PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorTypeMismatch); + } + + SECTION("Reports component type mismatch") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::VEC3; classProperty.componentType = ClassProperty::ComponentType::INT8; - classProperty.offset = 20; - classProperty.scale = 255; - classProperty.max = 127; - classProperty.min = -1000; + classProperty.array = true; - PropertyView view(classProperty); + PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorComponentTypeMismatch); + } + + SECTION("Reports array type mismatch") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::VEC3; + classProperty.componentType = ClassProperty::ComponentType::FLOAT32; + classProperty.array = false; + + PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorArrayTypeMismatch); + } + + SECTION("Reports invalid normalization") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::VEC3; + classProperty.componentType = ClassProperty::ComponentType::FLOAT32; + classProperty.array = true; + classProperty.normalized = true; + + PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNormalization); + } + + SECTION("Constructs with non-optional properties") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::VEC3; + classProperty.componentType = ClassProperty::ComponentType::INT16; + classProperty.array = true; + classProperty.normalized = true; + classProperty.required = true; + + PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::Valid); + REQUIRE(view.normalized()); + REQUIRE(view.required()); + + REQUIRE(view.arrayCount() == 0); + REQUIRE(!view.offset()); + REQUIRE(!view.scale()); + REQUIRE(!view.max()); + REQUIRE(!view.min()); + REQUIRE(!view.noData()); + REQUIRE(!view.defaultValue()); + } + + SECTION("Constructs with count") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::VEC3; + classProperty.componentType = ClassProperty::ComponentType::INT16; + classProperty.array = true; + classProperty.count = 5; + + PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::Valid); + REQUIRE(view.arrayCount() == classProperty.count); + } + + SECTION("Constructs with offset, scale, max, and min") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::VEC3; + classProperty.componentType = ClassProperty::ComponentType::INT8; + classProperty.array = true; + classProperty.offset = {{-1, 1, 2}, {4, 4, 0}}; + classProperty.scale = {{2, 1, 3}, {8, 2, 3}}; + classProperty.max = {{14, 28, 12}, {10, 5, 6}}; + classProperty.min = {{-11, -12, -13}, {-2, -4, 6}}; + + PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::Valid); REQUIRE(view.offset()); + REQUIRE(view.scale()); REQUIRE(view.max()); + REQUIRE(view.min()); + + PropertyArrayView value = *view.offset(); + REQUIRE(value.size() == 2); + REQUIRE(value[0] == glm::i8vec3(-1, 1, 2)); + REQUIRE(value[1] == glm::i8vec3(4, 4, 0)); + + value = *view.scale(); + REQUIRE(value.size() == 2); + REQUIRE(value[0] == glm::i8vec3(2, 1, 3)); + REQUIRE(value[1] == glm::i8vec3(8, 2, 3)); + + value = *view.max(); + REQUIRE(value.size() == 2); + REQUIRE(value[0] == glm::i8vec3(14, 28, 12)); + REQUIRE(value[1] == glm::i8vec3(10, 5, 6)); + + value = *view.min(); + REQUIRE(value.size() == 2); + REQUIRE(value[0] == glm::i8vec3(-11, -12, -13)); + REQUIRE(value[1] == glm::i8vec3(-2, -4, 6)); + } + + SECTION("Constructs with noData and defaultProperty") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::VEC2; + classProperty.componentType = ClassProperty::ComponentType::FLOAT32; + classProperty.array = true; + classProperty.required = false; + classProperty.noData = {{0.0f, 0.0f}, {1.0f, 2.0f}}; + classProperty.defaultProperty = {{3.0f, 4.0f}, {5.0f, 6.0f}}; + + PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::Valid); + REQUIRE(!view.required()); + REQUIRE(view.noData()); + REQUIRE(view.defaultValue()); + + PropertyArrayView value = *view.noData(); + REQUIRE(value.size() == 2); + REQUIRE(value[0] == glm::vec2(0.0f, 0.0f)); + REQUIRE(value[1] == glm::vec2(1.0f, 2.0f)); + + value = *view.defaultValue(); + REQUIRE(value.size() == 2); + REQUIRE(value[0] == glm::vec2(3.0f, 4.0f)); + REQUIRE(value[1] == glm::vec2(5.0f, 6.0f)); + } + + SECTION("Ignores noData and defaultProperty when required is true") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::VEC2; + classProperty.componentType = ClassProperty::ComponentType::FLOAT32; + classProperty.array = true; + classProperty.required = true; + classProperty.noData = {{0.0f, 0.0f}, {1.0f, 2.0f}}; + classProperty.defaultProperty = {{3.0f, 4.0f}, {5.0f, 6.0f}}; + + PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::Valid); + REQUIRE(view.required()); + REQUIRE(!view.noData()); + REQUIRE(!view.defaultValue()); + } + + SECTION("Reports errors for out-of-bounds values") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::VEC2; + classProperty.componentType = ClassProperty::ComponentType::INT8; + classProperty.array = true; + classProperty.defaultProperty = {{128, 129}, {0, 2}}; + + PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidDefaultValue); + + classProperty.noData = {{0, 0}, {-128, -129}}; + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNoDataValue); + + classProperty.min = {{-2, -3}, {-200, 0}}; + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMin); + + classProperty.max = {{10, 5}, {808, 3}}; + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMax); + + classProperty.scale = {{1, 128}, {2, 2}}; + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidScale); + + classProperty.offset = {{0, 0}, {-1, -222}}; + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidOffset); + } + + SECTION("Reports errors for invalid types") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::VEC2; + classProperty.componentType = ClassProperty::ComponentType::INT8; + classProperty.array = true; + classProperty.defaultProperty = {1, 20}; + + PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidDefaultValue); + + classProperty.noData = JsonValue::Array{{2.0f, 5.4f}}; + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNoDataValue); - REQUIRE(*view.offset() == 20); - REQUIRE(*view.max() == 127); + classProperty.min = {{-10, -1, 4}, {0, 0, 0}}; + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMin); + + classProperty.max = {{10, 20, 30, 40}, {1, 2, 3, 4}}; + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMax); + + classProperty.scale = 2; + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidScale); + + classProperty.offset = "(1, 2)"; + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidOffset); + } +} +TEST_CASE("MatN Array PropertyView") { + SECTION("Constructs empty PropertyView") { + PropertyView> view; + REQUIRE(view.status() == PropertyViewStatus::ErrorNonexistentProperty); + REQUIRE(view.arrayCount() == 0); + REQUIRE(!view.normalized()); + REQUIRE(!view.offset()); REQUIRE(!view.scale()); + REQUIRE(!view.max()); REQUIRE(!view.min()); + REQUIRE(!view.required()); + REQUIRE(!view.noData()); + REQUIRE(!view.defaultValue()); } - SECTION("Returns nullopt for invalid types") { + SECTION("Reports type mismatch") { ClassProperty classProperty; - classProperty.type = ClassProperty::Type::SCALAR; + classProperty.type = ClassProperty::Type::MAT4; + classProperty.array = true; + + PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorTypeMismatch); + } + + SECTION("Reports component type mismatch") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::MAT2; classProperty.componentType = ClassProperty::ComponentType::INT8; - classProperty.offset = "10"; - classProperty.scale = false; - classProperty.max = 5.5; - classProperty.min = JsonValue::Array{1}; + classProperty.array = true; - PropertyView view(classProperty); + PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorComponentTypeMismatch); + } + + SECTION("Reports array type mismatch") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::MAT2; + classProperty.componentType = ClassProperty::ComponentType::FLOAT32; + classProperty.array = false; + + PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorArrayTypeMismatch); + } + + SECTION("Reports invalid normalization") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::MAT2; + classProperty.componentType = ClassProperty::ComponentType::FLOAT32; + classProperty.array = true; + classProperty.normalized = true; + + PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNormalization); + } + + SECTION("Constructs with non-optional properties") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::MAT3; + classProperty.componentType = ClassProperty::ComponentType::INT32; + classProperty.array = true; + classProperty.normalized = true; + classProperty.required = true; + + PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::Valid); + REQUIRE(view.normalized()); + REQUIRE(view.required()); + + REQUIRE(view.arrayCount() == 0); REQUIRE(!view.offset()); REQUIRE(!view.scale()); REQUIRE(!view.max()); REQUIRE(!view.min()); + REQUIRE(!view.noData()); + REQUIRE(!view.defaultValue()); + } + + SECTION("Constructs with count") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::MAT3; + classProperty.componentType = ClassProperty::ComponentType::INT32; + classProperty.array = true; + classProperty.count = 5; + + PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::Valid); + REQUIRE(view.arrayCount() == classProperty.count); + } + + SECTION("Constructs with offset, scale, max, and min") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::MAT2; + classProperty.componentType = ClassProperty::ComponentType::INT8; + classProperty.array = true; + // clang-format off + classProperty.offset = { + {-1, 1, + 0, 2}, + {2, 40, + 6, -8}, + }; + classProperty.scale = { + {1, 1, + 1, 0}, + {-2, 5, + 7, 1} + }; + classProperty.max = { + {2, 4, + 8, 0}, + {-7, 8, + 4, 4}, + }; + classProperty.min = { + {-1, -6, + -1, 2}, + {0, 1, + 2, 3}, + }; + // clang-format on + + PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::Valid); + REQUIRE(view.offset()); + REQUIRE(view.scale()); + REQUIRE(view.max()); + REQUIRE(view.min()); + + PropertyArrayView value = *view.offset(); + REQUIRE(value.size() == 2); + REQUIRE(value[0] == glm::i8mat2x2(-1, 1, 0, 2)); + REQUIRE(value[1] == glm::i8mat2x2(2, 40, 6, -8)); + + value = *view.scale(); + REQUIRE(value.size() == 2); + REQUIRE(value[0] == glm::i8mat2x2(1, 1, 1, 0)); + REQUIRE(value[1] == glm::i8mat2x2(-2, 5, 7, 1)); + + value = *view.max(); + REQUIRE(value.size() == 2); + REQUIRE(value[0] == glm::i8mat2x2(2, 4, 8, 0)); + REQUIRE(value[1] == glm::i8mat2x2(-7, 8, 4, 4)); + + value = *view.min(); + REQUIRE(value.size() == 2); + REQUIRE(value[0] == glm::i8mat2x2(-1, -6, -1, 2)); + REQUIRE(value[1] == glm::i8mat2x2(0, 1, 2, 3)); } SECTION("Constructs with noData and defaultProperty") { ClassProperty classProperty; - classProperty.type = ClassProperty::Type::SCALAR; - classProperty.componentType = ClassProperty::ComponentType::UINT8; + classProperty.type = ClassProperty::Type::MAT2; + classProperty.componentType = ClassProperty::ComponentType::INT8; + classProperty.array = true; classProperty.required = false; - classProperty.noData = 0; - classProperty.defaultProperty = 1; + // clang-format off + classProperty.noData = { + {0, 0, + 0, 0}, + {-1, -1, + -1, -1}, + }; + classProperty.defaultProperty = { + {1, 1, + 1, 1}, + {2, 2, + 2, 2}, + }; + // clang-format on - PropertyView view(classProperty); + PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::Valid); REQUIRE(!view.required()); REQUIRE(view.noData()); REQUIRE(view.defaultValue()); - REQUIRE(*view.noData() == 0); - REQUIRE(*view.defaultValue() == 1); + PropertyArrayView value = *view.noData(); + REQUIRE(value.size() == 2); + REQUIRE(value[0] == glm::i8mat2x2(0, 0, 0, 0)); + REQUIRE(value[1] == glm::i8mat2x2(-1, -1, -1, -1)); + + value = view.defaultValue().value(); + REQUIRE(value.size() == 2); + REQUIRE(value[0] == glm::i8mat2x2(1, 1, 1, 1)); + REQUIRE(value[1] == glm::i8mat2x2(2, 2, 2, 2)); } SECTION("Ignores noData and defaultProperty when required is true") { ClassProperty classProperty; - classProperty.type = ClassProperty::Type::SCALAR; - classProperty.componentType = ClassProperty::ComponentType::UINT8; + classProperty.type = ClassProperty::Type::MAT2; + classProperty.componentType = ClassProperty::ComponentType::FLOAT32; + classProperty.array = true; classProperty.required = true; - classProperty.noData = 0; - classProperty.defaultProperty = 1; + // clang-format off + classProperty.noData = { + {0, 0, + 0, 0}, + {-1, -1, + -1, -1}, + }; + classProperty.defaultProperty = { + {1, 1, + 1, 1}, + {2, 2, + 2, 2}, + }; + // clang-format on - PropertyView view(classProperty); + PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::Valid); REQUIRE(view.required()); REQUIRE(!view.noData()); REQUIRE(!view.defaultValue()); } + + SECTION("Reports errors for out-of-bounds values") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::MAT2; + classProperty.componentType = ClassProperty::ComponentType::INT8; + classProperty.array = true; + + // clang-format off + classProperty.defaultProperty = { + {1, 1, + 1, 290}, + {2, 2, + 2, 2}, + }; + // clang-format on + + PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidDefaultValue); + + // clang-format off + classProperty.noData = { + {0, 0, + 0, 0}, + {-140, -1, + -1, -1}, + }; + // clang-format on + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNoDataValue); + + // clang-format off + classProperty.min = { + {-129, 0, + 0, 0}, + {-1, -1, + -1, -1}, + }; + // clang-format on + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMin); + + // clang-format off + classProperty.max = { + {-128, 189, + 20, 2}, + {10, 12, + 8, 4}, + }; + // clang-format on + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMax); + + //clang-format off + classProperty.scale = { + {1, 2, 3, 4}, + {256, 80, 9, 52}, + }; + // clang-format on + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidScale); + + // clang-format off + classProperty.offset = { + {129, 0, + 0, 2}, + {4, 0, + 0, 8},}; + // clang-format on + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidOffset); + } + + SECTION("Reports errors for invalid types") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::MAT2; + classProperty.componentType = ClassProperty::ComponentType::INT8; + classProperty.array = true; + classProperty.defaultProperty = {4, 1, 2, 0}; + + PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidDefaultValue); + + // clang-format off + classProperty.noData = { + {0.45, 0.0, + 1.0, -1.4} + }; + // clang-format on + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNoDataValue); + + classProperty.min = {{0, 1, 2, 3, 4, 5, 6}}; + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMin); + + classProperty.max = {{0, 1, 2, 3}, false}; + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMax); + + classProperty.scale = 1; + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidScale); + + classProperty.offset = "[(1, 2, 3, 4)]"; + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidOffset); + } } TEST_CASE("String Array PropertyView") { + SECTION("Constructs empty PropertyView") { + PropertyView> view; + REQUIRE(view.status() == PropertyViewStatus::ErrorNonexistentProperty); + REQUIRE(view.arrayCount() == 0); + REQUIRE(!view.normalized()); + REQUIRE(!view.offset()); + REQUIRE(!view.scale()); + REQUIRE(!view.max()); + REQUIRE(!view.min()); + REQUIRE(!view.required()); + REQUIRE(!view.noData()); + REQUIRE(!view.defaultValue()); + } + + SECTION("Reports type mismatch") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::BOOLEAN; + classProperty.array = true; + + PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorTypeMismatch); + } + + SECTION("Reports array type mismatch") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::STRING; + classProperty.array = false; + + PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorArrayTypeMismatch); + } + SECTION("Ignores non-applicable properties") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::STRING; @@ -1192,6 +1831,7 @@ TEST_CASE("String Array PropertyView") { classProperty.min = {"min"}; PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::Valid); REQUIRE(!view.normalized()); REQUIRE(!view.offset()); REQUIRE(!view.scale()); @@ -1206,6 +1846,7 @@ TEST_CASE("String Array PropertyView") { classProperty.count = 5; PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::Valid); REQUIRE(view.arrayCount() == classProperty.count); } @@ -1214,10 +1855,11 @@ TEST_CASE("String Array PropertyView") { classProperty.type = ClassProperty::Type::STRING; classProperty.array = true; classProperty.required = false; - classProperty.noData = JsonValue::Array{"null", "0"}; - classProperty.defaultProperty = JsonValue::Array{"default1", "default2"}; + classProperty.noData = {"null", "0"}; + classProperty.defaultProperty = {"default1", "default2"}; PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::Valid); REQUIRE(!view.required()); REQUIRE(view.noData()); REQUIRE(view.defaultValue()); @@ -1236,25 +1878,29 @@ TEST_CASE("String Array PropertyView") { SECTION("Ignores noData and defaultProperty when required is true") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::STRING; + classProperty.array = true; classProperty.required = true; - classProperty.noData = JsonValue::Array{"null", "0"}; - classProperty.defaultProperty = JsonValue::Array{"default1", "default2"}; + classProperty.noData = {"null", "0"}; + classProperty.defaultProperty = {"default1", "default2"}; PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::Valid); REQUIRE(view.required()); REQUIRE(!view.noData()); REQUIRE(!view.defaultValue()); } - SECTION("Returns nullopt for invalid types") { + SECTION("Reports errors for invalid types") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::STRING; - classProperty.noData = JsonValue::Array{"null"}; + classProperty.array = true; classProperty.defaultProperty = true; - PropertyView view(classProperty); - REQUIRE(!view.required()); - REQUIRE(!view.noData()); - REQUIRE(!view.defaultValue()); + PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidDefaultValue); + + classProperty.noData = JsonValue::Array{"null", 0}; + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNoDataValue); } } From e4852cdf7bc8b2d0c2aabd45923e73196050b3b3 Mon Sep 17 00:00:00 2001 From: Joseph Kaile Date: Thu, 17 Aug 2023 15:47:31 -0400 Subject: [PATCH 171/421] fully decompress ktx texture if unable to transcode --- CesiumGltfReader/src/GltfReader.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CesiumGltfReader/src/GltfReader.cpp b/CesiumGltfReader/src/GltfReader.cpp index 9c336df95..756f02b2b 100644 --- a/CesiumGltfReader/src/GltfReader.cpp +++ b/CesiumGltfReader/src/GltfReader.cpp @@ -612,6 +612,12 @@ ImageReaderResult GltfReader::readImage( errorCode = ktxTexture2_TranscodeBasis(pTexture, transcodeTargetFormat_, 0); + if (errorCode != KTX_SUCCESS){ + transcodeTargetFormat_ = KTX_TTF_RGBA32; + transcodeTargetFormat = GpuCompressedPixelFormat::NONE; + errorCode = + ktxTexture2_TranscodeBasis(pTexture, transcodeTargetFormat_, 0); + } if (errorCode == KTX_SUCCESS) { image.compressedPixelFormat = transcodeTargetFormat; image.width = static_cast(pTexture->baseWidth); @@ -688,7 +694,7 @@ ImageReaderResult GltfReader::readImage( } result.image.reset(); - result.errors.emplace_back("KTX2 loading failed"); + result.errors.emplace_back("KTX2 loading failed with error: " + std::string(ktxErrorString(errorCode))); return result; } else if (isWebP(data)) { From 410312ff32e94062cf581fdad3070d54d52a31f9 Mon Sep 17 00:00:00 2001 From: Joseph Kaile Date: Thu, 17 Aug 2023 15:49:31 -0400 Subject: [PATCH 172/421] formatting --- CesiumGltfReader/src/GltfReader.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CesiumGltfReader/src/GltfReader.cpp b/CesiumGltfReader/src/GltfReader.cpp index 756f02b2b..f45f55aaa 100644 --- a/CesiumGltfReader/src/GltfReader.cpp +++ b/CesiumGltfReader/src/GltfReader.cpp @@ -612,7 +612,7 @@ ImageReaderResult GltfReader::readImage( errorCode = ktxTexture2_TranscodeBasis(pTexture, transcodeTargetFormat_, 0); - if (errorCode != KTX_SUCCESS){ + if (errorCode != KTX_SUCCESS) { transcodeTargetFormat_ = KTX_TTF_RGBA32; transcodeTargetFormat = GpuCompressedPixelFormat::NONE; errorCode = @@ -694,7 +694,9 @@ ImageReaderResult GltfReader::readImage( } result.image.reset(); - result.errors.emplace_back("KTX2 loading failed with error: " + std::string(ktxErrorString(errorCode))); + result.errors.emplace_back( + "KTX2 loading failed with error: " + + std::string(ktxErrorString(errorCode))); return result; } else if (isWebP(data)) { From 3388f49f32e1f6d7c6efaaada170140ac0a1c4a7 Mon Sep 17 00:00:00 2001 From: Joseph Kaile Date: Thu, 17 Aug 2023 15:54:52 -0400 Subject: [PATCH 173/421] update changes --- CHANGES.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index ea79c2349..11eb52f98 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,11 @@ # Change Log +### ? - ? + +##### Fixes :wrench: + +- When KTX transcoding fails, the image will now be fully decompressed instead of returning an error. + ### v0.26.0 - 2023-08-01 ##### Additions :tada: From f6f7a2c5e944a5f784d600b6a65c7434439d5a12 Mon Sep 17 00:00:00 2001 From: Joseph Kaile Date: Thu, 17 Aug 2023 18:41:48 -0400 Subject: [PATCH 174/421] use astc before pvrtc if available --- CesiumGltf/src/Ktx2TranscodeTargets.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CesiumGltf/src/Ktx2TranscodeTargets.cpp b/CesiumGltf/src/Ktx2TranscodeTargets.cpp index 07f24d9ef..72fc9ebca 100644 --- a/CesiumGltf/src/Ktx2TranscodeTargets.cpp +++ b/CesiumGltf/src/Ktx2TranscodeTargets.cpp @@ -22,6 +22,8 @@ Ktx2TranscodeTargets::Ktx2TranscodeTargets( this->ETC1S_RGBA = GpuCompressedPixelFormat::BC7_RGBA; } else if (supportedFormats.BC3_RGBA) { this->ETC1S_RGBA = GpuCompressedPixelFormat::BC3_RGBA; + } else if (supportedFormats.ASTC_4x4_RGBA) { + this->ETC1S_RGBA = GpuCompressedPixelFormat::ASTC_4x4_RGBA; } else if (supportedFormats.PVRTC1_4_RGBA) { this->ETC1S_RGBA = GpuCompressedPixelFormat::PVRTC1_4_RGBA; } @@ -33,6 +35,8 @@ Ktx2TranscodeTargets::Ktx2TranscodeTargets( this->ETC1S_RGB = GpuCompressedPixelFormat::BC7_RGBA; } else if (supportedFormats.BC1_RGB) { this->ETC1S_RGB = GpuCompressedPixelFormat::BC1_RGB; + } else if (supportedFormats.ASTC_4x4_RGBA) { + this->ETC1S_RGBA = GpuCompressedPixelFormat::ASTC_4x4_RGBA; } else if (supportedFormats.PVRTC1_4_RGB) { this->ETC1S_RGB = GpuCompressedPixelFormat::PVRTC1_4_RGB; } From e8fbd8d139c1e67b876103da2db460395b54eeb7 Mon Sep 17 00:00:00 2001 From: Joseph Kaile Date: Thu, 17 Aug 2023 18:50:35 -0400 Subject: [PATCH 175/421] update changes --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 11eb52f98..54f68081d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,7 +5,7 @@ ##### Fixes :wrench: - When KTX transcoding fails, the image will now be fully decompressed instead of returning an error. - +- Fixed texture format selection for KTX on mobile platforms. Specifically, it will select ASTC if it is available and fall back to PVRTC if it is not. ### v0.26.0 - 2023-08-01 ##### Additions :tada: From 31f0ed55c9b84c5ff5b6ce96e54cdfa28dd209dc Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Fri, 18 Aug 2023 21:56:48 +1000 Subject: [PATCH 176/421] Expose Schema on Tileset. --- .../Cesium3DTilesReader/SchemaReader.h | 3 + Cesium3DTilesReader/src/SchemaReader.cpp | 15 ++ Cesium3DTilesSelection/CMakeLists.txt | 2 + .../include/Cesium3DTilesSelection/Tileset.h | 2 + .../TilesetContentLoader.h | 11 ++ Cesium3DTilesSelection/src/Tileset.cpp | 5 + .../src/TilesetContentManager.cpp | 10 ++ .../src/TilesetContentManager.h | 6 + .../src/TilesetJsonLoader.cpp | 14 +- .../test/TestTilesetJsonLoader.cpp | 13 ++ .../test/TestTilesetSelectionAlgorithm.cpp | 49 ++++++ .../test/data/WithMetadata/README.md | 2 + .../test/data/WithMetadata/ll.b3dm | Bin 0 -> 9700 bytes .../test/data/WithMetadata/lr.b3dm | Bin 0 -> 9704 bytes .../test/data/WithMetadata/parent.b3dm | Bin 0 -> 9680 bytes .../test/data/WithMetadata/tileset.json | 144 ++++++++++++++++++ .../test/data/WithMetadata/ul.b3dm | Bin 0 -> 9684 bytes .../test/data/WithMetadata/ur.b3dm | Bin 0 -> 9688 bytes .../include/CesiumJsonReader/JsonReader.h | 32 ++++ CesiumJsonReader/src/JsonReader.cpp | 11 ++ 20 files changed, 318 insertions(+), 1 deletion(-) create mode 100644 Cesium3DTilesSelection/test/data/WithMetadata/README.md create mode 100644 Cesium3DTilesSelection/test/data/WithMetadata/ll.b3dm create mode 100644 Cesium3DTilesSelection/test/data/WithMetadata/lr.b3dm create mode 100644 Cesium3DTilesSelection/test/data/WithMetadata/parent.b3dm create mode 100644 Cesium3DTilesSelection/test/data/WithMetadata/tileset.json create mode 100644 Cesium3DTilesSelection/test/data/WithMetadata/ul.b3dm create mode 100644 Cesium3DTilesSelection/test/data/WithMetadata/ur.b3dm diff --git a/Cesium3DTilesReader/include/Cesium3DTilesReader/SchemaReader.h b/Cesium3DTilesReader/include/Cesium3DTilesReader/SchemaReader.h index 1dd433aa1..c02339414 100644 --- a/Cesium3DTilesReader/include/Cesium3DTilesReader/SchemaReader.h +++ b/Cesium3DTilesReader/include/Cesium3DTilesReader/SchemaReader.h @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -65,6 +66,8 @@ class CESIUM3DTILESREADER_API SchemaReader { */ SchemaReaderResult readSchema(const gsl::span& data) const; + SchemaReaderResult readSchema(const rapidjson::Value& value) const; + private: CesiumJsonReader::JsonReaderOptions _context; }; diff --git a/Cesium3DTilesReader/src/SchemaReader.cpp b/Cesium3DTilesReader/src/SchemaReader.cpp index 6d76f9f95..28d726032 100644 --- a/Cesium3DTilesReader/src/SchemaReader.cpp +++ b/Cesium3DTilesReader/src/SchemaReader.cpp @@ -45,4 +45,19 @@ SchemaReader::readSchema(const gsl::span& data) const { return result; } + +SchemaReaderResult +SchemaReader::readSchema(const rapidjson::Value& value) const { + CESIUM_TRACE("Cesium3DTilesReader::SchemaReader::readSchemaValue"); + const CesiumJsonReader::JsonReaderOptions& context = this->getOptions(); + SchemaJsonHandler schemaHandler(context); + CesiumJsonReader::ReadJsonResult jsonResult = + CesiumJsonReader::JsonReader::readJson(value, schemaHandler); + + return SchemaReaderResult{ + std::move(jsonResult.value), + std::move(jsonResult.errors), + std::move(jsonResult.warnings)}; +} + } // namespace Cesium3DTilesReader diff --git a/Cesium3DTilesSelection/CMakeLists.txt b/Cesium3DTilesSelection/CMakeLists.txt index 793a7da73..e0736a313 100644 --- a/Cesium3DTilesSelection/CMakeLists.txt +++ b/Cesium3DTilesSelection/CMakeLists.txt @@ -43,6 +43,8 @@ target_include_directories( target_link_libraries(Cesium3DTilesSelection PUBLIC + Cesium3DTiles + Cesium3DTilesReader CesiumAsync CesiumGeospatial CesiumGeometry diff --git a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/Tileset.h b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/Tileset.h index c0de9c35d..4f0ee26ef 100644 --- a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/Tileset.h +++ b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/Tileset.h @@ -98,6 +98,8 @@ class CESIUM3DTILESSELECTION_API Tileset final { */ const std::vector& getTilesetCredits() const noexcept; + const std::optional& getSchema() const noexcept; + /** * @brief Sets whether or not the tileset's credits should be shown on screen. * @param showCreditsOnScreen Whether the credits should be shown on screen. diff --git a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/TilesetContentLoader.h b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/TilesetContentLoader.h index 5b767456b..1bfba842f 100644 --- a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/TilesetContentLoader.h +++ b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/TilesetContentLoader.h @@ -7,6 +7,7 @@ #include "TileLoadResult.h" #include "TilesetOptions.h" +#include #include #include #include @@ -135,5 +136,15 @@ class CESIUM3DTILESSELECTION_API TilesetContentLoader { * @return The {@link TileChildrenResult} that stores the tile's children */ virtual TileChildrenResult createTileChildren(const Tile& tile) = 0; + + std::optional& getSchema() noexcept { + return this->_schema; + } + const std::optional& getSchema() const noexcept { + return this->_schema; + } + +private: + std::optional _schema; }; } // namespace Cesium3DTilesSelection diff --git a/Cesium3DTilesSelection/src/Tileset.cpp b/Cesium3DTilesSelection/src/Tileset.cpp index b005e2532..91563585e 100644 --- a/Cesium3DTilesSelection/src/Tileset.cpp +++ b/Cesium3DTilesSelection/src/Tileset.cpp @@ -101,6 +101,11 @@ const std::vector& Tileset::getTilesetCredits() const noexcept { return this->_pTilesetContentManager->getTilesetCredits(); } +const std::optional& +Tileset::getSchema() const noexcept { + return this->_pTilesetContentManager->getSchema(); +} + void Tileset::setShowCreditsOnScreen(bool showCreditsOnScreen) noexcept { this->_options.showCreditsOnScreen = showCreditsOnScreen; diff --git a/Cesium3DTilesSelection/src/TilesetContentManager.cpp b/Cesium3DTilesSelection/src/TilesetContentManager.cpp index bf7b7e0c2..eb0b74026 100644 --- a/Cesium3DTilesSelection/src/TilesetContentManager.cpp +++ b/Cesium3DTilesSelection/src/TilesetContentManager.cpp @@ -1169,6 +1169,16 @@ void TilesetContentManager::finishLoading( updateTileContent(tile, 0.0, tilesetOptions); } +const std::optional& +TilesetContentManager::getSchema() const noexcept { + static const std::optional noSchema; + if (this->_pLoader) { + return this->_pLoader->getSchema(); + } else { + return noSchema; + } +} + void TilesetContentManager::setTileContent( Tile& tile, TileLoadResult&& result, diff --git a/Cesium3DTilesSelection/src/TilesetContentManager.h b/Cesium3DTilesSelection/src/TilesetContentManager.h index 355fd82a2..e0548adad 100644 --- a/Cesium3DTilesSelection/src/TilesetContentManager.h +++ b/Cesium3DTilesSelection/src/TilesetContentManager.h @@ -16,6 +16,10 @@ #include +namespace Cesium3DTiles { +struct Schema; +} + namespace Cesium3DTilesSelection { class TilesetContentManager @@ -102,6 +106,8 @@ class TilesetContentManager // Transition the tile from the ContentLoaded to the Done state. void finishLoading(Tile& tile, const TilesetOptions& tilesetOptions); + const std::optional& getSchema() const noexcept; + private: static void setTileContent( Tile& tile, diff --git a/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp b/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp index f61d07172..bd388faf7 100644 --- a/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp +++ b/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp @@ -4,6 +4,7 @@ #include "ImplicitQuadtreeLoader.h" #include "logTileLoadResult.h" +#include #include #include #include @@ -594,6 +595,17 @@ TilesetContentLoaderResult parseTilesetJson( std::unique_ptr pRootTile; auto gltfUpAxis = obtainGltfUpAxis(tilesetJson, pLogger); auto pLoader = std::make_unique(baseUrl, gltfUpAxis); + + const auto schemaIt = tilesetJson.FindMember("schema"); + if (schemaIt != tilesetJson.MemberEnd()) { + Cesium3DTilesReader::SchemaReader reader{}; + Cesium3DTilesReader::SchemaReaderResult schemaResult = + reader.readSchema(schemaIt->value); + if (schemaResult.schema) { + pLoader->getSchema() = std::move(*schemaResult.schema); + } + } + const auto rootIt = tilesetJson.FindMember("root"); if (rootIt != tilesetJson.MemberEnd()) { const rapidjson::Value& rootJson = rootIt->value; @@ -697,7 +709,7 @@ TileLoadResult parseExternalTilesetInWorkerThread( TilesetJsonLoader::TilesetJsonLoader( const std::string& baseUrl, CesiumGeometry::Axis upAxis) - : _baseUrl{baseUrl}, _upAxis{upAxis} {} + : _baseUrl{baseUrl}, _upAxis{upAxis}, _children{} {} CesiumAsync::Future> TilesetJsonLoader::createLoader( diff --git a/Cesium3DTilesSelection/test/TestTilesetJsonLoader.cpp b/Cesium3DTilesSelection/test/TestTilesetJsonLoader.cpp index be993bf03..99cba3035 100644 --- a/Cesium3DTilesSelection/test/TestTilesetJsonLoader.cpp +++ b/Cesium3DTilesSelection/test/TestTilesetJsonLoader.cpp @@ -381,6 +381,19 @@ TEST_CASE("Test creating tileset json loader") { std::get(child.getTileID()) == CesiumGeometry::OctreeTileID(0, 0, 0, 0)); } + + SECTION("Tileset with metadata") { + auto loaderResult = + createLoader(testDataPath / "WithMetadata" / "tileset.json"); + + CHECK(!loaderResult.errors.hasErrors()); + REQUIRE(loaderResult.pLoader); + + const std::optional& schema = + loaderResult.pLoader->getSchema(); + REQUIRE(schema); + CHECK(schema->id == "foo"); + } } TEST_CASE("Test loading individual tile of tileset json") { diff --git a/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp b/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp index ed11faff2..0c50a2873 100644 --- a/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp +++ b/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp @@ -1070,6 +1070,55 @@ TEST_CASE("Can load example tileset.json from 3DTILES_bounding_volume_S2 " REQUIRE(pGreatGrandchild->getChildren().empty()); } +TEST_CASE("Makes metadata available once root tile is loaded") { + Cesium3DTilesSelection::registerAllTileContentTypes(); + + std::filesystem::path testDataPath = Cesium3DTilesSelection_TEST_DATA_DIR; + testDataPath = testDataPath / "WithMetadata"; + std::vector files{ + "tileset.json", + "parent.b3dm", + "ll.b3dm", + "lr.b3dm", + "ul.b3dm", + "ur.b3dm" + }; + + std::map> + mockCompletedRequests; + for (const auto& file : files) { + std::unique_ptr mockCompletedResponse = + std::make_unique( + static_cast(200), + "doesn't matter", + CesiumAsync::HttpHeaders{}, + readFile(testDataPath / file)); + mockCompletedRequests.insert( + {file, + std::make_shared( + "GET", + file, + CesiumAsync::HttpHeaders{}, + std::move(mockCompletedResponse))}); + } + + std::shared_ptr mockAssetAccessor = + std::make_shared(std::move(mockCompletedRequests)); + TilesetExternals tilesetExternals{ + mockAssetAccessor, + std::make_shared(), + AsyncSystem(std::make_shared()), + nullptr}; + + // create tileset and call updateView() to give it a chance to load + Tileset tileset(tilesetExternals, "tileset.json"); + initializeTileset(tileset); + + const std::optional& schema = tileset.getSchema(); + REQUIRE(schema); + CHECK(schema->id == "foo"); +} + namespace { void runUnconditionallyRefinedTestCase(const TilesetOptions& options) { diff --git a/Cesium3DTilesSelection/test/data/WithMetadata/README.md b/Cesium3DTilesSelection/test/data/WithMetadata/README.md new file mode 100644 index 000000000..a57c35345 --- /dev/null +++ b/Cesium3DTilesSelection/test/data/WithMetadata/README.md @@ -0,0 +1,2 @@ +The contents of this directory are from CesiumJS: +https://github.com/CesiumGS/cesium/tree/4f0d17c/Apps/SampleData/Cesium3DTiles/Tilesets/Tileset diff --git a/Cesium3DTilesSelection/test/data/WithMetadata/ll.b3dm b/Cesium3DTilesSelection/test/data/WithMetadata/ll.b3dm new file mode 100644 index 0000000000000000000000000000000000000000..df79fe32f39e95cb641763ba5c6153c75f085b34 GIT binary patch literal 9700 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 Bm16(^ literal 0 HcmV?d00001 diff --git a/Cesium3DTilesSelection/test/data/WithMetadata/parent.b3dm b/Cesium3DTilesSelection/test/data/WithMetadata/parent.b3dm new file mode 100644 index 0000000000000000000000000000000000000000..8cb958955234e1ecaac3837ecbf0162b8fd47bf8 GIT binary patch literal 9680 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 literal 0 HcmV?d00001 diff --git a/Cesium3DTilesSelection/test/data/WithMetadata/ur.b3dm b/Cesium3DTilesSelection/test/data/WithMetadata/ur.b3dm new file mode 100644 index 0000000000000000000000000000000000000000..9ae74c7b0534d9e965355d8307a483d7127d5b63 GIT binary patch literal 9688 zcmeHNX>=4-7QSFaVO$U#2apWVY=T8o`x2FORoEvam_%d|v6D1OW6~YFI}iv#1PM#P zQ4B^=c3glA$R_AWk}4cUbOcdA7#9>!7R3=A2A3Jq6ZSXpxmlB{W>BFGBU$96ogxOtING*Q-gMr2h^R%N#&39_oliXx0H>RjmZ zXCed3a174~jL1lg%qWb?Xk!^yVla^9kK||iFrp9V7C23oG>K+zZ@ z;5Ic0+pcp~PTD}by-&CRUcMY)Xlim&q05_*;R}a@p)hW)(3PG)e!MRo_G;;3jZ@U zJc{fZ5;nokT&_3b3;De{)p^TH4-NB0yg51kjFe!0R(1f74_6#MY~HXhA(#^k4fJL> z^&G?D8xB9n5Q+vyzr&|7w}vD-#9~doIeFRMVL>~uuH@nR?RN~oVrB;O({p^Om~DJ* zjz<|j#^uZPhr|AfzUX9po8rf5tz2I?+gBqs!=iqoHYY@Q@sY}D&)6M zEqt5Re)?43&>=f3wo-?yrhP-fU?j<=wUZvsz$e4`))L&w_j)V^h=&3WFtGe>WC-vQv-}KnHz8K0+*QNZFhiagjX$;js`RQEB zPqk4El>gwr`sy*?#8@+PW2}OTjryXG&zd$qVyx0z23|71cwncwrbSzQYUFJP7k+-7 zUXrxO)DFd3ySJ{?+YR3CU?HwlZ@1=tbJVDF=HgQ)JwG0P%)#}=P=2~D<)=JU1Jz7p zs0PYU=Td&EjcTC$on~bi{hB|ow`WJ{@99OxIeCYJR~(ycypVTLUz|K&|E%Xd#*eq` zcJT16V~z6RALvO7Tk5SslZ?z+ryRWR`iQY`TyrD4*-*Xx$k9g5ynpI8t}ll2({(97 z<)Ip=W*S2^P<}d>@>6Y81LfaavDfS_%#S}dvqWcxuQi|j?s|`nx3Ab>uJSMOWPiU} zzpM2DlRtZ}gZJ(F$lS5Bh5l7Cr@KeLXf~hP!ND)Bt1x??P0-Kx%hbnpIBp)-SO?b^ zL;2~tl%MiY4OBCYp&BSZolE(tHmZU0AL%jAID6=3t4C$n`l0wqqwGtUgUdXR8&z2? zt)A&=)|4Z~Mu(I(4(@0a8^!luZB1x3+tOB+8O)*>2fs6>)L7HufO%kBz}oix4CC%E zKXY(>F_fRKOZh1e)j&1V7^;Et)47zNYNHw`|FxqPYiqNuo?RJd^vL*j*6nk?aj;kt zYkiwPR6n}!TH~#0w_58q^>%PxQM@%dW0byrAA}Tl{OZWj_Q~1;5_dO> zOha2;Z0$OIje|eTnrn?H=w|dPf7n@wRpKV-3WpGRAS+B3_l*u$LFbLvtDxA<1DCVaEN)cvtlv)c@^JZ71LcP$%gwR07i$2WE~ z2No;Vy>l`heB;xa_4k4V)9Q7iYDq<+wWQ6R4z4eT^3!!GKjonssAd{NHBf#!m-16> zR0HLIWRKBr!~WYUU)|g-zLB^BD65|`h%=t<2y$h!Gv;VT#NL-2W6*ch<;`Q6Q zR&<)%DSqDf3Gt1@{V=`~<0~<~LEOlk&>S|H6PmY1=7i?3!JHsxZA>F`LUY()PH5i# zXZbn#M%UV0T^v{M{p1^~YV&Xr{C|_5OV8IYK6kY}b16TU@>Bhe`^7xaGnwLO|Mb0T zu6>?3=WqkqIe#$5KCc?Ym(HJyzw0mM=Td&6&s}4GCyYLKc6?L!kG)3I{!EVtXvzyb zsJ{s>4{|r-7kO?g!ze^XvZkiRJ}&;gqAYK{C&c{PLPxPj(y8MJ`QAqK8M z9|KoHOK63*75oOSfve#vxEB3Y&<3u9*3cGxYq%bM3pb#>0e%PVpgnXz+W~Hb-$N{P zgih!?LTBg#F0?M_3O%4Zbc3GgyFqX01vkOX=x+iB`hXj)8~OqZ9Pns)5I_bAL{QL+ zpn?W*Xyf1(=m)pKtq_m?RxmL29!Nm%f&MT62BIAZgJ3WWfuU%J!XF?BhCw1Eqfdn4 za66=+O@UMx0e3(e+=)I7M#Cr=31iTY#Qz5x3wNWv8^*ys;DvOw>5u{A!3UX;g+3Fq z!4DJAPJnwM0J)F@LG(Egg8Lv3!szoL0{Jiz?L?Ra1uz+=pq&DRFb#@eD%_8LDolq5 zUE(LV_DU@pvo67+LmJ}iKT&^`ozgooh~ScrBZJPMD&A}ED2^ri4P zJOPW*E`}#zDJ+4fU>W+SU?r@8<**9ttT1U`mO(0&3(;Zryc$KW&c$KVv4gcEQY{R#LSzJN1m&%l@PA2Yc|XaE2J literal 0 HcmV?d00001 diff --git a/CesiumJsonReader/include/CesiumJsonReader/JsonReader.h b/CesiumJsonReader/include/CesiumJsonReader/JsonReader.h index 3680acaa0..3b6dac31e 100644 --- a/CesiumJsonReader/include/CesiumJsonReader/JsonReader.h +++ b/CesiumJsonReader/include/CesiumJsonReader/JsonReader.h @@ -4,6 +4,7 @@ #include "Library.h" #include +#include #include #include @@ -72,6 +73,30 @@ class CESIUMJSONREADER_API JsonReader { return result; } + template + static ReadJsonResult + readJson(const rapidjson::Value& jsonValue, T& handler) { + ReadJsonResult result; + + result.value.emplace(); + + FinalJsonHandler finalHandler(result.warnings); + handler.reset(&finalHandler, &result.value.value()); + + JsonReader::internalRead( + jsonValue, + handler, + finalHandler, + result.errors, + result.warnings); + + if (!result.errors.empty()) { + result.value.reset(); + } + + return result; + } + private: class FinalJsonHandler : public JsonHandler { public: @@ -92,6 +117,13 @@ class CESIUMJSONREADER_API JsonReader { FinalJsonHandler& finalHandler, std::vector& errors, std::vector& warnings); + + static void internalRead( + const rapidjson::Value& jsonValue, + IJsonHandler& handler, + FinalJsonHandler& finalHandler, + std::vector& errors, + std::vector& warnings); }; } // namespace CesiumJsonReader diff --git a/CesiumJsonReader/src/JsonReader.cpp b/CesiumJsonReader/src/JsonReader.cpp index 2049d940c..9546eb385 100644 --- a/CesiumJsonReader/src/JsonReader.cpp +++ b/CesiumJsonReader/src/JsonReader.cpp @@ -154,4 +154,15 @@ void JsonReader::FinalJsonHandler::setInputStream( errors.emplace_back(std::move(s)); } } + +void CesiumJsonReader::JsonReader::internalRead( + const rapidjson::Value& jsonValue, + IJsonHandler& handler, + FinalJsonHandler&, + std::vector&, + std::vector&) { + Dispatcher dispatcher{&handler}; + jsonValue.Accept(dispatcher); +} + } // namespace CesiumJsonReader From 0dbf93091c8bb9361a925c3e02f1389bd94478fb Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Fri, 18 Aug 2023 23:08:58 +1000 Subject: [PATCH 177/421] Start reading metadata and groups properties. --- .../include/Cesium3DTilesReader/Readers.h | 30 +++++++++++++ Cesium3DTilesReader/src/Readers.cpp | 43 +++++++++++++++++++ .../src/TilesetJsonLoader.cpp | 28 +++++++++--- .../CesiumJsonReader/ArrayJsonHandler.h | 2 + 4 files changed, 97 insertions(+), 6 deletions(-) create mode 100644 Cesium3DTilesReader/include/Cesium3DTilesReader/Readers.h create mode 100644 Cesium3DTilesReader/src/Readers.cpp diff --git a/Cesium3DTilesReader/include/Cesium3DTilesReader/Readers.h b/Cesium3DTilesReader/include/Cesium3DTilesReader/Readers.h new file mode 100644 index 000000000..ee09263eb --- /dev/null +++ b/Cesium3DTilesReader/include/Cesium3DTilesReader/Readers.h @@ -0,0 +1,30 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace Cesium3DTilesReader { + +CesiumJsonReader::ReadJsonResult readSchema( + const rapidjson::Value& value, + const CesiumJsonReader::JsonReaderOptions& options = {}); + +CesiumJsonReader::ReadJsonResult +readMetadataEntity( + const rapidjson::Value& value, + const CesiumJsonReader::JsonReaderOptions& options = {}); + +CesiumJsonReader::ReadJsonResult +readGroupMetadata( + const rapidjson::Value& value, + const CesiumJsonReader::JsonReaderOptions& options = {}); + +CesiumJsonReader::ReadJsonResult> +readGroupMetadataArray( + const rapidjson::Value& value, + const CesiumJsonReader::JsonReaderOptions& options = {}); + +} // namespace Cesium3DTilesReader diff --git a/Cesium3DTilesReader/src/Readers.cpp b/Cesium3DTilesReader/src/Readers.cpp new file mode 100644 index 000000000..4c0fcb08f --- /dev/null +++ b/Cesium3DTilesReader/src/Readers.cpp @@ -0,0 +1,43 @@ +#include "GroupMetadataJsonHandler.h" +#include "MetadataEntityJsonHandler.h" +#include "SchemaJsonHandler.h" + +#include +#include + +namespace Cesium3DTilesReader { + +CesiumJsonReader::ReadJsonResult readSchema( + const rapidjson::Value& value, + const CesiumJsonReader::JsonReaderOptions& options) { + SchemaJsonHandler handler(options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult +readMetadataEntity( + const rapidjson::Value& value, + const CesiumJsonReader::JsonReaderOptions& options) { + MetadataEntityJsonHandler handler(options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult +readGroupMetadata( + const rapidjson::Value& value, + const CesiumJsonReader::JsonReaderOptions& options) { + GroupMetadataJsonHandler handler(options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +readGroupMetadataArray( + const rapidjson::Value& value, + const CesiumJsonReader::JsonReaderOptions& options) { + CesiumJsonReader:: + ArrayJsonHandler + handler(options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +} // namespace Cesium3DTilesReader diff --git a/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp b/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp index bd388faf7..028eec6e1 100644 --- a/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp +++ b/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp @@ -4,7 +4,7 @@ #include "ImplicitQuadtreeLoader.h" #include "logTileLoadResult.h" -#include +#include #include #include #include @@ -598,11 +598,27 @@ TilesetContentLoaderResult parseTilesetJson( const auto schemaIt = tilesetJson.FindMember("schema"); if (schemaIt != tilesetJson.MemberEnd()) { - Cesium3DTilesReader::SchemaReader reader{}; - Cesium3DTilesReader::SchemaReaderResult schemaResult = - reader.readSchema(schemaIt->value); - if (schemaResult.schema) { - pLoader->getSchema() = std::move(*schemaResult.schema); + auto schemaResult = Cesium3DTilesReader::readSchema(schemaIt->value); + if (schemaResult.value) { + pLoader->getSchema() = std::move(*schemaResult.value); + } + } + + const auto metadataIt = tilesetJson.FindMember("metadata"); + if (metadataIt != tilesetJson.MemberEnd()) { + auto metadataResult = + Cesium3DTilesReader::readMetadataEntity(metadataIt->value); + if (metadataResult.value) { + // pLoader->getSchema() = std::move(*schemaResult.value); + } + } + + const auto groupsIt = tilesetJson.FindMember("groups"); + if (groupsIt != tilesetJson.MemberEnd()) { + auto groupsResult = + Cesium3DTilesReader::readGroupMetadataArray(groupsIt->value); + if (groupsResult.value) { + // pLoader->getSchema() = std::move(*schemaResult.value); } } diff --git a/CesiumJsonReader/include/CesiumJsonReader/ArrayJsonHandler.h b/CesiumJsonReader/include/CesiumJsonReader/ArrayJsonHandler.h index d2902cc0b..2ab7fdb45 100644 --- a/CesiumJsonReader/include/CesiumJsonReader/ArrayJsonHandler.h +++ b/CesiumJsonReader/include/CesiumJsonReader/ArrayJsonHandler.h @@ -15,6 +15,8 @@ namespace CesiumJsonReader { template class CESIUMJSONREADER_API ArrayJsonHandler : public JsonHandler { public: + using ValueType = std::vector; + template ArrayJsonHandler(Ts&&... args) noexcept : JsonHandler(), From 078f05d85585a5b158ba2a9ac608676c58d8046d Mon Sep 17 00:00:00 2001 From: Joseph Kaile Date: Fri, 18 Aug 2023 10:57:39 -0400 Subject: [PATCH 178/421] Revert "update changes" This reverts commit e8fbd8d139c1e67b876103da2db460395b54eeb7. --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 54f68081d..11eb52f98 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,7 +5,7 @@ ##### Fixes :wrench: - When KTX transcoding fails, the image will now be fully decompressed instead of returning an error. -- Fixed texture format selection for KTX on mobile platforms. Specifically, it will select ASTC if it is available and fall back to PVRTC if it is not. + ### v0.26.0 - 2023-08-01 ##### Additions :tada: From 9713d37e8c1854c0ef80abe4938a3301f4ece8ff Mon Sep 17 00:00:00 2001 From: Joseph Kaile Date: Fri, 18 Aug 2023 10:57:51 -0400 Subject: [PATCH 179/421] Revert "use astc before pvrtc if available" This reverts commit f6f7a2c5e944a5f784d600b6a65c7434439d5a12. --- CesiumGltf/src/Ktx2TranscodeTargets.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/CesiumGltf/src/Ktx2TranscodeTargets.cpp b/CesiumGltf/src/Ktx2TranscodeTargets.cpp index 72fc9ebca..07f24d9ef 100644 --- a/CesiumGltf/src/Ktx2TranscodeTargets.cpp +++ b/CesiumGltf/src/Ktx2TranscodeTargets.cpp @@ -22,8 +22,6 @@ Ktx2TranscodeTargets::Ktx2TranscodeTargets( this->ETC1S_RGBA = GpuCompressedPixelFormat::BC7_RGBA; } else if (supportedFormats.BC3_RGBA) { this->ETC1S_RGBA = GpuCompressedPixelFormat::BC3_RGBA; - } else if (supportedFormats.ASTC_4x4_RGBA) { - this->ETC1S_RGBA = GpuCompressedPixelFormat::ASTC_4x4_RGBA; } else if (supportedFormats.PVRTC1_4_RGBA) { this->ETC1S_RGBA = GpuCompressedPixelFormat::PVRTC1_4_RGBA; } @@ -35,8 +33,6 @@ Ktx2TranscodeTargets::Ktx2TranscodeTargets( this->ETC1S_RGB = GpuCompressedPixelFormat::BC7_RGBA; } else if (supportedFormats.BC1_RGB) { this->ETC1S_RGB = GpuCompressedPixelFormat::BC1_RGB; - } else if (supportedFormats.ASTC_4x4_RGBA) { - this->ETC1S_RGBA = GpuCompressedPixelFormat::ASTC_4x4_RGBA; } else if (supportedFormats.PVRTC1_4_RGB) { this->ETC1S_RGB = GpuCompressedPixelFormat::PVRTC1_4_RGB; } From 5da1ca3e789351b2c785209a693866f6693be7ac Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Fri, 18 Aug 2023 14:19:40 -0400 Subject: [PATCH 180/421] Add templating for normalization --- .../include/CesiumGltf/PropertyArrayView.h | 76 +++++++ .../CesiumGltf/PropertyTablePropertyView.h | 206 ++++++++++++++++-- .../include/CesiumGltf/PropertyTypeTraits.h | 12 + CesiumGltf/include/CesiumGltf/PropertyView.h | 164 +++++++------- CesiumGltf/test/TestPropertyView.cpp | 11 + 5 files changed, 378 insertions(+), 91 deletions(-) diff --git a/CesiumGltf/include/CesiumGltf/PropertyArrayView.h b/CesiumGltf/include/CesiumGltf/PropertyArrayView.h index 987908925..0fa14eb77 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyArrayView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyArrayView.h @@ -56,6 +56,61 @@ template class PropertyArrayView { _values); } + bool operator==(const PropertyArrayView& other) const noexcept { + for (int64_t i = 0; i < size(); i++) { + if ((*this)[i] != other[i]) { + return false; + } + } + + return true; + } + + PropertyArrayView + operator*(const PropertyArrayView& other) { + int64_t clampedSize = std::min(this->size(), other.size()); + std::vector result(static_cast(this->size())); + if constexpr (IsMetadataMatN::value) { + // Do component-wise multiplication instead of actual matrix + // multiplication. + ElementType matN; + constexpr glm::length_t N = ElementType::length(); + for (int64_t i = 0; i < clampedSize; i++) { + for (glm::length_t j = 0; j < N; j++) { + matN[j] = (*this)[i][j] * other[i][j]; + } + result[i] = matN; + } + } else { + for (int64_t i = 0; i < clampedSize; i++) { + result[i] = (*this)[i] * other[i]; + } + } + + // Copy anything that didn't have a component to multiply against. + for (int64_t i = clampedSize(); i < this->size(); i++) { + result[i] = (*this)[i]; + } + + return PropertyArrayView(std::move(result)); + } + + PropertyArrayView + operator+(const PropertyArrayView& other) { + int64_t clampedSize = std::min(this->size(), other.size()); + std::vector result(static_cast(this->size())); + for (int64_t i = 0; i < clampedSize; i++) { + result[i] = (*this)[i] + other[i]; + } + + // Copy anything that didn't have a component to multiply against. + for (int64_t i = clampedSize(); i < this->size(); i++) { + result[i] = (*this)[i]; + } + + return PropertyArrayView(std::move(result)); + } + private: using ArrayType = std::variant, std::vector>; @@ -93,6 +148,16 @@ template <> class PropertyArrayView { int64_t size() const noexcept { return _size; } + bool operator==(const PropertyArrayView& other) const noexcept { + for (int64_t i = 0; i < size(); i++) { + if ((*this)[i] != other[i]) { + return false; + } + } + + return true; + } + private: gsl::span _values; int64_t _bitOffset = 0; @@ -142,6 +207,17 @@ template <> class PropertyArrayView { int64_t size() const noexcept { return _size; } + bool + operator==(const PropertyArrayView& other) const noexcept { + for (int64_t i = 0; i < size(); i++) { + if ((*this)[i] != other[i]) { + return false; + } + } + + return true; + } + private: gsl::span _values; gsl::span _stringOffsets; diff --git a/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h index 065fb0162..3bceaa7d4 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h @@ -1,6 +1,7 @@ #pragma once #include "CesiumGltf/PropertyArrayView.h" +//#include "CesiumGltf/PropertyConversions.h" #include "CesiumGltf/PropertyTypeTraits.h" #include "CesiumGltf/PropertyView.h" @@ -185,11 +186,12 @@ class PropertyTablePropertyView : public PropertyView { } /** - * @brief Construct a valid instance pointing to non-array data specified by - * a {@link PropertyTableProperty}. - * @param values The raw buffer specified by {@link PropertyTableProperty::values} + * @brief Construct an instance pointing to non-array data specified by a {@link PropertyTableProperty}. + * + * @param property The {@link PropertyTableProperty} + * @param classProperty The {@link ClassProperty} this property conforms to. * @param size The number of elements in the property table specified by {@link PropertyTable::count} - * @param normalized Whether this property has a normalized integer type. + * @param value The raw buffer specified by {@link PropertyTableProperty::values} */ PropertyTablePropertyView( const PropertyTableProperty& property, @@ -198,27 +200,32 @@ class PropertyTablePropertyView : public PropertyView { gsl::span values) noexcept : PropertyView(classProperty, property), _status{PropertyTablePropertyViewStatus::Valid}, - _values{values}, - _size{size}, + _values{}, + _size{}, _arrayOffsets{}, _arrayOffsetType{PropertyComponentType::None}, _arrayOffsetTypeSize{0}, _stringOffsets{}, _stringOffsetType{PropertyComponentType::None}, - _stringOffsetTypeSize{0} {} + _stringOffsetTypeSize{0} { + if (PropertyView::status() == PropertyTablePropertyViewStatus::Valid) { + _values = values; + _size = size; + } + } /** - * @brief Construct a valid instance pointing to the data specified by - * a {@link PropertyTableProperty}. + * @brief Construct an instance pointing to the data specified by a {@link PropertyTableProperty}. + * + * + * @param property The {@link PropertyTableProperty} + * @param classProperty The {@link ClassProperty} this property conforms to. + * @param size The number of elements in the property table specified by {@link PropertyTable::count} * @param values The raw buffer specified by {@link PropertyTableProperty::values} * @param arrayOffsets The raw buffer specified by {@link PropertyTableProperty::arrayOffsets} * @param stringOffsets The raw buffer specified by {@link PropertyTableProperty::stringOffsets} * @param offsetType The offset type of arrayOffsets specified by {@link PropertyTableProperty::arrayOffsetType} * @param offsetType The offset type of stringOffsets specified by {@link PropertyTableProperty::stringOffsetType} - * @param arrayCount The number of elements in each array value specified by {@link ClassProperty::count} - * @param size The number of elements in the property table specified by {@link PropertyTable::count} - * @param normalized Whether this property has a normalized integer - * type. */ PropertyTablePropertyView( const PropertyTableProperty& property, @@ -238,7 +245,12 @@ class PropertyTablePropertyView : public PropertyView { _stringOffsets{stringOffsets}, _stringOffsetType{stringOffsetType}, _stringOffsetTypeSize{getOffsetTypeSize(stringOffsetType)}, - _size{size} {} + _size{size} { + if (PropertyView::status() == PropertyTablePropertyViewStatus::Valid) { + _values = values; + _size = size; + } + } /** * @copydoc IPropertyView::status @@ -246,7 +258,12 @@ class PropertyTablePropertyView : public PropertyView { virtual int32_t status() const noexcept override { return _status; } /** - * @brief Get the value of an element of the {@link PropertyTable}. + * @brief Get the raw value of an element of the {@link PropertyTable}, + * without offset, scale, or normalization applied. + * + * If this property has a "no data" value defined, the raw value will still be + * returned, even if it equals the "no data" value. + * * @param index The element index * @return The value of the element */ @@ -286,6 +303,48 @@ class PropertyTablePropertyView : public PropertyView { } } + ///** + // * @brief Gets the value of an element in the {@link PropertyTable} as an instance + // * of T. If T is not the same as the ElementType of the property, then + // * this attempts to convert it to the desired type. If such a conversion is + // * not possible, this returns std::nullopt. + // * + // * If this property contains a "no data" sentinel value, then std::nullopt is + // * returned for any raw values that equal the sentinel value. If a default + // * value is supplied, then it will be returned instead. + // * + // * If this property is affected by offset, scale, and / or normalization, + // * the value will be transformed before conversion like so: + // * + // * transformedValue = offset + scale * normalize(value) + // * + // * The transformed value will then attempt to be converted to the desired type + // * T. + // * + // * @param index The element index + // * @return The value of the element as T, or std::nullopt if conversion fails. + // */ + //template + //std::optional getAs(int64_t index) const noexcept { + // assert( + // _status == PropertyTablePropertyViewStatus::Valid && + // "Check the status() first to make sure view is valid"); + // assert( + // size() > 0 && + // "Check the size() of the view to make sure it's not empty"); + // assert(index >= 0 && "index must be non-negative"); + // assert(index < size() && "index must be less than size"); + // ElementType result = getRaw(index); + + // if (noData() && result == *noData()) { + // return defaultValue() ? *defaultValue() : std::nullopt; + // } + + // // apply transform + + // return PropertyConversions::convert(result); + //} + /** * @brief Get the number of elements in this * PropertyTablePropertyView. If the view is valid, this returns @@ -428,4 +487,121 @@ class PropertyTablePropertyView : public PropertyView { PropertyComponentType _stringOffsetType; int64_t _stringOffsetTypeSize; }; + +/** + * @brief A view on the boolean data of the {@link PropertyTableProperty} that is created + * by a {@link PropertyTableView}. + * + * It provides utility to retrieve the actual data stored in the + * {@link PropertyTableProperty::values} like an array of elements. Data of each + * instance can be accessed through the {@link PropertyTablePropertyView::get} method. + */ +template <> class PropertyTablePropertyView : public PropertyView { +public: + /** + * @brief Constructs an invalid instance for a non-existent property. + */ + PropertyTablePropertyView() + : PropertyView(), + _status{PropertyTablePropertyViewStatus::ErrorNonexistentProperty}, + _values{}, + _size{0} {} + + /** + * @brief Constructs an invalid instance for an erroneous property. + * + * @param status The value of {@link PropertyTablePropertyViewStatus} indicating the error with the property. + */ + PropertyTablePropertyView(PropertyViewStatusType status) + : PropertyView(), _status{status}, _values{}, _size{0} { + assert( + _status != PropertyTablePropertyViewStatus::Valid && + "An empty property view should not be constructed with a valid " + "status"); + } + + /** + * @brief Construct an instance pointing to the boolean data specified by a {@link PropertyTableProperty}. + * + * + * @param property The {@link PropertyTableProperty} + * @param classProperty The {@link ClassProperty} this property conforms to. + * @param values The raw buffer specified by {@link PropertyTableProperty::values} + * @param size The number of elements in the property table specified by {@link PropertyTable::count} + */ + PropertyTablePropertyView( + const PropertyTableProperty& property, + const ClassProperty& classProperty, + int64_t size, + gsl::span values) noexcept + : PropertyView(classProperty, property), + _status{PropertyView::status()}, + _values{values}, + _size{size} {} + + /** + * @copydoc IPropertyView::status + */ + virtual int32_t status() const noexcept override { return _status; } + + /** + * @brief Get the value of an element of the {@link PropertyTable}. + * + * @param index The element index + * @return The value of the element + */ + bool get(int64_t index) const noexcept { + assert( + _status == PropertyTablePropertyViewStatus::Valid && + "Check the status() first to make sure view is valid"); + assert( + size() > 0 && + "Check the size() of the view to make sure it's not empty"); + assert(index >= 0 && "index must be non-negative"); + assert(index < size() && "index must be less than size"); + + const int64_t byteIndex = index / 8; + const int64_t bitIndex = index % 8; + const int bitValue = static_cast(_values[byteIndex] >> bitIndex) & 1; + return bitValue == 1; + } + + /** + * @brief Gets the value of an element in the {@link PropertyTable} as an instance + * of T. If T is not the same as the ElementType of the property, then + * this attempts to convert it to the desired type. If such a conversion is + * not possible, this returns std::nullopt. + * + * @param index The element index + * @return The value of the element as T, or std::nullopt if conversion fails. + */ + template std::optional getAs(int64_t index) const noexcept { + assert( + _status == PropertyTablePropertyViewStatus::Valid && + "Check the status() first to make sure view is valid"); + assert( + size() > 0 && + "Check the size() of the view to make sure it's not empty"); + assert(index >= 0 && "index must be non-negative"); + assert(index < size() && "index must be less than size"); + + return PropertyConversions::convert(get(index)); + } + + /** + * @brief Get the number of elements in this + * PropertyTablePropertyView. If the view is valid, this returns + * {@link PropertyTable::count}. Otherwise, this returns 0. + * + * @return The number of elements in this PropertyTablePropertyView. + */ + int64_t size() const noexcept { + return status() == PropertyTablePropertyViewStatus::Valid ? _size : 0; + } + +private: + PropertyViewStatusType _status; + gsl::span _values; + int64_t _size; +}; } // namespace CesiumGltf diff --git a/CesiumGltf/include/CesiumGltf/PropertyTypeTraits.h b/CesiumGltf/include/CesiumGltf/PropertyTypeTraits.h index 999947f54..2c6aff53d 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTypeTraits.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTypeTraits.h @@ -264,4 +264,16 @@ template <> struct TypeToPropertyType { PropertyComponentType::None; static constexpr PropertyType value = PropertyType::String; }; + +template struct TypeToNormalizedType; + +template <> +struct TypeToNormalizedType { + using type = double; +}; + +template +struct TypeToNormalizedType> { + using type = glm::vec; +}; } // namespace CesiumGltf diff --git a/CesiumGltf/include/CesiumGltf/PropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyView.h index 56fa1a6ac..047fd9079 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyView.h @@ -2,6 +2,7 @@ #include "CesiumGltf/PropertyTableProperty.h" #include "CesiumGltf/PropertyTextureProperty.h" #include "CesiumGltf/PropertyTypeTraits.h" +#include "PropertyValue.h" #include #include @@ -10,6 +11,17 @@ namespace CesiumGltf { typedef int32_t PropertyViewStatusType; +/** + * @brief Indicates the status of a property view. + * + * The {@link PropertyView} constructor always completes successfully. + * However, there may be fundamental errors with the property definition. In + * such cases, this enumeration provides the reason. + * + * This is defined with a class of static consts as opposed to an enum, so that + * derived property view classes can extend the statuses with their own specific + * errors. + */ class PropertyViewStatus { public: /** @@ -81,13 +93,6 @@ class PropertyViewStatus { /** * @brief An interface for generic metadata property in EXT_structural_metadata. * - * Whether they belong to property tables, property textures, or property - * attributes, properties have their own sub-properties affecting the actual - * property values. Although they are typically defined via class property, they - * may be overridden by individual instances of the property. The constructor is - * responsible for resolving those differences. - * - * @tparam ElementType The C++ type of the values in this property */ template class IPropertyView { public: @@ -176,15 +181,22 @@ template class IPropertyView { virtual std::optional defaultValue() const noexcept = 0; }; +template +class PropertyView; + /** * @brief Represents a generic metadata property in EXT_structural_metadata. - * Implements the {@link IPropertyView} interface. * - * Child classes should validate that the class property type matches the - * "ElementType" before constructing a property view. + * Whether they belong to property tables, property textures, or property + * attributes, properties have their own sub-properties affecting the actual + * property values. Although they are typically defined via class property, they + * may be overridden by individual instances of the property themselves. The + * constructor is responsible for resolving those differences. + * + * @tparam ElementType The C++ type of the values in this property */ template -class PropertyView : IPropertyView { +class PropertyView { public: /** * @brief Constructs an empty property instance. @@ -330,7 +342,7 @@ class PropertyView : IPropertyView { return; } - // If the property has its own values, override the class-provided values. + // If the property has its own values, the class-provided values. if constexpr (IsMetadataNumeric::value) { if (property.offset) { _offset = getValue(*property.offset); @@ -382,7 +394,7 @@ class PropertyView : IPropertyView { return; } - // If the property has its own values, override the class-provided values. + // If the property has its own values, the class-provided values. if constexpr (IsMetadataNumeric::value) { if (property.offset) { _offset = getValue(*property.offset); @@ -426,64 +438,64 @@ class PropertyView : IPropertyView { /** * @copydoc IPropertyView::status */ - virtual PropertyViewStatusType status() const noexcept override { + virtual PropertyViewStatusType status() const noexcept { return _status; } /** * @copydoc IPropertyView::arrayCount */ - virtual int64_t arrayCount() const noexcept override { return 0; } + virtual int64_t arrayCount() const noexcept { return 0; } /** * @copydoc IPropertyView::normalized */ - virtual bool normalized() const noexcept override { return _normalized; } + virtual bool normalized() const noexcept { return _normalized; } /** * @copydoc IPropertyView::offset */ - virtual std::optional offset() const noexcept override { + virtual std::optional offset() const noexcept { return _offset; } /** * @copydoc IPropertyView::scale */ - virtual std::optional scale() const noexcept override { + virtual std::optional scale() const noexcept { return _scale; } /** * @copydoc IPropertyView::max */ - virtual std::optional max() const noexcept override { + virtual std::optional max() const noexcept { return _max; } /** * @copydoc IPropertyView::min */ - virtual std::optional min() const noexcept override { + virtual std::optional min() const noexcept { return _min; } /** * @copydoc IPropertyView::required */ - virtual bool required() const noexcept override { return _required; } + virtual bool required() const noexcept { return _required; } /** * @copydoc IPropertyView::noData */ - virtual std::optional noData() const noexcept override { + virtual std::optional noData() const noexcept { return _noData; } /** * @copydoc IPropertyView::defaultValue */ - virtual std::optional defaultValue() const noexcept override { + virtual std::optional defaultValue() const noexcept { return _defaultValue; } @@ -655,64 +667,64 @@ template <> class PropertyView : IPropertyView { /** * @copydoc IPropertyView::status */ - virtual PropertyViewStatusType status() const noexcept override { + virtual PropertyViewStatusType status() const noexcept { return _status; } /** * @copydoc IPropertyView::arrayCount */ - virtual int64_t arrayCount() const noexcept override { return 0; } + virtual int64_t arrayCount() const noexcept { return 0; } /** * @copydoc IPropertyView::normalized */ - virtual bool normalized() const noexcept override { return false; } + virtual bool normalized() const noexcept { return false; } /** * @copydoc IPropertyView::offset */ - virtual std::optional offset() const noexcept override { + virtual std::optional offset() const noexcept { return std::nullopt; } /** * @copydoc IPropertyView::scale */ - virtual std::optional scale() const noexcept override { + virtual std::optional scale() const noexcept { return std::nullopt; } /** * @copydoc IPropertyView::max */ - virtual std::optional max() const noexcept override { + virtual std::optional max() const noexcept { return std::nullopt; } /** * @copydoc IPropertyView::min */ - virtual std::optional min() const noexcept override { + virtual std::optional min() const noexcept { return std::nullopt; } /** * @copydoc IPropertyView::required */ - virtual bool required() const noexcept override { return _required; } + virtual bool required() const noexcept { return _required; } /** * @copydoc IPropertyView::noData */ - virtual std::optional noData() const noexcept override { + virtual std::optional noData() const noexcept { return std::nullopt; } /** * @copydoc IPropertyView::defaultValue */ - virtual std::optional defaultValue() const noexcept override { + virtual std::optional defaultValue() const noexcept { return _defaultValue; } @@ -804,57 +816,57 @@ class PropertyView : IPropertyView { /** * @copydoc IPropertyView::status */ - virtual PropertyViewStatusType status() const noexcept override { + virtual PropertyViewStatusType status() const noexcept { return _status; } /** * @copydoc IPropertyView::arrayCount */ - virtual int64_t arrayCount() const noexcept override { return 0; } + virtual int64_t arrayCount() const noexcept { return 0; } /** * @copydoc IPropertyView::normalized */ - virtual bool normalized() const noexcept override { return false; } + virtual bool normalized() const noexcept { return false; } /** * @copydoc IPropertyView::offset */ - virtual std::optional offset() const noexcept override { + virtual std::optional offset() const noexcept { return std::nullopt; } /** * @copydoc IPropertyView::scale */ - virtual std::optional scale() const noexcept override { + virtual std::optional scale() const noexcept { return std::nullopt; } /** * @copydoc IPropertyView::max */ - virtual std::optional max() const noexcept override { + virtual std::optional max() const noexcept { return std::nullopt; } /** * @copydoc IPropertyView::min */ - virtual std::optional min() const noexcept override { + virtual std::optional min() const noexcept { return std::nullopt; } /** * @copydoc IPropertyView::required */ - virtual bool required() const noexcept override { return _required; } + virtual bool required() const noexcept { return _required; } /** * @copydoc IPropertyView::noData */ - virtual std::optional noData() const noexcept override { + virtual std::optional noData() const noexcept { if (_noData) return std::string_view(*_noData); @@ -865,7 +877,7 @@ class PropertyView : IPropertyView { * @copydoc IPropertyView::defaultValue */ virtual std::optional - defaultValue() const noexcept override { + defaultValue() const noexcept { if (_defaultValue) return std::string_view(*_defaultValue); @@ -966,7 +978,7 @@ class PropertyView> } } - // If the property has its own values, override the class-provided values. + // If the property has its own values, the class-provided values. if constexpr (IsMetadataNumeric::value) { if (classProperty.offset) { _offset = getArrayValue(*classProperty.offset); @@ -1043,7 +1055,7 @@ class PropertyView> return; } - // If the property has its own values, override the class-provided values. + // If the property has its own values, the class-provided values. if constexpr (IsMetadataNumeric::value) { if (property.offset) { _offset = getArrayValue(*property.offset); @@ -1095,7 +1107,7 @@ class PropertyView> return; } - // If the property has its own values, override the class-provided values. + // If the property has its own values, the class-provided values. if constexpr (IsMetadataNumeric::value) { if (property.offset) { _offset = getArrayValue(*property.offset); @@ -1139,25 +1151,25 @@ class PropertyView> /** * @copydoc IPropertyView::status */ - virtual PropertyViewStatusType status() const noexcept override { + virtual PropertyViewStatusType status() const noexcept { return _status; } /** * @copydoc IPropertyView::arrayCount */ - virtual int64_t arrayCount() const noexcept override { return _count; } + virtual int64_t arrayCount() const noexcept { return _count; } /** * @copydoc IPropertyView::normalized */ - virtual bool normalized() const noexcept override { return _normalized; } + virtual bool normalized() const noexcept { return _normalized; } /** * @copydoc IPropertyView::offset */ virtual std::optional> - offset() const noexcept override { + offset() const noexcept { if (!_offset) { return std::nullopt; } @@ -1170,7 +1182,7 @@ class PropertyView> * @copydoc IPropertyView::scale */ virtual std::optional> - scale() const noexcept override { + scale() const noexcept { if (!_scale) { return std::nullopt; } @@ -1183,7 +1195,7 @@ class PropertyView> * @copydoc IPropertyView::max */ virtual std::optional> - max() const noexcept override { + max() const noexcept { if (!_max) { return std::nullopt; } @@ -1196,7 +1208,7 @@ class PropertyView> * @copydoc IPropertyView::min */ virtual std::optional> - min() const noexcept override { + min() const noexcept { if (!_min) { return std::nullopt; } @@ -1208,13 +1220,13 @@ class PropertyView> /** * @copydoc IPropertyView::required */ - virtual bool required() const noexcept override { return _required; } + virtual bool required() const noexcept { return _required; } /** * @copydoc IPropertyView::noData */ virtual std::optional> - noData() const noexcept override { + noData() const noexcept { if (!_noData) { return std::nullopt; } @@ -1227,7 +1239,7 @@ class PropertyView> * @copydoc IPropertyView::defaultValue */ virtual std::optional> - defaultValue() const noexcept override { + defaultValue() const noexcept { if (!_defaultValue) { return std::nullopt; } @@ -1437,25 +1449,25 @@ class PropertyView> /** * @copydoc IPropertyView::status */ - virtual PropertyViewStatusType status() const noexcept override { + virtual PropertyViewStatusType status() const noexcept { return _status; } /** * @copydoc IPropertyView::arrayCount */ - virtual int64_t arrayCount() const noexcept override { return _count; } + virtual int64_t arrayCount() const noexcept { return _count; } /** * @copydoc IPropertyView::normalized */ - virtual bool normalized() const noexcept override { return false; } + virtual bool normalized() const noexcept { return false; } /** * @copydoc IPropertyView::offset */ virtual std::optional> - offset() const noexcept override { + offset() const noexcept { return std::nullopt; } @@ -1463,34 +1475,34 @@ class PropertyView> * @copydoc IPropertyView::scale */ virtual std::optional> - scale() const noexcept override { + scale() const noexcept { return std::nullopt; } /** * @copydoc IPropertyView::max */ - virtual std::optional> max() const noexcept override { + virtual std::optional> max() const noexcept { return std::nullopt; } /** * @copydoc IPropertyView::min */ - virtual std::optional> min() const noexcept override { + virtual std::optional> min() const noexcept { return std::nullopt; } /** * @copydoc IPropertyView::required */ - virtual bool required() const noexcept override { return _required; } + virtual bool required() const noexcept { return _required; } /** * @copydoc IPropertyView::noData */ virtual std::optional> - noData() const noexcept override { + noData() const noexcept { return std::nullopt; } @@ -1498,7 +1510,7 @@ class PropertyView> * @copydoc IPropertyView::defaultValue */ virtual std::optional> - defaultValue() const noexcept override { + defaultValue() const noexcept { if (_size > 0) { return PropertyArrayView( gsl::span( @@ -1654,25 +1666,25 @@ class PropertyView> /** * @copydoc IPropertyView::status */ - virtual PropertyViewStatusType status() const noexcept override { + virtual PropertyViewStatusType status() const noexcept { return _status; } /** * @copydoc IPropertyView::arrayCount */ - virtual int64_t arrayCount() const noexcept override { return _count; } + virtual int64_t arrayCount() const noexcept { return _count; } /** * @copydoc IPropertyView::normalized */ - virtual bool normalized() const noexcept override { return false; } + virtual bool normalized() const noexcept { return false; } /** * @copydoc IPropertyView::offset */ virtual std::optional> - offset() const noexcept override { + offset() const noexcept { return std::nullopt; } @@ -1680,7 +1692,7 @@ class PropertyView> * @copydoc IPropertyView::scale */ virtual std::optional> - scale() const noexcept override { + scale() const noexcept { return std::nullopt; } @@ -1688,7 +1700,7 @@ class PropertyView> * @copydoc IPropertyView::max */ virtual std::optional> - max() const noexcept override { + max() const noexcept { return std::nullopt; } @@ -1696,20 +1708,20 @@ class PropertyView> * @copydoc IPropertyView::min */ virtual std::optional> - min() const noexcept override { + min() const noexcept { return std::nullopt; } /** * @copydoc IPropertyView::required */ - virtual bool required() const noexcept override { return _required; } + virtual bool required() const noexcept { return _required; } /** * @copydoc IPropertyView::noData */ virtual std::optional> - noData() const noexcept override { + noData() const noexcept { if (_noDataSize > 0) { return PropertyArrayView( gsl::span(_noData.data(), _noData.size()), @@ -1727,7 +1739,7 @@ class PropertyView> * @copydoc IPropertyView::defaultValue */ virtual std::optional> - defaultValue() const noexcept override { + defaultValue() const noexcept { if (_noDataSize > 0) { return PropertyArrayView( gsl::span( diff --git a/CesiumGltf/test/TestPropertyView.cpp b/CesiumGltf/test/TestPropertyView.cpp index f6fc099c2..cedf023fa 100644 --- a/CesiumGltf/test/TestPropertyView.cpp +++ b/CesiumGltf/test/TestPropertyView.cpp @@ -1,10 +1,21 @@ #include "CesiumGltf/PropertyView.h" +#include "CesiumGltf/PropertyValue.h" + #include using namespace CesiumGltf; using namespace CesiumUtility; +TEST_CASE("TEST") { TestPropertyTablePropertyView normalizedView; + REQUIRE(normalizedView.normalized()); + REQUIRE(normalizedView.getValue() == 1.0); + + TestPropertyTablePropertyView regView; + REQUIRE(!regView.normalized()); + REQUIRE(regView.getValue() == 1); +} + TEST_CASE("Boolean PropertyView") { SECTION("Constructs empty PropertyView") { PropertyView view; From 16565a209efea87deda877f6ca1576c30de3ab4a Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Fri, 18 Aug 2023 14:24:51 -0400 Subject: [PATCH 181/421] Remove unused file --- CesiumGltf/include/CesiumGltf/PropertyView.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CesiumGltf/include/CesiumGltf/PropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyView.h index 047fd9079..00300fd9f 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyView.h @@ -2,7 +2,7 @@ #include "CesiumGltf/PropertyTableProperty.h" #include "CesiumGltf/PropertyTextureProperty.h" #include "CesiumGltf/PropertyTypeTraits.h" -#include "PropertyValue.h" +//#include "PropertyValue.h" #include #include From 43cd1a512efb85cfd2b886200942add667549270 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Fri, 18 Aug 2023 15:51:06 -0400 Subject: [PATCH 182/421] Try to get CI to compile --- .../include/CesiumGltf/PropertyArrayView.h | 88 +-- .../CesiumGltf/PropertyTablePropertyView.h | 142 +--- .../include/CesiumGltf/PropertyTypeTraits.h | 21 +- CesiumGltf/include/CesiumGltf/PropertyView.h | 723 +++++++++++------- CesiumGltf/test/TestPropertyTypeTraits.cpp | 442 +++++++---- CesiumGltf/test/TestPropertyView.cpp | 17 +- 6 files changed, 858 insertions(+), 575 deletions(-) diff --git a/CesiumGltf/include/CesiumGltf/PropertyArrayView.h b/CesiumGltf/include/CesiumGltf/PropertyArrayView.h index 0fa14eb77..6f17cdeba 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyArrayView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyArrayView.h @@ -66,50 +66,50 @@ template class PropertyArrayView { return true; } - PropertyArrayView - operator*(const PropertyArrayView& other) { - int64_t clampedSize = std::min(this->size(), other.size()); - std::vector result(static_cast(this->size())); - if constexpr (IsMetadataMatN::value) { - // Do component-wise multiplication instead of actual matrix - // multiplication. - ElementType matN; - constexpr glm::length_t N = ElementType::length(); - for (int64_t i = 0; i < clampedSize; i++) { - for (glm::length_t j = 0; j < N; j++) { - matN[j] = (*this)[i][j] * other[i][j]; - } - result[i] = matN; - } - } else { - for (int64_t i = 0; i < clampedSize; i++) { - result[i] = (*this)[i] * other[i]; - } - } - - // Copy anything that didn't have a component to multiply against. - for (int64_t i = clampedSize(); i < this->size(); i++) { - result[i] = (*this)[i]; - } - - return PropertyArrayView(std::move(result)); - } - - PropertyArrayView - operator+(const PropertyArrayView& other) { - int64_t clampedSize = std::min(this->size(), other.size()); - std::vector result(static_cast(this->size())); - for (int64_t i = 0; i < clampedSize; i++) { - result[i] = (*this)[i] + other[i]; - } - - // Copy anything that didn't have a component to multiply against. - for (int64_t i = clampedSize(); i < this->size(); i++) { - result[i] = (*this)[i]; - } - - return PropertyArrayView(std::move(result)); - } + //PropertyArrayView + //operator*(const PropertyArrayView& other) { + // int64_t clampedSize = std::min(this->size(), other.size()); + // std::vector result(static_cast(this->size())); + // if constexpr (IsMetadataMatN::value) { + // // Do component-wise multiplication instead of actual matrix + // // multiplication. + // ElementType matN; + // constexpr glm::length_t N = ElementType::length(); + // for (int64_t i = 0; i < clampedSize; i++) { + // for (glm::length_t j = 0; j < N; j++) { + // matN[j] = (*this)[i][j] * other[i][j]; + // } + // result[i] = matN; + // } + // } else { + // for (int64_t i = 0; i < clampedSize; i++) { + // result[i] = (*this)[i] * other[i]; + // } + // } + + // // Copy anything that didn't have a component to multiply against. + // for (int64_t i = clampedSize(); i < this->size(); i++) { + // result[i] = (*this)[i]; + // } + + // return PropertyArrayView(std::move(result)); + //} + + //PropertyArrayView + //operator+(const PropertyArrayView& other) { + // int64_t clampedSize = std::min(this->size(), other.size()); + // std::vector result(static_cast(this->size())); + // for (int64_t i = 0; i < clampedSize; i++) { + // result[i] = (*this)[i] + other[i]; + // } + + // // Copy anything that didn't have a component to multiply against. + // for (int64_t i = clampedSize(); i < this->size(); i++) { + // result[i] = (*this)[i]; + // } + + // return PropertyArrayView(std::move(result)); + //} private: using ArrayType = diff --git a/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h index 3bceaa7d4..177af879f 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h @@ -131,6 +131,9 @@ class PropertyTablePropertyViewStatus : public PropertyViewStatus { static const PropertyViewStatusType ErrorStringOffsetOutOfBounds = 29; }; +template +class PropertyTablePropertyView; + /** * @brief A view on the data of the {@link PropertyTableProperty} that is created * by a {@link PropertyTableView}. @@ -146,15 +149,16 @@ class PropertyTablePropertyViewStatus : public PropertyViewStatus { * one of the aforementioned types. */ template -class PropertyTablePropertyView : public PropertyView { +class PropertyTablePropertyView + : public PropertyView { public: /** * @brief Constructs an invalid instance for a non-existent property. */ PropertyTablePropertyView() - : PropertyView(), + : PropertyView(), _status{PropertyTablePropertyViewStatus::ErrorNonexistentProperty}, - _values, + _values{}, _size{0}, _arrayOffsets{}, _arrayOffsetType{PropertyComponentType::None}, @@ -170,7 +174,7 @@ class PropertyTablePropertyView : public PropertyView { * @param status The value of {@link PropertyTablePropertyViewStatus} indicating the error with the property. */ PropertyTablePropertyView(PropertyViewStatusType status) - : PropertyView(), + : PropertyView(), _status{status}, _values{}, _size{0}, @@ -309,7 +313,8 @@ class PropertyTablePropertyView : public PropertyView { // * this attempts to convert it to the desired type. If such a conversion is // * not possible, this returns std::nullopt. // * - // * If this property contains a "no data" sentinel value, then std::nullopt is + // * If this property contains a "no data" sentinel value, then std::nullopt + // is // * returned for any raw values that equal the sentinel value. If a default // * value is supplied, then it will be returned instead. // * @@ -318,14 +323,16 @@ class PropertyTablePropertyView : public PropertyView { // * // * transformedValue = offset + scale * normalize(value) // * - // * The transformed value will then attempt to be converted to the desired type + // * The transformed value will then attempt to be converted to the desired + // type // * T. // * // * @param index The element index - // * @return The value of the element as T, or std::nullopt if conversion fails. + // * @return The value of the element as T, or std::nullopt if conversion + // fails. // */ - //template - //std::optional getAs(int64_t index) const noexcept { + // template + // std::optional getAs(int64_t index) const noexcept { // assert( // _status == PropertyTablePropertyViewStatus::Valid && // "Check the status() first to make sure view is valid"); @@ -487,121 +494,4 @@ class PropertyTablePropertyView : public PropertyView { PropertyComponentType _stringOffsetType; int64_t _stringOffsetTypeSize; }; - -/** - * @brief A view on the boolean data of the {@link PropertyTableProperty} that is created - * by a {@link PropertyTableView}. - * - * It provides utility to retrieve the actual data stored in the - * {@link PropertyTableProperty::values} like an array of elements. Data of each - * instance can be accessed through the {@link PropertyTablePropertyView::get} method. - */ -template <> class PropertyTablePropertyView : public PropertyView { -public: - /** - * @brief Constructs an invalid instance for a non-existent property. - */ - PropertyTablePropertyView() - : PropertyView(), - _status{PropertyTablePropertyViewStatus::ErrorNonexistentProperty}, - _values{}, - _size{0} {} - - /** - * @brief Constructs an invalid instance for an erroneous property. - * - * @param status The value of {@link PropertyTablePropertyViewStatus} indicating the error with the property. - */ - PropertyTablePropertyView(PropertyViewStatusType status) - : PropertyView(), _status{status}, _values{}, _size{0} { - assert( - _status != PropertyTablePropertyViewStatus::Valid && - "An empty property view should not be constructed with a valid " - "status"); - } - - /** - * @brief Construct an instance pointing to the boolean data specified by a {@link PropertyTableProperty}. - * - * - * @param property The {@link PropertyTableProperty} - * @param classProperty The {@link ClassProperty} this property conforms to. - * @param values The raw buffer specified by {@link PropertyTableProperty::values} - * @param size The number of elements in the property table specified by {@link PropertyTable::count} - */ - PropertyTablePropertyView( - const PropertyTableProperty& property, - const ClassProperty& classProperty, - int64_t size, - gsl::span values) noexcept - : PropertyView(classProperty, property), - _status{PropertyView::status()}, - _values{values}, - _size{size} {} - - /** - * @copydoc IPropertyView::status - */ - virtual int32_t status() const noexcept override { return _status; } - - /** - * @brief Get the value of an element of the {@link PropertyTable}. - * - * @param index The element index - * @return The value of the element - */ - bool get(int64_t index) const noexcept { - assert( - _status == PropertyTablePropertyViewStatus::Valid && - "Check the status() first to make sure view is valid"); - assert( - size() > 0 && - "Check the size() of the view to make sure it's not empty"); - assert(index >= 0 && "index must be non-negative"); - assert(index < size() && "index must be less than size"); - - const int64_t byteIndex = index / 8; - const int64_t bitIndex = index % 8; - const int bitValue = static_cast(_values[byteIndex] >> bitIndex) & 1; - return bitValue == 1; - } - - /** - * @brief Gets the value of an element in the {@link PropertyTable} as an instance - * of T. If T is not the same as the ElementType of the property, then - * this attempts to convert it to the desired type. If such a conversion is - * not possible, this returns std::nullopt. - * - * @param index The element index - * @return The value of the element as T, or std::nullopt if conversion fails. - */ - template std::optional getAs(int64_t index) const noexcept { - assert( - _status == PropertyTablePropertyViewStatus::Valid && - "Check the status() first to make sure view is valid"); - assert( - size() > 0 && - "Check the size() of the view to make sure it's not empty"); - assert(index >= 0 && "index must be non-negative"); - assert(index < size() && "index must be less than size"); - - return PropertyConversions::convert(get(index)); - } - - /** - * @brief Get the number of elements in this - * PropertyTablePropertyView. If the view is valid, this returns - * {@link PropertyTable::count}. Otherwise, this returns 0. - * - * @return The number of elements in this PropertyTablePropertyView. - */ - int64_t size() const noexcept { - return status() == PropertyTablePropertyViewStatus::Valid ? _size : 0; - } - -private: - PropertyViewStatusType _status; - gsl::span _values; - int64_t _size; -}; } // namespace CesiumGltf diff --git a/CesiumGltf/include/CesiumGltf/PropertyTypeTraits.h b/CesiumGltf/include/CesiumGltf/PropertyTypeTraits.h index 2c6aff53d..fb002d937 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTypeTraits.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTypeTraits.h @@ -265,15 +265,28 @@ template <> struct TypeToPropertyType { static constexpr PropertyType value = PropertyType::String; }; +/** + * @brief Convert an integer numeric type to the corresponding representation as + * a double type. Doubles are preferred over floats to maintain more precision. + */ template struct TypeToNormalizedType; -template <> -struct TypeToNormalizedType { - using type = double; -}; +template <> struct TypeToNormalizedType { using type = double; }; +template <> struct TypeToNormalizedType { using type = double; }; +template <> struct TypeToNormalizedType { using type = double; }; +template <> struct TypeToNormalizedType { using type = double; }; +template <> struct TypeToNormalizedType { using type = double; }; +template <> struct TypeToNormalizedType { using type = double; }; +template <> struct TypeToNormalizedType { using type = double; }; +template <> struct TypeToNormalizedType { using type = double; }; template struct TypeToNormalizedType> { using type = glm::vec; }; + +template +struct TypeToNormalizedType> { + using type = glm::mat; +}; } // namespace CesiumGltf diff --git a/CesiumGltf/include/CesiumGltf/PropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyView.h index 00300fd9f..35d1aab3e 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyView.h @@ -2,9 +2,9 @@ #include "CesiumGltf/PropertyTableProperty.h" #include "CesiumGltf/PropertyTextureProperty.h" #include "CesiumGltf/PropertyTypeTraits.h" -//#include "PropertyValue.h" #include +#include #include namespace CesiumGltf { @@ -90,11 +90,315 @@ class PropertyViewStatus { static const PropertyViewStatusType ErrorInvalidDefaultValue = 11; }; +template +class PropertyView; + +namespace { +template +static std::optional getScalar(const CesiumUtility::JsonValue& jsonValue) { + try { + return jsonValue.getSafeNumber(); + } catch (const CesiumUtility::JsonValueNotRealValue& /*error*/) { + return std::nullopt; + } catch (const gsl::narrowing_error& /*error*/) { + return std::nullopt; + } +} + +template +static std::optional +getVecN(const CesiumUtility::JsonValue& jsonValue) { + if (!jsonValue.isArray()) { + return std::nullopt; + } + + const CesiumUtility::JsonValue::Array& array = jsonValue.getArray(); + constexpr glm::length_t N = VecType::length(); + if (array.size() != N) { + return std::nullopt; + } + + using T = typename VecType::value_type; + + VecType result; + for (glm::length_t i = 0; i < N; i++) { + std::optional value = getScalar(array[i]); + if (!value) { + return std::nullopt; + } + + result[i] = *value; + } + + return result; +} + +template +static std::optional +getMatN(const CesiumUtility::JsonValue& jsonValue) { + if (!jsonValue.isArray()) { + return std::nullopt; + } + + const CesiumUtility::JsonValue::Array& array = jsonValue.getArray(); + constexpr glm::length_t N = MatType::length(); + if (array.size() != N * N) { + return std::nullopt; + } + + using T = typename MatType::value_type; + + MatType result; + for (glm::length_t i = 0; i < N; i++) { + // Try to parse each value in the column. + for (glm::length_t j = 0; j < N; j++) { + std::optional value = getScalar(array[i * N + j]); + if (!value) { + return std::nullopt; + } + + result[i][j] = *value; + } + } + + return result; +} + +} // namespace + /** - * @brief An interface for generic metadata property in EXT_structural_metadata. + * @brief Represents a non-normalized metadata property in + * EXT_structural_metadata. + * + * Whether they belong to property tables, property textures, or property + * attributes, properties have their own sub-properties affecting the actual + * property values. Although they are typically defined via class property, they + * may be overridden by individual instances of the property themselves. The + * constructor is responsible for resolving those differences. * + * @tparam ElementType The C++ type of the values in this property */ -template class IPropertyView { +template class PropertyView { +public: + /** + * @brief Constructs an empty property instance. + */ + PropertyView() + : _status(PropertyViewStatus::ErrorNonexistentProperty), + _offset(std::nullopt), + _scale(std::nullopt), + _max(std::nullopt), + _min(std::nullopt), + _required(false), + _noData(std::nullopt), + _defaultValue(std::nullopt) {} + + /** + * @brief Constructs a property instance from a class definition only. + */ + PropertyView(const ClassProperty& classProperty) + : _status(PropertyViewStatus::Valid), + _offset(std::nullopt), + _scale(std::nullopt), + _max(std::nullopt), + _min(std::nullopt), + _required(classProperty.required), + _noData(std::nullopt), + _defaultValue(std::nullopt) { + if (convertPropertyTypeToString(TypeToPropertyType::value) != + classProperty.type) { + _status = PropertyViewStatus::ErrorTypeMismatch; + return; + } + + if (!classProperty.componentType && + TypeToPropertyType::component != + PropertyComponentType::None) { + _status = PropertyViewStatus::ErrorComponentTypeMismatch; + return; + } + + if (classProperty.componentType && + convertPropertyComponentTypeToString( + TypeToPropertyType::component) != + *classProperty.componentType) { + _status = PropertyViewStatus::ErrorComponentTypeMismatch; + return; + } + + if (classProperty.array) { + _status = PropertyViewStatus::ErrorArrayTypeMismatch; + return; + } + + if (classProperty.normalized) { + _status = PropertyViewStatus::ErrorInvalidNormalization; + } + + if constexpr (IsMetadataNumeric::value) { + if (classProperty.offset) { + _offset = getValue(*classProperty.offset); + if (!_offset) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidOffset; + return; + } + } + + if (classProperty.scale) { + _scale = getValue(*classProperty.scale); + if (!_scale) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidScale; + return; + } + } + + if (classProperty.max) { + _max = getValue(*classProperty.max); + if (!_scale) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidMax; + return; + } + } + + if (classProperty.min) { + _min = getValue(*classProperty.min); + if (!_scale) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidMin; + return; + } + } + } + + if (!_required) { + if (classProperty.noData) { + _noData = getValue(*classProperty.noData); + if (!_noData) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidNoDataValue; + return; + } + } + + if (classProperty.defaultProperty) { + _defaultValue = getValue(*classProperty.defaultProperty); + if (!_defaultValue) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidDefaultValue; + return; + } + } + } + } + +protected: + /** + * @brief Constructs a property instance from a property table property and + * its class definition. + */ + PropertyView( + const ClassProperty& classProperty, + const PropertyTableProperty& property) + : PropertyView(classProperty) { + if (_status != PropertyViewStatus::Valid) { + return; + } + + // If the property has its own values, the class-provided values. + if constexpr (IsMetadataNumeric::value) { + if (property.offset) { + _offset = getValue(*property.offset); + if (!_offset) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidOffset; + return; + } + } + + if (property.scale) { + _scale = getValue(*property.scale); + if (!_scale) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidScale; + return; + } + } + + if (property.max) { + _max = getValue(*property.max); + if (!_scale) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidMax; + return; + } + } + + if (property.min) { + _min = getValue(*property.min); + if (!_scale) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidMin; + return; + } + } + } + } + + /** + * @brief Constructs a property instance from a property texture property and + * its class definition. + */ + PropertyView( + const ClassProperty& classProperty, + const PropertyTextureProperty& property) + : PropertyView(classProperty) { + if (_status != PropertyViewStatus::Valid) { + return; + } + + // If the property has its own values, the class-provided values. + if constexpr (IsMetadataNumeric::value) { + if (property.offset) { + _offset = getValue(*property.offset); + if (!_offset) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidOffset; + return; + } + } + + if (property.scale) { + _scale = getValue(*property.scale); + if (!_scale) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidScale; + return; + } + } + + if (property.max) { + _max = getValue(*property.max); + if (!_scale) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidMax; + return; + } + } + + if (property.min) { + _min = getValue(*property.min); + if (!_scale) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidMin; + return; + } + } + } + } + public: /** * @brief Gets the status of this property view, indicating whether an error @@ -102,7 +406,7 @@ template class IPropertyView { * * @return The status of this property view. */ - virtual PropertyViewStatusType status() const noexcept = 0; + virtual PropertyViewStatusType status() const noexcept { return _status; } /** * @brief Get the element count of the fixed-length arrays in this property. @@ -110,14 +414,14 @@ template class IPropertyView { * * @return The count of this property. */ - virtual int64_t arrayCount() const noexcept = 0; + virtual int64_t arrayCount() const noexcept { return 0; } /** * @brief Whether this property has a normalized integer type. * * @return Whether this property has a normalized integer type. */ - virtual bool normalized() const noexcept = 0; + virtual bool normalized() const noexcept { return false; } /** * @brief Gets the offset to apply to property values. Only applicable to @@ -126,7 +430,8 @@ template class IPropertyView { * * @returns The property's offset, or std::nullopt if it was not specified. */ - virtual std::optional offset() const noexcept = 0; + virtual std::optional offset() const noexcept { return _offset; } + /** * @brief Gets the scale to apply to property values. Only applicable to * SCALAR, VECN, and MATN types when the component type is FLOAT32 or @@ -134,7 +439,7 @@ template class IPropertyView { * * @returns The property's scale, or std::nullopt if it was not specified. */ - virtual std::optional scale() const noexcept = 0; + virtual std::optional scale() const noexcept { return _scale; } /** * @brief Gets the maximum allowed value for the property. Only applicable to @@ -145,7 +450,7 @@ template class IPropertyView { * @returns The property's maximum value, or std::nullopt if it was not * specified. */ - virtual std::optional max() const noexcept = 0; + virtual std::optional max() const noexcept { return _max; } /** * @brief Gets the minimum allowed value for the property. Only applicable to @@ -156,14 +461,14 @@ template class IPropertyView { * @returns The property's minimum value, or std::nullopt if it was not * specified. */ - virtual std::optional min() const noexcept = 0; + virtual std::optional min() const noexcept { return _min; } /** * @brief Whether the property must be present in every entity conforming to * the class. If not required, instances of the property may include "no data" * values, or the entire property may be omitted. */ - virtual bool required() const noexcept = 0; + virtual bool required() const noexcept { return _required; } /** * @brief Gets the "no data" value, i.e., the value representing missing data @@ -171,21 +476,57 @@ template class IPropertyView { * is given as the plain property value, without the transforms from the * normalized, offset, and scale properties. */ - virtual std::optional noData() const noexcept = 0; + virtual std::optional noData() const noexcept { return _noData; } /** * @brief Gets the default value to use when encountering a "no data" value or * an omitted property. The value is given in its final form, taking the * effect of normalized, offset, and scale properties into account. */ - virtual std::optional defaultValue() const noexcept = 0; -}; + virtual std::optional defaultValue() const noexcept { + return _defaultValue; + } -template -class PropertyView; +private: + PropertyViewStatusType _status; + bool _required; + + std::optional _offset; + std::optional _scale; + std::optional _max; + std::optional _min; + + std::optional _noData; + std::optional _defaultValue; + + /** + * @brief Attempts to parse from the given json value. + * + * If T is a type with multiple components, e.g. a VECN or MATN type, this + * will return std::nullopt if one or more components could not be parsed. + * + * @return The value as an instance of T, or std::nullopt if it could not be + * parsed. + */ + static std::optional + getValue(const CesiumUtility::JsonValue& jsonValue) { + if constexpr (IsMetadataScalar::value) { + return getScalar(jsonValue); + } + + if constexpr (IsMetadataVecN::value) { + return getVecN(jsonValue); + } + + if constexpr (IsMetadataMatN::value) { + return getMatN(jsonValue); + } + } +}; /** - * @brief Represents a generic metadata property in EXT_structural_metadata. + * @brief Represents a normalized metadata property in + * EXT_structural_metadata. * * Whether they belong to property tables, property textures, or property * attributes, properties have their own sub-properties affecting the actual @@ -195,15 +536,16 @@ class PropertyView; * * @tparam ElementType The C++ type of the values in this property */ -template -class PropertyView { +template class PropertyView { +private: + using NormalizedType = typename TypeToNormalizedType::type; + public: /** * @brief Constructs an empty property instance. */ PropertyView() : _status(PropertyViewStatus::ErrorNonexistentProperty), - _normalized(false), _offset(std::nullopt), _scale(std::nullopt), _max(std::nullopt), @@ -217,7 +559,6 @@ class PropertyView { */ PropertyView(const ClassProperty& classProperty) : _status(PropertyViewStatus::Valid), - _normalized(classProperty.normalized), _offset(std::nullopt), _scale(std::nullopt), _max(std::nullopt), @@ -251,28 +592,13 @@ class PropertyView { return; } - if (classProperty.normalized) { - PropertyComponentType componentType = - TypeToPropertyType::component; - switch (componentType) { - case PropertyComponentType::Int8: - case PropertyComponentType::Uint8: - case PropertyComponentType::Int16: - case PropertyComponentType::Uint16: - case PropertyComponentType::Int32: - case PropertyComponentType::Uint32: - case PropertyComponentType::Int64: - case PropertyComponentType::Uint64: - break; - default: - _status = PropertyViewStatus::ErrorInvalidNormalization; - return; - } + if (!classProperty.normalized) { + _status = PropertyViewStatus::ErrorInvalidNormalization; } if constexpr (IsMetadataNumeric::value) { if (classProperty.offset) { - _offset = getValue(*classProperty.offset); + _offset = getValue(*classProperty.offset); if (!_offset) { // The value was specified but something went wrong. _status = PropertyViewStatus::ErrorInvalidOffset; @@ -281,7 +607,7 @@ class PropertyView { } if (classProperty.scale) { - _scale = getValue(*classProperty.scale); + _scale = getValue(*classProperty.scale); if (!_scale) { // The value was specified but something went wrong. _status = PropertyViewStatus::ErrorInvalidScale; @@ -290,7 +616,7 @@ class PropertyView { } if (classProperty.max) { - _max = getValue(*classProperty.max); + _max = getValue(*classProperty.max); if (!_scale) { // The value was specified but something went wrong. _status = PropertyViewStatus::ErrorInvalidMax; @@ -299,7 +625,7 @@ class PropertyView { } if (classProperty.min) { - _min = getValue(*classProperty.min); + _min = getValue(*classProperty.min); if (!_scale) { // The value was specified but something went wrong. _status = PropertyViewStatus::ErrorInvalidMin; @@ -310,7 +636,7 @@ class PropertyView { if (!_required) { if (classProperty.noData) { - _noData = getValue(*classProperty.noData); + _noData = getValue(*classProperty.noData); if (!_noData) { // The value was specified but something went wrong. _status = PropertyViewStatus::ErrorInvalidNoDataValue; @@ -319,7 +645,8 @@ class PropertyView { } if (classProperty.defaultProperty) { - _defaultValue = getValue(*classProperty.defaultProperty); + _defaultValue = + getValue(*classProperty.defaultProperty); if (!_defaultValue) { // The value was specified but something went wrong. _status = PropertyViewStatus::ErrorInvalidDefaultValue; @@ -345,7 +672,7 @@ class PropertyView { // If the property has its own values, the class-provided values. if constexpr (IsMetadataNumeric::value) { if (property.offset) { - _offset = getValue(*property.offset); + _offset = getValue(*property.offset); if (!_offset) { // The value was specified but something went wrong. _status = PropertyViewStatus::ErrorInvalidOffset; @@ -354,7 +681,7 @@ class PropertyView { } if (property.scale) { - _scale = getValue(*property.scale); + _scale = getValue(*property.scale); if (!_scale) { // The value was specified but something went wrong. _status = PropertyViewStatus::ErrorInvalidScale; @@ -363,7 +690,7 @@ class PropertyView { } if (property.max) { - _max = getValue(*property.max); + _max = getValue(*property.max); if (!_scale) { // The value was specified but something went wrong. _status = PropertyViewStatus::ErrorInvalidMax; @@ -372,7 +699,7 @@ class PropertyView { } if (property.min) { - _min = getValue(*property.min); + _min = getValue(*property.min); if (!_scale) { // The value was specified but something went wrong. _status = PropertyViewStatus::ErrorInvalidMin; @@ -397,7 +724,7 @@ class PropertyView { // If the property has its own values, the class-provided values. if constexpr (IsMetadataNumeric::value) { if (property.offset) { - _offset = getValue(*property.offset); + _offset = getValue(*property.offset); if (!_offset) { // The value was specified but something went wrong. _status = PropertyViewStatus::ErrorInvalidOffset; @@ -406,7 +733,7 @@ class PropertyView { } if (property.scale) { - _scale = getValue(*property.scale); + _scale = getValue(*property.scale); if (!_scale) { // The value was specified but something went wrong. _status = PropertyViewStatus::ErrorInvalidScale; @@ -415,7 +742,7 @@ class PropertyView { } if (property.max) { - _max = getValue(*property.max); + _max = getValue(*property.max); if (!_scale) { // The value was specified but something went wrong. _status = PropertyViewStatus::ErrorInvalidMax; @@ -424,7 +751,7 @@ class PropertyView { } if (property.min) { - _min = getValue(*property.min); + _min = getValue(*property.min); if (!_scale) { // The value was specified but something went wrong. _status = PropertyViewStatus::ErrorInvalidMin; @@ -436,81 +763,75 @@ class PropertyView { public: /** - * @copydoc IPropertyView::status + * @brief Gets the status of this property view, indicating whether an error + * occurred. + * + * @return The status of this property view. */ - virtual PropertyViewStatusType status() const noexcept { - return _status; - } + virtual PropertyViewStatusType status() const noexcept { return _status; } /** * @copydoc IPropertyView::arrayCount */ - virtual int64_t arrayCount() const noexcept { return 0; } + virtual int64_t arrayCount() const noexcept { return 0; } /** * @copydoc IPropertyView::normalized */ - virtual bool normalized() const noexcept { return _normalized; } + virtual bool normalized() const noexcept { return false; } /** * @copydoc IPropertyView::offset */ - virtual std::optional offset() const noexcept { + virtual std::optional offset() const noexcept { return _offset; } /** * @copydoc IPropertyView::scale */ - virtual std::optional scale() const noexcept { + virtual std::optional scale() const noexcept { return _scale; } /** * @copydoc IPropertyView::max */ - virtual std::optional max() const noexcept { - return _max; - } + virtual std::optional max() const noexcept { return _max; } /** * @copydoc IPropertyView::min */ - virtual std::optional min() const noexcept { - return _min; - } + virtual std::optional min() const noexcept { return _min; } /** * @copydoc IPropertyView::required */ - virtual bool required() const noexcept { return _required; } + virtual bool required() const noexcept { return _required; } /** * @copydoc IPropertyView::noData */ - virtual std::optional noData() const noexcept { - return _noData; - } + virtual std::optional noData() const noexcept { return _noData; } /** * @copydoc IPropertyView::defaultValue */ - virtual std::optional defaultValue() const noexcept { + virtual std::optional defaultValue() const noexcept { return _defaultValue; } private: PropertyViewStatusType _status; - bool _normalized; + bool _required; - std::optional _offset; - std::optional _scale; - std::optional _max; - std::optional _min; + std::optional _offset; + std::optional _scale; + std::optional _max; + std::optional _min; - bool _required; std::optional _noData; - std::optional _defaultValue; + std::optional _defaultValue; /** * @brief Attempts to parse from the given json value. @@ -521,93 +842,23 @@ class PropertyView { * @return The value as an instance of T, or std::nullopt if it could not be * parsed. */ - static std::optional - getValue(const CesiumUtility::JsonValue& jsonValue) { - if constexpr (IsMetadataScalar::value) { - return getScalar(jsonValue); - } - - if constexpr (IsMetadataVecN::value) { - return getVecN(jsonValue); - } - - if constexpr (IsMetadataMatN::value) { - return getMatN(jsonValue); - } - } - template - static std::optional getScalar(const CesiumUtility::JsonValue& jsonValue) { - try { - return jsonValue.getSafeNumber(); - } catch (const CesiumUtility::JsonValueNotRealValue& /*error*/) { - return std::nullopt; - } catch (const gsl::narrowing_error& /*error*/) { - return std::nullopt; - } - } - - template - static std::optional - getVecN(const CesiumUtility::JsonValue& jsonValue) { - if (!jsonValue.isArray()) { - return std::nullopt; - } - - const CesiumUtility::JsonValue::Array& array = jsonValue.getArray(); - constexpr glm::length_t N = VecType::length(); - if (array.size() != N) { - return std::nullopt; - } - - using T = typename VecType::value_type; - - VecType result; - for (glm::length_t i = 0; i < N; i++) { - std::optional value = getScalar(array[i]); - if (!value) { - return std::nullopt; - } - - result[i] = *value; + static std::optional getValue(const CesiumUtility::JsonValue& jsonValue) { + if constexpr (IsMetadataScalar::value) { + return getScalar(jsonValue); } - return result; - } - - template - static std::optional - getMatN(const CesiumUtility::JsonValue& jsonValue) { - if (!jsonValue.isArray()) { - return std::nullopt; + if constexpr (IsMetadataVecN::value) { + return getVecN(jsonValue); } - const CesiumUtility::JsonValue::Array& array = jsonValue.getArray(); - constexpr glm::length_t N = MatType::length(); - if (array.size() != N * N) { - return std::nullopt; + if constexpr (IsMetadataMatN::value) { + return getMatN(jsonValue); } - - using T = typename MatType::value_type; - - MatType result; - for (glm::length_t i = 0; i < N; i++) { - // Try to parse each value in the column. - for (glm::length_t j = 0; j < N; j++) { - std::optional value = getScalar(array[i * N + j]); - if (!value) { - return std::nullopt; - } - - result[i][j] = *value; - } - } - - return result; } }; -template <> class PropertyView : IPropertyView { +template <> class PropertyView { public: /** * @brief Constructs an empty property instance. @@ -667,64 +918,52 @@ template <> class PropertyView : IPropertyView { /** * @copydoc IPropertyView::status */ - virtual PropertyViewStatusType status() const noexcept { - return _status; - } + virtual PropertyViewStatusType status() const noexcept { return _status; } /** * @copydoc IPropertyView::arrayCount */ - virtual int64_t arrayCount() const noexcept { return 0; } + virtual int64_t arrayCount() const noexcept { return 0; } /** * @copydoc IPropertyView::normalized */ - virtual bool normalized() const noexcept { return false; } + virtual bool normalized() const noexcept { return false; } /** * @copydoc IPropertyView::offset */ - virtual std::optional offset() const noexcept { - return std::nullopt; - } + virtual std::optional offset() const noexcept { return std::nullopt; } /** * @copydoc IPropertyView::scale */ - virtual std::optional scale() const noexcept { - return std::nullopt; - } + virtual std::optional scale() const noexcept { return std::nullopt; } /** * @copydoc IPropertyView::max */ - virtual std::optional max() const noexcept { - return std::nullopt; - } + virtual std::optional max() const noexcept { return std::nullopt; } /** * @copydoc IPropertyView::min */ - virtual std::optional min() const noexcept { - return std::nullopt; - } + virtual std::optional min() const noexcept { return std::nullopt; } /** * @copydoc IPropertyView::required */ - virtual bool required() const noexcept { return _required; } + virtual bool required() const noexcept { return _required; } /** * @copydoc IPropertyView::noData */ - virtual std::optional noData() const noexcept { - return std::nullopt; - } + virtual std::optional noData() const noexcept { return std::nullopt; } /** * @copydoc IPropertyView::defaultValue */ - virtual std::optional defaultValue() const noexcept { + virtual std::optional defaultValue() const noexcept { return _defaultValue; } @@ -743,8 +982,7 @@ template <> class PropertyView : IPropertyView { } }; -template <> -class PropertyView : IPropertyView { +template <> class PropertyView { public: /** * @brief Constructs an empty property instance. @@ -816,57 +1054,55 @@ class PropertyView : IPropertyView { /** * @copydoc IPropertyView::status */ - virtual PropertyViewStatusType status() const noexcept { - return _status; - } + virtual PropertyViewStatusType status() const noexcept { return _status; } /** * @copydoc IPropertyView::arrayCount */ - virtual int64_t arrayCount() const noexcept { return 0; } + virtual int64_t arrayCount() const noexcept { return 0; } /** * @copydoc IPropertyView::normalized */ - virtual bool normalized() const noexcept { return false; } + virtual bool normalized() const noexcept { return false; } /** * @copydoc IPropertyView::offset */ - virtual std::optional offset() const noexcept { + virtual std::optional offset() const noexcept { return std::nullopt; } /** * @copydoc IPropertyView::scale */ - virtual std::optional scale() const noexcept { + virtual std::optional scale() const noexcept { return std::nullopt; } /** * @copydoc IPropertyView::max */ - virtual std::optional max() const noexcept { + virtual std::optional max() const noexcept { return std::nullopt; } /** * @copydoc IPropertyView::min */ - virtual std::optional min() const noexcept { + virtual std::optional min() const noexcept { return std::nullopt; } /** * @copydoc IPropertyView::required */ - virtual bool required() const noexcept { return _required; } + virtual bool required() const noexcept { return _required; } /** * @copydoc IPropertyView::noData */ - virtual std::optional noData() const noexcept { + virtual std::optional noData() const noexcept { if (_noData) return std::string_view(*_noData); @@ -876,8 +1112,7 @@ class PropertyView : IPropertyView { /** * @copydoc IPropertyView::defaultValue */ - virtual std::optional - defaultValue() const noexcept { + virtual std::optional defaultValue() const noexcept { if (_defaultValue) return std::string_view(*_defaultValue); @@ -901,8 +1136,7 @@ class PropertyView : IPropertyView { }; template -class PropertyView> - : IPropertyView> { +class PropertyView> { public: /** * @brief Constructs an empty property instance. @@ -960,22 +1194,8 @@ class PropertyView> } if (classProperty.normalized) { - PropertyComponentType componentType = - TypeToPropertyType::component; - switch (componentType) { - case PropertyComponentType::Int8: - case PropertyComponentType::Uint8: - case PropertyComponentType::Int16: - case PropertyComponentType::Uint16: - case PropertyComponentType::Int32: - case PropertyComponentType::Uint32: - case PropertyComponentType::Int64: - case PropertyComponentType::Uint64: - break; - default: - _status = PropertyViewStatus::ErrorInvalidNormalization; - return; - } + _status = PropertyViewStatus::ErrorInvalidNormalization; + return; } // If the property has its own values, the class-provided values. @@ -1151,25 +1371,23 @@ class PropertyView> /** * @copydoc IPropertyView::status */ - virtual PropertyViewStatusType status() const noexcept { - return _status; - } + virtual PropertyViewStatusType status() const noexcept { return _status; } /** * @copydoc IPropertyView::arrayCount */ - virtual int64_t arrayCount() const noexcept { return _count; } + virtual int64_t arrayCount() const noexcept { return _count; } /** * @copydoc IPropertyView::normalized */ - virtual bool normalized() const noexcept { return _normalized; } + virtual bool normalized() const noexcept { return _normalized; } /** * @copydoc IPropertyView::offset */ virtual std::optional> - offset() const noexcept { + offset() const noexcept { if (!_offset) { return std::nullopt; } @@ -1181,8 +1399,7 @@ class PropertyView> /** * @copydoc IPropertyView::scale */ - virtual std::optional> - scale() const noexcept { + virtual std::optional> scale() const noexcept { if (!_scale) { return std::nullopt; } @@ -1194,8 +1411,7 @@ class PropertyView> /** * @copydoc IPropertyView::max */ - virtual std::optional> - max() const noexcept { + virtual std::optional> max() const noexcept { if (!_max) { return std::nullopt; } @@ -1207,8 +1423,7 @@ class PropertyView> /** * @copydoc IPropertyView::min */ - virtual std::optional> - min() const noexcept { + virtual std::optional> min() const noexcept { if (!_min) { return std::nullopt; } @@ -1220,13 +1435,13 @@ class PropertyView> /** * @copydoc IPropertyView::required */ - virtual bool required() const noexcept { return _required; } + virtual bool required() const noexcept { return _required; } /** * @copydoc IPropertyView::noData */ virtual std::optional> - noData() const noexcept { + noData() const noexcept { if (!_noData) { return std::nullopt; } @@ -1239,7 +1454,7 @@ class PropertyView> * @copydoc IPropertyView::defaultValue */ virtual std::optional> - defaultValue() const noexcept { + defaultValue() const noexcept { if (!_defaultValue) { return std::nullopt; } @@ -1382,9 +1597,7 @@ class PropertyView> } }; -template <> -class PropertyView> - : IPropertyView> { +template <> class PropertyView> { public: /** * @brief Constructs an empty property instance. @@ -1449,68 +1662,62 @@ class PropertyView> /** * @copydoc IPropertyView::status */ - virtual PropertyViewStatusType status() const noexcept { - return _status; - } + virtual PropertyViewStatusType status() const noexcept { return _status; } /** * @copydoc IPropertyView::arrayCount */ - virtual int64_t arrayCount() const noexcept { return _count; } + virtual int64_t arrayCount() const noexcept { return _count; } /** * @copydoc IPropertyView::normalized */ - virtual bool normalized() const noexcept { return false; } + virtual bool normalized() const noexcept { return false; } /** * @copydoc IPropertyView::offset */ - virtual std::optional> - offset() const noexcept { + virtual std::optional> offset() const noexcept { return std::nullopt; } /** * @copydoc IPropertyView::scale */ - virtual std::optional> - scale() const noexcept { + virtual std::optional> scale() const noexcept { return std::nullopt; } /** * @copydoc IPropertyView::max */ - virtual std::optional> max() const noexcept { + virtual std::optional> max() const noexcept { return std::nullopt; } /** * @copydoc IPropertyView::min */ - virtual std::optional> min() const noexcept { + virtual std::optional> min() const noexcept { return std::nullopt; } /** * @copydoc IPropertyView::required */ - virtual bool required() const noexcept { return _required; } + virtual bool required() const noexcept { return _required; } /** * @copydoc IPropertyView::noData */ - virtual std::optional> - noData() const noexcept { + virtual std::optional> noData() const noexcept { return std::nullopt; } /** * @copydoc IPropertyView::defaultValue */ - virtual std::optional> - defaultValue() const noexcept { + virtual std::optional> defaultValue() const noexcept { if (_size > 0) { return PropertyArrayView( gsl::span( @@ -1571,9 +1778,7 @@ class PropertyView> } }; -template <> -class PropertyView> - : IPropertyView> { +template <> class PropertyView> { public: /** * @brief Constructs an empty property instance. @@ -1666,25 +1871,23 @@ class PropertyView> /** * @copydoc IPropertyView::status */ - virtual PropertyViewStatusType status() const noexcept { - return _status; - } + virtual PropertyViewStatusType status() const noexcept { return _status; } /** * @copydoc IPropertyView::arrayCount */ - virtual int64_t arrayCount() const noexcept { return _count; } + virtual int64_t arrayCount() const noexcept { return _count; } /** * @copydoc IPropertyView::normalized */ - virtual bool normalized() const noexcept { return false; } + virtual bool normalized() const noexcept { return false; } /** * @copydoc IPropertyView::offset */ virtual std::optional> - offset() const noexcept { + offset() const noexcept { return std::nullopt; } @@ -1692,7 +1895,7 @@ class PropertyView> * @copydoc IPropertyView::scale */ virtual std::optional> - scale() const noexcept { + scale() const noexcept { return std::nullopt; } @@ -1700,7 +1903,7 @@ class PropertyView> * @copydoc IPropertyView::max */ virtual std::optional> - max() const noexcept { + max() const noexcept { return std::nullopt; } @@ -1708,20 +1911,20 @@ class PropertyView> * @copydoc IPropertyView::min */ virtual std::optional> - min() const noexcept { + min() const noexcept { return std::nullopt; } /** * @copydoc IPropertyView::required */ - virtual bool required() const noexcept { return _required; } + virtual bool required() const noexcept { return _required; } /** * @copydoc IPropertyView::noData */ virtual std::optional> - noData() const noexcept { + noData() const noexcept { if (_noDataSize > 0) { return PropertyArrayView( gsl::span(_noData.data(), _noData.size()), @@ -1739,7 +1942,7 @@ class PropertyView> * @copydoc IPropertyView::defaultValue */ virtual std::optional> - defaultValue() const noexcept { + defaultValue() const noexcept { if (_noDataSize > 0) { return PropertyArrayView( gsl::span( diff --git a/CesiumGltf/test/TestPropertyTypeTraits.cpp b/CesiumGltf/test/TestPropertyTypeTraits.cpp index caa8d1eaf..140a2bf2b 100644 --- a/CesiumGltf/test/TestPropertyTypeTraits.cpp +++ b/CesiumGltf/test/TestPropertyTypeTraits.cpp @@ -4,165 +4,165 @@ using namespace CesiumGltf; -TEST_CASE("Test PropertyTypeTraits") { - SECTION("IsScalar") { - REQUIRE(IsMetadataScalar::value); - REQUIRE(IsMetadataScalar::value); +TEST_CASE("Test IsMetadataScalar") { + REQUIRE(IsMetadataScalar::value); + REQUIRE(IsMetadataScalar::value); - REQUIRE(IsMetadataScalar::value); - REQUIRE(IsMetadataScalar::value); + REQUIRE(IsMetadataScalar::value); + REQUIRE(IsMetadataScalar::value); - REQUIRE(IsMetadataScalar::value); - REQUIRE(IsMetadataScalar::value); + REQUIRE(IsMetadataScalar::value); + REQUIRE(IsMetadataScalar::value); - REQUIRE(IsMetadataScalar::value); - REQUIRE(IsMetadataScalar::value); + REQUIRE(IsMetadataScalar::value); + REQUIRE(IsMetadataScalar::value); - REQUIRE(IsMetadataScalar::value); - REQUIRE(IsMetadataScalar::value); + REQUIRE(IsMetadataScalar::value); + REQUIRE(IsMetadataScalar::value); - REQUIRE(!IsMetadataScalar::value); - REQUIRE(!IsMetadataScalar::value); - REQUIRE(!IsMetadataScalar::value); - REQUIRE(!IsMetadataScalar::value); - } + REQUIRE(!IsMetadataScalar::value); + REQUIRE(!IsMetadataScalar::value); + REQUIRE(!IsMetadataScalar::value); + REQUIRE(!IsMetadataScalar::value); +} - SECTION("IsVecN") { - REQUIRE(IsMetadataVecN::value); - REQUIRE(IsMetadataVecN::value); - REQUIRE(IsMetadataVecN::value); +TEST_CASE("Test IsMetadataVecN") { + REQUIRE(IsMetadataVecN::value); + REQUIRE(IsMetadataVecN::value); + REQUIRE(IsMetadataVecN::value); - REQUIRE(IsMetadataVecN::value); - REQUIRE(IsMetadataVecN::value); - REQUIRE(IsMetadataVecN::value); + REQUIRE(IsMetadataVecN::value); + REQUIRE(IsMetadataVecN::value); + REQUIRE(IsMetadataVecN::value); - REQUIRE(IsMetadataVecN::value); - REQUIRE(IsMetadataVecN::value); - REQUIRE(IsMetadataVecN::value); + REQUIRE(IsMetadataVecN::value); + REQUIRE(IsMetadataVecN::value); + REQUIRE(IsMetadataVecN::value); - REQUIRE(IsMetadataVecN::value); - REQUIRE(IsMetadataVecN::value); - REQUIRE(IsMetadataVecN::value); + REQUIRE(IsMetadataVecN::value); + REQUIRE(IsMetadataVecN::value); + REQUIRE(IsMetadataVecN::value); - REQUIRE(IsMetadataVecN::value); - REQUIRE(IsMetadataVecN::value); - REQUIRE(IsMetadataVecN::value); + REQUIRE(IsMetadataVecN::value); + REQUIRE(IsMetadataVecN::value); + REQUIRE(IsMetadataVecN::value); - REQUIRE(IsMetadataVecN::value); - REQUIRE(IsMetadataVecN::value); - REQUIRE(IsMetadataVecN::value); + REQUIRE(IsMetadataVecN::value); + REQUIRE(IsMetadataVecN::value); + REQUIRE(IsMetadataVecN::value); - REQUIRE(IsMetadataVecN::value); - REQUIRE(IsMetadataVecN::value); - REQUIRE(IsMetadataVecN::value); + REQUIRE(IsMetadataVecN::value); + REQUIRE(IsMetadataVecN::value); + REQUIRE(IsMetadataVecN::value); - REQUIRE(IsMetadataVecN::value); - REQUIRE(IsMetadataVecN::value); - REQUIRE(IsMetadataVecN::value); + REQUIRE(IsMetadataVecN::value); + REQUIRE(IsMetadataVecN::value); + REQUIRE(IsMetadataVecN::value); - REQUIRE(IsMetadataVecN::value); - REQUIRE(IsMetadataVecN::value); - REQUIRE(IsMetadataVecN::value); + REQUIRE(IsMetadataVecN::value); + REQUIRE(IsMetadataVecN::value); + REQUIRE(IsMetadataVecN::value); - REQUIRE(IsMetadataVecN::value); - REQUIRE(IsMetadataVecN::value); - REQUIRE(IsMetadataVecN::value); + REQUIRE(IsMetadataVecN::value); + REQUIRE(IsMetadataVecN::value); + REQUIRE(IsMetadataVecN::value); - REQUIRE(!IsMetadataVecN::value); - REQUIRE(!IsMetadataVecN::value); - } + REQUIRE(!IsMetadataVecN::value); + REQUIRE(!IsMetadataVecN::value); +} - SECTION("IsMatN") { - REQUIRE(IsMetadataMatN::value); - REQUIRE(IsMetadataMatN::value); - REQUIRE(IsMetadataMatN::value); +TEST_CASE("Test IsMetadataMatN") { + REQUIRE(IsMetadataMatN::value); + REQUIRE(IsMetadataMatN::value); + REQUIRE(IsMetadataMatN::value); - REQUIRE(IsMetadataMatN::value); - REQUIRE(IsMetadataMatN::value); - REQUIRE(IsMetadataMatN::value); + REQUIRE(IsMetadataMatN::value); + REQUIRE(IsMetadataMatN::value); + REQUIRE(IsMetadataMatN::value); - REQUIRE(IsMetadataMatN::value); - REQUIRE(IsMetadataMatN::value); - REQUIRE(IsMetadataMatN::value); + REQUIRE(IsMetadataMatN::value); + REQUIRE(IsMetadataMatN::value); + REQUIRE(IsMetadataMatN::value); - REQUIRE(IsMetadataMatN::value); - REQUIRE(IsMetadataMatN::value); - REQUIRE(IsMetadataMatN::value); + REQUIRE(IsMetadataMatN::value); + REQUIRE(IsMetadataMatN::value); + REQUIRE(IsMetadataMatN::value); - REQUIRE(IsMetadataMatN::value); - REQUIRE(IsMetadataMatN::value); - REQUIRE(IsMetadataMatN::value); + REQUIRE(IsMetadataMatN::value); + REQUIRE(IsMetadataMatN::value); + REQUIRE(IsMetadataMatN::value); - REQUIRE(IsMetadataMatN::value); - REQUIRE(IsMetadataMatN::value); - REQUIRE(IsMetadataMatN::value); + REQUIRE(IsMetadataMatN::value); + REQUIRE(IsMetadataMatN::value); + REQUIRE(IsMetadataMatN::value); - REQUIRE(IsMetadataMatN::value); - REQUIRE(IsMetadataMatN::value); - REQUIRE(IsMetadataMatN::value); + REQUIRE(IsMetadataMatN::value); + REQUIRE(IsMetadataMatN::value); + REQUIRE(IsMetadataMatN::value); - REQUIRE(IsMetadataMatN::value); - REQUIRE(IsMetadataMatN::value); - REQUIRE(IsMetadataMatN::value); + REQUIRE(IsMetadataMatN::value); + REQUIRE(IsMetadataMatN::value); + REQUIRE(IsMetadataMatN::value); - REQUIRE(IsMetadataMatN::value); - REQUIRE(IsMetadataMatN::value); - REQUIRE(IsMetadataMatN::value); + REQUIRE(IsMetadataMatN::value); + REQUIRE(IsMetadataMatN::value); + REQUIRE(IsMetadataMatN::value); - REQUIRE(IsMetadataMatN::value); - REQUIRE(IsMetadataMatN::value); - REQUIRE(IsMetadataMatN::value); + REQUIRE(IsMetadataMatN::value); + REQUIRE(IsMetadataMatN::value); + REQUIRE(IsMetadataMatN::value); - REQUIRE(!IsMetadataMatN::value); - REQUIRE(!IsMetadataMatN::value); - } + REQUIRE(!IsMetadataMatN::value); + REQUIRE(!IsMetadataMatN::value); +} - SECTION("IsBoolean") { - REQUIRE(IsMetadataBoolean::value); - REQUIRE(!IsMetadataBoolean::value); - REQUIRE(!IsMetadataBoolean::value); - } +TEST_CASE("Test IsMetadataBoolean") { + REQUIRE(IsMetadataBoolean::value); + REQUIRE(!IsMetadataBoolean::value); + REQUIRE(!IsMetadataBoolean::value); +} - SECTION("IsString") { - REQUIRE(IsMetadataString::value); - REQUIRE(!IsMetadataString::value); - } +TEST_CASE("Test IsMetadataString") { + REQUIRE(IsMetadataString::value); + REQUIRE(!IsMetadataString::value); +} - SECTION("IsNumeric") { - REQUIRE(IsMetadataNumeric::value); - REQUIRE(IsMetadataNumeric::value); - REQUIRE(IsMetadataNumeric::value); - REQUIRE(IsMetadataNumeric::value); - REQUIRE(IsMetadataNumeric::value); - REQUIRE(IsMetadataNumeric::value); - REQUIRE(!IsMetadataNumeric::value); - REQUIRE(!IsMetadataNumeric::value); - } +TEST_CASE("Test IsMetadataNumeric") { + REQUIRE(IsMetadataNumeric::value); + REQUIRE(IsMetadataNumeric::value); + REQUIRE(IsMetadataNumeric::value); + REQUIRE(IsMetadataNumeric::value); + REQUIRE(IsMetadataNumeric::value); + REQUIRE(IsMetadataNumeric::value); + REQUIRE(!IsMetadataNumeric::value); + REQUIRE(!IsMetadataNumeric::value); +} - SECTION("IsNumericArray") { - REQUIRE(IsMetadataNumericArray>::value); - REQUIRE(IsMetadataNumericArray>::value); - REQUIRE(IsMetadataNumericArray>::value); - REQUIRE(!IsMetadataNumericArray>::value); - } +TEST_CASE("Test IsMetadataNumericArray") { + REQUIRE(IsMetadataNumericArray>::value); + REQUIRE(IsMetadataNumericArray>::value); + REQUIRE(IsMetadataNumericArray>::value); + REQUIRE(!IsMetadataNumericArray>::value); +} - SECTION("IsBooleanArray") { - REQUIRE(IsMetadataBooleanArray>::value); - REQUIRE(!IsMetadataBooleanArray>::value); - } +TEST_CASE("Test IsMetadataBooleanArray") { + REQUIRE(IsMetadataBooleanArray>::value); + REQUIRE(!IsMetadataBooleanArray>::value); +} - SECTION("IsStringArray") { - REQUIRE(IsMetadataStringArray>::value); - REQUIRE(!IsMetadataStringArray>::value); - REQUIRE(!IsMetadataStringArray>::value); - } +TEST_CASE("Test IsStringArray") { + REQUIRE(IsMetadataStringArray>::value); + REQUIRE(!IsMetadataStringArray>::value); + REQUIRE(!IsMetadataStringArray>::value); +} - SECTION("ArrayType") { - using type = MetadataArrayType>::type; - REQUIRE(std::is_same_v); - } +TEST_CASE("Test MetadataArrayType") { + using type = MetadataArrayType>::type; + REQUIRE(std::is_same_v); +} - SECTION("TypeToPropertyType scalar") { +TEST_CASE("TypeToPropertyType") { + SECTION("Works for scalar types") { REQUIRE(TypeToPropertyType::value == PropertyType::Scalar); REQUIRE( TypeToPropertyType::component == PropertyComponentType::Uint8); @@ -199,7 +199,7 @@ TEST_CASE("Test PropertyTypeTraits") { TypeToPropertyType::component == PropertyComponentType::Int64); } - SECTION("TypeToPropertyType vecN") { + SECTION("Works for vecN types") { // Vec2 REQUIRE(TypeToPropertyType::value == PropertyType::Vec2); REQUIRE( @@ -354,7 +354,7 @@ TEST_CASE("Test PropertyTypeTraits") { PropertyComponentType::Float64); } - SECTION("TypeToPropertyType matN") { + SECTION("Works for matN types") { // Mat2 REQUIRE(TypeToPropertyType::value == PropertyType::Mat2); REQUIRE( @@ -509,12 +509,12 @@ TEST_CASE("Test PropertyTypeTraits") { PropertyComponentType::Float64); } - SECTION("TypeToPropertyType boolean") { + SECTION("Works for boolean") { REQUIRE(TypeToPropertyType::value == PropertyType::Boolean); REQUIRE(TypeToPropertyType::component == PropertyComponentType::None); } - SECTION("TypeToPropertyType string") { + SECTION("Works for string") { REQUIRE( TypeToPropertyType::value == PropertyType::String); REQUIRE( @@ -522,3 +522,189 @@ TEST_CASE("Test PropertyTypeTraits") { PropertyComponentType::None); } } + +TEST_CASE("TypeToNormalizedType") { + SECTION("Works for scalars") { + REQUIRE(std::is_same_v::type, double>); + REQUIRE(std::is_same_v::type, double>); + REQUIRE(std::is_same_v::type, double>); + REQUIRE(std::is_same_v::type, double>); + REQUIRE(std::is_same_v::type, double>); + REQUIRE(std::is_same_v::type, double>); + REQUIRE(std::is_same_v::type, double>); + REQUIRE(std::is_same_v::type, double>); + REQUIRE(std::is_same_v::type, double>); + } + + SECTION("Works for vecNs") { + using ExpectedVec2Type = glm::dvec2; + using ExpectedVec3Type = glm::dvec3; + using ExpectedVec4Type = glm::dvec4; + + REQUIRE(std::is_same_v< + TypeToNormalizedType::type, + ExpectedVec2Type>); + REQUIRE(std::is_same_v< + TypeToNormalizedType::type, + ExpectedVec3Type>); + REQUIRE(std::is_same_v< + TypeToNormalizedType::type, + ExpectedVec4Type>); + + REQUIRE(std::is_same_v< + TypeToNormalizedType::type, + ExpectedVec2Type>); + REQUIRE(std::is_same_v< + TypeToNormalizedType::type, + ExpectedVec3Type>); + REQUIRE(std::is_same_v< + TypeToNormalizedType::type, + ExpectedVec4Type>); + + REQUIRE(std::is_same_v< + TypeToNormalizedType::type, + ExpectedVec2Type>); + REQUIRE(std::is_same_v< + TypeToNormalizedType::type, + ExpectedVec3Type>); + REQUIRE(std::is_same_v< + TypeToNormalizedType::type, + ExpectedVec4Type>); + + REQUIRE(std::is_same_v< + TypeToNormalizedType::type, + ExpectedVec2Type>); + REQUIRE(std::is_same_v< + TypeToNormalizedType::type, + ExpectedVec3Type>); + REQUIRE(std::is_same_v< + TypeToNormalizedType::type, + ExpectedVec4Type>); + + REQUIRE(std::is_same_v< + TypeToNormalizedType::type, + ExpectedVec2Type>); + REQUIRE(std::is_same_v< + TypeToNormalizedType::type, + ExpectedVec3Type>); + REQUIRE(std::is_same_v< + TypeToNormalizedType::type, + ExpectedVec4Type>); + + REQUIRE(std::is_same_v< + TypeToNormalizedType::type, + ExpectedVec2Type>); + REQUIRE(std::is_same_v< + TypeToNormalizedType::type, + ExpectedVec3Type>); + REQUIRE(std::is_same_v< + TypeToNormalizedType::type, + ExpectedVec4Type>); + + REQUIRE(std::is_same_v< + TypeToNormalizedType::type, + ExpectedVec2Type>); + REQUIRE(std::is_same_v< + TypeToNormalizedType::type, + ExpectedVec3Type>); + REQUIRE(std::is_same_v< + TypeToNormalizedType::type, + ExpectedVec4Type>); + + REQUIRE(std::is_same_v< + TypeToNormalizedType::type, + ExpectedVec2Type>); + REQUIRE(std::is_same_v< + TypeToNormalizedType::type, + ExpectedVec3Type>); + REQUIRE(std::is_same_v< + TypeToNormalizedType::type, + ExpectedVec4Type>); + } + + SECTION("Works for matNs") { + using ExpectedMat2Type = glm::dmat2; + using ExpectedMat3Type = glm::dmat3; + using ExpectedMat4Type = glm::dmat4; + + REQUIRE(std::is_same_v< + TypeToNormalizedType::type, + ExpectedMat2Type>); + REQUIRE(std::is_same_v< + TypeToNormalizedType::type, + ExpectedMat3Type>); + REQUIRE(std::is_same_v< + TypeToNormalizedType::type, + ExpectedMat4Type>); + + REQUIRE(std::is_same_v< + TypeToNormalizedType::type, + ExpectedMat2Type>); + REQUIRE(std::is_same_v< + TypeToNormalizedType::type, + ExpectedMat3Type>); + REQUIRE(std::is_same_v< + TypeToNormalizedType::type, + ExpectedMat4Type>); + + REQUIRE(std::is_same_v< + TypeToNormalizedType::type, + ExpectedMat2Type>); + REQUIRE(std::is_same_v< + TypeToNormalizedType::type, + ExpectedMat3Type>); + REQUIRE(std::is_same_v< + TypeToNormalizedType::type, + ExpectedMat4Type>); + + REQUIRE(std::is_same_v< + TypeToNormalizedType::type, + ExpectedMat2Type>); + REQUIRE(std::is_same_v< + TypeToNormalizedType::type, + ExpectedMat3Type>); + REQUIRE(std::is_same_v< + TypeToNormalizedType::type, + ExpectedMat4Type>); + + REQUIRE(std::is_same_v< + TypeToNormalizedType::type, + ExpectedMat2Type>); + REQUIRE(std::is_same_v< + TypeToNormalizedType::type, + ExpectedMat3Type>); + REQUIRE(std::is_same_v< + TypeToNormalizedType::type, + ExpectedMat4Type>); + + REQUIRE(std::is_same_v< + TypeToNormalizedType::type, + ExpectedMat2Type>); + REQUIRE(std::is_same_v< + TypeToNormalizedType::type, + ExpectedMat3Type>); + REQUIRE(std::is_same_v< + TypeToNormalizedType::type, + ExpectedMat4Type>); + + REQUIRE(std::is_same_v< + TypeToNormalizedType::type, + ExpectedMat2Type>); + REQUIRE(std::is_same_v< + TypeToNormalizedType::type, + ExpectedMat3Type>); + REQUIRE(std::is_same_v< + TypeToNormalizedType::type, + ExpectedMat4Type>); + + REQUIRE(std::is_same_v< + TypeToNormalizedType::type, + ExpectedMat2Type>); + REQUIRE(std::is_same_v< + TypeToNormalizedType::type, + ExpectedMat3Type>); + REQUIRE(std::is_same_v< + TypeToNormalizedType::type, + ExpectedMat4Type>); + } +} diff --git a/CesiumGltf/test/TestPropertyView.cpp b/CesiumGltf/test/TestPropertyView.cpp index cedf023fa..a8be2978e 100644 --- a/CesiumGltf/test/TestPropertyView.cpp +++ b/CesiumGltf/test/TestPropertyView.cpp @@ -1,21 +1,12 @@ #include "CesiumGltf/PropertyView.h" -#include "CesiumGltf/PropertyValue.h" +//#include "CesiumGltf/PropertyValue.h" #include using namespace CesiumGltf; using namespace CesiumUtility; -TEST_CASE("TEST") { TestPropertyTablePropertyView normalizedView; - REQUIRE(normalizedView.normalized()); - REQUIRE(normalizedView.getValue() == 1.0); - - TestPropertyTablePropertyView regView; - REQUIRE(!regView.normalized()); - REQUIRE(regView.getValue() == 1); -} - TEST_CASE("Boolean PropertyView") { SECTION("Constructs empty PropertyView") { PropertyView view; @@ -172,7 +163,7 @@ TEST_CASE("Scalar PropertyView") { classProperty.normalized = true; classProperty.required = true; - PropertyView view(classProperty); + PropertyView view(classProperty); REQUIRE(view.status() == PropertyViewStatus::Valid); REQUIRE(view.normalized()); REQUIRE(view.required()); @@ -372,7 +363,7 @@ TEST_CASE("VecN PropertyView") { classProperty.normalized = true; classProperty.required = true; - PropertyView view(classProperty); + PropertyView view(classProperty); REQUIRE(view.status() == PropertyViewStatus::Valid); REQUIRE(view.normalized()); REQUIRE(view.required()); @@ -572,7 +563,7 @@ TEST_CASE("MatN PropertyView") { classProperty.normalized = true; classProperty.required = true; - PropertyView view(classProperty); + PropertyView view(classProperty); REQUIRE(view.status() == PropertyViewStatus::Valid); REQUIRE(view.normalized()); REQUIRE(view.required()); From 8cde08611fd1fbd4de528b6b2c7d4e5379493d19 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Fri, 18 Aug 2023 16:00:54 -0400 Subject: [PATCH 183/421] Add missing constructor qualifiers --- CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h index 177af879f..24e4b683b 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h @@ -165,7 +165,7 @@ class PropertyTablePropertyView _arrayOffsetTypeSize{0}, _stringOffsets{}, _stringOffsetType{PropertyComponentType::None}, - _stringOffsetTypeSize{0}, + _stringOffsetTypeSize{0} {} /** @@ -202,7 +202,7 @@ class PropertyTablePropertyView const ClassProperty& classProperty, int64_t size, gsl::span values) noexcept - : PropertyView(classProperty, property), + : PropertyView(classProperty, property), _status{PropertyTablePropertyViewStatus::Valid}, _values{}, _size{}, @@ -240,7 +240,7 @@ class PropertyTablePropertyView gsl::span stringOffsets, PropertyComponentType arrayOffsetType, PropertyComponentType stringOffsetType) noexcept - : PropertyView(classProperty, property), + : PropertyView(classProperty, property), _status{PropertyTablePropertyViewStatus::Valid}, _values{values}, _arrayOffsets{arrayOffsets}, From bf324061b60bfccd4560eb7a1508673d83a6cb35 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Fri, 18 Aug 2023 17:48:03 -0400 Subject: [PATCH 184/421] Start reworking property view for normalization --- .../CesiumGltf/PropertyTablePropertyView.h | 20 +- .../include/CesiumGltf/PropertyTypeTraits.h | 20 + CesiumGltf/include/CesiumGltf/PropertyView.h | 491 +++++++++++------- CesiumGltf/test/TestPropertyTypeTraits.cpp | 17 + CesiumGltf/test/TestPropertyView.cpp | 112 ++-- 5 files changed, 389 insertions(+), 271 deletions(-) diff --git a/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h index 24e4b683b..059336753 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h @@ -157,7 +157,6 @@ class PropertyTablePropertyView */ PropertyTablePropertyView() : PropertyView(), - _status{PropertyTablePropertyViewStatus::ErrorNonexistentProperty}, _values{}, _size{0}, _arrayOffsets{}, @@ -165,8 +164,7 @@ class PropertyTablePropertyView _arrayOffsetTypeSize{0}, _stringOffsets{}, _stringOffsetType{PropertyComponentType::None}, - _stringOffsetTypeSize{0} - {} + _stringOffsetTypeSize{0} {} /** * @brief Constructs an invalid instance for an erroneous property. @@ -174,8 +172,7 @@ class PropertyTablePropertyView * @param status The value of {@link PropertyTablePropertyViewStatus} indicating the error with the property. */ PropertyTablePropertyView(PropertyViewStatusType status) - : PropertyView(), - _status{status}, + : PropertyView(status), _values{}, _size{0}, _arrayOffsets{}, @@ -203,7 +200,6 @@ class PropertyTablePropertyView int64_t size, gsl::span values) noexcept : PropertyView(classProperty, property), - _status{PropertyTablePropertyViewStatus::Valid}, _values{}, _size{}, _arrayOffsets{}, @@ -212,7 +208,7 @@ class PropertyTablePropertyView _stringOffsets{}, _stringOffsetType{PropertyComponentType::None}, _stringOffsetTypeSize{0} { - if (PropertyView::status() == PropertyTablePropertyViewStatus::Valid) { + if (_status == PropertyTablePropertyViewStatus::Valid) { _values = values; _size = size; } @@ -241,7 +237,6 @@ class PropertyTablePropertyView PropertyComponentType arrayOffsetType, PropertyComponentType stringOffsetType) noexcept : PropertyView(classProperty, property), - _status{PropertyTablePropertyViewStatus::Valid}, _values{values}, _arrayOffsets{arrayOffsets}, _arrayOffsetType{arrayOffsetType}, @@ -250,17 +245,12 @@ class PropertyTablePropertyView _stringOffsetType{stringOffsetType}, _stringOffsetTypeSize{getOffsetTypeSize(stringOffsetType)}, _size{size} { - if (PropertyView::status() == PropertyTablePropertyViewStatus::Valid) { + if (_status == PropertyTablePropertyViewStatus::Valid) { _values = values; _size = size; } } - /** - * @copydoc IPropertyView::status - */ - virtual int32_t status() const noexcept override { return _status; } - /** * @brief Get the raw value of an element of the {@link PropertyTable}, * without offset, scale, or normalization applied. @@ -482,7 +472,7 @@ class PropertyTablePropertyView } } - PropertyViewStatusType _status; +// PropertyViewStatusType _status; gsl::span _values; int64_t _size; diff --git a/CesiumGltf/include/CesiumGltf/PropertyTypeTraits.h b/CesiumGltf/include/CesiumGltf/PropertyTypeTraits.h index fb002d937..b7400cbc6 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTypeTraits.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTypeTraits.h @@ -265,6 +265,26 @@ template <> struct TypeToPropertyType { static constexpr PropertyType value = PropertyType::String; }; +/** + * @brief Check if a C++ type can be normalized. + */ +template struct CanBeNormalized; +template struct CanBeNormalized : std::false_type {}; +template <> struct CanBeNormalized : std::true_type {}; +template <> struct CanBeNormalized : std::true_type {}; +template <> struct CanBeNormalized : std::true_type {}; +template <> struct CanBeNormalized : std::true_type {}; +template <> struct CanBeNormalized : std::true_type {}; +template <> struct CanBeNormalized : std::true_type {}; +template <> struct CanBeNormalized : std::true_type {}; +template <> struct CanBeNormalized : std::true_type {}; + +template +struct CanBeNormalized> : CanBeNormalized {}; + +template +struct CanBeNormalized> : CanBeNormalized {}; + /** * @brief Convert an integer numeric type to the corresponding representation as * a double type. Doubles are preferred over floats to maintain more precision. diff --git a/CesiumGltf/include/CesiumGltf/PropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyView.h index 35d1aab3e..3db5165fa 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyView.h @@ -94,6 +94,52 @@ template class PropertyView; namespace { +template +PropertyViewStatusType +validatePropertyType(const ClassProperty& classProperty) { + using ElementType = T; + if constexpr (IsMetadataArray::value) { + using ElementType = typename MetadataArrayType::type; + } + + if (TypeToPropertyType::value != + convertStringToPropertyType(classProperty.type)) { + return PropertyViewStatus::ErrorTypeMismatch; + } + + PropertyComponentType expectedComponentType = + TypeToPropertyType::component; + + if (!classProperty.componentType && + expectedComponentType != PropertyComponentType::None) { + return PropertyViewStatus::ErrorComponentTypeMismatch; + } + + if (classProperty.componentType && + expectedComponentType != + convertStringToPropertyComponentType(*classProperty.componentType)) { + return PropertyViewStatus::ErrorComponentTypeMismatch; + } + + if constexpr (IsMetadataArray::value) { + if (!classProperty.array) { + return PropertyViewStatus::ErrorArrayTypeMismatch; + } + } else { + if (classProperty.array) { + return PropertyViewStatus::ErrorArrayTypeMismatch; + } + } + + if (!CanBeNormalized::value) { + if (classProperty.normalized) { + return PropertyViewStatus::ErrorInvalidNormalization; + } + } + + return PropertyViewStatus::Valid; +} + template static std::optional getScalar(const CesiumUtility::JsonValue& jsonValue) { try { @@ -197,7 +243,7 @@ template class PropertyView { * @brief Constructs a property instance from a class definition only. */ PropertyView(const ClassProperty& classProperty) - : _status(PropertyViewStatus::Valid), + : _status(validatePropertyType(classProperty)), _offset(std::nullopt), _scale(std::nullopt), _max(std::nullopt), @@ -205,96 +251,113 @@ template class PropertyView { _required(classProperty.required), _noData(std::nullopt), _defaultValue(std::nullopt) { - if (convertPropertyTypeToString(TypeToPropertyType::value) != - classProperty.type) { - _status = PropertyViewStatus::ErrorTypeMismatch; - return; - } - - if (!classProperty.componentType && - TypeToPropertyType::component != - PropertyComponentType::None) { - _status = PropertyViewStatus::ErrorComponentTypeMismatch; - return; - } - - if (classProperty.componentType && - convertPropertyComponentTypeToString( - TypeToPropertyType::component) != - *classProperty.componentType) { - _status = PropertyViewStatus::ErrorComponentTypeMismatch; - return; - } - - if (classProperty.array) { - _status = PropertyViewStatus::ErrorArrayTypeMismatch; + if (_status != PropertyViewStatus::Valid) { return; } if (classProperty.normalized) { _status = PropertyViewStatus::ErrorInvalidNormalization; + return; } - if constexpr (IsMetadataNumeric::value) { - if (classProperty.offset) { + if (classProperty.offset) { + // Only floating point types can specify an offset. + switch (TypeToPropertyType::component) { + case PropertyComponentType::Float32: + case PropertyComponentType::Float64: _offset = getValue(*classProperty.offset); - if (!_offset) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidOffset; - return; + if (_offset) { + break; } + // If it does not break here, something went wrong. + default: + _status = PropertyViewStatus::ErrorInvalidOffset; + return; } + } - if (classProperty.scale) { + if (classProperty.scale) { + // Only floating point types can specify a scale. + switch (TypeToPropertyType::component) { + case PropertyComponentType::Float32: + case PropertyComponentType::Float64: _scale = getValue(*classProperty.scale); - if (!_scale) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidScale; - return; + if (_scale) { + break; } + // If it does not break here, something went wrong. + default: + _status = PropertyViewStatus::ErrorInvalidScale; + return; } + } - if (classProperty.max) { - _max = getValue(*classProperty.max); - if (!_scale) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidMax; - return; - } + if (classProperty.max) { + _max = getValue(*classProperty.max); + if (!_scale) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidMax; + return; } + } - if (classProperty.min) { - _min = getValue(*classProperty.min); - if (!_scale) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidMin; - return; - } + if (classProperty.min) { + _min = getValue(*classProperty.min); + if (!_scale) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidMin; + return; } } - if (!_required) { + if (_required) { + // "noData" should not be defined if the property is required. if (classProperty.noData) { - _noData = getValue(*classProperty.noData); - if (!_noData) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidNoDataValue; - return; - } + _status = PropertyViewStatus::ErrorInvalidNoDataValue; + return; } if (classProperty.defaultProperty) { - _defaultValue = getValue(*classProperty.defaultProperty); - if (!_defaultValue) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidDefaultValue; - return; - } + _status = PropertyViewStatus::ErrorInvalidDefaultValue; + return; + } + } + + if (classProperty.noData) { + _noData = getValue(*classProperty.noData); + if (!_noData) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidNoDataValue; + return; + } + } + + if (classProperty.defaultProperty) { + _defaultValue = getValue(*classProperty.defaultProperty); + if (!_defaultValue) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidDefaultValue; + return; } } } protected: + /** + * @brief Constructs an invalid instance for an erroneous property. + * + * @param status The value of {@link PropertyViewStatus} indicating the error with the property. + */ + PropertyView(PropertyViewStatusType status) + : _status(status), + _offset(std::nullopt), + _scale(std::nullopt), + _max(std::nullopt), + _min(std::nullopt), + _required(false), + _noData(std::nullopt), + _defaultValue(std::nullopt) {} + /** * @brief Constructs a property instance from a property table property and * its class definition. @@ -406,7 +469,7 @@ template class PropertyView { * * @return The status of this property view. */ - virtual PropertyViewStatusType status() const noexcept { return _status; } + PropertyViewStatusType status() const noexcept { return _status; } /** * @brief Get the element count of the fixed-length arrays in this property. @@ -414,14 +477,14 @@ template class PropertyView { * * @return The count of this property. */ - virtual int64_t arrayCount() const noexcept { return 0; } + int64_t arrayCount() const noexcept { return 0; } /** * @brief Whether this property has a normalized integer type. * * @return Whether this property has a normalized integer type. */ - virtual bool normalized() const noexcept { return false; } + bool normalized() const noexcept { return false; } /** * @brief Gets the offset to apply to property values. Only applicable to @@ -430,7 +493,7 @@ template class PropertyView { * * @returns The property's offset, or std::nullopt if it was not specified. */ - virtual std::optional offset() const noexcept { return _offset; } + std::optional offset() const noexcept { return _offset; } /** * @brief Gets the scale to apply to property values. Only applicable to @@ -439,7 +502,7 @@ template class PropertyView { * * @returns The property's scale, or std::nullopt if it was not specified. */ - virtual std::optional scale() const noexcept { return _scale; } + std::optional scale() const noexcept { return _scale; } /** * @brief Gets the maximum allowed value for the property. Only applicable to @@ -450,7 +513,7 @@ template class PropertyView { * @returns The property's maximum value, or std::nullopt if it was not * specified. */ - virtual std::optional max() const noexcept { return _max; } + std::optional max() const noexcept { return _max; } /** * @brief Gets the minimum allowed value for the property. Only applicable to @@ -461,14 +524,14 @@ template class PropertyView { * @returns The property's minimum value, or std::nullopt if it was not * specified. */ - virtual std::optional min() const noexcept { return _min; } + std::optional min() const noexcept { return _min; } /** * @brief Whether the property must be present in every entity conforming to * the class. If not required, instances of the property may include "no data" * values, or the entire property may be omitted. */ - virtual bool required() const noexcept { return _required; } + bool required() const noexcept { return _required; } /** * @brief Gets the "no data" value, i.e., the value representing missing data @@ -476,19 +539,21 @@ template class PropertyView { * is given as the plain property value, without the transforms from the * normalized, offset, and scale properties. */ - virtual std::optional noData() const noexcept { return _noData; } + std::optional noData() const noexcept { return _noData; } /** * @brief Gets the default value to use when encountering a "no data" value or * an omitted property. The value is given in its final form, taking the * effect of normalized, offset, and scale properties into account. */ - virtual std::optional defaultValue() const noexcept { + std::optional defaultValue() const noexcept { return _defaultValue; } -private: +protected: PropertyViewStatusType _status; + +private: bool _required; std::optional _offset; @@ -500,13 +565,14 @@ template class PropertyView { std::optional _defaultValue; /** - * @brief Attempts to parse from the given json value. + * @brief Attempts to parse an ElementType from the given json value. * - * If T is a type with multiple components, e.g. a VECN or MATN type, this - * will return std::nullopt if one or more components could not be parsed. - * - * @return The value as an instance of T, or std::nullopt if it could not be + * If ElementType is a type with multiple components, e.g. a VECN or MATN + * type, this will return std::nullopt if one or more components could not be * parsed. + * + * @return The value as an instance of ElementType, or std::nullopt if it + * could not be parsed. */ static std::optional getValue(const CesiumUtility::JsonValue& jsonValue) { @@ -534,7 +600,8 @@ template class PropertyView { * may be overridden by individual instances of the property themselves. The * constructor is responsible for resolving those differences. * - * @tparam ElementType The C++ type of the values in this property + * @tparam ElementType The C++ type of the values in this property. Must have an + * integer component type. */ template class PropertyView { private: @@ -657,6 +724,21 @@ template class PropertyView { } protected: + /** + * @brief Constructs an invalid instance for an erroneous property. + * + * @param status The value of {@link PropertyViewStatus} indicating the error with the property. + */ + PropertyView(PropertyViewStatusType status) + : _status(status), + _offset(std::nullopt), + _scale(std::nullopt), + _max(std::nullopt), + _min(std::nullopt), + _required(false), + _noData(std::nullopt), + _defaultValue(std::nullopt) {} + /** * @brief Constructs a property instance from a property table property and * its class definition. @@ -768,61 +850,59 @@ template class PropertyView { * * @return The status of this property view. */ - virtual PropertyViewStatusType status() const noexcept { return _status; } + PropertyViewStatusType status() const noexcept { return _status; } /** * @copydoc IPropertyView::arrayCount */ - virtual int64_t arrayCount() const noexcept { return 0; } + int64_t arrayCount() const noexcept { return 0; } /** * @copydoc IPropertyView::normalized */ - virtual bool normalized() const noexcept { return false; } + bool normalized() const noexcept { return false; } /** * @copydoc IPropertyView::offset */ - virtual std::optional offset() const noexcept { - return _offset; - } + std::optional offset() const noexcept { return _offset; } /** * @copydoc IPropertyView::scale */ - virtual std::optional scale() const noexcept { - return _scale; - } + std::optional scale() const noexcept { return _scale; } /** * @copydoc IPropertyView::max */ - virtual std::optional max() const noexcept { return _max; } + std::optional max() const noexcept { return _max; } /** * @copydoc IPropertyView::min */ - virtual std::optional min() const noexcept { return _min; } + std::optional min() const noexcept { return _min; } /** * @copydoc IPropertyView::required */ - virtual bool required() const noexcept { return _required; } + bool required() const noexcept { return _required; } /** * @copydoc IPropertyView::noData */ - virtual std::optional noData() const noexcept { return _noData; } + std::optional noData() const noexcept { return _noData; } /** * @copydoc IPropertyView::defaultValue */ - virtual std::optional defaultValue() const noexcept { + std::optional defaultValue() const noexcept { return _defaultValue; } -private: +protected: PropertyViewStatusType _status; + +private: bool _required; std::optional _offset; @@ -896,6 +976,14 @@ template <> class PropertyView { } protected: + /** + * @brief Constructs an invalid instance for an erroneous property. + * + * @param status The value of {@link PropertyViewStatus} indicating the error with the property. + */ + PropertyView(PropertyViewStatusType status) + : _status(status), _required(false), _defaultValue(std::nullopt) {} + /** * @brief Constructs a property instance from a property table property and * its class definition. @@ -918,57 +1006,57 @@ template <> class PropertyView { /** * @copydoc IPropertyView::status */ - virtual PropertyViewStatusType status() const noexcept { return _status; } + PropertyViewStatusType status() const noexcept { return _status; } /** * @copydoc IPropertyView::arrayCount */ - virtual int64_t arrayCount() const noexcept { return 0; } + int64_t arrayCount() const noexcept { return 0; } /** * @copydoc IPropertyView::normalized */ - virtual bool normalized() const noexcept { return false; } + bool normalized() const noexcept { return false; } /** * @copydoc IPropertyView::offset */ - virtual std::optional offset() const noexcept { return std::nullopt; } + std::optional offset() const noexcept { return std::nullopt; } /** * @copydoc IPropertyView::scale */ - virtual std::optional scale() const noexcept { return std::nullopt; } + std::optional scale() const noexcept { return std::nullopt; } /** * @copydoc IPropertyView::max */ - virtual std::optional max() const noexcept { return std::nullopt; } + std::optional max() const noexcept { return std::nullopt; } /** * @copydoc IPropertyView::min */ - virtual std::optional min() const noexcept { return std::nullopt; } + std::optional min() const noexcept { return std::nullopt; } /** * @copydoc IPropertyView::required */ - virtual bool required() const noexcept { return _required; } + bool required() const noexcept { return _required; } /** * @copydoc IPropertyView::noData */ - virtual std::optional noData() const noexcept { return std::nullopt; } + std::optional noData() const noexcept { return std::nullopt; } /** * @copydoc IPropertyView::defaultValue */ - virtual std::optional defaultValue() const noexcept { - return _defaultValue; - } + std::optional defaultValue() const noexcept { return _defaultValue; } -private: +protected: PropertyViewStatusType _status; + +private: bool _required; std::optional _defaultValue; @@ -1032,6 +1120,17 @@ template <> class PropertyView { } protected: + /** + * @brief Constructs an invalid instance for an erroneous property. + * + * @param status The value of {@link PropertyViewStatus} indicating the error with the property. + */ + PropertyView(PropertyViewStatusType status) + : _status(status), + _required(false), + _noData(std::nullopt), + _defaultValue(std::nullopt) {} + /** * @brief Constructs a property instance from a property table property and * its class definition. @@ -1054,55 +1153,51 @@ template <> class PropertyView { /** * @copydoc IPropertyView::status */ - virtual PropertyViewStatusType status() const noexcept { return _status; } + PropertyViewStatusType status() const noexcept { return _status; } /** * @copydoc IPropertyView::arrayCount */ - virtual int64_t arrayCount() const noexcept { return 0; } + int64_t arrayCount() const noexcept { return 0; } /** * @copydoc IPropertyView::normalized */ - virtual bool normalized() const noexcept { return false; } + bool normalized() const noexcept { return false; } /** * @copydoc IPropertyView::offset */ - virtual std::optional offset() const noexcept { + std::optional offset() const noexcept { return std::nullopt; } /** * @copydoc IPropertyView::scale */ - virtual std::optional scale() const noexcept { + std::optional scale() const noexcept { return std::nullopt; } /** * @copydoc IPropertyView::max */ - virtual std::optional max() const noexcept { - return std::nullopt; - } + std::optional max() const noexcept { return std::nullopt; } /** * @copydoc IPropertyView::min */ - virtual std::optional min() const noexcept { - return std::nullopt; - } + std::optional min() const noexcept { return std::nullopt; } /** * @copydoc IPropertyView::required */ - virtual bool required() const noexcept { return _required; } + bool required() const noexcept { return _required; } /** * @copydoc IPropertyView::noData */ - virtual std::optional noData() const noexcept { + std::optional noData() const noexcept { if (_noData) return std::string_view(*_noData); @@ -1112,15 +1207,17 @@ template <> class PropertyView { /** * @copydoc IPropertyView::defaultValue */ - virtual std::optional defaultValue() const noexcept { + std::optional defaultValue() const noexcept { if (_defaultValue) return std::string_view(*_defaultValue); return std::nullopt; } -private: +protected: PropertyViewStatusType _status; + +private: bool _required; std::optional _noData; std::optional _defaultValue; @@ -1263,6 +1360,22 @@ class PropertyView> { } protected: + /** + * @brief Constructs an invalid instance for an erroneous property. + * + * @param status The value of {@link PropertyViewStatus} indicating the error with the property. + */ + PropertyView(PropertyViewStatusType status) + : _status(status), + _count(0), + _offset(std::nullopt), + _scale(std::nullopt), + _max(std::nullopt), + _min(std::nullopt), + _required(false), + _noData(std::nullopt), + _defaultValue(std::nullopt) {} + /** * @brief Constructs a property instance from a property table property and * its class definition. @@ -1371,23 +1484,22 @@ class PropertyView> { /** * @copydoc IPropertyView::status */ - virtual PropertyViewStatusType status() const noexcept { return _status; } + PropertyViewStatusType status() const noexcept { return _status; } /** * @copydoc IPropertyView::arrayCount */ - virtual int64_t arrayCount() const noexcept { return _count; } + int64_t arrayCount() const noexcept { return _count; } /** * @copydoc IPropertyView::normalized */ - virtual bool normalized() const noexcept { return _normalized; } + bool normalized() const noexcept { return _normalized; } /** * @copydoc IPropertyView::offset */ - virtual std::optional> - offset() const noexcept { + std::optional> offset() const noexcept { if (!_offset) { return std::nullopt; } @@ -1399,7 +1511,7 @@ class PropertyView> { /** * @copydoc IPropertyView::scale */ - virtual std::optional> scale() const noexcept { + std::optional> scale() const noexcept { if (!_scale) { return std::nullopt; } @@ -1411,7 +1523,7 @@ class PropertyView> { /** * @copydoc IPropertyView::max */ - virtual std::optional> max() const noexcept { + std::optional> max() const noexcept { if (!_max) { return std::nullopt; } @@ -1423,7 +1535,7 @@ class PropertyView> { /** * @copydoc IPropertyView::min */ - virtual std::optional> min() const noexcept { + std::optional> min() const noexcept { if (!_min) { return std::nullopt; } @@ -1435,13 +1547,12 @@ class PropertyView> { /** * @copydoc IPropertyView::required */ - virtual bool required() const noexcept { return _required; } + bool required() const noexcept { return _required; } /** * @copydoc IPropertyView::noData */ - virtual std::optional> - noData() const noexcept { + std::optional> noData() const noexcept { if (!_noData) { return std::nullopt; } @@ -1453,8 +1564,7 @@ class PropertyView> { /** * @copydoc IPropertyView::defaultValue */ - virtual std::optional> - defaultValue() const noexcept { + std::optional> defaultValue() const noexcept { if (!_defaultValue) { return std::nullopt; } @@ -1464,8 +1574,10 @@ class PropertyView> { _defaultValue->size())); } -private: +protected: PropertyViewStatusType _status; + +private: int64_t _count; bool _normalized; @@ -1640,6 +1752,18 @@ template <> class PropertyView> { } protected: + /** + * @brief Constructs an invalid instance for an erroneous property. + * + * @param status The value of {@link PropertyViewStatus} indicating the error with the property. + */ + PropertyView(PropertyViewStatusType status) + : _status(status), + _count(0), + _required(false), + _defaultValue(), + _size(0) {} + /** * @brief Constructs a property instance from a property table property and * its class definition. @@ -1662,62 +1786,62 @@ template <> class PropertyView> { /** * @copydoc IPropertyView::status */ - virtual PropertyViewStatusType status() const noexcept { return _status; } + PropertyViewStatusType status() const noexcept { return _status; } /** * @copydoc IPropertyView::arrayCount */ - virtual int64_t arrayCount() const noexcept { return _count; } + int64_t arrayCount() const noexcept { return _count; } /** * @copydoc IPropertyView::normalized */ - virtual bool normalized() const noexcept { return false; } + bool normalized() const noexcept { return false; } /** * @copydoc IPropertyView::offset */ - virtual std::optional> offset() const noexcept { + std::optional> offset() const noexcept { return std::nullopt; } /** * @copydoc IPropertyView::scale */ - virtual std::optional> scale() const noexcept { + std::optional> scale() const noexcept { return std::nullopt; } /** * @copydoc IPropertyView::max */ - virtual std::optional> max() const noexcept { + std::optional> max() const noexcept { return std::nullopt; } /** * @copydoc IPropertyView::min */ - virtual std::optional> min() const noexcept { + std::optional> min() const noexcept { return std::nullopt; } /** * @copydoc IPropertyView::required */ - virtual bool required() const noexcept { return _required; } + bool required() const noexcept { return _required; } /** * @copydoc IPropertyView::noData */ - virtual std::optional> noData() const noexcept { + std::optional> noData() const noexcept { return std::nullopt; } /** * @copydoc IPropertyView::defaultValue */ - virtual std::optional> defaultValue() const noexcept { + std::optional> defaultValue() const noexcept { if (_size > 0) { return PropertyArrayView( gsl::span( @@ -1730,8 +1854,10 @@ template <> class PropertyView> { return std::nullopt; } -private: +protected: PropertyViewStatusType _status; + +private: int64_t _count; bool _required; @@ -1850,8 +1976,26 @@ template <> class PropertyView> { protected: /** - * @brief Constructs a property instance from a property table property and - * its class definition. + * @brief Constructs an invalid instance for an erroneous property. + * + * @param status The value of {@link PropertyViewStatus} indicating the error with the property. + */ + PropertyView(PropertyViewStatusType status) + : _status(status), + _count(0), + _required(false), + _noData(), + _noDataOffsets(), + _noDataOffsetType(PropertyComponentType::None), + _noDataSize(0), + _defaultValue(), + _defaultValueOffsets(), + _defaultValueOffsetType(PropertyComponentType::None), + _defaultValueSize(0) {} + + /** + * @brief Constructs a property instance from a property table property + * and its class definition. */ PropertyView( const ClassProperty& classProperty, @@ -1869,62 +2013,57 @@ template <> class PropertyView> { public: /** - * @copydoc IPropertyView::status + * @copydoc PropertyView::status */ - virtual PropertyViewStatusType status() const noexcept { return _status; } + PropertyViewStatusType status() const noexcept { return _status; } /** - * @copydoc IPropertyView::arrayCount + * @copydoc PropertyView::arrayCount */ - virtual int64_t arrayCount() const noexcept { return _count; } + int64_t arrayCount() const noexcept { return _count; } /** - * @copydoc IPropertyView::normalized + * @copydoc PropertyView::normalized */ - virtual bool normalized() const noexcept { return false; } + bool normalized() const noexcept { return false; } /** - * @copydoc IPropertyView::offset + * @copydoc PropertyView::offset */ - virtual std::optional> - offset() const noexcept { + std::optional> offset() const noexcept { return std::nullopt; } /** - * @copydoc IPropertyView::scale + * @copydoc PropertyView::scale */ - virtual std::optional> - scale() const noexcept { + std::optional> scale() const noexcept { return std::nullopt; } /** - * @copydoc IPropertyView::max + * @copydoc PropertyView::max */ - virtual std::optional> - max() const noexcept { + std::optional> max() const noexcept { return std::nullopt; } /** - * @copydoc IPropertyView::min + * @copydoc PropertyView::min */ - virtual std::optional> - min() const noexcept { + std::optional> min() const noexcept { return std::nullopt; } /** - * @copydoc IPropertyView::required + * @copydoc PropertyView::required */ - virtual bool required() const noexcept { return _required; } + bool required() const noexcept { return _required; } /** - * @copydoc IPropertyView::noData + * @copydoc PropertyView::noData */ - virtual std::optional> - noData() const noexcept { + std::optional> noData() const noexcept { if (_noDataSize > 0) { return PropertyArrayView( gsl::span(_noData.data(), _noData.size()), @@ -1939,9 +2078,9 @@ template <> class PropertyView> { } /** - * @copydoc IPropertyView::defaultValue + * @copydoc PropertyView::defaultValue */ - virtual std::optional> + std::optional> defaultValue() const noexcept { if (_noDataSize > 0) { return PropertyArrayView( @@ -1958,8 +2097,10 @@ template <> class PropertyView> { return std::nullopt; } -private: +protected: PropertyViewStatusType _status; + +private: int64_t _count; bool _required; diff --git a/CesiumGltf/test/TestPropertyTypeTraits.cpp b/CesiumGltf/test/TestPropertyTypeTraits.cpp index 140a2bf2b..a6f7bafcf 100644 --- a/CesiumGltf/test/TestPropertyTypeTraits.cpp +++ b/CesiumGltf/test/TestPropertyTypeTraits.cpp @@ -523,6 +523,23 @@ TEST_CASE("TypeToPropertyType") { } } +TEST_CASE("Test CanBeNormalized") { + REQUIRE(CanBeNormalized::value); + REQUIRE(CanBeNormalized::value); + REQUIRE(CanBeNormalized::value); + REQUIRE(CanBeNormalized::value); + REQUIRE(CanBeNormalized::value); + REQUIRE(CanBeNormalized::value); + REQUIRE(CanBeNormalized::value); + REQUIRE(CanBeNormalized::value); + + + REQUIRE(!CanBeNormalized::value); + REQUIRE(!CanBeNormalized::value); + REQUIRE(!CanBeNormalized::value); + REQUIRE(!CanBeNormalized::value); +} + TEST_CASE("TypeToNormalizedType") { SECTION("Works for scalars") { REQUIRE(std::is_same_v::type, double>); diff --git a/CesiumGltf/test/TestPropertyView.cpp b/CesiumGltf/test/TestPropertyView.cpp index a8be2978e..98edab663 100644 --- a/CesiumGltf/test/TestPropertyView.cpp +++ b/CesiumGltf/test/TestPropertyView.cpp @@ -156,27 +156,6 @@ TEST_CASE("Scalar PropertyView") { REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNormalization); } - SECTION("Constructs with non-optional properties") { - ClassProperty classProperty; - classProperty.type = ClassProperty::Type::SCALAR; - classProperty.componentType = ClassProperty::ComponentType::UINT8; - classProperty.normalized = true; - classProperty.required = true; - - PropertyView view(classProperty); - REQUIRE(view.status() == PropertyViewStatus::Valid); - REQUIRE(view.normalized()); - REQUIRE(view.required()); - - REQUIRE(view.arrayCount() == 0); - REQUIRE(!view.offset()); - REQUIRE(!view.scale()); - REQUIRE(!view.max()); - REQUIRE(!view.min()); - REQUIRE(!view.noData()); - REQUIRE(!view.defaultValue()); - } - SECTION("Ignores count") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::SCALAR; @@ -191,23 +170,23 @@ TEST_CASE("Scalar PropertyView") { SECTION("Constructs with offset, scale, max, and min") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::SCALAR; - classProperty.componentType = ClassProperty::ComponentType::INT16; - classProperty.offset = 5; - classProperty.scale = 2; - classProperty.max = 10; - classProperty.min = -10; + classProperty.componentType = ClassProperty::ComponentType::FLOAT32; + classProperty.offset = 5.04f; + classProperty.scale = 2.2f; + classProperty.max = 10.5f; + classProperty.min = -10.5f; - PropertyView view(classProperty); + PropertyView view(classProperty); REQUIRE(view.status() == PropertyViewStatus::Valid); REQUIRE(view.offset()); REQUIRE(view.scale()); REQUIRE(view.max()); REQUIRE(view.min()); - REQUIRE(*view.offset() == 5); - REQUIRE(*view.scale() == 2); - REQUIRE(*view.max() == 10); - REQUIRE(*view.min() == -10); + REQUIRE(*view.offset() == 5.04f); + REQUIRE(*view.scale() == 2.2f); + REQUIRE(*view.max() == 10.5f); + REQUIRE(*view.min() == -10.5f); } SECTION("Constructs with noData and defaultProperty") { @@ -228,19 +207,27 @@ TEST_CASE("Scalar PropertyView") { REQUIRE(*view.defaultValue() == 1); } - SECTION("Ignores noData and defaultProperty when required is true") { + SECTION("Reports errors for incorrectly defined properties") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::SCALAR; - classProperty.componentType = ClassProperty::ComponentType::UINT8; + classProperty.componentType = ClassProperty::ComponentType::INT8; classProperty.required = true; - classProperty.noData = 0; classProperty.defaultProperty = 1; - PropertyView view(classProperty); - REQUIRE(view.status() == PropertyViewStatus::Valid); - REQUIRE(view.required()); - REQUIRE(!view.noData()); - REQUIRE(!view.defaultValue()); + PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidDefaultValue); + + classProperty.noData = 0; + view = PropertyView(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNoDataValue); + + classProperty.scale = 200; + view = PropertyView(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidScale); + + classProperty.offset = 1234; + view = PropertyView(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidOffset); } SECTION("Reports errors for out-of-bounds values") { @@ -263,43 +250,27 @@ TEST_CASE("Scalar PropertyView") { classProperty.max = 1000; view = PropertyView(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMax); - - classProperty.scale = 200; - view = PropertyView(classProperty); - REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidScale); - - classProperty.offset = 1234; - view = PropertyView(classProperty); - REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidOffset); } SECTION("Reports errors for invalid types") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::SCALAR; - classProperty.componentType = ClassProperty::ComponentType::INT8; + classProperty.componentType = ClassProperty::ComponentType::FLOAT32; classProperty.defaultProperty = JsonValue::Array{1}; - PropertyView view(classProperty); + PropertyView view(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidDefaultValue); classProperty.noData = "0"; - view = PropertyView(classProperty); + view = PropertyView(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNoDataValue); - classProperty.min = -12.7f; - view = PropertyView(classProperty); - REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMin); - - classProperty.max = 5.5; - view = PropertyView(classProperty); - REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMax); - classProperty.scale = false; - view = PropertyView(classProperty); + view = PropertyView(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidScale); classProperty.offset = JsonValue::Array{}; - view = PropertyView(classProperty); + view = PropertyView(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidOffset); } } @@ -356,27 +327,6 @@ TEST_CASE("VecN PropertyView") { REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNormalization); } - SECTION("Constructs with non-optional properties") { - ClassProperty classProperty; - classProperty.type = ClassProperty::Type::VEC3; - classProperty.componentType = ClassProperty::ComponentType::INT16; - classProperty.normalized = true; - classProperty.required = true; - - PropertyView view(classProperty); - REQUIRE(view.status() == PropertyViewStatus::Valid); - REQUIRE(view.normalized()); - REQUIRE(view.required()); - - REQUIRE(view.arrayCount() == 0); - REQUIRE(!view.offset()); - REQUIRE(!view.scale()); - REQUIRE(!view.max()); - REQUIRE(!view.min()); - REQUIRE(!view.noData()); - REQUIRE(!view.defaultValue()); - } - SECTION("Ignores count") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::VEC3; From 056eecc6b38bd6f61a045f7de1f11356f24e1e99 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Mon, 21 Aug 2023 18:36:49 +1000 Subject: [PATCH 185/421] Give tileset a root "external" tile. --- .../include/Cesium3DTilesSelection/Tile.h | 2 +- .../Cesium3DTilesSelection/TileContent.h | 28 ++- .../include/Cesium3DTilesSelection/Tileset.h | 2 - .../TilesetContentLoader.h | 10 - Cesium3DTilesSelection/src/Tile.cpp | 4 +- Cesium3DTilesSelection/src/TileContent.cpp | 32 ++- Cesium3DTilesSelection/src/Tileset.cpp | 18 +- .../src/TilesetContentManager.cpp | 15 +- .../src/TilesetContentManager.h | 2 - .../src/TilesetJsonLoader.cpp | 116 ++++++++--- .../test/TestTilesetContentManager.cpp | 6 +- .../test/TestTilesetJsonLoader.cpp | 185 ++++++++++-------- .../test/TestTilesetSelectionAlgorithm.cpp | 170 +++++++++------- 13 files changed, 352 insertions(+), 238 deletions(-) diff --git a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/Tile.h b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/Tile.h index 7e0a9f25f..3071acb29 100644 --- a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/Tile.h +++ b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/Tile.h @@ -115,7 +115,7 @@ class CESIUM3DTILESSELECTION_API Tile final { */ Tile( TilesetContentLoader* pLoader, - TileExternalContent externalContent) noexcept; + std::unique_ptr&& externalContent) noexcept; /** * @brief Construct a tile with an empty content and a loader that is diff --git a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/TileContent.h b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/TileContent.h index 981b1c085..7309f5fd8 100644 --- a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/TileContent.h +++ b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/TileContent.h @@ -47,7 +47,9 @@ struct CESIUM3DTILESSELECTION_API TileEmptyContent {}; * external tileset. When this tile is loaded, all the tiles in the * external tileset will become children of this external content tile */ -struct CESIUM3DTILESSELECTION_API TileExternalContent {}; +class CESIUM3DTILESSELECTION_API TileExternalContent { +public: +}; /** * @brief A content tag that indicates a tile has a glTF model content and @@ -198,7 +200,7 @@ class CESIUM3DTILESSELECTION_API TileContent { using TileContentKindImpl = std::variant< TileUnknownContent, TileEmptyContent, - TileExternalContent, + std::unique_ptr, std::unique_ptr>; public: @@ -218,7 +220,7 @@ class CESIUM3DTILESSELECTION_API TileContent { * @brief Construct an external content for a tile whose content * points to an external tileset */ - TileContent(TileExternalContent content); + TileContent(std::unique_ptr&& content); /** * @brief Set an unknown content tag for a tile. This constructor @@ -236,7 +238,7 @@ class CESIUM3DTILESSELECTION_API TileContent { * @brief Set an external content for a tile whose content * points to an external tileset */ - void setContentKind(TileExternalContent content); + void setContentKind(std::unique_ptr&& content); /** * @brief Set a glTF model content for a tile @@ -267,21 +269,27 @@ class CESIUM3DTILESSELECTION_API TileContent { /** * @brief Get the {@link TileRenderContent} which stores the glTF model * and render resources of the tile - * - * @return The {@link TileRenderContent} which stores the glTF model - * and render resources of the tile */ const TileRenderContent* getRenderContent() const noexcept; /** * @brief Get the {@link TileRenderContent} which stores the glTF model * and render resources of the tile - * - * @return The {@link TileRenderContent} which stores the glTF model - * and render resources of the tile */ TileRenderContent* getRenderContent() noexcept; + /** + * @brief Get the {@link TileExternalContent} which stores the details of + * the external tileset. + */ + const TileExternalContent* getExternalContent() const noexcept; + + /** + * @brief Get the {@link TileExternalContent} which stores the details of + * the external tileset. + */ + TileExternalContent* getExternalContent() noexcept; + private: TileContentKindImpl _contentKind; }; diff --git a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/Tileset.h b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/Tileset.h index 4f0ee26ef..c0de9c35d 100644 --- a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/Tileset.h +++ b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/Tileset.h @@ -98,8 +98,6 @@ class CESIUM3DTILESSELECTION_API Tileset final { */ const std::vector& getTilesetCredits() const noexcept; - const std::optional& getSchema() const noexcept; - /** * @brief Sets whether or not the tileset's credits should be shown on screen. * @param showCreditsOnScreen Whether the credits should be shown on screen. diff --git a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/TilesetContentLoader.h b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/TilesetContentLoader.h index 1bfba842f..ad10786e9 100644 --- a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/TilesetContentLoader.h +++ b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/TilesetContentLoader.h @@ -136,15 +136,5 @@ class CESIUM3DTILESSELECTION_API TilesetContentLoader { * @return The {@link TileChildrenResult} that stores the tile's children */ virtual TileChildrenResult createTileChildren(const Tile& tile) = 0; - - std::optional& getSchema() noexcept { - return this->_schema; - } - const std::optional& getSchema() const noexcept { - return this->_schema; - } - -private: - std::optional _schema; }; } // namespace Cesium3DTilesSelection diff --git a/Cesium3DTilesSelection/src/Tile.cpp b/Cesium3DTilesSelection/src/Tile.cpp index 87df7636c..5db390341 100644 --- a/Cesium3DTilesSelection/src/Tile.cpp +++ b/Cesium3DTilesSelection/src/Tile.cpp @@ -21,12 +21,12 @@ Tile::Tile(TilesetContentLoader* pLoader) noexcept Tile::Tile( TilesetContentLoader* pLoader, - TileExternalContent externalContent) noexcept + std::unique_ptr&& externalContent) noexcept : Tile( TileConstructorImpl{}, TileLoadState::ContentLoaded, pLoader, - externalContent) {} + TileContent(std::move(externalContent))) {} Tile::Tile( TilesetContentLoader* pLoader, diff --git a/Cesium3DTilesSelection/src/TileContent.cpp b/Cesium3DTilesSelection/src/TileContent.cpp index e039dd071..59813c2fd 100644 --- a/Cesium3DTilesSelection/src/TileContent.cpp +++ b/Cesium3DTilesSelection/src/TileContent.cpp @@ -78,7 +78,8 @@ TileContent::TileContent() : _contentKind{TileUnknownContent{}} {} TileContent::TileContent(TileEmptyContent content) : _contentKind{content} {} -TileContent::TileContent(TileExternalContent content) : _contentKind{content} {} +TileContent::TileContent(std::unique_ptr&& content) + : _contentKind{std::move(content)} {} void TileContent::setContentKind(TileUnknownContent content) { _contentKind = content; @@ -88,8 +89,9 @@ void TileContent::setContentKind(TileEmptyContent content) { _contentKind = content; } -void TileContent::setContentKind(TileExternalContent content) { - _contentKind = content; +void TileContent::setContentKind( + std::unique_ptr&& content) { + _contentKind = std::move(content); } void TileContent::setContentKind(std::unique_ptr&& content) { @@ -105,7 +107,8 @@ bool TileContent::isEmptyContent() const noexcept { } bool TileContent::isExternalContent() const noexcept { - return std::holds_alternative(this->_contentKind); + return std::holds_alternative>( + this->_contentKind); } bool TileContent::isRenderContent() const noexcept { @@ -132,4 +135,25 @@ TileRenderContent* TileContent::getRenderContent() noexcept { return nullptr; } + +const TileExternalContent* TileContent::getExternalContent() const noexcept { + const std::unique_ptr* pRenderContent = + std::get_if>(&this->_contentKind); + if (pRenderContent) { + return pRenderContent->get(); + } + + return nullptr; +} + +TileExternalContent* TileContent::getExternalContent() noexcept { + std::unique_ptr* pRenderContent = + std::get_if>(&this->_contentKind); + if (pRenderContent) { + return pRenderContent->get(); + } + + return nullptr; +} + } // namespace Cesium3DTilesSelection diff --git a/Cesium3DTilesSelection/src/Tileset.cpp b/Cesium3DTilesSelection/src/Tileset.cpp index 91563585e..4c908201e 100644 --- a/Cesium3DTilesSelection/src/Tileset.cpp +++ b/Cesium3DTilesSelection/src/Tileset.cpp @@ -101,11 +101,6 @@ const std::vector& Tileset::getTilesetCredits() const noexcept { return this->_pTilesetContentManager->getTilesetCredits(); } -const std::optional& -Tileset::getSchema() const noexcept { - return this->_pTilesetContentManager->getSchema(); -} - void Tileset::setShowCreditsOnScreen(bool showCreditsOnScreen) noexcept { this->_options.showCreditsOnScreen = showCreditsOnScreen; @@ -784,13 +779,16 @@ Tileset::TraversalDetails Tileset::_visitTileIfNeeded( this->_frustumCull(tile, frameState, cullWithChildrenBounds, cullResult); this->_fogCull(frameState, distances, cullResult); - if (this->_options.forbidHoles && !cullResult.shouldVisit && - tile.getRefine() == TileRefine::Replace && - tile.getUnconditionallyRefine()) { + if (!cullResult.shouldVisit && tile.getUnconditionallyRefine()) { // Unconditionally refined tiles must always be visited in forbidHoles // mode, because we need to load this tile's descendants before we can - // render any of its siblings. - cullResult.shouldVisit = true; + // render any of its siblings. An unconditionally refined root tile must be + // visited as well, otherwise we won't load anything at all. + if ((this->_options.forbidHoles && + tile.getRefine() == TileRefine::Replace) || + tile.getParent() == nullptr) { + cullResult.shouldVisit = true; + } } if (!cullResult.shouldVisit) { diff --git a/Cesium3DTilesSelection/src/TilesetContentManager.cpp b/Cesium3DTilesSelection/src/TilesetContentManager.cpp index eb0b74026..699aef4eb 100644 --- a/Cesium3DTilesSelection/src/TilesetContentManager.cpp +++ b/Cesium3DTilesSelection/src/TilesetContentManager.cpp @@ -37,8 +37,9 @@ struct ContentKindSetter { tileContent.setContentKind(content); } - void operator()(TileExternalContent content) { - tileContent.setContentKind(content); + void operator()(TileExternalContent&& content) { + tileContent.setContentKind( + std::make_unique(std::move(content))); } void operator()(CesiumGltf::Model&& model) { @@ -1169,16 +1170,6 @@ void TilesetContentManager::finishLoading( updateTileContent(tile, 0.0, tilesetOptions); } -const std::optional& -TilesetContentManager::getSchema() const noexcept { - static const std::optional noSchema; - if (this->_pLoader) { - return this->_pLoader->getSchema(); - } else { - return noSchema; - } -} - void TilesetContentManager::setTileContent( Tile& tile, TileLoadResult&& result, diff --git a/Cesium3DTilesSelection/src/TilesetContentManager.h b/Cesium3DTilesSelection/src/TilesetContentManager.h index e0548adad..6b7d6b7c0 100644 --- a/Cesium3DTilesSelection/src/TilesetContentManager.h +++ b/Cesium3DTilesSelection/src/TilesetContentManager.h @@ -106,8 +106,6 @@ class TilesetContentManager // Transition the tile from the ContentLoaded to the Done state. void finishLoading(Tile& tile, const TilesetOptions& tilesetOptions); - const std::optional& getSchema() const noexcept; - private: static void setTileContent( Tile& tile, diff --git a/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp b/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp index 028eec6e1..4f6b050d0 100644 --- a/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp +++ b/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp @@ -512,7 +512,7 @@ std::optional parseTileJsonRecursively( if (implicitTilingJson) { // mark this tile as external - Tile tile{¤tLoader, TileExternalContent{}}; + Tile tile{¤tLoader, std::make_unique()}; tile.setTileID(""); tile.setTransform(tileTransform); tile.setBoundingVolume(tileBoundingVolume); @@ -596,31 +596,17 @@ TilesetContentLoaderResult parseTilesetJson( auto gltfUpAxis = obtainGltfUpAxis(tilesetJson, pLogger); auto pLoader = std::make_unique(baseUrl, gltfUpAxis); - const auto schemaIt = tilesetJson.FindMember("schema"); - if (schemaIt != tilesetJson.MemberEnd()) { - auto schemaResult = Cesium3DTilesReader::readSchema(schemaIt->value); - if (schemaResult.value) { - pLoader->getSchema() = std::move(*schemaResult.value); - } - } - - const auto metadataIt = tilesetJson.FindMember("metadata"); - if (metadataIt != tilesetJson.MemberEnd()) { - auto metadataResult = - Cesium3DTilesReader::readMetadataEntity(metadataIt->value); - if (metadataResult.value) { - // pLoader->getSchema() = std::move(*schemaResult.value); - } - } + pRootTile = std::make_unique( + pLoader.get(), + std::make_unique()); - const auto groupsIt = tilesetJson.FindMember("groups"); - if (groupsIt != tilesetJson.MemberEnd()) { - auto groupsResult = - Cesium3DTilesReader::readGroupMetadataArray(groupsIt->value); - if (groupsResult.value) { - // pLoader->getSchema() = std::move(*schemaResult.value); - } - } + pRootTile->setTileID(""); + pRootTile->setTransform(parentTransform); + pRootTile->setBoundingVolume(CesiumGeometry::BoundingSphere( + glm::dvec3(0.0), + std::numeric_limits::max())); + pRootTile->setGeometricError(10000000.0); + pRootTile->setRefine(parentRefine); const auto rootIt = tilesetJson.FindMember("root"); if (rootIt != tilesetJson.MemberEnd()) { @@ -727,6 +713,42 @@ TilesetJsonLoader::TilesetJsonLoader( CesiumGeometry::Axis upAxis) : _baseUrl{baseUrl}, _upAxis{upAxis}, _children{} {} +namespace { + +void parseTilesetMetadata(const rapidjson::Document& tilesetJson, Tile& tile) { + TileExternalContent* pExternal = tile.getContent().getExternalContent(); + assert(pExternal); + if (!pExternal) + return; + + auto schemaIt = tilesetJson.FindMember("schema"); + if (schemaIt != tilesetJson.MemberEnd()) { + auto schemaResult = Cesium3DTilesReader::readSchema(schemaIt->value); + if (schemaResult.value) { + // pLoader->setSchema(std::move(*schemaResult.value)); + } + } + + const auto metadataIt = tilesetJson.FindMember("metadata"); + if (metadataIt != tilesetJson.MemberEnd()) { + auto metadataResult = + Cesium3DTilesReader::readMetadataEntity(metadataIt->value); + if (metadataResult.value) { + // pLoader->getSchema() = std::move(*schemaResult.value); + } + } + + const auto groupsIt = tilesetJson.FindMember("groups"); + if (groupsIt != tilesetJson.MemberEnd()) { + auto groupsResult = + Cesium3DTilesReader::readGroupMetadataArray(groupsIt->value); + if (groupsResult.value) { + // pLoader->getSchema() = std::move(*schemaResult.value); + } + } +} +} // namespace + CesiumAsync::Future> TilesetJsonLoader::createLoader( const TilesetExternals& externals, @@ -759,12 +781,26 @@ TilesetJsonLoader::createLoader( return result; } - return parseTilesetJson( + gsl::span data = pResponse->data(); + + rapidjson::Document tilesetJson; + tilesetJson.Parse( + reinterpret_cast(data.data()), + data.size()); + if (tilesetJson.HasParseError()) { + TilesetContentLoaderResult result; + result.errors.emplaceError(fmt::format( + "Error when parsing tileset JSON, error code {} at byte offset " + "{}", + tilesetJson.GetParseError(), + tilesetJson.GetErrorOffset())); + return result; + } + + return TilesetJsonLoader::createLoader( pLogger, pCompletedRequest->url(), - pResponse->data(), - glm::dmat4(1.0), - TileRefine::Replace); + tilesetJson); }); } @@ -772,12 +808,32 @@ TilesetContentLoaderResult TilesetJsonLoader::createLoader( const std::shared_ptr& pLogger, const std::string& tilesetJsonUrl, const rapidjson::Document& tilesetJson) { - return parseTilesetJson( + TilesetContentLoaderResult result = parseTilesetJson( pLogger, tilesetJsonUrl, tilesetJson, glm::dmat4(1.0), TileRefine::Replace); + + // Create a root tile to represent the tileset.json itself. + std::vector children; + children.emplace_back(std::move(*result.pRootTile)); + + result.pRootTile = std::make_unique( + children[0].getLoader(), + std::make_unique()); + + result.pRootTile->setTileID(""); + result.pRootTile->setTransform(children[0].getTransform()); + result.pRootTile->setBoundingVolume(children[0].getBoundingVolume()); + result.pRootTile->setUnconditionallyRefine(); + result.pRootTile->setRefine(children[0].getRefine()); + result.pRootTile->createChildTiles(std::move(children)); + + // Populate the root tile with metadata + parseTilesetMetadata(tilesetJson, *result.pRootTile); + + return result; } CesiumAsync::Future diff --git a/Cesium3DTilesSelection/test/TestTilesetContentManager.cpp b/Cesium3DTilesSelection/test/TestTilesetContentManager.cpp index 925abb554..9755229ea 100644 --- a/Cesium3DTilesSelection/test/TestTilesetContentManager.cpp +++ b/Cesium3DTilesSelection/test/TestTilesetContentManager.cpp @@ -219,8 +219,10 @@ TEST_CASE("Test the manager can be initialized with correct loaders") { CHECK(manager.getNumberOfTilesLoaded() == 1); // check root - const Tile* pRootTile = manager.getRootTile(); - CHECK(pRootTile); + const Tile* pTilesetJson = manager.getRootTile(); + REQUIRE(pTilesetJson); + REQUIRE(pTilesetJson->getChildren().size() == 1); + const Tile* pRootTile = &pTilesetJson->getChildren()[0]; CHECK(std::get(pRootTile->getTileID()) == "parent.b3dm"); CHECK(pRootTile->getGeometricError() == 70.0); CHECK(pRootTile->getRefine() == TileRefine::Add); diff --git a/Cesium3DTilesSelection/test/TestTilesetJsonLoader.cpp b/Cesium3DTilesSelection/test/TestTilesetJsonLoader.cpp index 99cba3035..d03b7c8da 100644 --- a/Cesium3DTilesSelection/test/TestTilesetJsonLoader.cpp +++ b/Cesium3DTilesSelection/test/TestTilesetJsonLoader.cpp @@ -118,9 +118,12 @@ TEST_CASE("Test creating tileset json loader") { CHECK(!loaderResult.errors.hasErrors()); // check root tile - auto pRootTile = loaderResult.pRootTile.get(); - CHECK(pRootTile != nullptr); - CHECK(pRootTile->getParent() == nullptr); + auto pTilesetJson = loaderResult.pRootTile.get(); + REQUIRE(pTilesetJson); + REQUIRE(pTilesetJson->getChildren().size() == 1); + CHECK(pTilesetJson->getParent() == nullptr); + auto pRootTile = &pTilesetJson->getChildren()[0]; + CHECK(pRootTile->getParent() == pTilesetJson); CHECK(pRootTile->getChildren().size() == 4); CHECK(pRootTile->getGeometricError() == 70.0); CHECK(pRootTile->getRefine() == TileRefine::Replace); @@ -182,9 +185,12 @@ TEST_CASE("Test creating tileset json loader") { CHECK(!loaderResult.errors.hasErrors()); // check root tile - auto pRootTile = loaderResult.pRootTile.get(); - CHECK(pRootTile != nullptr); - CHECK(pRootTile->getParent() == nullptr); + auto pTilesetJson = loaderResult.pRootTile.get(); + REQUIRE(pTilesetJson != nullptr); + CHECK(pTilesetJson->getParent() == nullptr); + REQUIRE(pTilesetJson->getChildren().size() == 1); + auto pRootTile = &pTilesetJson->getChildren()[0]; + CHECK(pRootTile->getParent() == pTilesetJson); CHECK(pRootTile->getChildren().size() == 4); CHECK(pRootTile->getGeometricError() == 70.0); CHECK(pRootTile->getRefine() == TileRefine::Add); @@ -225,9 +231,10 @@ TEST_CASE("Test creating tileset json loader") { testDataPath / "MultipleKindsOfTilesets" / "SphereBoundingVolumeTileset.json"); - CHECK(loaderResult.pRootTile); + REQUIRE(loaderResult.pRootTile); + REQUIRE(loaderResult.pRootTile->getChildren().size() == 1); - const Tile& rootTile = *loaderResult.pRootTile; + const Tile& rootTile = loaderResult.pRootTile->getChildren()[0]; const CesiumGeometry::BoundingSphere& sphere = std::get(rootTile.getBoundingVolume()); CHECK(sphere.getCenter() == glm::dvec3(0.0, 0.0, 10.0)); @@ -242,9 +249,10 @@ TEST_CASE("Test creating tileset json loader") { testDataPath / "MultipleKindsOfTilesets" / "BoxBoundingVolumeTileset.json"); - CHECK(loaderResult.pRootTile); + REQUIRE(loaderResult.pRootTile); + REQUIRE(loaderResult.pRootTile->getChildren().size() == 1); - const Tile& rootTile = *loaderResult.pRootTile; + const Tile& rootTile = loaderResult.pRootTile->getChildren()[0]; const CesiumGeometry::OrientedBoundingBox& box = std::get( rootTile.getBoundingVolume()); @@ -261,8 +269,10 @@ TEST_CASE("Test creating tileset json loader") { "NoBoundingVolumeTileset.json"); CHECK(!loaderResult.errors.hasErrors()); - CHECK(loaderResult.pRootTile); - CHECK(loaderResult.pRootTile->getChildren().empty()); + REQUIRE(loaderResult.pRootTile); + REQUIRE(loaderResult.pRootTile->getChildren().size() == 1); + auto pRootTile = &loaderResult.pRootTile->getChildren()[0]; + CHECK(pRootTile->getChildren().empty()); // check loader up axis CHECK(loaderResult.pLoader->getUpAxis() == CesiumGeometry::Axis::Y); @@ -274,10 +284,12 @@ TEST_CASE("Test creating tileset json loader") { "NoGeometricErrorTileset.json"); CHECK(!loaderResult.errors.hasErrors()); - CHECK(loaderResult.pRootTile); - CHECK(loaderResult.pRootTile->getGeometricError() == Approx(70.0)); - CHECK(loaderResult.pRootTile->getChildren().size() == 4); - for (const Tile& child : loaderResult.pRootTile->getChildren()) { + REQUIRE(loaderResult.pRootTile); + REQUIRE(loaderResult.pRootTile->getChildren().size() == 1); + auto pRootTile = &loaderResult.pRootTile->getChildren()[0]; + CHECK(pRootTile->getGeometricError() == Approx(70.0)); + CHECK(pRootTile->getChildren().size() == 4); + for (const Tile& child : pRootTile->getChildren()) { CHECK(child.getGeometricError() == Approx(35.0)); } @@ -291,11 +303,13 @@ TEST_CASE("Test creating tileset json loader") { "NoCapitalizedRefineTileset.json"); CHECK(!loaderResult.errors.hasErrors()); - CHECK(loaderResult.pRootTile); - CHECK(loaderResult.pRootTile->getGeometricError() == Approx(70.0)); - CHECK(loaderResult.pRootTile->getRefine() == TileRefine::Add); - CHECK(loaderResult.pRootTile->getChildren().size() == 4); - for (const Tile& child : loaderResult.pRootTile->getChildren()) { + REQUIRE(loaderResult.pRootTile); + REQUIRE(loaderResult.pRootTile->getChildren().size() == 1); + auto pRootTile = &loaderResult.pRootTile->getChildren()[0]; + CHECK(pRootTile->getGeometricError() == Approx(70.0)); + CHECK(pRootTile->getRefine() == TileRefine::Add); + CHECK(pRootTile->getChildren().size() == 4); + for (const Tile& child : pRootTile->getChildren()) { CHECK(child.getGeometricError() == Approx(5.0)); CHECK(child.getRefine() == TileRefine::Replace); } @@ -310,10 +324,12 @@ TEST_CASE("Test creating tileset json loader") { "ScaleGeometricErrorTileset.json"); CHECK(!loaderResult.errors.hasErrors()); - CHECK(loaderResult.pRootTile); - CHECK(loaderResult.pRootTile->getGeometricError() == Approx(210.0)); - CHECK(loaderResult.pRootTile->getChildren().size() == 4); - for (const Tile& child : loaderResult.pRootTile->getChildren()) { + REQUIRE(loaderResult.pRootTile); + REQUIRE(loaderResult.pRootTile->getChildren().size() == 1); + auto pRootTile = &loaderResult.pRootTile->getChildren()[0]; + CHECK(pRootTile->getGeometricError() == Approx(210.0)); + CHECK(pRootTile->getChildren().size() == 4); + for (const Tile& child : pRootTile->getChildren()) { CHECK(child.getGeometricError() == Approx(15.0)); } @@ -325,11 +341,13 @@ TEST_CASE("Test creating tileset json loader") { auto loaderResult = createLoader( testDataPath / "MultipleKindsOfTilesets" / "EmptyTileTileset.json"); CHECK(!loaderResult.errors.hasErrors()); - CHECK(loaderResult.pRootTile); - CHECK(loaderResult.pRootTile->getGeometricError() == Approx(70.0)); - CHECK(loaderResult.pRootTile->getChildren().size() == 1); + REQUIRE(loaderResult.pRootTile); + REQUIRE(loaderResult.pRootTile->getChildren().size() == 1); + auto pRootTile = &loaderResult.pRootTile->getChildren()[0]; + CHECK(pRootTile->getGeometricError() == Approx(70.0)); + CHECK(pRootTile->getChildren().size() == 1); - const Tile& child = loaderResult.pRootTile->getChildren().front(); + const Tile& child = pRootTile->getChildren().front(); CHECK(child.isEmptyContent()); // check loader up axis @@ -341,19 +359,19 @@ TEST_CASE("Test creating tileset json loader") { testDataPath / "MultipleKindsOfTilesets" / "QuadtreeImplicitTileset.json"); CHECK(!loaderResult.errors.hasErrors()); - CHECK(loaderResult.pRootTile); + REQUIRE(loaderResult.pRootTile); CHECK(loaderResult.pRootTile->isExternalContent()); - CHECK(loaderResult.pRootTile->getChildren().size() == 1); - CHECK( - loaderResult.pRootTile->getTransform() == glm::dmat4(glm::dmat3(2.0))); + REQUIRE(loaderResult.pRootTile->getChildren().size() == 1); + auto pRootTile = &loaderResult.pRootTile->getChildren()[0]; + CHECK(pRootTile->isExternalContent()); + CHECK(pRootTile->getChildren().size() == 1); + CHECK(pRootTile->getTransform() == glm::dmat4(glm::dmat3(2.0))); - const Tile& child = loaderResult.pRootTile->getChildren().front(); + const Tile& child = pRootTile->getChildren().front(); CHECK(child.getLoader() != loaderResult.pLoader.get()); - CHECK( - child.getGeometricError() == - loaderResult.pRootTile->getGeometricError()); - CHECK(child.getRefine() == loaderResult.pRootTile->getRefine()); - CHECK(child.getTransform() == loaderResult.pRootTile->getTransform()); + CHECK(child.getGeometricError() == pRootTile->getGeometricError()); + CHECK(child.getRefine() == pRootTile->getRefine()); + CHECK(child.getTransform() == pRootTile->getTransform()); CHECK( std::get(child.getTileID()) == CesiumGeometry::QuadtreeTileID(0, 0, 0)); @@ -364,36 +382,36 @@ TEST_CASE("Test creating tileset json loader") { testDataPath / "MultipleKindsOfTilesets" / "OctreeImplicitTileset.json"); CHECK(!loaderResult.errors.hasErrors()); - CHECK(loaderResult.pRootTile); + REQUIRE(loaderResult.pRootTile); CHECK(loaderResult.pRootTile->isExternalContent()); - CHECK(loaderResult.pRootTile->getChildren().size() == 1); - CHECK( - loaderResult.pRootTile->getTransform() == glm::dmat4(glm::dmat3(2.0))); + REQUIRE(loaderResult.pRootTile->getChildren().size() == 1); + auto pRootTile = &loaderResult.pRootTile->getChildren()[0]; + CHECK(pRootTile->isExternalContent()); + CHECK(pRootTile->getChildren().size() == 1); + CHECK(pRootTile->getTransform() == glm::dmat4(glm::dmat3(2.0))); - const Tile& child = loaderResult.pRootTile->getChildren().front(); + const Tile& child = pRootTile->getChildren().front(); CHECK(child.getLoader() != loaderResult.pLoader.get()); - CHECK( - child.getGeometricError() == - loaderResult.pRootTile->getGeometricError()); - CHECK(child.getRefine() == loaderResult.pRootTile->getRefine()); - CHECK(child.getTransform() == loaderResult.pRootTile->getTransform()); + CHECK(child.getGeometricError() == pRootTile->getGeometricError()); + CHECK(child.getRefine() == pRootTile->getRefine()); + CHECK(child.getTransform() == pRootTile->getTransform()); CHECK( std::get(child.getTileID()) == CesiumGeometry::OctreeTileID(0, 0, 0, 0)); } - SECTION("Tileset with metadata") { - auto loaderResult = - createLoader(testDataPath / "WithMetadata" / "tileset.json"); + // SECTION("Tileset with metadata") { + // auto loaderResult = + // createLoader(testDataPath / "WithMetadata" / "tileset.json"); - CHECK(!loaderResult.errors.hasErrors()); - REQUIRE(loaderResult.pLoader); + // CHECK(!loaderResult.errors.hasErrors()); + // REQUIRE(loaderResult.pLoader); - const std::optional& schema = - loaderResult.pLoader->getSchema(); - REQUIRE(schema); - CHECK(schema->id == "foo"); - } + // const std::optional& schema = + // loaderResult.pLoader->getSchema(); + // REQUIRE(schema); + // CHECK(schema->id == "foo"); + // } } TEST_CASE("Test loading individual tile of tileset json") { @@ -402,17 +420,19 @@ TEST_CASE("Test loading individual tile of tileset json") { SECTION("Load tile that has render content") { auto loaderResult = createLoader(testDataPath / "ReplaceTileset" / "tileset.json"); - CHECK(loaderResult.pRootTile); + REQUIRE(loaderResult.pRootTile); + REQUIRE(loaderResult.pRootTile->getChildren().size() == 1); - const auto& tileID = - std::get(loaderResult.pRootTile->getTileID()); + auto pRootTile = &loaderResult.pRootTile->getChildren()[0]; + + const auto& tileID = std::get(pRootTile->getTileID()); CHECK(tileID == "parent.b3dm"); // check tile content auto tileLoadResult = loadTileContent( testDataPath / "ReplaceTileset" / tileID, *loaderResult.pLoader, - *loaderResult.pRootTile); + *pRootTile); CHECK( std::holds_alternative(tileLoadResult.contentKind)); CHECK(tileLoadResult.updatedBoundingVolume == std::nullopt); @@ -425,17 +445,19 @@ TEST_CASE("Test loading individual tile of tileset json") { auto loaderResult = createLoader(testDataPath / "AddTileset" / "tileset.json"); - CHECK(loaderResult.pRootTile); + REQUIRE(loaderResult.pRootTile); + REQUIRE(loaderResult.pRootTile->getChildren().size() == 1); - const auto& tileID = - std::get(loaderResult.pRootTile->getTileID()); + auto pRootTile = &loaderResult.pRootTile->getChildren()[0]; + + const auto& tileID = std::get(pRootTile->getTileID()); CHECK(tileID == "tileset2.json"); // check tile content auto tileLoadResult = loadTileContent( testDataPath / "AddTileset" / tileID, *loaderResult.pLoader, - *loaderResult.pRootTile); + *pRootTile); CHECK(tileLoadResult.updatedBoundingVolume == std::nullopt); CHECK(tileLoadResult.updatedContentBoundingVolume == std::nullopt); CHECK(std::holds_alternative( @@ -444,11 +466,12 @@ TEST_CASE("Test loading individual tile of tileset json") { CHECK(tileLoadResult.tileInitializer); // check tile is really an external tile - loaderResult.pRootTile->getContent().setContentKind( - std::get(tileLoadResult.contentKind)); - tileLoadResult.tileInitializer(*loaderResult.pRootTile); - const auto& children = loaderResult.pRootTile->getChildren(); - CHECK(children.size() == 1); + pRootTile->getContent().setContentKind( + std::make_unique( + std::get(tileLoadResult.contentKind))); + tileLoadResult.tileInitializer(*pRootTile); + const auto& children = pRootTile->getChildren(); + REQUIRE(children.size() == 1); const Tile& parentB3dmTile = children[0]; CHECK(std::get(parentB3dmTile.getTileID()) == "parent.b3dm"); @@ -474,11 +497,14 @@ TEST_CASE("Test loading individual tile of tileset json") { auto loaderResult = createLoader(testDataPath / "ImplicitTileset" / "tileset_1.1.json"); - CHECK(loaderResult.pRootTile); + REQUIRE(loaderResult.pRootTile); CHECK(loaderResult.pRootTile->isExternalContent()); - CHECK(loaderResult.pRootTile->getChildren().size() == 1); + REQUIRE(loaderResult.pRootTile->getChildren().size() == 1); - auto& implicitTile = loaderResult.pRootTile->getChildren().front(); + auto pRootTile = &loaderResult.pRootTile->getChildren()[0]; + REQUIRE(pRootTile->getChildren().size() == 1); + + auto& implicitTile = pRootTile->getChildren().front(); const auto& tileID = std::get(implicitTile.getTileID()); CHECK(tileID == CesiumGeometry::QuadtreeTileID(0, 0, 0)); @@ -570,11 +596,14 @@ TEST_CASE("Test loading individual tile of tileset json") { auto loaderResult = createLoader(testDataPath / "ImplicitTileset" / "tileset_1.0.json"); - CHECK(loaderResult.pRootTile); + REQUIRE(loaderResult.pRootTile); CHECK(loaderResult.pRootTile->isExternalContent()); - CHECK(loaderResult.pRootTile->getChildren().size() == 1); + REQUIRE(loaderResult.pRootTile->getChildren().size() == 1); + + auto pRootTile = &loaderResult.pRootTile->getChildren()[0]; + REQUIRE(pRootTile->getChildren().size() == 1); - auto& implicitTile = loaderResult.pRootTile->getChildren().front(); + auto& implicitTile = pRootTile->getChildren().front(); const auto& tileID = std::get(implicitTile.getTileID()); CHECK(tileID == CesiumGeometry::QuadtreeTileID(0, 0, 0)); diff --git a/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp b/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp index 0c50a2873..c527bb3aa 100644 --- a/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp +++ b/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp @@ -169,7 +169,12 @@ TEST_CASE("Test replace refinement for render") { initializeTileset(tileset); // check the tiles status - const Tile* root = tileset.getRootTile(); + const Tile* pTilesetJson = tileset.getRootTile(); + REQUIRE(pTilesetJson); + REQUIRE(pTilesetJson->getChildren().size() == 1); + + const Tile* root = &pTilesetJson->getChildren()[0]; + REQUIRE(root->getState() == TileLoadState::ContentLoading); for (const auto& child : root->getChildren()) { REQUIRE(child.getState() == TileLoadState::Unloaded); @@ -208,7 +213,7 @@ TEST_CASE("Test replace refinement for render") { REQUIRE(result.tilesFadingOut.size() == 0); - REQUIRE(result.tilesVisited == 1); + REQUIRE(result.tilesVisited == 2); REQUIRE(result.workerThreadTileLoadQueueLength == 0); REQUIRE(result.mainThreadTileLoadQueueLength == 0); REQUIRE(result.tilesCulled == 0); @@ -269,7 +274,7 @@ TEST_CASE("Test replace refinement for render") { REQUIRE(result.tilesFadingOut.size() == 0); - REQUIRE(result.tilesVisited == 5); + REQUIRE(result.tilesVisited == 6); REQUIRE(result.workerThreadTileLoadQueueLength == 4); REQUIRE(result.tilesCulled == 0); REQUIRE(result.culledTilesVisited == 0); @@ -293,7 +298,7 @@ TEST_CASE("Test replace refinement for render") { REQUIRE(result.tilesFadingOut.size() == 1); - REQUIRE(result.tilesVisited == 5); + REQUIRE(result.tilesVisited == 6); REQUIRE(result.workerThreadTileLoadQueueLength == 0); REQUIRE(result.tilesCulled == 0); REQUIRE(result.culledTilesVisited == 0); @@ -342,7 +347,7 @@ TEST_CASE("Test replace refinement for render") { REQUIRE(result.tilesFadingOut.size() == 0); - REQUIRE(result.tilesVisited == 6); + REQUIRE(result.tilesVisited == 7); REQUIRE(result.workerThreadTileLoadQueueLength == 5); REQUIRE(result.tilesCulled == 0); REQUIRE(result.culledTilesVisited == 0); @@ -384,7 +389,7 @@ TEST_CASE("Test replace refinement for render") { REQUIRE(result.tilesFadingOut.size() == 1); - REQUIRE(result.tilesVisited == 6); + REQUIRE(result.tilesVisited == 7); REQUIRE(result.workerThreadTileLoadQueueLength == 0); REQUIRE(result.tilesCulled == 0); REQUIRE(result.culledTilesVisited == 0); @@ -437,7 +442,7 @@ TEST_CASE("Test replace refinement for render") { REQUIRE(result.tilesFadingOut.size() == 1); - REQUIRE(result.tilesVisited == 5); + REQUIRE(result.tilesVisited == 6); REQUIRE(result.workerThreadTileLoadQueueLength == 0); REQUIRE(result.tilesCulled == 0); REQUIRE(result.culledTilesVisited == 0); @@ -467,7 +472,7 @@ TEST_CASE("Test replace refinement for render") { REQUIRE(result.tilesFadingOut.size() == 0); - REQUIRE(result.tilesVisited == 5); + REQUIRE(result.tilesVisited == 6); REQUIRE(result.workerThreadTileLoadQueueLength == 4); REQUIRE(result.tilesCulled == 0); REQUIRE(result.culledTilesVisited == 0); @@ -496,7 +501,7 @@ TEST_CASE("Test replace refinement for render") { REQUIRE(result.tilesFadingOut.size() == 1); - REQUIRE(result.tilesVisited == 5); + REQUIRE(result.tilesVisited == 6); REQUIRE(result.workerThreadTileLoadQueueLength == 0); REQUIRE(result.tilesCulled == 0); REQUIRE(result.culledTilesVisited == 0); @@ -551,7 +556,11 @@ TEST_CASE("Test additive refinement") { // root is external tileset. Since its content is loading, we won't know if it // has children or not - const Tile* root = tileset.getRootTile(); + const Tile* pTilesetJson = tileset.getRootTile(); + REQUIRE(pTilesetJson); + REQUIRE(pTilesetJson->getChildren().size() == 1); + + const Tile* root = &pTilesetJson->getChildren()[0]; REQUIRE(root->getState() == TileLoadState::ContentLoading); REQUIRE(root->getChildren().size() == 0); @@ -581,11 +590,11 @@ TEST_CASE("Test additive refinement") { } REQUIRE(result.tilesToRenderThisFrame.size() == 1); - REQUIRE(result.tilesToRenderThisFrame.front() == root); + REQUIRE(result.tilesToRenderThisFrame.front() == pTilesetJson); REQUIRE(result.tilesFadingOut.size() == 0); - REQUIRE(result.tilesVisited == 6); + REQUIRE(result.tilesVisited == 7); REQUIRE(result.workerThreadTileLoadQueueLength == 5); REQUIRE(result.tilesCulled == 0); REQUIRE(result.culledTilesVisited == 0); @@ -627,13 +636,14 @@ TEST_CASE("Test additive refinement") { } } - REQUIRE(result.tilesToRenderThisFrame.size() == 2); - REQUIRE(result.tilesToRenderThisFrame[0] == root); - REQUIRE(result.tilesToRenderThisFrame[1] == &parentB3DM); + REQUIRE(result.tilesToRenderThisFrame.size() == 3); + REQUIRE(result.tilesToRenderThisFrame[0] == pTilesetJson); + REQUIRE(result.tilesToRenderThisFrame[1] == root); + REQUIRE(result.tilesToRenderThisFrame[2] == &parentB3DM); REQUIRE(result.tilesFadingOut.size() == 0); - REQUIRE(result.tilesVisited == 7); + REQUIRE(result.tilesVisited == 8); REQUIRE(result.workerThreadTileLoadQueueLength == 1); REQUIRE(result.tilesCulled == 0); REQUIRE(result.culledTilesVisited == 0); @@ -643,11 +653,11 @@ TEST_CASE("Test additive refinement") { { ViewUpdateResult result = tileset.updateView({viewState}); - REQUIRE(result.tilesToRenderThisFrame.size() == 7); + REQUIRE(result.tilesToRenderThisFrame.size() == 8); REQUIRE(result.tilesFadingOut.size() == 0); - REQUIRE(result.tilesVisited == 7); + REQUIRE(result.tilesVisited == 8); REQUIRE(result.workerThreadTileLoadQueueLength == 0); REQUIRE(result.tilesCulled == 0); REQUIRE(result.culledTilesVisited == 0); @@ -700,7 +710,11 @@ TEST_CASE("Render any tiles even when one of children can't be rendered for " initializeTileset(tileset); ViewState viewState = zoomToTileset(tileset); - Tile* root = tileset.getRootTile(); + Tile* pTilesetJson = tileset.getRootTile(); + REQUIRE(pTilesetJson); + REQUIRE(pTilesetJson->getChildren().size() == 1); + Tile* root = &pTilesetJson->getChildren()[0]; + REQUIRE(!doesTileMeetSSE(viewState, *root, tileset)); REQUIRE(root->getState() == TileLoadState::ContentLoading); REQUIRE(root->getChildren().size() == 3); @@ -714,9 +728,9 @@ TEST_CASE("Render any tiles even when one of children can't be rendered for " CHECK(child.getState() == TileLoadState::ContentLoading); } - REQUIRE(result.tilesToRenderThisFrame.size() == 1); + REQUIRE(result.tilesToRenderThisFrame.size() == 2); REQUIRE(result.tilesFadingOut.size() == 0); - REQUIRE(result.tilesVisited == 4); + REQUIRE(result.tilesVisited == 5); REQUIRE(result.workerThreadTileLoadQueueLength == 3); REQUIRE(result.tilesCulled == 0); REQUIRE(result.culledTilesVisited == 0); @@ -738,9 +752,9 @@ TEST_CASE("Render any tiles even when one of children can't be rendered for " REQUIRE(child.isRenderable()); } - REQUIRE(result.tilesToRenderThisFrame.size() == 4); + REQUIRE(result.tilesToRenderThisFrame.size() == 5); REQUIRE(result.tilesFadingOut.size() == 0); - REQUIRE(result.tilesVisited == 4); + REQUIRE(result.tilesVisited == 5); REQUIRE(result.workerThreadTileLoadQueueLength == 0); REQUIRE(result.tilesCulled == 0); REQUIRE(result.culledTilesVisited == 0); @@ -793,7 +807,10 @@ TEST_CASE("Test multiple frustums") { initializeTileset(tileset); // check the tiles status - const Tile* root = tileset.getRootTile(); + const Tile* pTilesetJson = tileset.getRootTile(); + REQUIRE(pTilesetJson); + REQUIRE(pTilesetJson->getChildren().size() == 1); + const Tile* root = &pTilesetJson->getChildren()[0]; REQUIRE(root->getState() == TileLoadState::ContentLoading); for (const auto& child : root->getChildren()) { REQUIRE(child.getState() == TileLoadState::Unloaded); @@ -856,7 +873,7 @@ TEST_CASE("Test multiple frustums") { REQUIRE(result.tilesFadingOut.size() == 1); REQUIRE(*result.tilesFadingOut.begin() == root); - REQUIRE(result.tilesVisited == 5); + REQUIRE(result.tilesVisited == 6); REQUIRE(result.workerThreadTileLoadQueueLength == 0); REQUIRE(result.tilesCulled == 0); } @@ -905,7 +922,7 @@ TEST_CASE("Test multiple frustums") { // The grand child and the second child are the only ones rendered. // The third and fourth children of the root are culled. REQUIRE(result.tilesToRenderThisFrame.size() == 2); - REQUIRE(result.tilesVisited == 4); + REQUIRE(result.tilesVisited == 5); REQUIRE( std::find( result.tilesToRenderThisFrame.begin(), @@ -1028,7 +1045,11 @@ TEST_CASE("Can load example tileset.json from 3DTILES_bounding_volume_S2 " tilesetExternals.asyncSystem.dispatchMainThreadTasks(); } - const Tile* pRoot = tileset.getRootTile(); + const Tile* pTilesetJson = tileset.getRootTile(); + REQUIRE(pTilesetJson); + REQUIRE(pTilesetJson->getChildren().size() == 1); + const Tile* pRoot = &pTilesetJson->getChildren()[0]; + const S2CellBoundingVolume* pS2 = std::get_if(&pRoot->getBoundingVolume()); REQUIRE(pS2); @@ -1070,54 +1091,53 @@ TEST_CASE("Can load example tileset.json from 3DTILES_bounding_volume_S2 " REQUIRE(pGreatGrandchild->getChildren().empty()); } -TEST_CASE("Makes metadata available once root tile is loaded") { - Cesium3DTilesSelection::registerAllTileContentTypes(); - - std::filesystem::path testDataPath = Cesium3DTilesSelection_TEST_DATA_DIR; - testDataPath = testDataPath / "WithMetadata"; - std::vector files{ - "tileset.json", - "parent.b3dm", - "ll.b3dm", - "lr.b3dm", - "ul.b3dm", - "ur.b3dm" - }; - - std::map> - mockCompletedRequests; - for (const auto& file : files) { - std::unique_ptr mockCompletedResponse = - std::make_unique( - static_cast(200), - "doesn't matter", - CesiumAsync::HttpHeaders{}, - readFile(testDataPath / file)); - mockCompletedRequests.insert( - {file, - std::make_shared( - "GET", - file, - CesiumAsync::HttpHeaders{}, - std::move(mockCompletedResponse))}); - } - - std::shared_ptr mockAssetAccessor = - std::make_shared(std::move(mockCompletedRequests)); - TilesetExternals tilesetExternals{ - mockAssetAccessor, - std::make_shared(), - AsyncSystem(std::make_shared()), - nullptr}; - - // create tileset and call updateView() to give it a chance to load - Tileset tileset(tilesetExternals, "tileset.json"); - initializeTileset(tileset); - - const std::optional& schema = tileset.getSchema(); - REQUIRE(schema); - CHECK(schema->id == "foo"); -} +// TEST_CASE("Makes metadata available once root tile is loaded") { +// Cesium3DTilesSelection::registerAllTileContentTypes(); + +// std::filesystem::path testDataPath = Cesium3DTilesSelection_TEST_DATA_DIR; +// testDataPath = testDataPath / "WithMetadata"; +// std::vector files{ +// "tileset.json", +// "parent.b3dm", +// "ll.b3dm", +// "lr.b3dm", +// "ul.b3dm", +// "ur.b3dm"}; + +// std::map> +// mockCompletedRequests; +// for (const auto& file : files) { +// std::unique_ptr mockCompletedResponse = +// std::make_unique( +// static_cast(200), +// "doesn't matter", +// CesiumAsync::HttpHeaders{}, +// readFile(testDataPath / file)); +// mockCompletedRequests.insert( +// {file, +// std::make_shared( +// "GET", +// file, +// CesiumAsync::HttpHeaders{}, +// std::move(mockCompletedResponse))}); +// } + +// std::shared_ptr mockAssetAccessor = +// std::make_shared(std::move(mockCompletedRequests)); +// TilesetExternals tilesetExternals{ +// mockAssetAccessor, +// std::make_shared(), +// AsyncSystem(std::make_shared()), +// nullptr}; + +// // create tileset and call updateView() to give it a chance to load +// Tileset tileset(tilesetExternals, "tileset.json"); +// initializeTileset(tileset); + +// const std::optional& schema = tileset.getSchema(); +// REQUIRE(schema); +// CHECK(schema->id == "foo"); +// } namespace { From 44ad7866d05822ab72a85b7d6634de7d96378e9b Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Mon, 21 Aug 2023 18:51:12 +1000 Subject: [PATCH 186/421] Expose metadata on root tile and external tilesets. --- .../Cesium3DTilesSelection/TileContent.h | 29 ++++- .../src/TilesetJsonLoader.cpp | 25 +++-- .../test/TestTilesetJsonLoader.cpp | 28 ++--- .../test/TestTilesetSelectionAlgorithm.cpp | 100 ++++++++++-------- 4 files changed, 110 insertions(+), 72 deletions(-) diff --git a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/TileContent.h b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/TileContent.h index 7309f5fd8..6d5c6ab0e 100644 --- a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/TileContent.h +++ b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/TileContent.h @@ -6,9 +6,12 @@ #include #include +#include +#include #include #include +#include namespace Cesium3DTilesSelection { /** @@ -47,8 +50,30 @@ struct CESIUM3DTILESSELECTION_API TileEmptyContent {}; * external tileset. When this tile is loaded, all the tiles in the * external tileset will become children of this external content tile */ -class CESIUM3DTILESSELECTION_API TileExternalContent { -public: +struct CESIUM3DTILESSELECTION_API TileExternalContent { + /** + * @brief An object defining the structure of metadata classes and enums. When + * this is defined, then `schemaUri` shall be undefined. + */ + std::optional schema; + + /** + * @brief The URI (or IRI) of the external schema file. When this is defined, + * then `schema` shall be undefined. + */ + std::optional schemaUri; + + /** + * @brief An array of groups that tile content may belong to. Each element of + * this array is a metadata entity that describes the group. The tile content + * `group` property is an index into this array. + */ + std::vector groups; + + /** + * @brief A metadata entity that is associated with this tileset. + */ + std::optional metadata; }; /** diff --git a/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp b/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp index 4f6b050d0..026aac69d 100644 --- a/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp +++ b/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp @@ -706,14 +706,6 @@ TileLoadResult parseExternalTilesetInWorkerThread( std::move(externalContentInitializer), TileLoadResultState::Success}; } -} // namespace - -TilesetJsonLoader::TilesetJsonLoader( - const std::string& baseUrl, - CesiumGeometry::Axis upAxis) - : _baseUrl{baseUrl}, _upAxis{upAxis}, _children{} {} - -namespace { void parseTilesetMetadata(const rapidjson::Document& tilesetJson, Tile& tile) { TileExternalContent* pExternal = tile.getContent().getExternalContent(); @@ -725,16 +717,21 @@ void parseTilesetMetadata(const rapidjson::Document& tilesetJson, Tile& tile) { if (schemaIt != tilesetJson.MemberEnd()) { auto schemaResult = Cesium3DTilesReader::readSchema(schemaIt->value); if (schemaResult.value) { - // pLoader->setSchema(std::move(*schemaResult.value)); + pExternal->schema = std::move(*schemaResult.value); } } + auto schemaUriIt = tilesetJson.FindMember("schemaUri"); + if (schemaUriIt != tilesetJson.MemberEnd() && schemaUriIt->value.IsString()) { + pExternal->schemaUri = schemaUriIt->value.GetString(); + } + const auto metadataIt = tilesetJson.FindMember("metadata"); if (metadataIt != tilesetJson.MemberEnd()) { auto metadataResult = Cesium3DTilesReader::readMetadataEntity(metadataIt->value); if (metadataResult.value) { - // pLoader->getSchema() = std::move(*schemaResult.value); + pExternal->metadata = std::move(*metadataResult.value); } } @@ -743,12 +740,18 @@ void parseTilesetMetadata(const rapidjson::Document& tilesetJson, Tile& tile) { auto groupsResult = Cesium3DTilesReader::readGroupMetadataArray(groupsIt->value); if (groupsResult.value) { - // pLoader->getSchema() = std::move(*schemaResult.value); + pExternal->groups = std::move(*groupsResult.value); } } } + } // namespace +TilesetJsonLoader::TilesetJsonLoader( + const std::string& baseUrl, + CesiumGeometry::Axis upAxis) + : _baseUrl{baseUrl}, _upAxis{upAxis}, _children{} {} + CesiumAsync::Future> TilesetJsonLoader::createLoader( const TilesetExternals& externals, diff --git a/Cesium3DTilesSelection/test/TestTilesetJsonLoader.cpp b/Cesium3DTilesSelection/test/TestTilesetJsonLoader.cpp index d03b7c8da..8d1d778c4 100644 --- a/Cesium3DTilesSelection/test/TestTilesetJsonLoader.cpp +++ b/Cesium3DTilesSelection/test/TestTilesetJsonLoader.cpp @@ -400,18 +400,22 @@ TEST_CASE("Test creating tileset json loader") { CesiumGeometry::OctreeTileID(0, 0, 0, 0)); } - // SECTION("Tileset with metadata") { - // auto loaderResult = - // createLoader(testDataPath / "WithMetadata" / "tileset.json"); - - // CHECK(!loaderResult.errors.hasErrors()); - // REQUIRE(loaderResult.pLoader); - - // const std::optional& schema = - // loaderResult.pLoader->getSchema(); - // REQUIRE(schema); - // CHECK(schema->id == "foo"); - // } + SECTION("Tileset with metadata") { + auto loaderResult = + createLoader(testDataPath / "WithMetadata" / "tileset.json"); + + CHECK(!loaderResult.errors.hasErrors()); + REQUIRE(loaderResult.pLoader); + REQUIRE(loaderResult.pRootTile); + + TileExternalContent* pExternal = + loaderResult.pRootTile->getContent().getExternalContent(); + REQUIRE(pExternal); + + const std::optional& schema = pExternal->schema; + REQUIRE(schema); + CHECK(schema->id == "foo"); + } } TEST_CASE("Test loading individual tile of tileset json") { diff --git a/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp b/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp index c527bb3aa..de3d82753 100644 --- a/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp +++ b/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp @@ -1091,53 +1091,59 @@ TEST_CASE("Can load example tileset.json from 3DTILES_bounding_volume_S2 " REQUIRE(pGreatGrandchild->getChildren().empty()); } -// TEST_CASE("Makes metadata available once root tile is loaded") { -// Cesium3DTilesSelection::registerAllTileContentTypes(); - -// std::filesystem::path testDataPath = Cesium3DTilesSelection_TEST_DATA_DIR; -// testDataPath = testDataPath / "WithMetadata"; -// std::vector files{ -// "tileset.json", -// "parent.b3dm", -// "ll.b3dm", -// "lr.b3dm", -// "ul.b3dm", -// "ur.b3dm"}; - -// std::map> -// mockCompletedRequests; -// for (const auto& file : files) { -// std::unique_ptr mockCompletedResponse = -// std::make_unique( -// static_cast(200), -// "doesn't matter", -// CesiumAsync::HttpHeaders{}, -// readFile(testDataPath / file)); -// mockCompletedRequests.insert( -// {file, -// std::make_shared( -// "GET", -// file, -// CesiumAsync::HttpHeaders{}, -// std::move(mockCompletedResponse))}); -// } - -// std::shared_ptr mockAssetAccessor = -// std::make_shared(std::move(mockCompletedRequests)); -// TilesetExternals tilesetExternals{ -// mockAssetAccessor, -// std::make_shared(), -// AsyncSystem(std::make_shared()), -// nullptr}; - -// // create tileset and call updateView() to give it a chance to load -// Tileset tileset(tilesetExternals, "tileset.json"); -// initializeTileset(tileset); - -// const std::optional& schema = tileset.getSchema(); -// REQUIRE(schema); -// CHECK(schema->id == "foo"); -// } +TEST_CASE("Makes metadata available once root tile is loaded") { + Cesium3DTilesSelection::registerAllTileContentTypes(); + + std::filesystem::path testDataPath = Cesium3DTilesSelection_TEST_DATA_DIR; + testDataPath = testDataPath / "WithMetadata"; + std::vector files{ + "tileset.json", + "parent.b3dm", + "ll.b3dm", + "lr.b3dm", + "ul.b3dm", + "ur.b3dm"}; + + std::map> + mockCompletedRequests; + for (const auto& file : files) { + std::unique_ptr mockCompletedResponse = + std::make_unique( + static_cast(200), + "doesn't matter", + CesiumAsync::HttpHeaders{}, + readFile(testDataPath / file)); + mockCompletedRequests.insert( + {file, + std::make_shared( + "GET", + file, + CesiumAsync::HttpHeaders{}, + std::move(mockCompletedResponse))}); + } + + std::shared_ptr mockAssetAccessor = + std::make_shared(std::move(mockCompletedRequests)); + TilesetExternals tilesetExternals{ + mockAssetAccessor, + std::make_shared(), + AsyncSystem(std::make_shared()), + nullptr}; + + // create tileset and call updateView() to give it a chance to load + Tileset tileset(tilesetExternals, "tileset.json"); + initializeTileset(tileset); + + Tile* pRoot = tileset.getRootTile(); + REQUIRE(pRoot); + + TileExternalContent* pExternal = pRoot->getContent().getExternalContent(); + REQUIRE(pExternal); + + const std::optional& schema = pExternal->schema; + REQUIRE(schema); + CHECK(schema->id == "foo"); +} namespace { From 789fed81fcc3cf8295d3f7f0d409548278890ad4 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Mon, 21 Aug 2023 20:07:01 +1000 Subject: [PATCH 187/421] Support metadata on external tilesets. --- .../src/TilesetJsonLoader.cpp | 137 ++++++++---------- .../test/TestTilesetSelectionAlgorithm.cpp | 69 +++++++++ .../data/WithMetadata/external-tileset.json | 67 +++++++++ .../test/data/WithMetadata/tileset.json | 16 ++ 4 files changed, 211 insertions(+), 78 deletions(-) create mode 100644 Cesium3DTilesSelection/test/data/WithMetadata/external-tileset.json diff --git a/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp b/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp index 026aac69d..9b0aaee6e 100644 --- a/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp +++ b/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp @@ -31,10 +31,13 @@ struct ExternalContentInitializer { std::shared_ptr> pExternalTilesetLoaders; TilesetJsonLoader* tilesetJsonLoader; + TileExternalContent externalContent; void operator()(Tile& tile) { - TileContent& content = tile.getContent(); - if (content.isExternalContent()) { + TileExternalContent* pExternalContent = + tile.getContent().getExternalContent(); + if (pExternalContent) { + *pExternalContent = std::move(externalContent); std::unique_ptr& pExternalRoot = pExternalTilesetLoaders->pRootTile; if (pExternalRoot) { // propagate all the external tiles to be the children of this tile @@ -595,19 +598,6 @@ TilesetContentLoaderResult parseTilesetJson( std::unique_ptr pRootTile; auto gltfUpAxis = obtainGltfUpAxis(tilesetJson, pLogger); auto pLoader = std::make_unique(baseUrl, gltfUpAxis); - - pRootTile = std::make_unique( - pLoader.get(), - std::make_unique()); - - pRootTile->setTileID(""); - pRootTile->setTransform(parentTransform); - pRootTile->setBoundingVolume(CesiumGeometry::BoundingSphere( - glm::dvec3(0.0), - std::numeric_limits::max())); - pRootTile->setGeometricError(10000000.0); - pRootTile->setRefine(parentRefine); - const auto rootIt = tilesetJson.FindMember("root"); if (rootIt != tilesetJson.MemberEnd()) { const rapidjson::Value& rootJson = rootIt->value; @@ -632,31 +622,39 @@ TilesetContentLoaderResult parseTilesetJson( ErrorList{}}; } -TilesetContentLoaderResult parseTilesetJson( - const std::shared_ptr& pLogger, - const std::string& baseUrl, - const gsl::span& tilesetJsonBinary, - const glm::dmat4& parentTransform, - TileRefine parentRefine) { - rapidjson::Document tilesetJson; - tilesetJson.Parse( - reinterpret_cast(tilesetJsonBinary.data()), - tilesetJsonBinary.size()); - if (tilesetJson.HasParseError()) { - TilesetContentLoaderResult result; - result.errors.emplaceError(fmt::format( - "Error when parsing tileset JSON, error code {} at byte offset {}", - tilesetJson.GetParseError(), - tilesetJson.GetErrorOffset())); - return result; +void parseTilesetMetadata( + const rapidjson::Document& tilesetJson, + TileExternalContent& externalContent) { + auto schemaIt = tilesetJson.FindMember("schema"); + if (schemaIt != tilesetJson.MemberEnd()) { + auto schemaResult = Cesium3DTilesReader::readSchema(schemaIt->value); + if (schemaResult.value) { + externalContent.schema = std::move(*schemaResult.value); + } } - return parseTilesetJson( - pLogger, - baseUrl, - tilesetJson, - parentTransform, - parentRefine); + auto schemaUriIt = tilesetJson.FindMember("schemaUri"); + if (schemaUriIt != tilesetJson.MemberEnd() && schemaUriIt->value.IsString()) { + externalContent.schemaUri = schemaUriIt->value.GetString(); + } + + const auto metadataIt = tilesetJson.FindMember("metadata"); + if (metadataIt != tilesetJson.MemberEnd()) { + auto metadataResult = + Cesium3DTilesReader::readMetadataEntity(metadataIt->value); + if (metadataResult.value) { + externalContent.metadata = std::move(*metadataResult.value); + } + } + + const auto groupsIt = tilesetJson.FindMember("groups"); + if (groupsIt != tilesetJson.MemberEnd()) { + auto groupsResult = + Cesium3DTilesReader::readGroupMetadataArray(groupsIt->value); + if (groupsResult.value) { + externalContent.groups = std::move(*groupsResult.value); + } + } } TileLoadResult parseExternalTilesetInWorkerThread( @@ -671,6 +669,19 @@ TileLoadResult parseExternalTilesetInWorkerThread( const auto& responseData = pResponse->data(); const auto& tileUrl = pCompletedRequest->url(); + rapidjson::Document tilesetJson; + tilesetJson.Parse( + reinterpret_cast(responseData.data()), + responseData.size()); + if (tilesetJson.HasParseError()) { + SPDLOG_LOGGER_ERROR( + pLogger, + "Error when parsing tileset JSON, error code {} at byte offset {}", + tilesetJson.GetParseError(), + tilesetJson.GetErrorOffset()); + return TileLoadResult::createFailedResult(std::move(pCompletedRequest)); + } + // Save the parsed external tileset into custom data. // We will propagate it back to tile later in the main // thread @@ -678,10 +689,13 @@ TileLoadResult parseExternalTilesetInWorkerThread( parseTilesetJson( pLogger, tileUrl, - responseData, + tilesetJson, tileTransform, tileRefine); + // Populate the root tile with metadata + parseTilesetMetadata(tilesetJson, externalContentInitializer.externalContent); + // check and log any errors const auto& errors = externalTilesetLoader.errors; if (errors) { @@ -707,44 +721,6 @@ TileLoadResult parseExternalTilesetInWorkerThread( TileLoadResultState::Success}; } -void parseTilesetMetadata(const rapidjson::Document& tilesetJson, Tile& tile) { - TileExternalContent* pExternal = tile.getContent().getExternalContent(); - assert(pExternal); - if (!pExternal) - return; - - auto schemaIt = tilesetJson.FindMember("schema"); - if (schemaIt != tilesetJson.MemberEnd()) { - auto schemaResult = Cesium3DTilesReader::readSchema(schemaIt->value); - if (schemaResult.value) { - pExternal->schema = std::move(*schemaResult.value); - } - } - - auto schemaUriIt = tilesetJson.FindMember("schemaUri"); - if (schemaUriIt != tilesetJson.MemberEnd() && schemaUriIt->value.IsString()) { - pExternal->schemaUri = schemaUriIt->value.GetString(); - } - - const auto metadataIt = tilesetJson.FindMember("metadata"); - if (metadataIt != tilesetJson.MemberEnd()) { - auto metadataResult = - Cesium3DTilesReader::readMetadataEntity(metadataIt->value); - if (metadataResult.value) { - pExternal->metadata = std::move(*metadataResult.value); - } - } - - const auto groupsIt = tilesetJson.FindMember("groups"); - if (groupsIt != tilesetJson.MemberEnd()) { - auto groupsResult = - Cesium3DTilesReader::readGroupMetadataArray(groupsIt->value); - if (groupsResult.value) { - pExternal->groups = std::move(*groupsResult.value); - } - } -} - } // namespace TilesetJsonLoader::TilesetJsonLoader( @@ -834,7 +810,12 @@ TilesetContentLoaderResult TilesetJsonLoader::createLoader( result.pRootTile->createChildTiles(std::move(children)); // Populate the root tile with metadata - parseTilesetMetadata(tilesetJson, *result.pRootTile); + TileExternalContent* pExternal = + result.pRootTile->getContent().getExternalContent(); + assert(pExternal); + if (pExternal) { + parseTilesetMetadata(tilesetJson, *pExternal); + } return result; } diff --git a/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp b/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp index de3d82753..40d4c9288 100644 --- a/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp +++ b/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp @@ -1098,6 +1098,7 @@ TEST_CASE("Makes metadata available once root tile is loaded") { testDataPath = testDataPath / "WithMetadata"; std::vector files{ "tileset.json", + "external-tileset.json", "parent.b3dm", "ll.b3dm", "lr.b3dm", @@ -1145,6 +1146,74 @@ TEST_CASE("Makes metadata available once root tile is loaded") { CHECK(schema->id == "foo"); } +TEST_CASE("Makes metadata available on external tilesets") { + Cesium3DTilesSelection::registerAllTileContentTypes(); + + std::filesystem::path testDataPath = Cesium3DTilesSelection_TEST_DATA_DIR; + testDataPath = testDataPath / "WithMetadata"; + std::vector files{ + "tileset.json", + "external-tileset.json", + "parent.b3dm", + "ll.b3dm", + "lr.b3dm", + "ul.b3dm", + "ur.b3dm"}; + + std::map> + mockCompletedRequests; + for (const auto& file : files) { + std::unique_ptr mockCompletedResponse = + std::make_unique( + static_cast(200), + "doesn't matter", + CesiumAsync::HttpHeaders{}, + readFile(testDataPath / file)); + mockCompletedRequests.insert( + {file, + std::make_shared( + "GET", + file, + CesiumAsync::HttpHeaders{}, + std::move(mockCompletedResponse))}); + } + + std::shared_ptr mockAssetAccessor = + std::make_shared(std::move(mockCompletedRequests)); + TilesetExternals tilesetExternals{ + mockAssetAccessor, + std::make_shared(), + AsyncSystem(std::make_shared()), + nullptr}; + + // create tileset and call updateView() to give it a chance to load + Tileset tileset(tilesetExternals, "tileset.json"); + initializeTileset(tileset); + + Tile* pTilesetJson = tileset.getRootTile(); + REQUIRE(pTilesetJson); + REQUIRE(pTilesetJson->getChildren().size() == 1); + + Tile* pRoot = &pTilesetJson->getChildren()[0]; + REQUIRE(pRoot); + REQUIRE(pRoot->getChildren().size() == 5); + Tile* pExternal = &pRoot->getChildren()[4]; + + TileExternalContent* pExternalContent = nullptr; + + for (int i = 0; i < 10 && pExternalContent == nullptr; ++i) { + ViewState zoomToTileViewState = zoomToTile(*pExternal); + tileset.updateView({zoomToTileViewState}); + pExternalContent = pExternal->getContent().getExternalContent(); + } + + REQUIRE(pExternalContent); + + REQUIRE(pExternalContent->groups.size() == 2); + CHECK(pExternalContent->groups[0].classProperty == "someClass"); + CHECK(pExternalContent->groups[1].classProperty == "someClass"); +} + namespace { void runUnconditionallyRefinedTestCase(const TilesetOptions& options) { diff --git a/Cesium3DTilesSelection/test/data/WithMetadata/external-tileset.json b/Cesium3DTilesSelection/test/data/WithMetadata/external-tileset.json new file mode 100644 index 000000000..4ae1a25f2 --- /dev/null +++ b/Cesium3DTilesSelection/test/data/WithMetadata/external-tileset.json @@ -0,0 +1,67 @@ +{ + "asset": { + "version": "1.0", + "tilesetVersion": "1.2.3" + }, + "extras": { + "name": "Sample Tileset" + }, + "schema": { + "id": "bar", + "name": "Bar", + "description": "A description of Bar.", + "version": "3.2.1", + "classes": { + "someClass": { + "name": "Some Class!", + "description": "A description of Some Class.", + "properties": { + "someProperty": { + "name": "Some Property", + "description": "A description of Some Property.", + "type": "STRING", + "semantic": "GREAT" + } + } + } + } + }, + "groups": [ + { + "class": "someClass", + "properties": { + "someProperty": "test" + } + }, + { + "class": "someClass", + "properties": { + "someProperty": "another" + } + } + ], + "metadata": { + "class": "someClass", + "properties": { + "someProperty": "foo" + } + }, + "geometricError": 35, + "root": { + "boundingVolume": { + "region": [ + -1.3197209591796106, + 0.6988424218, + -1.3196390408203893, + 0.6989055782, + 0, + 88 + ] + }, + "geometricError": 35, + "refine": "REPLACE", + "content": { + "uri": "parent.b3dm" + } + } +} diff --git a/Cesium3DTilesSelection/test/data/WithMetadata/tileset.json b/Cesium3DTilesSelection/test/data/WithMetadata/tileset.json index 4e101d8de..8d5452a90 100644 --- a/Cesium3DTilesSelection/test/data/WithMetadata/tileset.json +++ b/Cesium3DTilesSelection/test/data/WithMetadata/tileset.json @@ -138,6 +138,22 @@ "content": { "uri": "ul.b3dm" } + }, + { + "boundingVolume": { + "region": [ + -1.3197209591796106, + 0.698874, + -1.31968, + 0.6989055782, + 0, + 20 + ] + }, + "geometricError": 35, + "content": { + "uri": "external-tileset.json" + } } ] } From e99b00d2287338698390af1f547fc9cc3c1a2a33 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Mon, 21 Aug 2023 21:18:35 +1000 Subject: [PATCH 188/421] Formatting. --- .../include/Cesium3DTilesSelection/TileContent.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/TileContent.h b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/TileContent.h index 6d5c6ab0e..87d112e6b 100644 --- a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/TileContent.h +++ b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/TileContent.h @@ -4,10 +4,10 @@ #include "Library.h" #include "RasterOverlayDetails.h" +#include +#include #include #include -#include -#include #include #include From 79d33e236541c43c623ad950902d3bafe39a28a0 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Mon, 21 Aug 2023 21:44:22 +1000 Subject: [PATCH 189/421] Fix Clang/GCC compiler error. --- Cesium3DTilesSelection/src/TilesetJsonLoader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp b/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp index 9b0aaee6e..282d8e1d7 100644 --- a/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp +++ b/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp @@ -839,7 +839,7 @@ TilesetJsonLoader::loadTileContent(const TileLoadInput& loadInput) { const glm::dmat4& tileTransform = tile.getTransform(); TileRefine tileRefine = tile.getRefine(); - ExternalContentInitializer externalContentInitializer{nullptr, this}; + ExternalContentInitializer externalContentInitializer{nullptr, this, {}}; const auto& asyncSystem = loadInput.asyncSystem; const auto& pAssetAccessor = loadInput.pAssetAccessor; From 78099d3a70884e837aa6e915133f327ae03413a5 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Mon, 21 Aug 2023 15:33:05 -0400 Subject: [PATCH 190/421] Fix PropertyView and unit tests --- .../include/CesiumGltf/PropertyConversions.h | 55 + CesiumGltf/include/CesiumGltf/PropertyView.h | 1546 ++++++++----- CesiumGltf/test/TestPropertyView.cpp | 2011 +++++++++++++---- 3 files changed, 2550 insertions(+), 1062 deletions(-) create mode 100644 CesiumGltf/include/CesiumGltf/PropertyConversions.h diff --git a/CesiumGltf/include/CesiumGltf/PropertyConversions.h b/CesiumGltf/include/CesiumGltf/PropertyConversions.h new file mode 100644 index 000000000..9fef8b1cc --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/PropertyConversions.h @@ -0,0 +1,55 @@ +#pragma once + +#include "CesiumGltf/PropertyArrayView.h" +#include "CesiumGltf/PropertyTypeTraits.h" + +#include + +#include +#include +#include +#include +#include + +namespace CesiumGltf { +template < + typename TSigned, + std::enable_if_t< + IsMetadataInteger::value && std::is_signed_v>> +double normalize(TSigned value) { + double max = static_cast(std::numeric_limits::max()); + return std::max(static_cast(value) / max, -1.0); +} + +template < + typename TUnsigned, + std::enable_if_t< + IsMetadataInteger::value && std::is_unsigned_v>> +double normalize(TUnsigned value) { + double max = static_cast(std::numeric_limits::max()); + return static_cast(value) / max; +} + +template < + glm::length_t N, + typename TSigned, + glm::qualifier Q, + std::enable_if_t< + IsMetadataInteger::value && std::is_signed_v>> +glm::vec normalize(glm::vec value) { + double max = static_cast(std::numeric_limits::max()); + return glm::max(static_cast>(value) / max, -1.0); +} + +template < + glm::length_t N, + typename TUnsigned, + glm::qualifier Q, + std::enable_if_t< + IsMetadataInteger::value && std::is_unsigned_v>> +glm::mat normalize(glm::mat value) { + double max = static_cast(std::numeric_limits::max()); + return glm::max(static_cast>(value) / max, -1.0); +} + +} // namespace CesiumGltf diff --git a/CesiumGltf/include/CesiumGltf/PropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyView.h index 3db5165fa..a6013a835 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyView.h @@ -1,4 +1,5 @@ #include "CesiumGltf/ClassProperty.h" +#include "CesiumGltf/PropertyConversions.h" #include "CesiumGltf/PropertyTableProperty.h" #include "CesiumGltf/PropertyTextureProperty.h" #include "CesiumGltf/PropertyTypeTraits.h" @@ -90,18 +91,40 @@ class PropertyViewStatus { static const PropertyViewStatusType ErrorInvalidDefaultValue = 11; }; -template -class PropertyView; - namespace { template PropertyViewStatusType validatePropertyType(const ClassProperty& classProperty) { - using ElementType = T; - if constexpr (IsMetadataArray::value) { - using ElementType = typename MetadataArrayType::type; + if (TypeToPropertyType::value != + convertStringToPropertyType(classProperty.type)) { + return PropertyViewStatus::ErrorTypeMismatch; + } + + PropertyComponentType expectedComponentType = + TypeToPropertyType::component; + + if (!classProperty.componentType && + expectedComponentType != PropertyComponentType::None) { + return PropertyViewStatus::ErrorComponentTypeMismatch; + } + + if (classProperty.componentType && + expectedComponentType != + convertStringToPropertyComponentType(*classProperty.componentType)) { + return PropertyViewStatus::ErrorComponentTypeMismatch; + } + + if (classProperty.array) { + return PropertyViewStatus::ErrorArrayTypeMismatch; } + return PropertyViewStatus::Valid; +} + +template +PropertyViewStatusType +validateArrayPropertyType(const ClassProperty& classProperty) { + using ElementType = typename MetadataArrayType::type; if (TypeToPropertyType::value != convertStringToPropertyType(classProperty.type)) { return PropertyViewStatus::ErrorTypeMismatch; @@ -121,20 +144,8 @@ validatePropertyType(const ClassProperty& classProperty) { return PropertyViewStatus::ErrorComponentTypeMismatch; } - if constexpr (IsMetadataArray::value) { - if (!classProperty.array) { - return PropertyViewStatus::ErrorArrayTypeMismatch; - } - } else { - if (classProperty.array) { - return PropertyViewStatus::ErrorArrayTypeMismatch; - } - } - - if (!CanBeNormalized::value) { - if (classProperty.normalized) { - return PropertyViewStatus::ErrorInvalidNormalization; - } + if (!classProperty.array) { + return PropertyViewStatus::ErrorArrayTypeMismatch; } return PropertyViewStatus::Valid; @@ -212,6 +223,12 @@ getMatN(const CesiumUtility::JsonValue& jsonValue) { } // namespace +/** + * @brief Represents a metadata property in EXT_structural_metadata. + */ +template +class PropertyView; + /** * @brief Represents a non-normalized metadata property in * EXT_structural_metadata. @@ -270,6 +287,7 @@ template class PropertyView { break; } // If it does not break here, something went wrong. + [[fallthrough]]; default: _status = PropertyViewStatus::ErrorInvalidOffset; return; @@ -286,6 +304,7 @@ template class PropertyView { break; } // If it does not break here, something went wrong. + [[fallthrough]]; default: _status = PropertyViewStatus::ErrorInvalidScale; return; @@ -310,21 +329,12 @@ template class PropertyView { } } - if (_required) { - // "noData" should not be defined if the property is required. - if (classProperty.noData) { - _status = PropertyViewStatus::ErrorInvalidNoDataValue; - return; - } - - if (classProperty.defaultProperty) { - _status = PropertyViewStatus::ErrorInvalidDefaultValue; - return; + if (classProperty.noData) { + if (!_required) { + // "noData" can only be defined if the property is required. + _noData = getValue(*classProperty.noData); } - } - if (classProperty.noData) { - _noData = getValue(*classProperty.noData); if (!_noData) { // The value was specified but something went wrong. _status = PropertyViewStatus::ErrorInvalidNoDataValue; @@ -333,7 +343,11 @@ template class PropertyView { } if (classProperty.defaultProperty) { - _defaultValue = getValue(*classProperty.defaultProperty); + if (!_required) { + // "default" can only be defined if the property is required. + _defaultValue = getValue(*classProperty.defaultProperty); + } + if (!_defaultValue) { // The value was specified but something went wrong. _status = PropertyViewStatus::ErrorInvalidDefaultValue; @@ -370,42 +384,57 @@ template class PropertyView { return; } - // If the property has its own values, the class-provided values. - if constexpr (IsMetadataNumeric::value) { - if (property.offset) { + // If the property has its own values, override the class-provided values. + + if (property.offset) { + // Only floating point types can specify an offset. + switch (TypeToPropertyType::component) { + case PropertyComponentType::Float32: + case PropertyComponentType::Float64: _offset = getValue(*property.offset); - if (!_offset) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidOffset; - return; + if (_offset) { + break; } + // If it does not break here, something went wrong. + [[fallthrough]]; + default: + _status = PropertyViewStatus::ErrorInvalidOffset; + return; } + } - if (property.scale) { + if (property.scale) { + // Only floating point types can specify a scale. + switch (TypeToPropertyType::component) { + case PropertyComponentType::Float32: + case PropertyComponentType::Float64: _scale = getValue(*property.scale); - if (!_scale) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidScale; - return; + if (_scale) { + break; } + // If it does not break here, something went wrong. + [[fallthrough]]; + default: + _status = PropertyViewStatus::ErrorInvalidScale; + return; } + } - if (property.max) { - _max = getValue(*property.max); - if (!_scale) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidMax; - return; - } + if (property.max) { + _max = getValue(*property.max); + if (!_scale) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidMax; + return; } + } - if (property.min) { - _min = getValue(*property.min); - if (!_scale) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidMin; - return; - } + if (property.min) { + _min = getValue(*property.min); + if (!_scale) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidMin; + return; } } } @@ -422,42 +451,57 @@ template class PropertyView { return; } - // If the property has its own values, the class-provided values. - if constexpr (IsMetadataNumeric::value) { - if (property.offset) { + // If the property has its own values, override the class-provided values. + + if (property.offset) { + // Only floating point types can specify an offset. + switch (TypeToPropertyType::component) { + case PropertyComponentType::Float32: + case PropertyComponentType::Float64: _offset = getValue(*property.offset); - if (!_offset) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidOffset; - return; + if (_offset) { + break; } + // If it does not break here, something went wrong. + [[fallthrough]]; + default: + _status = PropertyViewStatus::ErrorInvalidOffset; + return; } + } - if (property.scale) { + if (property.scale) { + // Only floating point types can specify a scale. + switch (TypeToPropertyType::component) { + case PropertyComponentType::Float32: + case PropertyComponentType::Float64: _scale = getValue(*property.scale); - if (!_scale) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidScale; - return; + if (_scale) { + break; } + // If it does not break here, something went wrong. + [[fallthrough]]; + default: + _status = PropertyViewStatus::ErrorInvalidScale; + return; } + } - if (property.max) { - _max = getValue(*property.max); - if (!_scale) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidMax; - return; - } + if (property.max) { + _max = getValue(*property.max); + if (!_scale) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidMax; + return; } + } - if (property.min) { - _min = getValue(*property.min); - if (!_scale) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidMin; - return; - } + if (property.min) { + _min = getValue(*property.min); + if (!_scale) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidMin; + return; } } } @@ -603,7 +647,11 @@ template class PropertyView { * @tparam ElementType The C++ type of the values in this property. Must have an * integer component type. */ -template class PropertyView { +template +class PropertyView< + ElementType, + true, + std::enable_if_t::value>> { private: using NormalizedType = typename TypeToNormalizedType::type; @@ -625,7 +673,7 @@ template class PropertyView { * @brief Constructs a property instance from a class definition only. */ PropertyView(const ClassProperty& classProperty) - : _status(PropertyViewStatus::Valid), + : _status(validatePropertyType(classProperty)), _offset(std::nullopt), _scale(std::nullopt), _max(std::nullopt), @@ -633,29 +681,7 @@ template class PropertyView { _required(classProperty.required), _noData(std::nullopt), _defaultValue(std::nullopt) { - if (convertPropertyTypeToString(TypeToPropertyType::value) != - classProperty.type) { - _status = PropertyViewStatus::ErrorTypeMismatch; - return; - } - - if (!classProperty.componentType && - TypeToPropertyType::component != - PropertyComponentType::None) { - _status = PropertyViewStatus::ErrorComponentTypeMismatch; - return; - } - - if (classProperty.componentType && - convertPropertyComponentTypeToString( - TypeToPropertyType::component) != - *classProperty.componentType) { - _status = PropertyViewStatus::ErrorComponentTypeMismatch; - return; - } - - if (classProperty.array) { - _status = PropertyViewStatus::ErrorArrayTypeMismatch; + if (_status != PropertyViewStatus::Valid) { return; } @@ -663,62 +689,71 @@ template class PropertyView { _status = PropertyViewStatus::ErrorInvalidNormalization; } - if constexpr (IsMetadataNumeric::value) { - if (classProperty.offset) { - _offset = getValue(*classProperty.offset); - if (!_offset) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidOffset; - return; - } + if (classProperty.offset) { + _offset = getValue(*classProperty.offset); + if (!_offset) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidOffset; + return; } + } - if (classProperty.scale) { - _scale = getValue(*classProperty.scale); - if (!_scale) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidScale; - return; - } + if (classProperty.scale) { + _scale = getValue(*classProperty.scale); + if (!_scale) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidScale; + return; } + } - if (classProperty.max) { - _max = getValue(*classProperty.max); - if (!_scale) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidMax; - return; - } + if (classProperty.max) { + _max = getValue(*classProperty.max); + if (!_scale) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidMax; + return; } + } - if (classProperty.min) { - _min = getValue(*classProperty.min); - if (!_scale) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidMin; - return; - } + if (classProperty.min) { + _min = getValue(*classProperty.min); + if (!_scale) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidMin; + return; } } - if (!_required) { + if (_required) { + // "noData" should not be defined if the property is required. if (classProperty.noData) { - _noData = getValue(*classProperty.noData); - if (!_noData) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidNoDataValue; - return; - } + _status = PropertyViewStatus::ErrorInvalidNoDataValue; + return; } + // default value should not be defined if the property is required. if (classProperty.defaultProperty) { - _defaultValue = - getValue(*classProperty.defaultProperty); - if (!_defaultValue) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidDefaultValue; - return; - } + _status = PropertyViewStatus::ErrorInvalidDefaultValue; + return; + } + } + + if (classProperty.noData) { + _noData = getValue(*classProperty.noData); + if (!_noData) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidNoDataValue; + return; + } + } + + if (classProperty.defaultProperty) { + _defaultValue = getValue(*classProperty.defaultProperty); + if (!_defaultValue) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidDefaultValue; + return; } } } @@ -751,42 +786,41 @@ template class PropertyView { return; } - // If the property has its own values, the class-provided values. - if constexpr (IsMetadataNumeric::value) { - if (property.offset) { - _offset = getValue(*property.offset); - if (!_offset) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidOffset; - return; - } + // If the property has its own values, override the class-provided values. + + if (property.offset) { + _offset = getValue(*property.offset); + if (!_offset) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidOffset; + return; } + } - if (property.scale) { - _scale = getValue(*property.scale); - if (!_scale) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidScale; - return; - } + if (property.scale) { + _scale = getValue(*property.scale); + if (!_scale) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidScale; + return; } + } - if (property.max) { - _max = getValue(*property.max); - if (!_scale) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidMax; - return; - } + if (property.max) { + _max = getValue(*property.max); + if (!_scale) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidMax; + return; } + } - if (property.min) { - _min = getValue(*property.min); - if (!_scale) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidMin; - return; - } + if (property.min) { + _min = getValue(*property.min); + if (!_scale) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidMin; + return; } } } @@ -803,42 +837,41 @@ template class PropertyView { return; } - // If the property has its own values, the class-provided values. - if constexpr (IsMetadataNumeric::value) { - if (property.offset) { - _offset = getValue(*property.offset); - if (!_offset) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidOffset; - return; - } + // If the property has its own values, override the class-provided values. + + if (property.offset) { + _offset = getValue(*property.offset); + if (!_offset) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidOffset; + return; } + } - if (property.scale) { - _scale = getValue(*property.scale); - if (!_scale) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidScale; - return; - } + if (property.scale) { + _scale = getValue(*property.scale); + if (!_scale) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidScale; + return; } + } - if (property.max) { - _max = getValue(*property.max); - if (!_scale) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidMax; - return; - } + if (property.max) { + _max = getValue(*property.max); + if (!_scale) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidMax; + return; } + } - if (property.min) { - _min = getValue(*property.min); - if (!_scale) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidMin; - return; - } + if (property.min) { + _min = getValue(*property.min); + if (!_scale) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidMin; + return; } } } @@ -853,47 +886,47 @@ template class PropertyView { PropertyViewStatusType status() const noexcept { return _status; } /** - * @copydoc IPropertyView::arrayCount + * @copydoc PropertyView::arrayCount */ int64_t arrayCount() const noexcept { return 0; } /** - * @copydoc IPropertyView::normalized + * @copydoc PropertyView::normalized */ - bool normalized() const noexcept { return false; } + bool normalized() const noexcept { return true; } /** - * @copydoc IPropertyView::offset + * @copydoc PropertyView::offset */ std::optional offset() const noexcept { return _offset; } /** - * @copydoc IPropertyView::scale + * @copydoc PropertyView::scale */ std::optional scale() const noexcept { return _scale; } /** - * @copydoc IPropertyView::max + * @copydoc PropertyView::max */ std::optional max() const noexcept { return _max; } /** - * @copydoc IPropertyView::min + * @copydoc PropertyView::min */ std::optional min() const noexcept { return _min; } /** - * @copydoc IPropertyView::required + * @copydoc PropertyView::required */ bool required() const noexcept { return _required; } /** - * @copydoc IPropertyView::noData + * @copydoc PropertyView::noData */ std::optional noData() const noexcept { return _noData; } /** - * @copydoc IPropertyView::defaultValue + * @copydoc PropertyView::defaultValue */ std::optional defaultValue() const noexcept { return _defaultValue; @@ -902,6 +935,11 @@ template class PropertyView { protected: PropertyViewStatusType _status; + NormalizedType applyValueTransforms(ElementType value) { + NormalizedType result = normalize(value); + return result; + } + private: bool _required; @@ -938,6 +976,10 @@ template class PropertyView { } }; +/** + * @brief Represents a boolean metadata property in + * EXT_structural_metadata. + */ template <> class PropertyView { public: /** @@ -952,21 +994,18 @@ template <> class PropertyView { * @brief Constructs a property instance from a class definition only. */ PropertyView(const ClassProperty& classProperty) - : _status(PropertyViewStatus::Valid), + : _status(validatePropertyType(classProperty)), _required(classProperty.required), _defaultValue(std::nullopt) { - if (classProperty.type != ClassProperty::Type::BOOLEAN) { - _status = PropertyViewStatus::ErrorTypeMismatch; + if (_status != PropertyViewStatus::Valid) { return; } - if (classProperty.array) { - _status = PropertyViewStatus::ErrorArrayTypeMismatch; - return; - } + if (classProperty.defaultProperty) { + if (!_required) { + _defaultValue = getBooleanValue(*classProperty.defaultProperty); + } - if (!_required && classProperty.defaultProperty) { - _defaultValue = getBooleanValue(*classProperty.defaultProperty); if (!_defaultValue) { // The value was specified but something went wrong. _status = PropertyViewStatus::ErrorInvalidDefaultValue; @@ -1004,52 +1043,52 @@ template <> class PropertyView { public: /** - * @copydoc IPropertyView::status + * @copydoc PropertyView::status */ PropertyViewStatusType status() const noexcept { return _status; } /** - * @copydoc IPropertyView::arrayCount + * @copydoc PropertyView::arrayCount */ int64_t arrayCount() const noexcept { return 0; } /** - * @copydoc IPropertyView::normalized + * @copydoc PropertyView::normalized */ bool normalized() const noexcept { return false; } /** - * @copydoc IPropertyView::offset + * @copydoc PropertyView::offset */ std::optional offset() const noexcept { return std::nullopt; } /** - * @copydoc IPropertyView::scale + * @copydoc PropertyView::scale */ std::optional scale() const noexcept { return std::nullopt; } /** - * @copydoc IPropertyView::max + * @copydoc PropertyView::max */ std::optional max() const noexcept { return std::nullopt; } /** - * @copydoc IPropertyView::min + * @copydoc PropertyView::min */ std::optional min() const noexcept { return std::nullopt; } /** - * @copydoc IPropertyView::required + * @copydoc PropertyView::required */ bool required() const noexcept { return _required; } /** - * @copydoc IPropertyView::noData + * @copydoc PropertyView::noData */ std::optional noData() const noexcept { return std::nullopt; } /** - * @copydoc IPropertyView::defaultValue + * @copydoc PropertyView::defaultValue */ std::optional defaultValue() const noexcept { return _defaultValue; } @@ -1070,6 +1109,10 @@ template <> class PropertyView { } }; +/** + * @brief Represents a string metadata property in + * EXT_structural_metadata. + */ template <> class PropertyView { public: /** @@ -1085,36 +1128,35 @@ template <> class PropertyView { * @brief Constructs a property instance from a class definition only. */ PropertyView(const ClassProperty& classProperty) - : _status(PropertyViewStatus::Valid), + : _status(validatePropertyType(classProperty)), _required(classProperty.required), _noData(std::nullopt), _defaultValue(std::nullopt) { - if (classProperty.type != ClassProperty::Type::STRING) { - _status = PropertyViewStatus::ErrorTypeMismatch; - return; - } - - if (classProperty.array) { - _status = PropertyViewStatus::ErrorArrayTypeMismatch; + if (_status != PropertyViewStatus::Valid) { return; } - if (!_required) { - if (classProperty.noData) { + if (classProperty.noData) { + if (!_required) { _noData = getStringValue(*classProperty.noData); - if (!_noData) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidNoDataValue; - return; - } } - if (classProperty.defaultProperty) { + + if (!_noData) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidNoDataValue; + return; + } + } + + if (classProperty.defaultProperty) { + if (!_required) { _defaultValue = getStringValue(*classProperty.defaultProperty); - if (!_defaultValue) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidDefaultValue; - return; - } + } + + if (!_defaultValue) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidDefaultValue; + return; } } } @@ -1151,51 +1193,51 @@ template <> class PropertyView { public: /** - * @copydoc IPropertyView::status + * @copydoc PropertyView::status */ PropertyViewStatusType status() const noexcept { return _status; } /** - * @copydoc IPropertyView::arrayCount + * @copydoc PropertyView::arrayCount */ int64_t arrayCount() const noexcept { return 0; } /** - * @copydoc IPropertyView::normalized + * @copydoc PropertyView::normalized */ bool normalized() const noexcept { return false; } /** - * @copydoc IPropertyView::offset + * @copydoc PropertyView::offset */ std::optional offset() const noexcept { return std::nullopt; } /** - * @copydoc IPropertyView::scale + * @copydoc PropertyView::scale */ std::optional scale() const noexcept { return std::nullopt; } /** - * @copydoc IPropertyView::max + * @copydoc PropertyView::max */ std::optional max() const noexcept { return std::nullopt; } /** - * @copydoc IPropertyView::min + * @copydoc PropertyView::min */ std::optional min() const noexcept { return std::nullopt; } /** - * @copydoc IPropertyView::required + * @copydoc PropertyView::required */ bool required() const noexcept { return _required; } /** - * @copydoc IPropertyView::noData + * @copydoc PropertyView::noData */ std::optional noData() const noexcept { if (_noData) @@ -1205,7 +1247,7 @@ template <> class PropertyView { } /** - * @copydoc IPropertyView::defaultValue + * @copydoc PropertyView::defaultValue */ std::optional defaultValue() const noexcept { if (_defaultValue) @@ -1214,26 +1256,490 @@ template <> class PropertyView { return std::nullopt; } -protected: - PropertyViewStatusType _status; +protected: + PropertyViewStatusType _status; + +private: + bool _required; + std::optional _noData; + std::optional _defaultValue; + + static std::optional + getStringValue(const CesiumUtility::JsonValue& value) { + if (!value.isString()) { + return std::nullopt; + } + + return std::string(value.getString().c_str()); + } +}; + +/** + * @brief Represents a non-normalized array metadata property in + * EXT_structural_metadata. + * + * Whether they belong to property tables, property textures, or property + * attributes, properties have their own sub-properties affecting the actual + * property values. Although they are typically defined via class property, they + * may be overridden by individual instances of the property themselves. The + * constructor is responsible for resolving those differences. + * + * @tparam ElementType The C++ type of the elements in the array values for this + * property. + */ +template +class PropertyView> { +public: + /** + * @brief Constructs an empty property instance. + */ + PropertyView() + : _status(PropertyViewStatus::ErrorNonexistentProperty), + _count(0), + _offset(std::nullopt), + _scale(std::nullopt), + _max(std::nullopt), + _min(std::nullopt), + _required(false), + _noData(std::nullopt), + _defaultValue(std::nullopt) {} + + /** + * @brief Constructs a property instance from a class definition only. + */ + PropertyView(const ClassProperty& classProperty) + : _status(validateArrayPropertyType>( + classProperty)), + _count(_count = classProperty.count ? *classProperty.count : 0), + _offset(std::nullopt), + _scale(std::nullopt), + _max(std::nullopt), + _min(std::nullopt), + _required(classProperty.required), + _noData(std::nullopt), + _defaultValue(std::nullopt) { + if (_status != PropertyViewStatus::Valid) { + return; + } + + if (classProperty.normalized) { + _status = PropertyViewStatus::ErrorInvalidNormalization; + return; + } + + if (classProperty.offset) { + // Only floating point types can specify an offset. + switch (TypeToPropertyType::component) { + case PropertyComponentType::Float32: + case PropertyComponentType::Float64: + _offset = getArrayValue(*classProperty.offset); + if (_offset && + (_count == 0 || _offset->size() == static_cast(_count))) { + break; + } + // If it does not break here, something went wrong. + [[fallthrough]]; + default: + _status = PropertyViewStatus::ErrorInvalidOffset; + return; + } + } + + if (classProperty.scale) { + // Only floating point types can specify an offset. + switch (TypeToPropertyType::component) { + case PropertyComponentType::Float32: + case PropertyComponentType::Float64: + _scale = getArrayValue(*classProperty.scale); + if (_scale && + (_count == 0 || _scale->size() == static_cast(_count))) { + break; + } + // If it does not break here, something went wrong. + [[fallthrough]]; + default: + _status = PropertyViewStatus::ErrorInvalidScale; + return; + } + } + + if (classProperty.max) { + _max = getArrayValue(*classProperty.max); + if (!_max || + (_count > 0 && _max->size() != static_cast(_count))) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidMax; + return; + } + } + + if (classProperty.min) { + _min = getArrayValue(*classProperty.min); + if (!_min || + (_count > 0 && _min->size() != static_cast(_count))) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidMin; + return; + } + } + + if (classProperty.noData) { + if (!_required) { + _noData = getArrayValue(*classProperty.noData); + } + + if (!_noData) { + _status = PropertyViewStatus::ErrorInvalidNoDataValue; + return; + } + } + + if (classProperty.defaultProperty) { + if (!_required) { + _defaultValue = getArrayValue(*classProperty.defaultProperty); + } + + if (!_defaultValue) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidDefaultValue; + return; + } + } + } + +protected: + /** + * @brief Constructs an invalid instance for an erroneous property. + * + * @param status The value of {@link PropertyViewStatus} indicating the error with the property. + */ + PropertyView(PropertyViewStatusType status) + : _status(status), + _count(0), + _offset(std::nullopt), + _scale(std::nullopt), + _max(std::nullopt), + _min(std::nullopt), + _required(false), + _noData(std::nullopt), + _defaultValue(std::nullopt) {} + + /** + * @brief Constructs a property instance from a property table property and + * its class definition. + */ + PropertyView( + const ClassProperty& classProperty, + const PropertyTableProperty& property) + : PropertyView(classProperty) { + if (_status != PropertyViewStatus::Valid) { + return; + } + + // If the property has its own values, override the class-provided values. + + if (property.offset) { + // Only floating point types can specify an offset. + switch (TypeToPropertyType::component) { + case PropertyComponentType::Float32: + case PropertyComponentType::Float64: + _offset = getArrayValue(*property.offset); + if (_offset && + (_count == 0 || _offset->size() == static_cast(_count))) { + break; + } + // If it does not break here, something went wrong. + [[fallthrough]]; + default: + _status = PropertyViewStatus::ErrorInvalidOffset; + return; + } + } + + if (property.scale) { + // Only floating point types can specify an offset. + switch (TypeToPropertyType::component) { + case PropertyComponentType::Float32: + case PropertyComponentType::Float64: + _scale = getArrayValue(*property.scale); + if (_scale && + (_count == 0 || _scale->size() == static_cast(_count))) { + break; + } + // If it does not break here, something went wrong. + [[fallthrough]]; + default: + _status = PropertyViewStatus::ErrorInvalidScale; + return; + } + } + + if (property.max) { + _max = getArrayValue(*property.max); + if (!_scale) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidMax; + return; + } + } + + if (property.min) { + _min = getArrayValue(*property.min); + if (!_scale) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidMin; + return; + } + } + } + + /** + * @brief Constructs a property instance from a property texture property and + * its class definition. + */ + PropertyView( + const ClassProperty& classProperty, + const PropertyTextureProperty& property) + : PropertyView(classProperty) { + if (_status != PropertyViewStatus::Valid) { + return; + } + + // If the property has its own values, override the class-provided values. + if (property.offset) { + // Only floating point types can specify an offset. + switch (TypeToPropertyType::component) { + case PropertyComponentType::Float32: + case PropertyComponentType::Float64: + _offset = getArrayValue(*property.offset); + if (_offset && + (count == 0 || _offset->size() == static_cast(_count))) { + break; + } + // If it does not break here, something went wrong. + [[fallthrough]]; + default: + _status = PropertyViewStatus::ErrorInvalidOffset; + return; + } + } + + if (property.scale) { + // Only floating point types can specify an offset. + switch (TypeToPropertyType::component) { + case PropertyComponentType::Float32: + case PropertyComponentType::Float64: + _scale = getArrayValue(*property.scale); + if (_scale && + (count == 0 || _scale->size() == static_cast(_count))) { + break; + } + // If it does not break here, something went wrong. + [[fallthrough]]; + default: + _status = PropertyViewStatus::ErrorInvalidScale; + return; + } + } + + if (property.max) { + _max = getArrayValue(*property.max); + if (!_scale) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidMax; + return; + } + } + + if (property.min) { + _min = getArrayValue(*property.min); + if (!_scale) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidMin; + return; + } + } + } + +public: + /** + * @copydoc PropertyView::status + */ + PropertyViewStatusType status() const noexcept { return _status; } + + /** + * @copydoc PropertyView::arrayCount + */ + int64_t arrayCount() const noexcept { return _count; } + + /** + * @copydoc PropertyView::normalized + */ + bool normalized() const noexcept { return false; } + + /** + * @copydoc PropertyView::offset + */ + std::optional> offset() const noexcept { + if (!_offset) { + return std::nullopt; + } + + return PropertyArrayView( + gsl::span(_offset->data(), _offset->size())); + } + + /** + * @copydoc PropertyView::scale + */ + std::optional> scale() const noexcept { + if (!_scale) { + return std::nullopt; + } + + return PropertyArrayView( + gsl::span(_scale->data(), _scale->size())); + } + + /** + * @copydoc PropertyView::max + */ + std::optional> max() const noexcept { + if (!_max) { + return std::nullopt; + } + + return PropertyArrayView( + gsl::span(_max->data(), _max->size())); + } + + /** + * @copydoc PropertyView::min + */ + std::optional> min() const noexcept { + if (!_min) { + return std::nullopt; + } + + return PropertyArrayView( + gsl::span(_min->data(), _min->size())); + } + + /** + * @copydoc PropertyView::required + */ + bool required() const noexcept { return _required; } + + /** + * @copydoc PropertyView::noData + */ + std::optional> noData() const noexcept { + if (!_noData) { + return std::nullopt; + } + + return PropertyArrayView( + gsl::span(_noData->data(), _noData->size())); + } + + /** + * @copydoc PropertyView::defaultValue + */ + std::optional> defaultValue() const noexcept { + if (!_defaultValue) { + return std::nullopt; + } + + return PropertyArrayView(gsl::span( + _defaultValue->data(), + _defaultValue->size())); + } + +protected: + PropertyViewStatusType _status; + +private: + int64_t _count; + + std::optional> _offset; + std::optional> _scale; + std::optional> _max; + std::optional> _min; + + bool _required; + std::optional> _noData; + std::optional> _defaultValue; + + static std::optional> + getArrayValue(const CesiumUtility::JsonValue& jsonValue) { + if (!jsonValue.isArray()) { + return std::nullopt; + } + + const CesiumUtility::JsonValue::Array& array = jsonValue.getArray(); + std::vector values; + values.reserve(array.size()); + + if constexpr (IsMetadataScalar::value) { + for (size_t i = 0; i < array.size(); i++) { + std::optional element = getScalar(array[i]); + if (!element) { + return std::nullopt; + } + + values.push_back(*element); + } + } + if constexpr (IsMetadataVecN::value) { + for (size_t i = 0; i < array.size(); i++) { + std::optional element = getVecN(array[i]); + if (!element) { + return std::nullopt; + } + + values.push_back(*element); + } + } -private: - bool _required; - std::optional _noData; - std::optional _defaultValue; + if constexpr (IsMetadataMatN::value) { + for (size_t i = 0; i < array.size(); i++) { + std::optional element = getMatN(array[i]); + if (!element) { + return std::nullopt; + } - static std::optional - getStringValue(const CesiumUtility::JsonValue& value) { - if (!value.isString()) { - return std::nullopt; + values.push_back(*element); + } } - return std::string(value.getString().c_str()); + std::vector result(values.size() * sizeof(ElementType)); + std::memcpy(result.data(), values.data(), result.size()); + + return result; } }; +/** + * @brief Represents a normalized array metadata property in + * EXT_structural_metadata. + * + * Whether they belong to property tables, property textures, or property + * attributes, properties have their own sub-properties affecting the actual + * property values. Although they are typically defined via class property, they + * may be overridden by individual instances of the property themselves. The + * constructor is responsible for resolving those differences. + * + * @tparam ElementType The C++ type of the elements in the array values for this + * property. Must have an integer component type. + */ template -class PropertyView> { +class PropertyView< + PropertyArrayView, + true, + std::enable_if_t::value>> { +private: + using NormalizedType = typename TypeToNormalizedType::type; + public: /** * @brief Constructs an empty property instance. @@ -1241,7 +1747,6 @@ class PropertyView> { PropertyView() : _status(PropertyViewStatus::ErrorNonexistentProperty), _count(0), - _normalized(false), _offset(std::nullopt), _scale(std::nullopt), _max(std::nullopt), @@ -1254,9 +1759,9 @@ class PropertyView> { * @brief Constructs a property instance from a class definition only. */ PropertyView(const ClassProperty& classProperty) - : _status(PropertyViewStatus::Valid), + : _status(validateArrayPropertyType>( + classProperty)), _count(_count = classProperty.count ? *classProperty.count : 0), - _normalized(classProperty.normalized), _offset(std::nullopt), _scale(std::nullopt), _max(std::nullopt), @@ -1264,97 +1769,75 @@ class PropertyView> { _required(classProperty.required), _noData(std::nullopt), _defaultValue(std::nullopt) { - if (convertPropertyTypeToString(TypeToPropertyType::value) != - classProperty.type) { - _status = PropertyViewStatus::ErrorTypeMismatch; - return; - } - - if (!classProperty.componentType && - TypeToPropertyType::component != - PropertyComponentType::None) { - _status = PropertyViewStatus::ErrorComponentTypeMismatch; + if (_status != PropertyViewStatus::Valid) { return; } - if (classProperty.componentType && - convertPropertyComponentTypeToString( - TypeToPropertyType::component) != - *classProperty.componentType) { - _status = PropertyViewStatus::ErrorComponentTypeMismatch; + if (!classProperty.normalized) { + _status = PropertyViewStatus::ErrorInvalidNormalization; return; } - if (!classProperty.array) { - _status = PropertyViewStatus::ErrorArrayTypeMismatch; - return; + if (classProperty.offset) { + _offset = getArrayValue(*classProperty.offset); + if (!_offset || + (_count > 0 && _offset->size() != static_cast(_count))) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidOffset; + return; + } } - if (classProperty.normalized) { - _status = PropertyViewStatus::ErrorInvalidNormalization; - return; + if (classProperty.scale) { + _scale = getArrayValue(*classProperty.scale); + if (!_scale || + (_count > 0 && _scale->size() != static_cast(_count))) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidScale; + return; + } } - // If the property has its own values, the class-provided values. - if constexpr (IsMetadataNumeric::value) { - if (classProperty.offset) { - _offset = getArrayValue(*classProperty.offset); - if (!_offset || - (_count > 0 && _offset->size() != static_cast(_count))) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidOffset; - return; - } + if (classProperty.max) { + _max = getArrayValue(*classProperty.max); + if (!_max || + (_count > 0 && _max->size() != static_cast(_count))) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidMax; + return; } + } - if (classProperty.scale) { - _scale = getArrayValue(*classProperty.scale); - if (!_scale || - (_count > 0 && _scale->size() != static_cast(_count))) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidScale; - return; - } + if (classProperty.min) { + _min = getArrayValue(*classProperty.min); + if (!_min || + (_count > 0 && _min->size() != static_cast(_count))) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidMin; + return; } + } - if (classProperty.max) { - _max = getArrayValue(*classProperty.max); - if (!_max || - (_count > 0 && _max->size() != static_cast(_count))) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidMax; - return; - } + if (classProperty.noData) { + if (!_required) { + _noData = getArrayValue(*classProperty.noData); } - if (classProperty.min) { - _min = getArrayValue(*classProperty.min); - if (!_min || - (_count > 0 && _min->size() != static_cast(_count))) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidMin; - return; - } + if (!_noData) { + _status = PropertyViewStatus::ErrorInvalidNoDataValue; + return; } } - if (!_required) { - if (classProperty.noData) { - _noData = getArrayValue(*classProperty.noData); - if (!_noData) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidNoDataValue; - return; - } + if (classProperty.defaultProperty) { + if (!_required) { + _defaultValue = + getArrayValue(*classProperty.defaultProperty); } - if (classProperty.defaultProperty) { - _defaultValue = getArrayValue(*classProperty.defaultProperty); - if (!_defaultValue) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidDefaultValue; - return; - } + if (!_defaultValue) { + _status = PropertyViewStatus::ErrorInvalidDefaultValue; + return; } } } @@ -1388,42 +1871,45 @@ class PropertyView> { return; } - // If the property has its own values, the class-provided values. - if constexpr (IsMetadataNumeric::value) { - if (property.offset) { - _offset = getArrayValue(*property.offset); - if (!_offset) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidOffset; - return; - } + // If the property has its own values, override the class-provided values. + + if (property.offset) { + _offset = getArrayValue(*property.offset); + if (!_offset || + (_count > 0 && _offset->size() != static_cast(_count))) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidOffset; + return; } + } - if (property.scale) { - _scale = getArrayValue(*property.scale); - if (!_scale) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidScale; - return; - } + if (property.scale) { + _scale = getArrayValue(*property.scale); + if (!_scale || + (_count > 0 || _scale->size() != static_cast(_count))) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidScale; + return; } + } - if (property.max) { - _max = getArrayValue(*property.max); - if (!_scale) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidMax; - return; - } + if (property.max) { + _max = getArrayValue(*property.max); + if (!_max || + (_count > 0 && _max->size() != static_cast(_count))) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidMax; + return; } + } - if (property.min) { - _min = getArrayValue(*property.min); - if (!_scale) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidMin; - return; - } + if (property.min) { + _min = getArrayValue(*property.min); + if (!_min || + (_count > 0 && _min->size() != static_cast(_count))) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidMin; + return; } } } @@ -1440,117 +1926,120 @@ class PropertyView> { return; } - // If the property has its own values, the class-provided values. - if constexpr (IsMetadataNumeric::value) { - if (property.offset) { - _offset = getArrayValue(*property.offset); - if (!_offset) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidOffset; - return; - } + // If the property has its own values, override the class-provided values. + + if (property.offset) { + _offset = getArrayValue(*property.offset); + if (!_offset || + (_count > 0 && _offset->size() != static_cast(_count))) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidOffset; + return; } + } - if (property.scale) { - _scale = getArrayValue(*property.scale); - if (!_scale) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidScale; - return; - } + if (property.scale) { + _scale = getArrayValue(*property.scale); + if (!_scale || + (_count > 0 || _scale->size() != static_cast(_count))) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidScale; + return; } + } - if (property.max) { - _max = getArrayValue(*property.max); - if (!_scale) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidMax; - return; - } + if (property.max) { + _max = getArrayValue(*property.max); + if (!_max || + (_count > 0 && _max->size() != static_cast(_count))) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidMax; + return; } + } - if (property.min) { - _min = getArrayValue(*property.min); - if (!_scale) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidMin; - return; - } + if (property.min) { + _min = getArrayValue(*property.min); + if (!_min || + (_count > 0 && _min->size() != static_cast(_count))) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidMin; + return; } } } public: /** - * @copydoc IPropertyView::status + * @copydoc PropertyView::status */ PropertyViewStatusType status() const noexcept { return _status; } /** - * @copydoc IPropertyView::arrayCount + * @copydoc PropertyView::arrayCount */ int64_t arrayCount() const noexcept { return _count; } /** - * @copydoc IPropertyView::normalized + * @copydoc PropertyView::normalized */ - bool normalized() const noexcept { return _normalized; } + bool normalized() const noexcept { return true; } /** - * @copydoc IPropertyView::offset + * @copydoc PropertyView::offset */ - std::optional> offset() const noexcept { + std::optional> offset() const noexcept { if (!_offset) { return std::nullopt; } - return PropertyArrayView( + return PropertyArrayView( gsl::span(_offset->data(), _offset->size())); } /** - * @copydoc IPropertyView::scale + * @copydoc PropertyView::scale */ - std::optional> scale() const noexcept { + std::optional> scale() const noexcept { if (!_scale) { return std::nullopt; } - return PropertyArrayView( + return PropertyArrayView( gsl::span(_scale->data(), _scale->size())); } /** - * @copydoc IPropertyView::max + * @copydoc PropertyView::max */ - std::optional> max() const noexcept { + std::optional> max() const noexcept { if (!_max) { return std::nullopt; } - return PropertyArrayView( + return PropertyArrayView( gsl::span(_max->data(), _max->size())); } /** - * @copydoc IPropertyView::min + * @copydoc PropertyView::min */ - std::optional> min() const noexcept { + std::optional> min() const noexcept { if (!_min) { return std::nullopt; } - return PropertyArrayView( + return PropertyArrayView( gsl::span(_min->data(), _min->size())); } /** - * @copydoc IPropertyView::required + * @copydoc PropertyView::required */ bool required() const noexcept { return _required; } /** - * @copydoc IPropertyView::noData + * @copydoc PropertyView::noData */ std::optional> noData() const noexcept { if (!_noData) { @@ -1562,14 +2051,15 @@ class PropertyView> { } /** - * @copydoc IPropertyView::defaultValue + * @copydoc PropertyView::defaultValue */ - std::optional> defaultValue() const noexcept { + std::optional> + defaultValue() const noexcept { if (!_defaultValue) { return std::nullopt; } - return PropertyArrayView(gsl::span( + return PropertyArrayView(gsl::span( _defaultValue->data(), _defaultValue->size())); } @@ -1579,7 +2069,6 @@ class PropertyView> { private: int64_t _count; - bool _normalized; std::optional> _offset; std::optional> _scale; @@ -1590,6 +2079,7 @@ class PropertyView> { std::optional> _noData; std::optional> _defaultValue; + template static std::optional> getArrayValue(const CesiumUtility::JsonValue& jsonValue) { if (!jsonValue.isArray()) { @@ -1597,12 +2087,12 @@ class PropertyView> { } const CesiumUtility::JsonValue::Array& array = jsonValue.getArray(); - std::vector values; + std::vector values; values.reserve(array.size()); - if constexpr (IsMetadataScalar::value) { + if constexpr (IsMetadataScalar::value) { for (size_t i = 0; i < array.size(); i++) { - std::optional element = getScalar(array[i]); + std::optional element = getScalar(array[i]); if (!element) { return std::nullopt; } @@ -1610,9 +2100,10 @@ class PropertyView> { values.push_back(*element); } } - if constexpr (IsMetadataVecN::value) { + + if constexpr (IsMetadataVecN::value) { for (size_t i = 0; i < array.size(); i++) { - std::optional element = getVecN(array[i]); + std::optional element = getVecN(array[i]); if (!element) { return std::nullopt; } @@ -1621,9 +2112,9 @@ class PropertyView> { } } - if constexpr (IsMetadataMatN::value) { + if constexpr (IsMetadataMatN::value) { for (size_t i = 0; i < array.size(); i++) { - std::optional element = getMatN(array[i]); + std::optional element = getMatN(array[i]); if (!element) { return std::nullopt; } @@ -1632,83 +2123,17 @@ class PropertyView> { } } - std::vector result(values.size() * sizeof(ElementType)); + std::vector result(values.size() * sizeof(T)); std::memcpy(result.data(), values.data(), result.size()); return result; } - - template - static std::optional getScalar(const CesiumUtility::JsonValue& jsonValue) { - try { - return jsonValue.getSafeNumber(); - } catch (const CesiumUtility::JsonValueNotRealValue& /*error*/) { - return std::nullopt; - } catch (const gsl::narrowing_error& /*error*/) { - return std::nullopt; - } - } - - template - static std::optional - getVecN(const CesiumUtility::JsonValue& jsonValue) { - if (!jsonValue.isArray()) { - return std::nullopt; - } - - const CesiumUtility::JsonValue::Array& array = jsonValue.getArray(); - constexpr glm::length_t N = VecType::length(); - if (array.size() != N) { - return std::nullopt; - } - - using T = typename VecType::value_type; - - VecType result; - for (glm::length_t i = 0; i < N; i++) { - std::optional value = getScalar(array[i]); - if (!value) { - return std::nullopt; - } - - result[i] = *value; - } - - return result; - } - - template - static std::optional - getMatN(const CesiumUtility::JsonValue& jsonValue) { - if (!jsonValue.isArray()) { - return std::nullopt; - } - - const CesiumUtility::JsonValue::Array& array = jsonValue.getArray(); - constexpr glm::length_t N = MatType::length(); - if (array.size() != N * N) { - return std::nullopt; - } - - using T = typename MatType::value_type; - - MatType result; - for (glm::length_t i = 0; i < N; i++) { - // Try to parse each value in the column. - for (glm::length_t j = 0; j < N; j++) { - std::optional value = getScalar(array[i * N + j]); - if (!value) { - return std::nullopt; - } - - result[i][j] = *value; - } - } - - return result; - } }; +/** + * @brief Represents a boolean array metadata property in + * EXT_structural_metadata. + */ template <> class PropertyView> { public: /** @@ -1725,26 +2150,23 @@ template <> class PropertyView> { * @brief Constructs a property instance from a class definition only. */ PropertyView(const ClassProperty& classProperty) - : _status(PropertyViewStatus::Valid), + : _status( + validateArrayPropertyType>(classProperty)), _count(classProperty.count ? *classProperty.count : 0), _required(classProperty.required), _defaultValue(), _size(0) { - if (classProperty.type != ClassProperty::Type::BOOLEAN) { - _status = PropertyViewStatus::ErrorTypeMismatch; + if (_status != PropertyViewStatus::Valid) { return; } - if (!classProperty.array) { - _status = PropertyViewStatus::ErrorArrayTypeMismatch; - return; - } + if (classProperty.defaultProperty) { + if (!_required) { + _defaultValue = + getBooleanArrayValue(*classProperty.defaultProperty, _size); + } - if (!_required && classProperty.defaultProperty) { - _defaultValue = - getBooleanArrayValue(*classProperty.defaultProperty, _size); if (_size == 0 || (_count > 0 && _size != _count)) { - // The value was specified but something went wrong. _status = PropertyViewStatus::ErrorInvalidDefaultValue; return; } @@ -1784,62 +2206,62 @@ template <> class PropertyView> { public: /** - * @copydoc IPropertyView::status + * @copydoc PropertyView::status */ PropertyViewStatusType status() const noexcept { return _status; } /** - * @copydoc IPropertyView::arrayCount + * @copydoc PropertyView::arrayCount */ int64_t arrayCount() const noexcept { return _count; } /** - * @copydoc IPropertyView::normalized + * @copydoc PropertyView::normalized */ bool normalized() const noexcept { return false; } /** - * @copydoc IPropertyView::offset + * @copydoc PropertyView::offset */ std::optional> offset() const noexcept { return std::nullopt; } /** - * @copydoc IPropertyView::scale + * @copydoc PropertyView::scale */ std::optional> scale() const noexcept { return std::nullopt; } /** - * @copydoc IPropertyView::max + * @copydoc PropertyView::max */ std::optional> max() const noexcept { return std::nullopt; } /** - * @copydoc IPropertyView::min + * @copydoc PropertyView::min */ std::optional> min() const noexcept { return std::nullopt; } /** - * @copydoc IPropertyView::required + * @copydoc PropertyView::required */ bool required() const noexcept { return _required; } /** - * @copydoc IPropertyView::noData + * @copydoc PropertyView::noData */ std::optional> noData() const noexcept { return std::nullopt; } /** - * @copydoc IPropertyView::defaultValue + * @copydoc PropertyView::defaultValue */ std::optional> defaultValue() const noexcept { if (_size > 0) { @@ -1904,6 +2326,10 @@ template <> class PropertyView> { } }; +/** + * @brief Represents a string array metadata property in + * EXT_structural_metadata. + */ template <> class PropertyView> { public: /** @@ -1926,7 +2352,8 @@ template <> class PropertyView> { * @brief Constructs a property instance from a class definition only. */ PropertyView(const ClassProperty& classProperty) - : _status(PropertyViewStatus::Valid), + : _status(validateArrayPropertyType>( + classProperty)), _count(classProperty.count ? *classProperty.count : 0), _required(classProperty.required), _noData(), @@ -1937,35 +2364,34 @@ template <> class PropertyView> { _defaultValueOffsets(), _defaultValueOffsetType(PropertyComponentType::None), _defaultValueSize(0) { - if (classProperty.type != ClassProperty::Type::STRING) { - _status = PropertyViewStatus::ErrorTypeMismatch; + if (_status != PropertyViewStatus::Valid) { return; } - if (!classProperty.array) { - _status = PropertyViewStatus::ErrorArrayTypeMismatch; - return; - } + if (classProperty.noData) { + if (!_required) { + _noData = getStringArrayValue( + *classProperty.noData, + _noDataOffsets, + _noDataOffsetType, + _noDataSize); + } - if (!_required && classProperty.noData) { - _noData = getStringArrayValue( - *classProperty.noData, - _noDataOffsets, - _noDataOffsetType, - _noDataSize); if (_noDataSize == 0 || (_count > 0 && _noDataSize != _count)) { - // The value was specified but something went wrong. _status = PropertyViewStatus::ErrorInvalidNoDataValue; return; } } - if (!_required && classProperty.defaultProperty) { - _defaultValue = getStringArrayValue( - *classProperty.defaultProperty, - _defaultValueOffsets, - _defaultValueOffsetType, - _defaultValueSize); + if (classProperty.defaultProperty) { + if (!_required) { + _defaultValue = getStringArrayValue( + *classProperty.defaultProperty, + _defaultValueOffsets, + _defaultValueOffsetType, + _defaultValueSize); + } + if (_defaultValueSize == 0 || (_count > 0 && _noDataSize != _count)) { // The value was specified but something went wrong. _status = PropertyViewStatus::ErrorInvalidDefaultValue; diff --git a/CesiumGltf/test/TestPropertyView.cpp b/CesiumGltf/test/TestPropertyView.cpp index 98edab663..c9cf11553 100644 --- a/CesiumGltf/test/TestPropertyView.cpp +++ b/CesiumGltf/test/TestPropertyView.cpp @@ -1,7 +1,5 @@ #include "CesiumGltf/PropertyView.h" -//#include "CesiumGltf/PropertyValue.h" - #include using namespace CesiumGltf; @@ -39,36 +37,6 @@ TEST_CASE("Boolean PropertyView") { REQUIRE(view.status() == PropertyViewStatus::ErrorArrayTypeMismatch); } - SECTION("Ignores non-applicable properties") { - ClassProperty classProperty; - classProperty.type = ClassProperty::Type::BOOLEAN; - classProperty.normalized = true; - classProperty.offset = true; - classProperty.scale = true; - classProperty.max = true; - classProperty.min = true; - classProperty.noData = true; - - PropertyView view(classProperty); - REQUIRE(view.status() == PropertyViewStatus::Valid); - REQUIRE(!view.normalized()); - REQUIRE(!view.offset()); - REQUIRE(!view.scale()); - REQUIRE(!view.max()); - REQUIRE(!view.min()); - REQUIRE(!view.noData()); - } - - SECTION("Ignores count") { - ClassProperty classProperty; - classProperty.type = ClassProperty::Type::BOOLEAN; - classProperty.count = 5; - - PropertyView view(classProperty); - REQUIRE(view.status() == PropertyViewStatus::Valid); - REQUIRE(view.arrayCount() == 0); - } - SECTION("Constructs with defaultProperty") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::BOOLEAN; @@ -82,16 +50,14 @@ TEST_CASE("Boolean PropertyView") { REQUIRE(!*view.defaultValue()); } - SECTION("Ignores defaultProperty when required is true") { + SECTION("Reports errors for incorrectly defined properties") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::BOOLEAN; classProperty.required = true; classProperty.defaultProperty = false; PropertyView view(classProperty); - REQUIRE(view.status() == PropertyViewStatus::Valid); - REQUIRE(view.required()); - REQUIRE(!view.defaultValue()); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidDefaultValue); } SECTION("Reports default value invalid type") { @@ -149,24 +115,13 @@ TEST_CASE("Scalar PropertyView") { SECTION("Reports invalid normalization") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::SCALAR; - classProperty.componentType = ClassProperty::ComponentType::FLOAT32; + classProperty.componentType = ClassProperty::ComponentType::INT8; classProperty.normalized = true; - PropertyView view(classProperty); + PropertyView view(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNormalization); } - SECTION("Ignores count") { - ClassProperty classProperty; - classProperty.type = ClassProperty::Type::SCALAR; - classProperty.componentType = ClassProperty::ComponentType::UINT8; - classProperty.count = 5; - - PropertyView view(classProperty); - REQUIRE(view.status() == PropertyViewStatus::Valid); - REQUIRE(view.arrayCount() == 0); - } - SECTION("Constructs with offset, scale, max, and min") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::SCALAR; @@ -275,6 +230,151 @@ TEST_CASE("Scalar PropertyView") { } } +TEST_CASE("Scalar PropertyView (normalized)") { + SECTION("Constructs empty PropertyView") { + PropertyView view; + REQUIRE(view.status() == PropertyViewStatus::ErrorNonexistentProperty); + REQUIRE(view.arrayCount() == 0); + REQUIRE(view.normalized()); + REQUIRE(!view.offset()); + REQUIRE(!view.scale()); + REQUIRE(!view.max()); + REQUIRE(!view.min()); + REQUIRE(!view.required()); + REQUIRE(!view.noData()); + REQUIRE(!view.defaultValue()); + } + + SECTION("Reports type mismatch") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::STRING; + + PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorTypeMismatch); + } + + SECTION("Reports component type mismatch") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::SCALAR; + classProperty.componentType = ClassProperty::ComponentType::INT8; + + PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorComponentTypeMismatch); + } + + SECTION("Reports array type mismatch") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::SCALAR; + classProperty.componentType = ClassProperty::ComponentType::UINT8; + classProperty.array = true; + + PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorArrayTypeMismatch); + } + + SECTION("Reports invalid normalization") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::SCALAR; + classProperty.componentType = ClassProperty::ComponentType::INT8; + classProperty.normalized = false; + + PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNormalization); + } + + SECTION("Constructs with offset, scale, max, and min") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::SCALAR; + classProperty.componentType = ClassProperty::ComponentType::INT32; + classProperty.normalized = true; + classProperty.offset = 5.04f; + classProperty.scale = 2.2f; + classProperty.max = 10.5f; + classProperty.min = -10.5f; + + PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::Valid); + REQUIRE(view.offset()); + REQUIRE(view.scale()); + REQUIRE(view.max()); + REQUIRE(view.min()); + + REQUIRE(*view.offset() == 5.04f); + REQUIRE(*view.scale() == 2.2f); + REQUIRE(*view.max() == 10.5f); + REQUIRE(*view.min() == -10.5f); + } + + SECTION("Constructs with noData and defaultProperty") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::SCALAR; + classProperty.componentType = ClassProperty::ComponentType::UINT8; + classProperty.normalized = true; + classProperty.required = false; + classProperty.noData = 0; + classProperty.defaultProperty = 1.5; + + PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::Valid); + REQUIRE(!view.required()); + REQUIRE(view.noData()); + REQUIRE(view.defaultValue()); + + REQUIRE(*view.noData() == 0); + REQUIRE(*view.defaultValue() == 1.5); + } + + SECTION("Reports errors for incorrectly defined properties") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::SCALAR; + classProperty.componentType = ClassProperty::ComponentType::INT8; + classProperty.normalized = true; + classProperty.required = true; + classProperty.defaultProperty = 1.0; + + PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidDefaultValue); + + classProperty.noData = 0; + view = PropertyView(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNoDataValue); + } + + SECTION("Reports errors for out-of-bounds values") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::SCALAR; + classProperty.componentType = ClassProperty::ComponentType::INT8; + classProperty.normalized = true; + classProperty.noData = -129; + + PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNoDataValue); + } + + SECTION("Reports errors for invalid types") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::SCALAR; + classProperty.componentType = ClassProperty::ComponentType::INT8; + classProperty.normalized = true; + classProperty.defaultProperty = JsonValue::Array{1}; + + PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidDefaultValue); + + classProperty.noData = "0"; + view = PropertyView(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNoDataValue); + + classProperty.scale = false; + view = PropertyView(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidScale); + + classProperty.offset = JsonValue::Array{}; + view = PropertyView(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidOffset); + } +} + TEST_CASE("VecN PropertyView") { SECTION("Constructs empty PropertyView") { PropertyView view; @@ -320,44 +420,33 @@ TEST_CASE("VecN PropertyView") { SECTION("Reports invalid normalization") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::VEC3; - classProperty.componentType = ClassProperty::ComponentType::FLOAT32; + classProperty.componentType = ClassProperty::ComponentType::INT8; classProperty.normalized = true; - PropertyView view(classProperty); + PropertyView view(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNormalization); } - SECTION("Ignores count") { - ClassProperty classProperty; - classProperty.type = ClassProperty::Type::VEC3; - classProperty.componentType = ClassProperty::ComponentType::INT16; - classProperty.count = 5; - - PropertyView view(classProperty); - REQUIRE(view.status() == PropertyViewStatus::Valid); - REQUIRE(view.arrayCount() == 0); - } - SECTION("Constructs with offset, scale, max, and min") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::VEC3; - classProperty.componentType = ClassProperty::ComponentType::INT16; + classProperty.componentType = ClassProperty::ComponentType::FLOAT32; classProperty.offset = {-1, 1, 2}; classProperty.scale = {2, 1, 3}; classProperty.max = {10, 5, 6}; classProperty.min = {-11, -12, -13}; - PropertyView view(classProperty); + PropertyView view(classProperty); REQUIRE(view.status() == PropertyViewStatus::Valid); REQUIRE(view.offset()); REQUIRE(view.scale()); REQUIRE(view.max()); REQUIRE(view.min()); - REQUIRE(*view.offset() == glm::i16vec3(-1, 1, 2)); - REQUIRE(*view.scale() == glm::i16vec3(2, 1, 3)); - REQUIRE(*view.max() == glm::i16vec3(10, 5, 6)); - REQUIRE(*view.min() == glm::i16vec3(-11, -12, -13)); + REQUIRE(*view.offset() == glm::vec3(-1, 1, 2)); + REQUIRE(*view.scale() == glm::vec3(2, 1, 3)); + REQUIRE(*view.max() == glm::vec3(10, 5, 6)); + REQUIRE(*view.min() == glm::vec3(-11, -12, -13)); } SECTION("Constructs with noData and defaultProperty") { @@ -378,19 +467,27 @@ TEST_CASE("VecN PropertyView") { REQUIRE(*view.defaultValue() == glm::vec4(1.0f, 2.0f, 3.0f, 4.0f)); } - SECTION("Ignores noData and defaultProperty when required is true") { + SECTION("Reports errors for incorrectly defined properties") { ClassProperty classProperty; - classProperty.type = ClassProperty::Type::VEC4; - classProperty.componentType = ClassProperty::ComponentType::FLOAT32; + classProperty.type = ClassProperty::Type::VEC2; + classProperty.componentType = ClassProperty::ComponentType::INT8; classProperty.required = true; - classProperty.noData = {0.0f, 0.0f, 0.0f, 0.0f}; - classProperty.defaultProperty = {1.0f, 2.0f, 3.0f, 4.0f}; + classProperty.defaultProperty = {1, 2}; - PropertyView view(classProperty); - REQUIRE(view.status() == PropertyViewStatus::Valid); - REQUIRE(view.required()); - REQUIRE(!view.noData()); - REQUIRE(!view.defaultValue()); + PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidDefaultValue); + + classProperty.noData = {0, 0}; + view = PropertyView(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNoDataValue); + + classProperty.scale = {3, 2}; + view = PropertyView(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidScale); + + classProperty.offset = {12, 8}; + view = PropertyView(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidOffset); } SECTION("Reports errors for out-of-bounds values") { @@ -413,53 +510,45 @@ TEST_CASE("VecN PropertyView") { classProperty.max = {10, 5}; view = PropertyView(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMax); - - classProperty.scale = {1, 128}; - view = PropertyView(classProperty); - REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidScale); - - classProperty.offset = {-1, -222}; - view = PropertyView(classProperty); - REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidOffset); } SECTION("Reports errors for invalid types") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::VEC2; - classProperty.componentType = ClassProperty::ComponentType::INT8; - classProperty.defaultProperty = {1.0f, 20.9f}; + classProperty.componentType = ClassProperty::ComponentType::FLOAT32; + classProperty.defaultProperty = true; - PropertyView view(classProperty); + PropertyView view(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidDefaultValue); classProperty.noData = "0"; - view = PropertyView(classProperty); + view = PropertyView(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNoDataValue); - classProperty.min = {-10, -1}; - view = PropertyView(classProperty); + classProperty.min = JsonValue::Array{-10}; + view = PropertyView(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMin); classProperty.max = {10, 20, 30, 40}; - view = PropertyView(classProperty); + view = PropertyView(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMax); classProperty.scale = 1; - view = PropertyView(classProperty); + view = PropertyView(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidScale); classProperty.offset = "(1, 2, 3)"; - view = PropertyView(classProperty); + view = PropertyView(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidOffset); } } -TEST_CASE("MatN PropertyView") { +TEST_CASE("VecN PropertyView (normalized)") { SECTION("Constructs empty PropertyView") { - PropertyView view; + PropertyView view; REQUIRE(view.status() == PropertyViewStatus::ErrorNonexistentProperty); REQUIRE(view.arrayCount() == 0); - REQUIRE(!view.normalized()); + REQUIRE(view.normalized()); REQUIRE(!view.offset()); REQUIRE(!view.scale()); REQUIRE(!view.max()); @@ -471,77 +560,199 @@ TEST_CASE("MatN PropertyView") { SECTION("Reports type mismatch") { ClassProperty classProperty; - classProperty.type = ClassProperty::Type::MAT4; + classProperty.type = ClassProperty::Type::VEC2; - PropertyView view(classProperty); + PropertyView view(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorTypeMismatch); } SECTION("Reports component type mismatch") { ClassProperty classProperty; - classProperty.type = ClassProperty::Type::MAT2; + classProperty.type = ClassProperty::Type::VEC3; classProperty.componentType = ClassProperty::ComponentType::INT8; - PropertyView view(classProperty); + PropertyView view(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorComponentTypeMismatch); } SECTION("Reports array type mismatch") { ClassProperty classProperty; - classProperty.type = ClassProperty::Type::MAT2; - classProperty.componentType = ClassProperty::ComponentType::FLOAT32; + classProperty.type = ClassProperty::Type::VEC3; + classProperty.componentType = ClassProperty::ComponentType::UINT8; classProperty.array = true; - PropertyView view(classProperty); + PropertyView view(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorArrayTypeMismatch); } SECTION("Reports invalid normalization") { ClassProperty classProperty; - classProperty.type = ClassProperty::Type::MAT2; - classProperty.componentType = ClassProperty::ComponentType::FLOAT32; - classProperty.normalized = true; + classProperty.type = ClassProperty::Type::VEC3; + classProperty.componentType = ClassProperty::ComponentType::UINT8; + classProperty.normalized = false; - PropertyView view(classProperty); + PropertyView view(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNormalization); } - SECTION("Constructs with non-optional properties") { + SECTION("Constructs with offset, scale, max, and min") { ClassProperty classProperty; - classProperty.type = ClassProperty::Type::MAT3; + classProperty.type = ClassProperty::Type::VEC3; classProperty.componentType = ClassProperty::ComponentType::INT32; classProperty.normalized = true; - classProperty.required = true; + classProperty.offset = {-1, 1, 2}; + classProperty.scale = {2, 1, 3}; + classProperty.max = {10, 5, 6}; + classProperty.min = {-11, -12, -13}; - PropertyView view(classProperty); + PropertyView view(classProperty); REQUIRE(view.status() == PropertyViewStatus::Valid); - REQUIRE(view.normalized()); - REQUIRE(view.required()); + REQUIRE(view.offset()); + REQUIRE(view.scale()); + REQUIRE(view.max()); + REQUIRE(view.min()); - REQUIRE(view.arrayCount() == 0); + REQUIRE(*view.offset() == glm::dvec3(-1, 1, 2)); + REQUIRE(*view.scale() == glm::dvec3(2, 1, 3)); + REQUIRE(*view.max() == glm::dvec3(10, 5, 6)); + REQUIRE(*view.min() == glm::dvec3(-11, -12, -13)); + } + + SECTION("Constructs with noData and defaultProperty") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::VEC4; + classProperty.componentType = ClassProperty::ComponentType::INT8; + classProperty.normalized = true; + classProperty.required = false; + classProperty.noData = {0, 0, -1, -1}; + classProperty.defaultProperty = {1.0, 2.0, 3.0, 4.5}; + + PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::Valid); + REQUIRE(!view.required()); + REQUIRE(view.noData()); + REQUIRE(view.defaultValue()); + + REQUIRE(*view.noData() == glm::i8vec4(0, 0, -1, -1)); + REQUIRE(*view.defaultValue() == glm::dvec4(1.0, 2.0, 3.0, 4.5)); + } + + SECTION("Reports errors for incorrectly defined properties") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::VEC2; + classProperty.componentType = ClassProperty::ComponentType::INT8; + classProperty.normalized = true; + classProperty.required = true; + classProperty.defaultProperty = {1, 2}; + + PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidDefaultValue); + + classProperty.noData = {0, 0}; + view = PropertyView(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNoDataValue); + } + + SECTION("Reports errors for out-of-bounds values") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::VEC2; + classProperty.componentType = ClassProperty::ComponentType::INT8; + classProperty.normalized = true; + classProperty.noData = {-128, -129}; + + PropertyView view(classProperty); + view = PropertyView(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNoDataValue); + } + + SECTION("Reports errors for invalid types") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::VEC2; + classProperty.componentType = ClassProperty::ComponentType::INT32; + classProperty.normalized = true; + classProperty.defaultProperty = true; + + PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidDefaultValue); + + classProperty.noData = "0"; + view = PropertyView(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNoDataValue); + + classProperty.min = JsonValue::Array{-10}; + view = PropertyView(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMin); + + classProperty.max = {10, 20, 30, 40}; + view = PropertyView(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMax); + + classProperty.scale = 1; + view = PropertyView(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidScale); + + classProperty.offset = "(1, 2, 3)"; + view = PropertyView(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidOffset); + } +} + +TEST_CASE("MatN PropertyView") { + SECTION("Constructs empty PropertyView") { + PropertyView view; + REQUIRE(view.status() == PropertyViewStatus::ErrorNonexistentProperty); + REQUIRE(view.arrayCount() == 0); + REQUIRE(!view.normalized()); REQUIRE(!view.offset()); REQUIRE(!view.scale()); REQUIRE(!view.max()); REQUIRE(!view.min()); + REQUIRE(!view.required()); REQUIRE(!view.noData()); REQUIRE(!view.defaultValue()); } - SECTION("Ignores count") { + SECTION("Reports type mismatch") { ClassProperty classProperty; - classProperty.type = ClassProperty::Type::MAT3; - classProperty.componentType = ClassProperty::ComponentType::INT32; - classProperty.count = 5; + classProperty.type = ClassProperty::Type::MAT4; - PropertyView view(classProperty); - REQUIRE(view.status() == PropertyViewStatus::Valid); - REQUIRE(view.arrayCount() == 0); + PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorTypeMismatch); + } + + SECTION("Reports component type mismatch") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::MAT2; + classProperty.componentType = ClassProperty::ComponentType::INT8; + + PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorComponentTypeMismatch); + } + + SECTION("Reports array type mismatch") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::MAT2; + classProperty.componentType = ClassProperty::ComponentType::FLOAT32; + classProperty.array = true; + + PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorArrayTypeMismatch); + } + + SECTION("Reports invalid normalization") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::MAT2; + classProperty.componentType = ClassProperty::ComponentType::FLOAT32; + classProperty.normalized = true; + + PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNormalization); } SECTION("Constructs with offset, scale, max, and min") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::MAT3; - classProperty.componentType = ClassProperty::ComponentType::INT32; + classProperty.componentType = ClassProperty::ComponentType::FLOAT32; // clang-format off classProperty.offset = { -1, 1, 2, @@ -561,7 +772,7 @@ TEST_CASE("MatN PropertyView") { 9, 4, 5}; // clang-format on - PropertyView view(classProperty); + PropertyView view(classProperty); REQUIRE(view.status() == PropertyViewStatus::Valid); REQUIRE(view.offset()); REQUIRE(view.scale()); @@ -569,25 +780,25 @@ TEST_CASE("MatN PropertyView") { REQUIRE(view.min()); // clang-format off - glm::imat3x3 expectedOffset( + glm::mat3 expectedOffset( -1, 1, 2, 3, -1, 4, -5, -5, 0); REQUIRE(*view.offset() == expectedOffset); - glm::imat3x3 expectedScale( + glm::mat3 expectedScale( 1, 1, 1, 2, 2, 3, 3, 4, 5); REQUIRE(*view.scale() == expectedScale); - glm::imat3x3 expectedMax( + glm::mat3 expectedMax( 20, 5, 20, 30, 22, 43, 37, 1, 8); REQUIRE(*view.max() == expectedMax); - glm::imat3x3 expectedMin( + glm::mat3 expectedMin( -10, -2, -3, 0, 20, 4, 9, 4, 5); @@ -606,7 +817,7 @@ TEST_CASE("MatN PropertyView") { 0.0f, 0.0f}; classProperty.defaultProperty = { 1.0f, 2.0f, - 3.0f, 4.0f}; + 3.0f, 4.5f}; // clang-format on PropertyView view(classProperty); @@ -623,29 +834,47 @@ TEST_CASE("MatN PropertyView") { glm::mat2 expectedDefaultValue( 1.0f, 2.0f, - 3.0f, 4.0f); + 3.0f, 4.5f); REQUIRE(*view.defaultValue() == expectedDefaultValue); } - SECTION("Ignores noData and defaultProperty when required is true") { + SECTION("Reports errors for incorrectly defined properties") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::MAT2; - classProperty.componentType = ClassProperty::ComponentType::FLOAT32; + classProperty.componentType = ClassProperty::ComponentType::INT8; classProperty.required = true; + // clang-format off - classProperty.noData = { - 0.0f, 0.0f, - 0.0f, 0.0f}; classProperty.defaultProperty = { - 1.0f, 2.0f, - 3.0f, 4.0f}; + 1, 2, + 3, 4}; // clang-format on + PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidDefaultValue); - PropertyView view(classProperty); - REQUIRE(view.status() == PropertyViewStatus::Valid); - REQUIRE(view.required()); - REQUIRE(!view.noData()); - REQUIRE(!view.defaultValue()); + // clang-format off + classProperty.noData = { + 0, 0, + 0, 0}; + // clang-format on + view = PropertyView(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNoDataValue); + + // clang-format off + classProperty.scale = { + 1, 1, + -1, 1}; + // clang-format on + view = PropertyView(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidScale); + + // clang-format off + classProperty.offset = { + 0, 0, + 2, 1}; + // clang-format on + view = PropertyView(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidOffset); } SECTION("Reports errors for out-of-bounds values") { @@ -740,12 +969,12 @@ TEST_CASE("MatN PropertyView") { } } -TEST_CASE("String PropertyView") { +TEST_CASE("MatN PropertyView (normalized)") { SECTION("Constructs empty PropertyView") { - PropertyView view; + PropertyView view; REQUIRE(view.status() == PropertyViewStatus::ErrorNonexistentProperty); REQUIRE(view.arrayCount() == 0); - REQUIRE(!view.normalized()); + REQUIRE(view.normalized()); REQUIRE(!view.offset()); REQUIRE(!view.scale()); REQUIRE(!view.max()); @@ -757,80 +986,274 @@ TEST_CASE("String PropertyView") { SECTION("Reports type mismatch") { ClassProperty classProperty; - classProperty.type = ClassProperty::Type::BOOLEAN; + classProperty.type = ClassProperty::Type::MAT4; - PropertyView view(classProperty); + PropertyView view(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorTypeMismatch); } + SECTION("Reports component type mismatch") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::MAT2; + classProperty.componentType = ClassProperty::ComponentType::INT8; + + PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorComponentTypeMismatch); + } + SECTION("Reports array type mismatch") { ClassProperty classProperty; - classProperty.type = ClassProperty::Type::STRING; + classProperty.type = ClassProperty::Type::MAT2; + classProperty.componentType = ClassProperty::ComponentType::INT32; classProperty.array = true; - PropertyView view(classProperty); + PropertyView view(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorArrayTypeMismatch); } - SECTION("Ignores non-applicable properties") { + SECTION("Reports invalid normalization") { ClassProperty classProperty; - classProperty.type = ClassProperty::Type::STRING; - classProperty.normalized = true; - classProperty.offset = "offset"; - classProperty.scale = "scale"; - classProperty.max = "max"; - classProperty.min = "min"; + classProperty.type = ClassProperty::Type::MAT2; + classProperty.componentType = ClassProperty::ComponentType::INT32; + classProperty.normalized = false; - PropertyView view(classProperty); - REQUIRE(view.status() == PropertyViewStatus::Valid); - REQUIRE(!view.normalized()); - REQUIRE(!view.offset()); - REQUIRE(!view.scale()); - REQUIRE(!view.max()); - REQUIRE(!view.min()); + PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNormalization); } - SECTION("Ignores count") { + SECTION("Constructs with offset, scale, max, and min") { ClassProperty classProperty; - classProperty.type = ClassProperty::Type::STRING; - classProperty.count = 5; + classProperty.type = ClassProperty::Type::MAT3; + classProperty.componentType = ClassProperty::ComponentType::INT32; + classProperty.normalized = true; + // clang-format off + classProperty.offset = { + -1, 1, 2, + 3, -1, 4, + -5, -5, 0}; + classProperty.scale = { + 1, 1, 1, + 2, 2, 3, + 3, 4, 5}; + classProperty.max = { + 20, 5, 20, + 30, 22, 43, + 37, 1, 8}; + classProperty.min = { + -10, -2, -3, + 0, 20, 4, + 9, 4, 5}; + // clang-format on - PropertyView view(classProperty); + PropertyView view(classProperty); REQUIRE(view.status() == PropertyViewStatus::Valid); - REQUIRE(view.arrayCount() == 0); + REQUIRE(view.offset()); + REQUIRE(view.scale()); + REQUIRE(view.max()); + REQUIRE(view.min()); + + // clang-format off + glm::dmat3 expectedOffset( + -1, 1, 2, + 3, -1, 4, + -5, -5, 0); + REQUIRE(*view.offset() == expectedOffset); + + glm::dmat3 expectedScale( + 1, 1, 1, + 2, 2, 3, + 3, 4, 5); + REQUIRE(*view.scale() == expectedScale); + + glm::dmat3 expectedMax( + 20, 5, 20, + 30, 22, 43, + 37, 1, 8); + REQUIRE(*view.max() == expectedMax); + + glm::dmat3 expectedMin( + -10, -2, -3, + 0, 20, 4, + 9, 4, 5); + REQUIRE(*view.min() == expectedMin); + // clang-format on } SECTION("Constructs with noData and defaultProperty") { ClassProperty classProperty; - classProperty.type = ClassProperty::Type::STRING; + classProperty.type = ClassProperty::Type::MAT2; + classProperty.componentType = ClassProperty::ComponentType::INT32; + classProperty.normalized = true; classProperty.required = false; - classProperty.noData = "null"; - classProperty.defaultProperty = "default"; + // clang-format off + classProperty.noData = { + 0, 0, + 0, 0}; + classProperty.defaultProperty = { + 1.0, 2.0, + 3.0, 4.5}; + // clang-format on - PropertyView view(classProperty); + PropertyView view(classProperty); REQUIRE(view.status() == PropertyViewStatus::Valid); REQUIRE(!view.required()); REQUIRE(view.noData()); REQUIRE(view.defaultValue()); - REQUIRE(*view.noData() == "null"); - REQUIRE(*view.defaultValue() == "default"); + glm::imat2x2 expectedNoData(0); + REQUIRE(*view.noData() == expectedNoData); + + // clang-format off + glm::dmat2 expectedDefaultValue( + 1.0, 2.0, + 3.0, 4.5); + REQUIRE(*view.defaultValue() == expectedDefaultValue); } - SECTION("Ignores noData and defaultProperty when required is true") { + SECTION("Reports errors for incorrectly defined properties") { ClassProperty classProperty; - classProperty.type = ClassProperty::Type::STRING; + classProperty.type = ClassProperty::Type::MAT2; + classProperty.componentType = ClassProperty::ComponentType::INT32; + classProperty.normalized = true; classProperty.required = true; - classProperty.noData = "null"; - classProperty.defaultProperty = "default"; + // clang-format off + classProperty.defaultProperty = { + 1.0, 2.0, + 3.0, 4.5}; + // clang-format on - PropertyView view(classProperty); - REQUIRE(view.status() == PropertyViewStatus::Valid); - REQUIRE(view.required()); + PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidDefaultValue); + + // clang-format off + classProperty.noData = { + 0, 0, + 0, 0}; + // clang-format on + view = PropertyView(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNoDataValue); + } + + SECTION("Reports errors for out-of-bounds values") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::MAT2; + classProperty.componentType = ClassProperty::ComponentType::INT8; + classProperty.normalized = true; + // clang-format off + classProperty.noData = { + 0, 0, + 1, -129}; + // clang-format on + + PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNoDataValue); + } + + SECTION("Reports errors for invalid types") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::MAT2; + classProperty.componentType = ClassProperty::ComponentType::INT8; + classProperty.normalized = true; + + // clang-format off + classProperty.defaultProperty = JsonValue::Array{ + JsonValue::Array{4, 1, 2, 0}, + JsonValue::Array{2, 3, 1, 1} + }; + // clang-format on + + PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidDefaultValue); + + // clang-format off + classProperty.noData = { + 0.45, 0.0, + 1.0, -1.4}; + // clang-format on + view = PropertyView(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNoDataValue); + + classProperty.min = {0, 0}; + view = PropertyView(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMin); + + classProperty.max = {10, 20, 30, 40, 50}; + view = PropertyView(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMax); + + classProperty.scale = 1; + view = PropertyView(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidScale); + + classProperty.offset = "(1, 2, 3, 4)"; + view = PropertyView(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidOffset); + } +} + +TEST_CASE("String PropertyView") { + SECTION("Constructs empty PropertyView") { + PropertyView view; + REQUIRE(view.status() == PropertyViewStatus::ErrorNonexistentProperty); + REQUIRE(view.arrayCount() == 0); + REQUIRE(!view.normalized()); + REQUIRE(!view.offset()); + REQUIRE(!view.scale()); + REQUIRE(!view.max()); + REQUIRE(!view.min()); + REQUIRE(!view.required()); REQUIRE(!view.noData()); REQUIRE(!view.defaultValue()); } + SECTION("Reports type mismatch") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::BOOLEAN; + + PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorTypeMismatch); + } + + SECTION("Reports array type mismatch") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::STRING; + classProperty.array = true; + + PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorArrayTypeMismatch); + } + + SECTION("Constructs with noData and defaultProperty") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::STRING; + classProperty.required = false; + classProperty.noData = "null"; + classProperty.defaultProperty = "default"; + + PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::Valid); + REQUIRE(!view.required()); + REQUIRE(view.noData()); + REQUIRE(view.defaultValue()); + + REQUIRE(*view.noData() == "null"); + REQUIRE(*view.defaultValue() == "default"); + } + + SECTION("Reports errors for incorrectly defined properties") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::STRING; + classProperty.required = true; + classProperty.defaultProperty = "default"; + + PropertyView view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidDefaultValue); + + classProperty.noData = "null"; + view = PropertyView(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNoDataValue); + } + SECTION("Reports errors for invalid types") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::STRING; @@ -878,27 +1301,6 @@ TEST_CASE("Boolean Array PropertyView") { REQUIRE(view.status() == PropertyViewStatus::ErrorArrayTypeMismatch); } - SECTION("Ignores non-applicable properties") { - ClassProperty classProperty; - classProperty.type = ClassProperty::Type::BOOLEAN; - classProperty.array = true; - classProperty.normalized = true; - classProperty.offset = JsonValue::Array{true}; - classProperty.scale = JsonValue::Array{true}; - classProperty.max = JsonValue::Array{true}; - classProperty.min = JsonValue::Array{true}; - classProperty.noData = JsonValue::Array{true}; - - PropertyView> view(classProperty); - REQUIRE(view.status() == PropertyViewStatus::Valid); - REQUIRE(!view.normalized()); - REQUIRE(!view.offset()); - REQUIRE(!view.scale()); - REQUIRE(!view.max()); - REQUIRE(!view.min()); - REQUIRE(!view.noData()); - } - SECTION("Constructs with count") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::BOOLEAN; @@ -928,7 +1330,7 @@ TEST_CASE("Boolean Array PropertyView") { REQUIRE(defaultValue[1]); } - SECTION("Ignores defaultProperty when required is true") { + SECTION("Reports errors for incorrectly defined properties") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::BOOLEAN; classProperty.array = true; @@ -936,9 +1338,7 @@ TEST_CASE("Boolean Array PropertyView") { classProperty.defaultProperty = JsonValue::Array{false, true}; PropertyView> view(classProperty); - REQUIRE(view.status() == PropertyViewStatus::Valid); - REQUIRE(view.required()); - REQUIRE(!view.defaultValue()); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidDefaultValue); } SECTION("Reports errors for invalid types") { @@ -999,36 +1399,14 @@ TEST_CASE("Scalar Array PropertyView") { SECTION("Reports invalid normalization") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::SCALAR; - classProperty.componentType = ClassProperty::ComponentType::FLOAT32; + classProperty.componentType = ClassProperty::ComponentType::INT32; classProperty.array = true; classProperty.normalized = true; - PropertyView> view(classProperty); + PropertyView> view(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNormalization); } - SECTION("Constructs with non-optional properties") { - ClassProperty classProperty; - classProperty.type = ClassProperty::Type::SCALAR; - classProperty.componentType = ClassProperty::ComponentType::UINT8; - classProperty.array = true; - classProperty.normalized = true; - classProperty.required = true; - - PropertyView> view(classProperty); - REQUIRE(view.status() == PropertyViewStatus::Valid); - REQUIRE(view.normalized()); - REQUIRE(view.required()); - - REQUIRE(view.arrayCount() == 0); - REQUIRE(!view.offset()); - REQUIRE(!view.scale()); - REQUIRE(!view.max()); - REQUIRE(!view.min()); - REQUIRE(!view.noData()); - REQUIRE(!view.defaultValue()); - } - SECTION("Constructs with count") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::SCALAR; @@ -1044,39 +1422,39 @@ TEST_CASE("Scalar Array PropertyView") { SECTION("Constructs with offset, scale, max, and min") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::SCALAR; - classProperty.componentType = ClassProperty::ComponentType::INT16; + classProperty.componentType = ClassProperty::ComponentType::FLOAT32; classProperty.array = true; - classProperty.offset = JsonValue::Array{5, 10}; - classProperty.scale = JsonValue::Array{2, 1}; - classProperty.max = JsonValue::Array{10, 20}; - classProperty.min = JsonValue::Array{-10, -1}; + classProperty.offset = JsonValue::Array{5.0f, 10.0f}; + classProperty.scale = JsonValue::Array{2.0f, 1.0f}; + classProperty.max = JsonValue::Array{10.0f, 20.0f}; + classProperty.min = JsonValue::Array{-10.0f, -1.0f}; - PropertyView> view(classProperty); + PropertyView> view(classProperty); REQUIRE(view.status() == PropertyViewStatus::Valid); REQUIRE(view.offset()); REQUIRE(view.scale()); REQUIRE(view.max()); REQUIRE(view.min()); - PropertyArrayView value = *view.offset(); + PropertyArrayView value = *view.offset(); REQUIRE(value.size() == 2); - REQUIRE(value[0] == 5); - REQUIRE(value[1] == 10); + REQUIRE(value[0] == 5.0f); + REQUIRE(value[1] == 10.0f); value = *view.scale(); REQUIRE(value.size() == 2); - REQUIRE(value[0] == 2); - REQUIRE(value[1] == 1); + REQUIRE(value[0] == 2.0f); + REQUIRE(value[1] == 1.0f); value = *view.max(); REQUIRE(value.size() == 2); - REQUIRE(value[0] == 10); - REQUIRE(value[1] == 20); + REQUIRE(value[0] == 10.0f); + REQUIRE(value[1] == 20.0f); value = *view.min(); REQUIRE(value.size() == 2); - REQUIRE(value[0] == -10); - REQUIRE(value[1] == -1); + REQUIRE(value[0] == -10.0f); + REQUIRE(value[1] == -1.0f); } SECTION("Constructs with noData and defaultProperty") { @@ -1105,20 +1483,28 @@ TEST_CASE("Scalar Array PropertyView") { REQUIRE(value[1] == 3); } - SECTION("Ignores noData and defaultProperty when required is true") { + SECTION("Reports errors for incorrectly defined properties") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::SCALAR; classProperty.componentType = ClassProperty::ComponentType::UINT8; classProperty.array = true; classProperty.required = true; - classProperty.noData = {0, 1}; classProperty.defaultProperty = {2, 3}; PropertyView> view(classProperty); - REQUIRE(view.status() == PropertyViewStatus::Valid); - REQUIRE(view.required()); - REQUIRE(!view.noData()); - REQUIRE(!view.defaultValue()); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidDefaultValue); + + classProperty.noData = {0, 1}; + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNoDataValue); + + classProperty.scale = {1, 1}; + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidScale); + + classProperty.offset = {0, 2}; + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidOffset); } SECTION("Reports errors for out-of-bounds values") { @@ -1184,12 +1570,12 @@ TEST_CASE("Scalar Array PropertyView") { } } -TEST_CASE("VecN Array PropertyView") { +TEST_CASE("Scalar Array PropertyView (normalized)") { SECTION("Constructs empty PropertyView") { - PropertyView> view; + PropertyView, true> view; REQUIRE(view.status() == PropertyViewStatus::ErrorNonexistentProperty); REQUIRE(view.arrayCount() == 0); - REQUIRE(!view.normalized()); + REQUIRE(view.normalized()); REQUIRE(!view.offset()); REQUIRE(!view.scale()); REQUIRE(!view.max()); @@ -1201,156 +1587,339 @@ TEST_CASE("VecN Array PropertyView") { SECTION("Reports type mismatch") { ClassProperty classProperty; - classProperty.type = ClassProperty::Type::VEC2; + classProperty.type = ClassProperty::Type::STRING; classProperty.array = true; - PropertyView> view(classProperty); + PropertyView, true> view(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorTypeMismatch); } SECTION("Reports component type mismatch") { ClassProperty classProperty; - classProperty.type = ClassProperty::Type::VEC3; + classProperty.type = ClassProperty::Type::SCALAR; classProperty.componentType = ClassProperty::ComponentType::INT8; classProperty.array = true; - PropertyView> view(classProperty); + PropertyView, true> view(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorComponentTypeMismatch); } SECTION("Reports array type mismatch") { ClassProperty classProperty; - classProperty.type = ClassProperty::Type::VEC3; - classProperty.componentType = ClassProperty::ComponentType::FLOAT32; + classProperty.type = ClassProperty::Type::SCALAR; + classProperty.componentType = ClassProperty::ComponentType::UINT8; classProperty.array = false; - PropertyView> view(classProperty); + PropertyView, true> view(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorArrayTypeMismatch); } SECTION("Reports invalid normalization") { ClassProperty classProperty; - classProperty.type = ClassProperty::Type::VEC3; - classProperty.componentType = ClassProperty::ComponentType::FLOAT32; + classProperty.type = ClassProperty::Type::SCALAR; + classProperty.componentType = ClassProperty::ComponentType::INT32; classProperty.array = true; - classProperty.normalized = true; + classProperty.normalized = false; - PropertyView> view(classProperty); + PropertyView, true> view(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNormalization); } - SECTION("Constructs with non-optional properties") { - ClassProperty classProperty; - classProperty.type = ClassProperty::Type::VEC3; - classProperty.componentType = ClassProperty::ComponentType::INT16; - classProperty.array = true; - classProperty.normalized = true; - classProperty.required = true; - - PropertyView> view(classProperty); - REQUIRE(view.status() == PropertyViewStatus::Valid); - REQUIRE(view.normalized()); - REQUIRE(view.required()); - - REQUIRE(view.arrayCount() == 0); - REQUIRE(!view.offset()); - REQUIRE(!view.scale()); - REQUIRE(!view.max()); - REQUIRE(!view.min()); - REQUIRE(!view.noData()); - REQUIRE(!view.defaultValue()); - } - SECTION("Constructs with count") { ClassProperty classProperty; - classProperty.type = ClassProperty::Type::VEC3; - classProperty.componentType = ClassProperty::ComponentType::INT16; + classProperty.type = ClassProperty::Type::SCALAR; + classProperty.componentType = ClassProperty::ComponentType::UINT8; classProperty.array = true; + classProperty.normalized = true; classProperty.count = 5; - PropertyView> view(classProperty); + PropertyView, true> view(classProperty); REQUIRE(view.status() == PropertyViewStatus::Valid); - REQUIRE(view.arrayCount() == classProperty.count); + REQUIRE(view.arrayCount() == *classProperty.count); } SECTION("Constructs with offset, scale, max, and min") { ClassProperty classProperty; - classProperty.type = ClassProperty::Type::VEC3; - classProperty.componentType = ClassProperty::ComponentType::INT8; + classProperty.type = ClassProperty::Type::SCALAR; + classProperty.componentType = ClassProperty::ComponentType::INT16; classProperty.array = true; - classProperty.offset = {{-1, 1, 2}, {4, 4, 0}}; - classProperty.scale = {{2, 1, 3}, {8, 2, 3}}; - classProperty.max = {{14, 28, 12}, {10, 5, 6}}; - classProperty.min = {{-11, -12, -13}, {-2, -4, 6}}; + classProperty.normalized = true; + classProperty.offset = JsonValue::Array{5.0, 10.0}; + classProperty.scale = JsonValue::Array{2.0, 1.0}; + classProperty.max = JsonValue::Array{10.0, 20.0}; + classProperty.min = JsonValue::Array{-10.0, -1.0}; - PropertyView> view(classProperty); + PropertyView, true> view(classProperty); REQUIRE(view.status() == PropertyViewStatus::Valid); REQUIRE(view.offset()); REQUIRE(view.scale()); REQUIRE(view.max()); REQUIRE(view.min()); - PropertyArrayView value = *view.offset(); + PropertyArrayView value = *view.offset(); REQUIRE(value.size() == 2); - REQUIRE(value[0] == glm::i8vec3(-1, 1, 2)); - REQUIRE(value[1] == glm::i8vec3(4, 4, 0)); + REQUIRE(value[0] == 5.0); + REQUIRE(value[1] == 10.0); value = *view.scale(); REQUIRE(value.size() == 2); - REQUIRE(value[0] == glm::i8vec3(2, 1, 3)); - REQUIRE(value[1] == glm::i8vec3(8, 2, 3)); + REQUIRE(value[0] == 2.0); + REQUIRE(value[1] == 1.0); value = *view.max(); REQUIRE(value.size() == 2); - REQUIRE(value[0] == glm::i8vec3(14, 28, 12)); - REQUIRE(value[1] == glm::i8vec3(10, 5, 6)); + REQUIRE(value[0] == 10.0); + REQUIRE(value[1] == 20.0); value = *view.min(); REQUIRE(value.size() == 2); - REQUIRE(value[0] == glm::i8vec3(-11, -12, -13)); - REQUIRE(value[1] == glm::i8vec3(-2, -4, 6)); + REQUIRE(value[0] == -10.0); + REQUIRE(value[1] == -1.0); } SECTION("Constructs with noData and defaultProperty") { ClassProperty classProperty; - classProperty.type = ClassProperty::Type::VEC2; - classProperty.componentType = ClassProperty::ComponentType::FLOAT32; + classProperty.type = ClassProperty::Type::SCALAR; + classProperty.componentType = ClassProperty::ComponentType::UINT8; classProperty.array = true; + classProperty.normalized = true; classProperty.required = false; - classProperty.noData = {{0.0f, 0.0f}, {1.0f, 2.0f}}; - classProperty.defaultProperty = {{3.0f, 4.0f}, {5.0f, 6.0f}}; + classProperty.noData = {0, 1}; + classProperty.defaultProperty = {2.5, 3.5}; - PropertyView> view(classProperty); + PropertyView, true> view(classProperty); REQUIRE(view.status() == PropertyViewStatus::Valid); REQUIRE(!view.required()); REQUIRE(view.noData()); REQUIRE(view.defaultValue()); - PropertyArrayView value = *view.noData(); - REQUIRE(value.size() == 2); - REQUIRE(value[0] == glm::vec2(0.0f, 0.0f)); - REQUIRE(value[1] == glm::vec2(1.0f, 2.0f)); + PropertyArrayView noData(*view.noData()); + REQUIRE(noData.size() == 2); + REQUIRE(noData[0] == 0); + REQUIRE(noData[1] == 1); - value = *view.defaultValue(); - REQUIRE(value.size() == 2); - REQUIRE(value[0] == glm::vec2(3.0f, 4.0f)); - REQUIRE(value[1] == glm::vec2(5.0f, 6.0f)); + PropertyArrayView defaultValue(*view.defaultValue()); + REQUIRE(defaultValue.size() == 2); + REQUIRE(defaultValue[0] == 2.5); + REQUIRE(defaultValue[1] == 3.5); } - SECTION("Ignores noData and defaultProperty when required is true") { + SECTION("Reports errors for incorrectly defined properties") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::SCALAR; + classProperty.componentType = ClassProperty::ComponentType::UINT8; + classProperty.array = true; + classProperty.normalized = true; + classProperty.required = true; + classProperty.defaultProperty = {2, 3}; + + PropertyView, true> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidDefaultValue); + + classProperty.noData = {0, 1}; + view = PropertyView, true>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNoDataValue); + } + + SECTION("Reports errors for out-of-bounds values") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::SCALAR; + classProperty.componentType = ClassProperty::ComponentType::UINT8; + classProperty.array = true; + classProperty.normalized = true; + classProperty.noData = {-1, 0}; + + PropertyView, true> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNoDataValue); + } + + SECTION("Reports errors for invalid types") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::SCALAR; + classProperty.componentType = ClassProperty::ComponentType::UINT8; + classProperty.array = true; + classProperty.normalized = true; + classProperty.defaultProperty = "[256, 256]"; + + PropertyView, true> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidDefaultValue); + + classProperty.noData = 0; + view = PropertyView, true>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNoDataValue); + + classProperty.min = false; + view = PropertyView, true>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMin); + + classProperty.max = JsonValue::Array{10.4, "30.0"}; + view = PropertyView, true>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMax); + + classProperty.scale = + JsonValue::Array{JsonValue::Array{2.3}, JsonValue::Array{1.3}}; + view = PropertyView, true>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidScale); + + classProperty.offset = "10"; + view = PropertyView, true>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidOffset); + } +} + +TEST_CASE("VecN Array PropertyView") { + SECTION("Constructs empty PropertyView") { + PropertyView> view; + REQUIRE(view.status() == PropertyViewStatus::ErrorNonexistentProperty); + REQUIRE(view.arrayCount() == 0); + REQUIRE(!view.normalized()); + REQUIRE(!view.offset()); + REQUIRE(!view.scale()); + REQUIRE(!view.max()); + REQUIRE(!view.min()); + REQUIRE(!view.required()); + REQUIRE(!view.noData()); + REQUIRE(!view.defaultValue()); + } + + SECTION("Reports type mismatch") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::VEC2; + classProperty.array = true; + + PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorTypeMismatch); + } + + SECTION("Reports component type mismatch") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::VEC3; + classProperty.componentType = ClassProperty::ComponentType::INT8; + classProperty.array = true; + + PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorComponentTypeMismatch); + } + + SECTION("Reports array type mismatch") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::VEC3; + classProperty.componentType = ClassProperty::ComponentType::FLOAT32; + classProperty.array = false; + + PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorArrayTypeMismatch); + } + + SECTION("Reports invalid normalization") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::VEC3; classProperty.componentType = ClassProperty::ComponentType::FLOAT32; classProperty.array = true; - classProperty.required = true; + classProperty.normalized = true; + + PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNormalization); + } + + SECTION("Constructs with count") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::VEC3; + classProperty.componentType = ClassProperty::ComponentType::INT16; + classProperty.array = true; + classProperty.count = 5; + + PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::Valid); + REQUIRE(view.arrayCount() == classProperty.count); + } + + SECTION("Constructs with offset, scale, max, and min") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::VEC3; + classProperty.componentType = ClassProperty::ComponentType::FLOAT32; + classProperty.array = true; + classProperty.offset = {{-1, 1, 2}, {4, 4, 0}}; + classProperty.scale = {{2, 1, 3}, {8, 2, 3}}; + classProperty.max = {{14, 28, 12}, {10, 5, 6}}; + classProperty.min = {{-11, -12, -13}, {-2, -4, 6}}; + + PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::Valid); + REQUIRE(view.offset()); + REQUIRE(view.scale()); + REQUIRE(view.max()); + REQUIRE(view.min()); + + PropertyArrayView value = *view.offset(); + REQUIRE(value.size() == 2); + REQUIRE(value[0] == glm::vec3(-1, 1, 2)); + REQUIRE(value[1] == glm::vec3(4, 4, 0)); + + value = *view.scale(); + REQUIRE(value.size() == 2); + REQUIRE(value[0] == glm::vec3(2, 1, 3)); + REQUIRE(value[1] == glm::vec3(8, 2, 3)); + + value = *view.max(); + REQUIRE(value.size() == 2); + REQUIRE(value[0] == glm::vec3(14, 28, 12)); + REQUIRE(value[1] == glm::vec3(10, 5, 6)); + + value = *view.min(); + REQUIRE(value.size() == 2); + REQUIRE(value[0] == glm::vec3(-11, -12, -13)); + REQUIRE(value[1] == glm::vec3(-2, -4, 6)); + } + + SECTION("Constructs with noData and defaultProperty") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::VEC2; + classProperty.componentType = ClassProperty::ComponentType::FLOAT32; + classProperty.array = true; + classProperty.required = false; classProperty.noData = {{0.0f, 0.0f}, {1.0f, 2.0f}}; classProperty.defaultProperty = {{3.0f, 4.0f}, {5.0f, 6.0f}}; PropertyView> view(classProperty); REQUIRE(view.status() == PropertyViewStatus::Valid); - REQUIRE(view.required()); - REQUIRE(!view.noData()); - REQUIRE(!view.defaultValue()); + REQUIRE(!view.required()); + REQUIRE(view.noData()); + REQUIRE(view.defaultValue()); + + PropertyArrayView value = *view.noData(); + REQUIRE(value.size() == 2); + REQUIRE(value[0] == glm::vec2(0.0f, 0.0f)); + REQUIRE(value[1] == glm::vec2(1.0f, 2.0f)); + + value = *view.defaultValue(); + REQUIRE(value.size() == 2); + REQUIRE(value[0] == glm::vec2(3.0f, 4.0f)); + REQUIRE(value[1] == glm::vec2(5.0f, 6.0f)); + } + + SECTION("Reports errors for incorrectly defined properties") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::VEC2; + classProperty.componentType = ClassProperty::ComponentType::INT32; + classProperty.array = true; + classProperty.required = true; + classProperty.defaultProperty = {{3, 4}, {5, 6}}; + + PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidDefaultValue); + + classProperty.noData = {{0, 0}, {1, 2}}; + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNoDataValue); + + classProperty.scale = {{1, 1}, {-1, -1}}; + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidScale); + + classProperty.offset = {{0, 0}, {-4, 7}}; + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidOffset); } SECTION("Reports errors for out-of-bounds values") { @@ -1360,68 +1929,589 @@ TEST_CASE("VecN Array PropertyView") { classProperty.array = true; classProperty.defaultProperty = {{128, 129}, {0, 2}}; - PropertyView> view(classProperty); + PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidDefaultValue); + + classProperty.noData = {{0, 0}, {-128, -129}}; + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNoDataValue); + + classProperty.min = {{-2, -3}, {-200, 0}}; + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMin); + + classProperty.max = {{10, 5}, {808, 3}}; + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMax); + + classProperty.scale = {{1, 128}, {2, 2}}; + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidScale); + + classProperty.offset = {{0, 0}, {-1, -222}}; + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidOffset); + } + + SECTION("Reports errors for invalid types") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::VEC2; + classProperty.componentType = ClassProperty::ComponentType::INT8; + classProperty.array = true; + classProperty.defaultProperty = {1, 20}; + + PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidDefaultValue); + + classProperty.noData = JsonValue::Array{{2.0f, 5.4f}}; + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNoDataValue); + + classProperty.min = {{-10, -1, 4}, {0, 0, 0}}; + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMin); + + classProperty.max = {{10, 20, 30, 40}, {1, 2, 3, 4}}; + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMax); + + classProperty.scale = 2; + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidScale); + + classProperty.offset = "(1, 2)"; + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidOffset); + } +} + +TEST_CASE("VecN Array PropertyView (normalized)") { + SECTION("Constructs empty PropertyView") { + PropertyView, true> view; + REQUIRE(view.status() == PropertyViewStatus::ErrorNonexistentProperty); + REQUIRE(view.arrayCount() == 0); + REQUIRE(view.normalized()); + REQUIRE(!view.offset()); + REQUIRE(!view.scale()); + REQUIRE(!view.max()); + REQUIRE(!view.min()); + REQUIRE(!view.required()); + REQUIRE(!view.noData()); + REQUIRE(!view.defaultValue()); + } + + SECTION("Reports type mismatch") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::VEC2; + classProperty.array = true; + + PropertyView, true> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorTypeMismatch); + } + + SECTION("Reports component type mismatch") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::VEC3; + classProperty.componentType = ClassProperty::ComponentType::INT8; + classProperty.array = true; + + PropertyView, true> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorComponentTypeMismatch); + } + + SECTION("Reports array type mismatch") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::VEC3; + classProperty.componentType = ClassProperty::ComponentType::INT32; + classProperty.array = false; + + PropertyView, true> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorArrayTypeMismatch); + } + + SECTION("Reports invalid normalization") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::VEC3; + classProperty.componentType = ClassProperty::ComponentType::INT32; + classProperty.array = true; + classProperty.normalized = false; + + PropertyView, true> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNormalization); + } + + SECTION("Constructs with count") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::VEC3; + classProperty.componentType = ClassProperty::ComponentType::INT32; + classProperty.array = true; + classProperty.count = 5; + classProperty.normalized = true; + + PropertyView, true> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::Valid); + REQUIRE(view.arrayCount() == classProperty.count); + } + + SECTION("Constructs with offset, scale, max, and min") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::VEC3; + classProperty.componentType = ClassProperty::ComponentType::INT32; + classProperty.array = true; + classProperty.normalized = true; + classProperty.offset = {{-1, 1, 2}, {4, 4, 0}}; + classProperty.scale = {{2, 1, 3}, {8, 2, 3}}; + classProperty.max = {{14, 28, 12}, {10, 5, 6}}; + classProperty.min = {{-11, -12, -13}, {-2, -4, 6}}; + + PropertyView, true> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::Valid); + REQUIRE(view.offset()); + REQUIRE(view.scale()); + REQUIRE(view.max()); + REQUIRE(view.min()); + + PropertyArrayView value = *view.offset(); + REQUIRE(value.size() == 2); + REQUIRE(value[0] == glm::dvec3(-1, 1, 2)); + REQUIRE(value[1] == glm::dvec3(4, 4, 0)); + + value = *view.scale(); + REQUIRE(value.size() == 2); + REQUIRE(value[0] == glm::dvec3(2, 1, 3)); + REQUIRE(value[1] == glm::dvec3(8, 2, 3)); + + value = *view.max(); + REQUIRE(value.size() == 2); + REQUIRE(value[0] == glm::dvec3(14, 28, 12)); + REQUIRE(value[1] == glm::dvec3(10, 5, 6)); + + value = *view.min(); + REQUIRE(value.size() == 2); + REQUIRE(value[0] == glm::dvec3(-11, -12, -13)); + REQUIRE(value[1] == glm::dvec3(-2, -4, 6)); + } + + SECTION("Constructs with noData and defaultProperty") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::VEC2; + classProperty.componentType = ClassProperty::ComponentType::INT32; + classProperty.array = true; + classProperty.normalized = true; + classProperty.required = false; + classProperty.noData = {{0, 0}, {1, 2}}; + classProperty.defaultProperty = {{3.5, 4.5}, {5.0, 6.0}}; + + PropertyView, true> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::Valid); + REQUIRE(!view.required()); + REQUIRE(view.noData()); + REQUIRE(view.defaultValue()); + + PropertyArrayView noData = *view.noData(); + REQUIRE(noData.size() == 2); + REQUIRE(noData[0] == glm::ivec2(0, 0)); + REQUIRE(noData[1] == glm::ivec2(1, 2)); + + PropertyArrayView defaultValue = *view.defaultValue(); + REQUIRE(defaultValue.size() == 2); + REQUIRE(defaultValue[0] == glm::dvec2(3.5, 4.5)); + REQUIRE(defaultValue[1] == glm::dvec2(5.0, 6.0)); + } + + SECTION("Reports errors for incorrectly defined properties") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::VEC2; + classProperty.componentType = ClassProperty::ComponentType::INT32; + classProperty.array = true; + classProperty.normalized = true; + classProperty.required = true; + classProperty.defaultProperty = {{3, 4}, {5, 6}}; + + PropertyView, true> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidDefaultValue); + + classProperty.noData = {{0, 0}, {1, 2}}; + view = PropertyView, true>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNoDataValue); + } + + SECTION("Reports errors for out-of-bounds values") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::VEC2; + classProperty.componentType = ClassProperty::ComponentType::INT8; + classProperty.normalized = true; + classProperty.array = true; + classProperty.noData = {{0, 0}, {-128, -129}}; + + PropertyView, true> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNoDataValue); + } + + SECTION("Reports errors for invalid types") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::VEC2; + classProperty.componentType = ClassProperty::ComponentType::INT8; + classProperty.array = true; + classProperty.normalized = true; + classProperty.defaultProperty = {1, 20}; + + PropertyView, true> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidDefaultValue); + + classProperty.noData = JsonValue::Array{{2.0f, 5.4f}, "not a vec2"}; + view = PropertyView, true>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNoDataValue); + + classProperty.min = {{-10, -1, 4}, {0, 0, 0}}; + view = PropertyView, true>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMin); + + classProperty.max = {{10, 20, 30, 40}, {1, 2, 3, 4}}; + view = PropertyView, true>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMax); + + classProperty.scale = 2; + view = PropertyView, true>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidScale); + + classProperty.offset = "(1, 2)"; + view = PropertyView, true>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidOffset); + } +} + +TEST_CASE("MatN Array PropertyView") { + SECTION("Constructs empty PropertyView") { + PropertyView> view; + REQUIRE(view.status() == PropertyViewStatus::ErrorNonexistentProperty); + REQUIRE(view.arrayCount() == 0); + REQUIRE(!view.normalized()); + REQUIRE(!view.offset()); + REQUIRE(!view.scale()); + REQUIRE(!view.max()); + REQUIRE(!view.min()); + REQUIRE(!view.required()); + REQUIRE(!view.noData()); + REQUIRE(!view.defaultValue()); + } + + SECTION("Reports type mismatch") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::MAT4; + classProperty.array = true; + + PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorTypeMismatch); + } + + SECTION("Reports component type mismatch") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::MAT2; + classProperty.componentType = ClassProperty::ComponentType::INT8; + classProperty.array = true; + + PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorComponentTypeMismatch); + } + + SECTION("Reports array type mismatch") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::MAT2; + classProperty.componentType = ClassProperty::ComponentType::FLOAT32; + classProperty.array = false; + + PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorArrayTypeMismatch); + } + + SECTION("Reports invalid normalization") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::MAT2; + classProperty.componentType = ClassProperty::ComponentType::FLOAT32; + classProperty.array = true; + classProperty.normalized = true; + + PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNormalization); + } + + SECTION("Constructs with count") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::MAT3; + classProperty.componentType = ClassProperty::ComponentType::INT32; + classProperty.array = true; + classProperty.count = 5; + + PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::Valid); + REQUIRE(view.arrayCount() == classProperty.count); + } + + SECTION("Constructs with offset, scale, max, and min") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::MAT2; + classProperty.componentType = ClassProperty::ComponentType::FLOAT32; + classProperty.array = true; + // clang-format off + classProperty.offset = { + {-1, 1, + 0, 2}, + {2, 40, + 6, -8}, + }; + classProperty.scale = { + {1, 1, + 1, 0}, + {-2, 5, + 7, 1} + }; + classProperty.max = { + {2, 4, + 8, 0}, + {-7, 8, + 4, 4}, + }; + classProperty.min = { + {-1, -6, + -1, 2}, + {0, 1, + 2, 3}, + }; + // clang-format on + + PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::Valid); + REQUIRE(view.offset()); + REQUIRE(view.scale()); + REQUIRE(view.max()); + REQUIRE(view.min()); + + PropertyArrayView value = *view.offset(); + REQUIRE(value.size() == 2); + REQUIRE(value[0] == glm::mat2(-1, 1, 0, 2)); + REQUIRE(value[1] == glm::mat2(2, 40, 6, -8)); + + value = *view.scale(); + REQUIRE(value.size() == 2); + REQUIRE(value[0] == glm::mat2(1, 1, 1, 0)); + REQUIRE(value[1] == glm::mat2(-2, 5, 7, 1)); + + value = *view.max(); + REQUIRE(value.size() == 2); + REQUIRE(value[0] == glm::mat2(2, 4, 8, 0)); + REQUIRE(value[1] == glm::mat2(-7, 8, 4, 4)); + + value = *view.min(); + REQUIRE(value.size() == 2); + REQUIRE(value[0] == glm::mat2(-1, -6, -1, 2)); + REQUIRE(value[1] == glm::mat2(0, 1, 2, 3)); + } + + SECTION("Constructs with noData and defaultProperty") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::MAT2; + classProperty.componentType = ClassProperty::ComponentType::INT8; + classProperty.array = true; + classProperty.required = false; + // clang-format off + classProperty.noData = { + {0, 0, + 0, 0}, + {-1, -1, + -1, -1}, + }; + classProperty.defaultProperty = { + {1, 1, + 1, 1}, + {2, 2, + 2, 2}, + }; + // clang-format on + + PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::Valid); + REQUIRE(!view.required()); + REQUIRE(view.noData()); + REQUIRE(view.defaultValue()); + + PropertyArrayView value = *view.noData(); + REQUIRE(value.size() == 2); + REQUIRE(value[0] == glm::i8mat2x2(0, 0, 0, 0)); + REQUIRE(value[1] == glm::i8mat2x2(-1, -1, -1, -1)); + + value = view.defaultValue().value(); + REQUIRE(value.size() == 2); + REQUIRE(value[0] == glm::i8mat2x2(1, 1, 1, 1)); + REQUIRE(value[1] == glm::i8mat2x2(2, 2, 2, 2)); + } + + SECTION("Reports errors for incorrectly defined properties") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::MAT2; + classProperty.componentType = ClassProperty::ComponentType::INT32; + classProperty.array = true; + classProperty.required = true; + // clang-format off + classProperty.defaultProperty = { + {1, 1, + 1, 1}, + {2, 2, + 2, 2}, + }; + // clang-format on + + PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidDefaultValue); + + // clang-format off + classProperty.noData = { + {0, 0, + 0, 0}, + {-1, -1, + -1, -1}, + }; + // clang-format on + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNoDataValue); + + // clang-format off + classProperty.scale = { + {1, 0, + 0, 1}, + {-1, 0, + 0, -1}, + }; + // clang-format on + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidScale); + + // clang-format off + classProperty.offset = { + {2, 2, + 1, 1}, + {0, 2, + 1, 2}, + }; + // clang-format on + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidOffset); + } + + SECTION("Reports errors for out-of-bounds values") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::MAT2; + classProperty.componentType = ClassProperty::ComponentType::INT8; + classProperty.array = true; + + // clang-format off + classProperty.defaultProperty = { + {1, 1, + 1, 290}, + {2, 2, + 2, 2}, + }; + // clang-format on + + PropertyView> view(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidDefaultValue); - classProperty.noData = {{0, 0}, {-128, -129}}; - view = PropertyView>(classProperty); + // clang-format off + classProperty.noData = { + {0, 0, + 0, 0}, + {-140, -1, + -1, -1}, + }; + // clang-format on + view = PropertyView>(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNoDataValue); - classProperty.min = {{-2, -3}, {-200, 0}}; - view = PropertyView>(classProperty); + // clang-format off + classProperty.min = { + {-129, 0, + 0, 0}, + {-1, -1, + -1, -1}, + }; + // clang-format on + view = PropertyView>(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMin); - classProperty.max = {{10, 5}, {808, 3}}; - view = PropertyView>(classProperty); + // clang-format off + classProperty.max = { + {-128, 189, + 20, 2}, + {10, 12, + 8, 4}, + }; + // clang-format on + view = PropertyView>(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMax); - classProperty.scale = {{1, 128}, {2, 2}}; - view = PropertyView>(classProperty); + //clang-format off + classProperty.scale = { + {1, 2, 3, 4}, + {256, 80, 9, 52}, + }; + // clang-format on + view = PropertyView>(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidScale); - classProperty.offset = {{0, 0}, {-1, -222}}; - view = PropertyView>(classProperty); + // clang-format off + classProperty.offset = { + {129, 0, + 0, 2}, + {4, 0, + 0, 8},}; + // clang-format on + view = PropertyView>(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidOffset); } SECTION("Reports errors for invalid types") { ClassProperty classProperty; - classProperty.type = ClassProperty::Type::VEC2; + classProperty.type = ClassProperty::Type::MAT2; classProperty.componentType = ClassProperty::ComponentType::INT8; classProperty.array = true; - classProperty.defaultProperty = {1, 20}; + classProperty.defaultProperty = {4, 1, 2, 0}; - PropertyView> view(classProperty); + PropertyView> view(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidDefaultValue); - classProperty.noData = JsonValue::Array{{2.0f, 5.4f}}; - view = PropertyView>(classProperty); + // clang-format off + classProperty.noData = { + {0.45, 0.0, + 1.0, -1.4} + }; + // clang-format on + view = PropertyView>(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNoDataValue); - classProperty.min = {{-10, -1, 4}, {0, 0, 0}}; - view = PropertyView>(classProperty); + classProperty.min = {{0, 1, 2, 3, 4, 5, 6}}; + view = PropertyView>(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMin); - classProperty.max = {{10, 20, 30, 40}, {1, 2, 3, 4}}; - view = PropertyView>(classProperty); + classProperty.max = {{0, 1, 2, 3}, false}; + view = PropertyView>(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMax); - classProperty.scale = 2; - view = PropertyView>(classProperty); + classProperty.scale = 1; + view = PropertyView>(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidScale); - classProperty.offset = "(1, 2)"; - view = PropertyView>(classProperty); + classProperty.offset = "[(1, 2, 3, 4)]"; + view = PropertyView>(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidOffset); } } -TEST_CASE("MatN Array PropertyView") { +TEST_CASE("MatN Array PropertyView (normalized)") { SECTION("Constructs empty PropertyView") { - PropertyView> view; + PropertyView, true> view; REQUIRE(view.status() == PropertyViewStatus::ErrorNonexistentProperty); REQUIRE(view.arrayCount() == 0); - REQUIRE(!view.normalized()); + REQUIRE(view.normalized()); REQUIRE(!view.offset()); REQUIRE(!view.scale()); REQUIRE(!view.max()); @@ -1436,7 +2526,7 @@ TEST_CASE("MatN Array PropertyView") { classProperty.type = ClassProperty::Type::MAT4; classProperty.array = true; - PropertyView> view(classProperty); + PropertyView, true> view(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorTypeMismatch); } @@ -1446,51 +2536,29 @@ TEST_CASE("MatN Array PropertyView") { classProperty.componentType = ClassProperty::ComponentType::INT8; classProperty.array = true; - PropertyView> view(classProperty); + PropertyView, true> view(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorComponentTypeMismatch); } SECTION("Reports array type mismatch") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::MAT2; - classProperty.componentType = ClassProperty::ComponentType::FLOAT32; + classProperty.componentType = ClassProperty::ComponentType::INT32; classProperty.array = false; - PropertyView> view(classProperty); + PropertyView, true> view(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorArrayTypeMismatch); } SECTION("Reports invalid normalization") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::MAT2; - classProperty.componentType = ClassProperty::ComponentType::FLOAT32; - classProperty.array = true; - classProperty.normalized = true; - - PropertyView> view(classProperty); - REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNormalization); - } - - SECTION("Constructs with non-optional properties") { - ClassProperty classProperty; - classProperty.type = ClassProperty::Type::MAT3; classProperty.componentType = ClassProperty::ComponentType::INT32; classProperty.array = true; - classProperty.normalized = true; - classProperty.required = true; - - PropertyView> view(classProperty); - REQUIRE(view.status() == PropertyViewStatus::Valid); - REQUIRE(view.normalized()); - REQUIRE(view.required()); + classProperty.normalized = false; - REQUIRE(view.arrayCount() == 0); - REQUIRE(!view.offset()); - REQUIRE(!view.scale()); - REQUIRE(!view.max()); - REQUIRE(!view.min()); - REQUIRE(!view.noData()); - REQUIRE(!view.defaultValue()); + PropertyView, true> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNormalization); } SECTION("Constructs with count") { @@ -1498,9 +2566,10 @@ TEST_CASE("MatN Array PropertyView") { classProperty.type = ClassProperty::Type::MAT3; classProperty.componentType = ClassProperty::ComponentType::INT32; classProperty.array = true; + classProperty.normalized = true; classProperty.count = 5; - PropertyView> view(classProperty); + PropertyView, true> view(classProperty); REQUIRE(view.status() == PropertyViewStatus::Valid); REQUIRE(view.arrayCount() == classProperty.count); } @@ -1508,8 +2577,10 @@ TEST_CASE("MatN Array PropertyView") { SECTION("Constructs with offset, scale, max, and min") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::MAT2; - classProperty.componentType = ClassProperty::ComponentType::INT8; + classProperty.componentType = ClassProperty::ComponentType::INT32; classProperty.array = true; + classProperty.normalized = true; + // clang-format off classProperty.offset = { {-1, 1, @@ -1537,32 +2608,32 @@ TEST_CASE("MatN Array PropertyView") { }; // clang-format on - PropertyView> view(classProperty); + PropertyView, true> view(classProperty); REQUIRE(view.status() == PropertyViewStatus::Valid); REQUIRE(view.offset()); REQUIRE(view.scale()); REQUIRE(view.max()); REQUIRE(view.min()); - PropertyArrayView value = *view.offset(); + PropertyArrayView value = *view.offset(); REQUIRE(value.size() == 2); - REQUIRE(value[0] == glm::i8mat2x2(-1, 1, 0, 2)); - REQUIRE(value[1] == glm::i8mat2x2(2, 40, 6, -8)); + REQUIRE(value[0] == glm::dmat2(-1, 1, 0, 2)); + REQUIRE(value[1] == glm::dmat2(2, 40, 6, -8)); value = *view.scale(); REQUIRE(value.size() == 2); - REQUIRE(value[0] == glm::i8mat2x2(1, 1, 1, 0)); - REQUIRE(value[1] == glm::i8mat2x2(-2, 5, 7, 1)); + REQUIRE(value[0] == glm::dmat2(1, 1, 1, 0)); + REQUIRE(value[1] == glm::dmat2(-2, 5, 7, 1)); value = *view.max(); REQUIRE(value.size() == 2); - REQUIRE(value[0] == glm::i8mat2x2(2, 4, 8, 0)); - REQUIRE(value[1] == glm::i8mat2x2(-7, 8, 4, 4)); + REQUIRE(value[0] == glm::dmat2(2, 4, 8, 0)); + REQUIRE(value[1] == glm::dmat2(-7, 8, 4, 4)); value = *view.min(); REQUIRE(value.size() == 2); - REQUIRE(value[0] == glm::i8mat2x2(-1, -6, -1, 2)); - REQUIRE(value[1] == glm::i8mat2x2(0, 1, 2, 3)); + REQUIRE(value[0] == glm::dmat2(-1, -6, -1, 2)); + REQUIRE(value[1] == glm::dmat2(0, 1, 2, 3)); } SECTION("Constructs with noData and defaultProperty") { @@ -1570,6 +2641,7 @@ TEST_CASE("MatN Array PropertyView") { classProperty.type = ClassProperty::Type::MAT2; classProperty.componentType = ClassProperty::ComponentType::INT8; classProperty.array = true; + classProperty.normalized = true; classProperty.required = false; // clang-format off classProperty.noData = { @@ -1586,36 +2658,31 @@ TEST_CASE("MatN Array PropertyView") { }; // clang-format on - PropertyView> view(classProperty); + PropertyView, true> view(classProperty); REQUIRE(view.status() == PropertyViewStatus::Valid); REQUIRE(!view.required()); REQUIRE(view.noData()); REQUIRE(view.defaultValue()); - PropertyArrayView value = *view.noData(); - REQUIRE(value.size() == 2); - REQUIRE(value[0] == glm::i8mat2x2(0, 0, 0, 0)); - REQUIRE(value[1] == glm::i8mat2x2(-1, -1, -1, -1)); + PropertyArrayView noData = *view.noData(); + REQUIRE(noData.size() == 2); + REQUIRE(noData[0] == glm::i8mat2x2(0, 0, 0, 0)); + REQUIRE(noData[1] == glm::i8mat2x2(-1, -1, -1, -1)); - value = view.defaultValue().value(); - REQUIRE(value.size() == 2); - REQUIRE(value[0] == glm::i8mat2x2(1, 1, 1, 1)); - REQUIRE(value[1] == glm::i8mat2x2(2, 2, 2, 2)); + PropertyArrayView defaultValue = *view.defaultValue(); + REQUIRE(defaultValue.size() == 2); + REQUIRE(defaultValue[0] == glm::dmat2(1, 1, 1, 1)); + REQUIRE(defaultValue[1] == glm::dmat2(2, 2, 2, 2)); } - SECTION("Ignores noData and defaultProperty when required is true") { + SECTION("Reports errors for incorrectly defined properties") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::MAT2; - classProperty.componentType = ClassProperty::ComponentType::FLOAT32; + classProperty.componentType = ClassProperty::ComponentType::INT32; classProperty.array = true; + classProperty.normalized = true; classProperty.required = true; // clang-format off - classProperty.noData = { - {0, 0, - 0, 0}, - {-1, -1, - -1, -1}, - }; classProperty.defaultProperty = { {1, 1, 1, 1}, @@ -1624,11 +2691,20 @@ TEST_CASE("MatN Array PropertyView") { }; // clang-format on - PropertyView> view(classProperty); - REQUIRE(view.status() == PropertyViewStatus::Valid); - REQUIRE(view.required()); - REQUIRE(!view.noData()); - REQUIRE(!view.defaultValue()); + PropertyView, true> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidDefaultValue); + + // clang-format off + classProperty.noData = { + {0, 0, + 0, 0}, + {-1, -1, + -1, -1}, + }; + // clang-format on + view = PropertyView, true>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNoDataValue); + ; } SECTION("Reports errors for out-of-bounds values") { @@ -1636,18 +2712,7 @@ TEST_CASE("MatN Array PropertyView") { classProperty.type = ClassProperty::Type::MAT2; classProperty.componentType = ClassProperty::ComponentType::INT8; classProperty.array = true; - - // clang-format off - classProperty.defaultProperty = { - {1, 1, - 1, 290}, - {2, 2, - 2, 2}, - }; - // clang-format on - - PropertyView> view(classProperty); - REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidDefaultValue); + classProperty.normalized = true; // clang-format off classProperty.noData = { @@ -1657,49 +2722,8 @@ TEST_CASE("MatN Array PropertyView") { -1, -1}, }; // clang-format on - view = PropertyView>(classProperty); + PropertyView, true> view(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNoDataValue); - - // clang-format off - classProperty.min = { - {-129, 0, - 0, 0}, - {-1, -1, - -1, -1}, - }; - // clang-format on - view = PropertyView>(classProperty); - REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMin); - - // clang-format off - classProperty.max = { - {-128, 189, - 20, 2}, - {10, 12, - 8, 4}, - }; - // clang-format on - view = PropertyView>(classProperty); - REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMax); - - //clang-format off - classProperty.scale = { - {1, 2, 3, 4}, - {256, 80, 9, 52}, - }; - // clang-format on - view = PropertyView>(classProperty); - REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidScale); - - // clang-format off - classProperty.offset = { - {129, 0, - 0, 2}, - {4, 0, - 0, 8},}; - // clang-format on - view = PropertyView>(classProperty); - REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidOffset); } SECTION("Reports errors for invalid types") { @@ -1707,34 +2731,36 @@ TEST_CASE("MatN Array PropertyView") { classProperty.type = ClassProperty::Type::MAT2; classProperty.componentType = ClassProperty::ComponentType::INT8; classProperty.array = true; + classProperty.normalized = true; classProperty.defaultProperty = {4, 1, 2, 0}; - PropertyView> view(classProperty); + PropertyView, true> view(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidDefaultValue); // clang-format off classProperty.noData = { {0.45, 0.0, - 1.0, -1.4} + 1.0, -1.4}, + "not a matrix" }; // clang-format on - view = PropertyView>(classProperty); + view = PropertyView, true>(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNoDataValue); classProperty.min = {{0, 1, 2, 3, 4, 5, 6}}; - view = PropertyView>(classProperty); + view = PropertyView, true>(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMin); classProperty.max = {{0, 1, 2, 3}, false}; - view = PropertyView>(classProperty); + view = PropertyView, true>(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMax); classProperty.scale = 1; - view = PropertyView>(classProperty); + view = PropertyView, true>(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidScale); classProperty.offset = "[(1, 2, 3, 4)]"; - view = PropertyView>(classProperty); + view = PropertyView, true>(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidOffset); } } @@ -1772,25 +2798,6 @@ TEST_CASE("String Array PropertyView") { REQUIRE(view.status() == PropertyViewStatus::ErrorArrayTypeMismatch); } - SECTION("Ignores non-applicable properties") { - ClassProperty classProperty; - classProperty.type = ClassProperty::Type::STRING; - classProperty.array = true; - classProperty.normalized = true; - classProperty.offset = {"offset"}; - classProperty.scale = {"scale"}; - classProperty.max = {"max"}; - classProperty.min = {"min"}; - - PropertyView> view(classProperty); - REQUIRE(view.status() == PropertyViewStatus::Valid); - REQUIRE(!view.normalized()); - REQUIRE(!view.offset()); - REQUIRE(!view.scale()); - REQUIRE(!view.max()); - REQUIRE(!view.min()); - } - SECTION("Constructs with count") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::STRING; @@ -1827,19 +2834,19 @@ TEST_CASE("String Array PropertyView") { REQUIRE(defaultValue[1] == "default2"); } - SECTION("Ignores noData and defaultProperty when required is true") { + SECTION("Reports errors for incorrectly defined properties") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::STRING; classProperty.array = true; classProperty.required = true; - classProperty.noData = {"null", "0"}; classProperty.defaultProperty = {"default1", "default2"}; PropertyView> view(classProperty); - REQUIRE(view.status() == PropertyViewStatus::Valid); - REQUIRE(view.required()); - REQUIRE(!view.noData()); - REQUIRE(!view.defaultValue()); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidDefaultValue); + + classProperty.noData = {"null", "0"}; + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNoDataValue); } SECTION("Reports errors for invalid types") { From e19cdae61cd89d9e793638d8edac3ea4646937ed Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Mon, 21 Aug 2023 16:51:24 -0400 Subject: [PATCH 191/421] Try to fix CI, add PropertyTransformations --- .../include/CesiumGltf/PropertyArrayView.h | 72 ++++------ .../include/CesiumGltf/PropertyConversions.h | 55 -------- .../CesiumGltf/PropertyTablePropertyView.h | 75 ++-------- .../CesiumGltf/PropertyTransformations.h | 129 ++++++++++++++++++ CesiumGltf/include/CesiumGltf/PropertyView.h | 54 +++++++- 5 files changed, 214 insertions(+), 171 deletions(-) delete mode 100644 CesiumGltf/include/CesiumGltf/PropertyConversions.h create mode 100644 CesiumGltf/include/CesiumGltf/PropertyTransformations.h diff --git a/CesiumGltf/include/CesiumGltf/PropertyArrayView.h b/CesiumGltf/include/CesiumGltf/PropertyArrayView.h index 6f17cdeba..f599ec58d 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyArrayView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyArrayView.h @@ -66,50 +66,34 @@ template class PropertyArrayView { return true; } - //PropertyArrayView - //operator*(const PropertyArrayView& other) { - // int64_t clampedSize = std::min(this->size(), other.size()); - // std::vector result(static_cast(this->size())); - // if constexpr (IsMetadataMatN::value) { - // // Do component-wise multiplication instead of actual matrix - // // multiplication. - // ElementType matN; - // constexpr glm::length_t N = ElementType::length(); - // for (int64_t i = 0; i < clampedSize; i++) { - // for (glm::length_t j = 0; j < N; j++) { - // matN[j] = (*this)[i][j] * other[i][j]; - // } - // result[i] = matN; - // } - // } else { - // for (int64_t i = 0; i < clampedSize; i++) { - // result[i] = (*this)[i] * other[i]; - // } - // } - - // // Copy anything that didn't have a component to multiply against. - // for (int64_t i = clampedSize(); i < this->size(); i++) { - // result[i] = (*this)[i]; - // } - - // return PropertyArrayView(std::move(result)); - //} - - //PropertyArrayView - //operator+(const PropertyArrayView& other) { - // int64_t clampedSize = std::min(this->size(), other.size()); - // std::vector result(static_cast(this->size())); - // for (int64_t i = 0; i < clampedSize; i++) { - // result[i] = (*this)[i] + other[i]; - // } - - // // Copy anything that didn't have a component to multiply against. - // for (int64_t i = clampedSize(); i < this->size(); i++) { - // result[i] = (*this)[i]; - // } - - // return PropertyArrayView(std::move(result)); - //} + PropertyArrayView + operator*(const PropertyArrayView& other) { + int64_t clampedSize = std::min(this->size(), other.size()); + std::vector result(static_cast(this->size())); + if constexpr (IsMetadataMatN::value) { + // Do component-wise multiplication instead of actual matrix + // multiplication. + ElementType matN; + constexpr glm::length_t N = ElementType::length(); + for (int64_t i = 0; i < clampedSize; i++) { + for (glm::length_t j = 0; j < N; j++) { + matN[j] = (*this)[i][j] * other[i][j]; + } + result[i] = matN; + } + } else { + for (int64_t i = 0; i < clampedSize; i++) { + result[i] = (*this)[i] * other[i]; + } + } + + // Copy anything that didn't have a component to multiply against. + for (int64_t i = clampedSize(); i < this->size(); i++) { + result[i] = (*this)[i]; + } + + return PropertyArrayView(std::move(result)); + } private: using ArrayType = diff --git a/CesiumGltf/include/CesiumGltf/PropertyConversions.h b/CesiumGltf/include/CesiumGltf/PropertyConversions.h deleted file mode 100644 index 9fef8b1cc..000000000 --- a/CesiumGltf/include/CesiumGltf/PropertyConversions.h +++ /dev/null @@ -1,55 +0,0 @@ -#pragma once - -#include "CesiumGltf/PropertyArrayView.h" -#include "CesiumGltf/PropertyTypeTraits.h" - -#include - -#include -#include -#include -#include -#include - -namespace CesiumGltf { -template < - typename TSigned, - std::enable_if_t< - IsMetadataInteger::value && std::is_signed_v>> -double normalize(TSigned value) { - double max = static_cast(std::numeric_limits::max()); - return std::max(static_cast(value) / max, -1.0); -} - -template < - typename TUnsigned, - std::enable_if_t< - IsMetadataInteger::value && std::is_unsigned_v>> -double normalize(TUnsigned value) { - double max = static_cast(std::numeric_limits::max()); - return static_cast(value) / max; -} - -template < - glm::length_t N, - typename TSigned, - glm::qualifier Q, - std::enable_if_t< - IsMetadataInteger::value && std::is_signed_v>> -glm::vec normalize(glm::vec value) { - double max = static_cast(std::numeric_limits::max()); - return glm::max(static_cast>(value) / max, -1.0); -} - -template < - glm::length_t N, - typename TUnsigned, - glm::qualifier Q, - std::enable_if_t< - IsMetadataInteger::value && std::is_unsigned_v>> -glm::mat normalize(glm::mat value) { - double max = static_cast(std::numeric_limits::max()); - return glm::max(static_cast>(value) / max, -1.0); -} - -} // namespace CesiumGltf diff --git a/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h index 059336753..ad183f48b 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h @@ -1,7 +1,6 @@ #pragma once #include "CesiumGltf/PropertyArrayView.h" -//#include "CesiumGltf/PropertyConversions.h" #include "CesiumGltf/PropertyTypeTraits.h" #include "CesiumGltf/PropertyView.h" @@ -182,7 +181,7 @@ class PropertyTablePropertyView _stringOffsetType{PropertyComponentType::None}, _stringOffsetTypeSize{0} { assert( - _status != PropertyTablePropertyViewStatus::Valid && + this->_status != PropertyTablePropertyViewStatus::Valid && "An empty property view should not be constructed with a valid status"); } @@ -200,19 +199,15 @@ class PropertyTablePropertyView int64_t size, gsl::span values) noexcept : PropertyView(classProperty, property), - _values{}, - _size{}, + _values{values}, + _size{ + this->_status == PropertyTablePropertyViewStatus::Valid ? size : 0}, _arrayOffsets{}, _arrayOffsetType{PropertyComponentType::None}, _arrayOffsetTypeSize{0}, _stringOffsets{}, _stringOffsetType{PropertyComponentType::None}, - _stringOffsetTypeSize{0} { - if (_status == PropertyTablePropertyViewStatus::Valid) { - _values = values; - _size = size; - } - } + _stringOffsetTypeSize{0} {} /** * @brief Construct an instance pointing to the data specified by a {@link PropertyTableProperty}. @@ -238,18 +233,14 @@ class PropertyTablePropertyView PropertyComponentType stringOffsetType) noexcept : PropertyView(classProperty, property), _values{values}, + _size{ + this->_status == PropertyTablePropertyViewStatus::Valid ? size : 0}, _arrayOffsets{arrayOffsets}, _arrayOffsetType{arrayOffsetType}, _arrayOffsetTypeSize{getOffsetTypeSize(arrayOffsetType)}, _stringOffsets{stringOffsets}, _stringOffsetType{stringOffsetType}, - _stringOffsetTypeSize{getOffsetTypeSize(stringOffsetType)}, - _size{size} { - if (_status == PropertyTablePropertyViewStatus::Valid) { - _values = values; - _size = size; - } - } + _stringOffsetTypeSize{getOffsetTypeSize(stringOffsetType)} {} /** * @brief Get the raw value of an element of the {@link PropertyTable}, @@ -263,7 +254,7 @@ class PropertyTablePropertyView */ ElementType get(int64_t index) const noexcept { assert( - _status == PropertyTablePropertyViewStatus::Valid && + this->_status == PropertyTablePropertyViewStatus::Valid && "Check the status() first to make sure view is valid"); assert( size() > 0 && @@ -297,51 +288,6 @@ class PropertyTablePropertyView } } - ///** - // * @brief Gets the value of an element in the {@link PropertyTable} as an instance - // * of T. If T is not the same as the ElementType of the property, then - // * this attempts to convert it to the desired type. If such a conversion is - // * not possible, this returns std::nullopt. - // * - // * If this property contains a "no data" sentinel value, then std::nullopt - // is - // * returned for any raw values that equal the sentinel value. If a default - // * value is supplied, then it will be returned instead. - // * - // * If this property is affected by offset, scale, and / or normalization, - // * the value will be transformed before conversion like so: - // * - // * transformedValue = offset + scale * normalize(value) - // * - // * The transformed value will then attempt to be converted to the desired - // type - // * T. - // * - // * @param index The element index - // * @return The value of the element as T, or std::nullopt if conversion - // fails. - // */ - // template - // std::optional getAs(int64_t index) const noexcept { - // assert( - // _status == PropertyTablePropertyViewStatus::Valid && - // "Check the status() first to make sure view is valid"); - // assert( - // size() > 0 && - // "Check the size() of the view to make sure it's not empty"); - // assert(index >= 0 && "index must be non-negative"); - // assert(index < size() && "index must be less than size"); - // ElementType result = getRaw(index); - - // if (noData() && result == *noData()) { - // return defaultValue() ? *defaultValue() : std::nullopt; - // } - - // // apply transform - - // return PropertyConversions::convert(result); - //} - /** * @brief Get the number of elements in this * PropertyTablePropertyView. If the view is valid, this returns @@ -350,7 +296,7 @@ class PropertyTablePropertyView * @return The number of elements in this PropertyTablePropertyView. */ int64_t size() const noexcept { - return status() == PropertyTablePropertyViewStatus::Valid ? _size : 0; + return this->_status == PropertyTablePropertyViewStatus::Valid ? _size : 0; } private: @@ -472,7 +418,6 @@ class PropertyTablePropertyView } } -// PropertyViewStatusType _status; gsl::span _values; int64_t _size; diff --git a/CesiumGltf/include/CesiumGltf/PropertyTransformations.h b/CesiumGltf/include/CesiumGltf/PropertyTransformations.h new file mode 100644 index 000000000..f3523c1c0 --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/PropertyTransformations.h @@ -0,0 +1,129 @@ +#pragma once + +#include "CesiumGltf/PropertyArrayView.h" +#include "CesiumGltf/PropertyTypeTraits.h" + +#include + +#include +#include + +namespace CesiumGltf { +template < + typename TSigned, + std::enable_if_t< + IsMetadataInteger::value && std::is_signed_v>> +double normalize(TSigned value) { + double max = static_cast(std::numeric_limits::max()); + return std::max(static_cast(value) / max, -1.0); +} + +template < + typename TUnsigned, + std::enable_if_t< + IsMetadataInteger::value && std::is_unsigned_v>> +double normalize(TUnsigned value) { + double max = static_cast(std::numeric_limits::max()); + return static_cast(value) / max; +} + +template < + glm::length_t N, + typename TSigned, + glm::qualifier Q, + std::enable_if_t< + IsMetadataInteger::value && std::is_signed_v>> +glm::vec normalize(glm::vec value) { + double max = static_cast(std::numeric_limits::max()); + return glm::max(static_cast>(value) / max, -1.0); +} + +template < + glm::length_t N, + typename TUnsigned, + glm::qualifier Q, + std::enable_if_t< + IsMetadataInteger::value && std::is_unsigned_v>> +glm::vec normalize(glm::vec value) { + double max = static_cast(std::numeric_limits::max()); + return static_cast>(value) / max; +} + +template < + glm::length_t N, + typename TSigned, + glm::qualifier Q, + std::enable_if_t< + IsMetadataInteger::value && std::is_signed_v>> +glm::mat normalize(glm::mat value) { + double max = static_cast(std::numeric_limits::max()); + return glm::max(static_cast>(value) / max, -1.0); +} + +template < + glm::length_t N, + typename TUnsigned, + glm::qualifier Q, + std::enable_if_t< + IsMetadataInteger::value && std::is_unsigned_v>> +glm::mat normalize(glm::mat value) { + double max = static_cast(std::numeric_limits::max()); + return static_cast>(value) / max; +} + +template T applyScale(const T& value, const T& scale) { + if constexpr (IsMetadataMatN::value) { + // Do component-wise multiplication instead of actual matrix + // multiplication. + T matN; + constexpr glm::length_t N = T::length(); + for (glm::length_t i = 0; i < N; i++) { + matN[i] = value[i] * scale[i]; + } + return matN; + } else { + return value * scale; + } +} + +template +T applyOffsetAndScale( + const T& value, + const std::optional& offset, + const std::optional& scale) { + T result = value; + if (scale) { + result = applyScale(result, scale); + } + + if (offset) { + result += offset; + } + + return result; +} + +template +PropertyArrayView applyOffsetAndScale( + const PropertyArrayView& value, + const std::optional>& offset, + const std::optional>& scale) { + int64_t offsetSize = offset ? offset->size() : 0; + int64_t scaleSize = scale ? scale->size() : 0; + std::vector result(static_cast(value.size())); + for (int64_t i = 0; i < value.size(); i++) { + result[i] = value[i]; + + if (i < scaleSize) { + result = applyScale(result[i], scale[i]); + } + + if (i < offsetSize) { + result[i] = result[i] + offset[i]; + } + } + + return PropertyArrayView(std::move(result)); +} + +} // namespace CesiumGltf diff --git a/CesiumGltf/include/CesiumGltf/PropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyView.h index a6013a835..a093d1407 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyView.h @@ -1,7 +1,7 @@ #include "CesiumGltf/ClassProperty.h" -#include "CesiumGltf/PropertyConversions.h" #include "CesiumGltf/PropertyTableProperty.h" #include "CesiumGltf/PropertyTextureProperty.h" +#include "CesiumGltf/PropertyTransformations.h" #include "CesiumGltf/PropertyTypeTraits.h" #include @@ -597,6 +597,13 @@ template class PropertyView { protected: PropertyViewStatusType _status; + ElementType applyValueTransforms(ElementType value) { + if (_offset || _scale) { + return applyOffsetAndScale(value, _offset, _scale); + } + return value; + } + private: bool _required; @@ -937,6 +944,9 @@ class PropertyView< NormalizedType applyValueTransforms(ElementType value) { NormalizedType result = normalize(value); + if (_offset || _scale) { + return applyOffsetAndScale(result, _offset, _scale); + } return result; } @@ -1513,7 +1523,7 @@ class PropertyView> { case PropertyComponentType::Float64: _offset = getArrayValue(*property.offset); if (_offset && - (count == 0 || _offset->size() == static_cast(_count))) { + (_count == 0 || _offset->size() == static_cast(_count))) { break; } // If it does not break here, something went wrong. @@ -1531,7 +1541,7 @@ class PropertyView> { case PropertyComponentType::Float64: _scale = getArrayValue(*property.scale); if (_scale && - (count == 0 || _scale->size() == static_cast(_count))) { + (_count == 0 || _scale->size() == static_cast(_count))) { break; } // If it does not break here, something went wrong. @@ -1658,6 +1668,14 @@ class PropertyView> { protected: PropertyViewStatusType _status; + PropertyArrayView + applyValueTransforms(const PropertyArrayView& value) { + if (_offset || _scale) { + return applyOffsetAndScale(value, _offset, _scale); + } + return value; + } + private: int64_t _count; @@ -1929,7 +1947,7 @@ class PropertyView< // If the property has its own values, override the class-provided values. if (property.offset) { - _offset = getArrayValue(*property.offset); + _offset = getArrayValue(*property.offset); if (!_offset || (_count > 0 && _offset->size() != static_cast(_count))) { // The value was specified but something went wrong. @@ -1939,7 +1957,7 @@ class PropertyView< } if (property.scale) { - _scale = getArrayValue(*property.scale); + _scale = getArrayValue(*property.scale); if (!_scale || (_count > 0 || _scale->size() != static_cast(_count))) { // The value was specified but something went wrong. @@ -1949,7 +1967,7 @@ class PropertyView< } if (property.max) { - _max = getArrayValue(*property.max); + _max = getArrayValue(*property.max); if (!_max || (_count > 0 && _max->size() != static_cast(_count))) { // The value was specified but something went wrong. @@ -1959,7 +1977,7 @@ class PropertyView< } if (property.min) { - _min = getArrayValue(*property.min); + _min = getArrayValue(*property.min); if (!_min || (_count > 0 && _min->size() != static_cast(_count))) { // The value was specified but something went wrong. @@ -2067,6 +2085,28 @@ class PropertyView< protected: PropertyViewStatusType _status; + PropertyArrayView + applyValueTransforms(const PropertyArrayView& value) { + auto offset = this->offset(); + auto scale = this->scale(); + int64_t offsetSize = offset ? offset.size() : 0; + int64_t scaleSize = scale ? scale.size() : 0; + std::vector result(static_cast(value.size())); + for (int64_t i = 0; i < value.size(); i++) { + result[i] = normalize(value[i]); + + if (i < scaleSize) { + result = applyScale(result[i], scale[i]); + } + + if (i < offsetSize) { + result[i] = result[i] + offset[i]; + } + } + + return PropertyArrayView(std::move(result)); + } + private: int64_t _count; From 734c8cfbb647865f5670eafb138a59efc7428bc6 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Mon, 21 Aug 2023 17:43:23 -0400 Subject: [PATCH 192/421] Add get vs. getRaw to property view --- .../include/CesiumGltf/PropertyArrayView.h | 29 -- .../CesiumGltf/PropertyTablePropertyView.h | 269 +++++++++++++++++- .../CesiumGltf/PropertyTexturePropertyView.h | 2 +- .../CesiumGltf/PropertyTransformations.h | 3 +- .../include/CesiumGltf/PropertyTypeTraits.h | 36 +++ CesiumGltf/include/CesiumGltf/PropertyView.h | 1 - .../test/TestPropertyTablePropertyView.cpp | 24 +- CesiumGltf/test/TestPropertyTableView.cpp | 58 ++-- CesiumGltf/test/TestPropertyTypeTraits.cpp | 13 +- 9 files changed, 357 insertions(+), 78 deletions(-) diff --git a/CesiumGltf/include/CesiumGltf/PropertyArrayView.h b/CesiumGltf/include/CesiumGltf/PropertyArrayView.h index f599ec58d..9be1867cf 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyArrayView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyArrayView.h @@ -66,35 +66,6 @@ template class PropertyArrayView { return true; } - PropertyArrayView - operator*(const PropertyArrayView& other) { - int64_t clampedSize = std::min(this->size(), other.size()); - std::vector result(static_cast(this->size())); - if constexpr (IsMetadataMatN::value) { - // Do component-wise multiplication instead of actual matrix - // multiplication. - ElementType matN; - constexpr glm::length_t N = ElementType::length(); - for (int64_t i = 0; i < clampedSize; i++) { - for (glm::length_t j = 0; j < N; j++) { - matN[j] = (*this)[i][j] * other[i][j]; - } - result[i] = matN; - } - } else { - for (int64_t i = 0; i < clampedSize; i++) { - result[i] = (*this)[i] * other[i]; - } - } - - // Copy anything that didn't have a component to multiply against. - for (int64_t i = clampedSize(); i < this->size(); i++) { - result[i] = (*this)[i]; - } - - return PropertyArrayView(std::move(result)); - } - private: using ArrayType = std::variant, std::vector>; diff --git a/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h index ad183f48b..440e3dc43 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h @@ -219,8 +219,8 @@ class PropertyTablePropertyView * @param values The raw buffer specified by {@link PropertyTableProperty::values} * @param arrayOffsets The raw buffer specified by {@link PropertyTableProperty::arrayOffsets} * @param stringOffsets The raw buffer specified by {@link PropertyTableProperty::stringOffsets} - * @param offsetType The offset type of arrayOffsets specified by {@link PropertyTableProperty::arrayOffsetType} - * @param offsetType The offset type of stringOffsets specified by {@link PropertyTableProperty::stringOffsetType} + * @param arrayOffsetType The offset type of arrayOffsets specified by {@link PropertyTableProperty::arrayOffsetType} + * @param stringOffsetType The offset type of stringOffsets specified by {@link PropertyTableProperty::stringOffsetType} */ PropertyTablePropertyView( const PropertyTableProperty& property, @@ -242,9 +242,39 @@ class PropertyTablePropertyView _stringOffsetType{stringOffsetType}, _stringOffsetTypeSize{getOffsetTypeSize(stringOffsetType)} {} + /** + * @brief Get the value of an element in the {@link PropertyTable}, + * with all value transforms applied. That is, if the property specifies an + * offset and scale, they will be applied to the value before the value is + * returned. + * + * If this property has a defined "no data" value, and the retrieved element + * is equal to that value, then this will return the property's specified + * default value. If the property did not provide a default value, this + * returns std::nullopt. + * + * @param index The element index + * @return The value of the element + */ + std::optional get(int64_t index) const noexcept { + ElementType value = getRaw(index); + + if (this->noData() && value == *(this->noData())) { + return this->defaultValue(); + } + + if constexpr ( + IsMetadataNumeric::value || + IsMetadataNumericArray::value) { + value = applyOffsetAndScale(value, this->offset(), this->scale()); + } + + return value; + } + /** * @brief Get the raw value of an element of the {@link PropertyTable}, - * without offset, scale, or normalization applied. + * without offset or scale applied. * * If this property has a "no data" value defined, the raw value will still be * returned, even if it equals the "no data" value. @@ -252,7 +282,7 @@ class PropertyTablePropertyView * @param index The element index * @return The value of the element */ - ElementType get(int64_t index) const noexcept { + ElementType getRaw(int64_t index) const noexcept { assert( this->_status == PropertyTablePropertyViewStatus::Valid && "Check the status() first to make sure view is valid"); @@ -429,4 +459,235 @@ class PropertyTablePropertyView PropertyComponentType _stringOffsetType; int64_t _stringOffsetTypeSize; }; + +/** + * @brief A view on the normalized data of the {@link PropertyTableProperty} + * that is created by a {@link PropertyTableView}. + * + * It provides utility to retrieve the actual data stored in the + * {@link PropertyTableProperty::values} like an array of elements. Data of each + * instance can be accessed through the {@link PropertyTablePropertyView::get} method. + * + * @param ElementType must be one of the following: an integer scalar (uint8_t, + * int8_t, uint16_t, int16_t, uint32_t, int32_t, uint64_t, int64_t), a glm vecN + * composed of one of the integer scalar types, a glm matN composed of one of + * the integer scalar types, or PropertyArrayView with T as one of the + * aforementioned types. + */ +// template +// class PropertyTablePropertyView +// : public PropertyView { +// private: +// using NormalizedType = typename TypeToNormalizedType::type; +// +// public: +// /** +// * @brief Constructs an invalid instance for a non-existent property. +// */ +// PropertyTablePropertyView() +// : PropertyView(), +// _values{}, +// _size{0}, +// _arrayOffsets{}, +// _arrayOffsetType{PropertyComponentType::None}, +// _arrayOffsetTypeSize{0} {} +// +// /** +// * @brief Constructs an invalid instance for an erroneous property. +// * +// * @param status The value of {@link PropertyTablePropertyViewStatus} indicating the error with the property. +// */ +// PropertyTablePropertyView(PropertyViewStatusType status) +// : PropertyView(status), +// _values{}, +// _size{0}, +// _arrayOffsets{}, +// _arrayOffsetType{PropertyComponentType::None}, +// _arrayOffsetTypeSize{0} { +// assert( +// this->_status != PropertyTablePropertyViewStatus::Valid && +// "An empty property view should not be constructed with a valid +// status"); +// } +// +// /** +// * @brief Construct an instance pointing to non-array data specified by a {@link PropertyTableProperty}. +// * +// * @param property The {@link PropertyTableProperty} +// * @param classProperty The {@link ClassProperty} this property conforms to. +// * @param size The number of elements in the property table specified by {@link PropertyTable::count} +// * @param value The raw buffer specified by {@link PropertyTableProperty::values} +// */ +// PropertyTablePropertyView( +// const PropertyTableProperty& property, +// const ClassProperty& classProperty, +// int64_t size, +// gsl::span values) noexcept +// : PropertyView(classProperty, property), +// _values{values}, +// _size{ +// this->_status == PropertyTablePropertyViewStatus::Valid ? size : +// 0}, +// _arrayOffsets{}, +// _arrayOffsetType{PropertyComponentType::None}, +// _arrayOffsetTypeSize{0} {} +// +// /** +// * @brief Construct an instance pointing to the data specified by a {@link PropertyTableProperty}. +// * +// * +// * @param property The {@link PropertyTableProperty} +// * @param classProperty The {@link ClassProperty} this property conforms to. +// * @param size The number of elements in the property table specified by {@link PropertyTable::count} +// * @param values The raw buffer specified by {@link PropertyTableProperty::values} +// * @param arrayOffsets The raw buffer specified by {@link PropertyTableProperty::arrayOffsets} +// * @param offsetType The offset type of arrayOffsets specified by {@link PropertyTableProperty::arrayOffsetType} +// */ +// PropertyTablePropertyView( +// const PropertyTableProperty& property, +// const ClassProperty& classProperty, +// int64_t size, +// gsl::span values, +// gsl::span arrayOffsets, +// PropertyComponentType arrayOffsetType) noexcept +// : PropertyView(classProperty, property), +// _values{values}, +// _size{ +// this->_status == PropertyTablePropertyViewStatus::Valid ? size : +// 0}, +// _arrayOffsets{arrayOffsets}, +// _arrayOffsetType{arrayOffsetType}, +// _arrayOffsetTypeSize{getOffsetTypeSize(arrayOffsetType)} {} +// +// /** +// * @brief Get the value of an element of the {@link PropertyTable}, +// * with all value transforms applied. This means the returned value will be +// * normalized. If the property specifies an offset and scale, they will be +// * applied to the value as well. +// * +// * If this property has a "no data" value defined, and the returned this +// returns the "default value". +// * +// * @param index The element index +// * @return The value of the element +// */ +// std::optional get(int64_t index) const noexcept { +// assert( +// this->_status == PropertyTablePropertyViewStatus::Valid && +// "Check the status() first to make sure view is valid"); +// assert( +// size() > 0 && +// "Check the size() of the view to make sure it's not empty"); +// assert(index >= 0 && "index must be non-negative"); +// assert(index < size() && "index must be less than size"); +// +// if constexpr (IsMetadataNumeric::value) { +// ElementType value = getValue(index); +// if (value == this->noData()) { +// +// } +// } +// +// if constexpr (IsMetadataNumericArray::value) { +// return getArrayValues::type>( +// index); +// } +// } +// +// /** +// * @brief Get the raw value of an element of the {@link PropertyTable}, +// * without offset, scale, or normalization applied. +// * +// * If this property has a "no data" value defined, the raw value will still +// be +// * returned, even if it equals the "no data" value. +// * +// * @param index The element index +// * @return The value of the element +// */ +// ElementType getRaw(int64_t index) const noexcept { +// assert( +// this->_status == PropertyTablePropertyViewStatus::Valid && +// "Check the status() first to make sure view is valid"); +// assert( +// size() > 0 && +// "Check the size() of the view to make sure it's not empty"); +// assert(index >= 0 && "index must be non-negative"); +// assert(index < size() && "index must be less than size"); +// +// if constexpr (IsMetadataNumeric::value) { +// return getValue(index); +// } +// +// if constexpr (IsMetadataNumericArray::value) { +// return getArrayValues::type>( +// index); +// } +// } +// +// /** +// * @brief Get the number of elements in this +// * PropertyTablePropertyView. If the view is valid, this returns +// * {@link PropertyTable::count}. Otherwise, this returns 0. +// * +// * @return The number of elements in this PropertyTablePropertyView. +// */ +// int64_t size() const noexcept { +// return this->_status == PropertyTablePropertyViewStatus::Valid ? _size : +// 0; +// } +// +// private: +// ElementType getValue(int64_t index) const noexcept { +// ElementType value = +// reinterpret_cast(_values.data())[index]; +// } +// +// template +// PropertyArrayView getArrayValues(int64_t index) const noexcept { +// size_t count = static_cast(this->arrayCount()); +// // Handle fixed-length arrays +// if (count > 0) { +// size_t arraySize = count * sizeof(T); +// const gsl::span values( +// _values.data() + index * arraySize, +// arraySize); +// return PropertyArrayView{values}; +// } +// +// // Handle variable-length arrays +// const size_t currentOffset = +// getOffsetFromOffsetsBuffer(index, _arrayOffsets, _arrayOffsetType); +// const size_t nextOffset = +// getOffsetFromOffsetsBuffer(index + 1, _arrayOffsets, +// _arrayOffsetType); +// const gsl::span values( +// _values.data() + currentOffset, +// nextOffset - currentOffset); +// return PropertyArrayView{values}; +// } +// +// static int64_t getOffsetTypeSize(PropertyComponentType offsetType) noexcept +// { +// switch (offsetType) { +// case PropertyComponentType::Uint8: +// return sizeof(uint8_t); +// case PropertyComponentType::Uint16: +// return sizeof(uint16_t); +// case PropertyComponentType::Uint32: +// return sizeof(uint32_t); +// case PropertyComponentType::Uint64: +// return sizeof(uint64_t); +// default: +// return 0; +// } +// } +// +// gsl::span _values; +// int64_t _size; +// +// gsl::span _arrayOffsets; +// PropertyComponentType _arrayOffsetType; +// int64_t _arrayOffsetTypeSize; +//}; } // namespace CesiumGltf diff --git a/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h index 95417462a..cf5c16aaa 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h @@ -3,8 +3,8 @@ #include "CesiumGltf/ImageCesium.h" #include "CesiumGltf/PropertyTextureProperty.h" #include "CesiumGltf/PropertyTypeTraits.h" -#include "CesiumGltf/Sampler.h" #include "CesiumGltf/PropertyView.h" +#include "CesiumGltf/Sampler.h" #include #include diff --git a/CesiumGltf/include/CesiumGltf/PropertyTransformations.h b/CesiumGltf/include/CesiumGltf/PropertyTransformations.h index f3523c1c0..6282400d1 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTransformations.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTransformations.h @@ -7,6 +7,7 @@ #include #include +#include namespace CesiumGltf { template < @@ -56,7 +57,7 @@ template < std::enable_if_t< IsMetadataInteger::value && std::is_signed_v>> glm::mat normalize(glm::mat value) { - double max = static_cast(std::numeric_limits::max()); + double max = static_cast(std::numeric_limits::max()); return glm::max(static_cast>(value) / max, -1.0); } diff --git a/CesiumGltf/include/CesiumGltf/PropertyTypeTraits.h b/CesiumGltf/include/CesiumGltf/PropertyTypeTraits.h index b7400cbc6..eff9ab37a 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTypeTraits.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTypeTraits.h @@ -309,4 +309,40 @@ template struct TypeToNormalizedType> { using type = glm::mat; }; + +template <> struct TypeToNormalizedType> { + using type = PropertyArrayView; +}; +template <> struct TypeToNormalizedType> { + using type = PropertyArrayView; +}; +template <> struct TypeToNormalizedType> { + using type = PropertyArrayView; +}; +template <> struct TypeToNormalizedType> { + using type = PropertyArrayView; +}; +template <> struct TypeToNormalizedType> { + using type = PropertyArrayView; +}; +template <> struct TypeToNormalizedType> { + using type = PropertyArrayView; +}; +template <> struct TypeToNormalizedType> { + using type = PropertyArrayView; +}; +template <> struct TypeToNormalizedType> { + using type = PropertyArrayView; +}; + +template +struct TypeToNormalizedType>> { + using type = PropertyArrayView>; +}; + +template +struct TypeToNormalizedType>> { + using type = PropertyArrayView>; +}; + } // namespace CesiumGltf diff --git a/CesiumGltf/include/CesiumGltf/PropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyView.h index a093d1407..fe9c3ca3d 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyView.h @@ -1,7 +1,6 @@ #include "CesiumGltf/ClassProperty.h" #include "CesiumGltf/PropertyTableProperty.h" #include "CesiumGltf/PropertyTextureProperty.h" -#include "CesiumGltf/PropertyTransformations.h" #include "CesiumGltf/PropertyTypeTraits.h" #include diff --git a/CesiumGltf/test/TestPropertyTablePropertyView.cpp b/CesiumGltf/test/TestPropertyTablePropertyView.cpp index 8524cde90..22c24b4d1 100644 --- a/CesiumGltf/test/TestPropertyTablePropertyView.cpp +++ b/CesiumGltf/test/TestPropertyTablePropertyView.cpp @@ -34,7 +34,7 @@ template static void checkNumeric(const std::vector& expected) { gsl::span(data.data(), data.size())); for (int64_t i = 0; i < property.size(); ++i) { - REQUIRE(property.get(i) == expected[static_cast(i)]); + REQUIRE(property.getRaw(i) == expected[static_cast(i)]); } } @@ -84,7 +84,7 @@ static void checkVariableLengthArray( size_t expectedIdx = 0; for (int64_t i = 0; i < property.size(); ++i) { - PropertyArrayView values = property.get(i); + PropertyArrayView values = property.getRaw(i); for (int64_t j = 0; j < values.size(); ++j) { REQUIRE(values[j] == data[expectedIdx]); ++expectedIdx; @@ -131,7 +131,7 @@ static void checkFixedLengthArray( size_t expectedIdx = 0; for (int64_t i = 0; i < property.size(); ++i) { - PropertyArrayView values = property.get(i); + PropertyArrayView values = property.getRaw(i); for (int64_t j = 0; j < values.size(); ++j) { REQUIRE(values[j] == data[expectedIdx]); ++expectedIdx; @@ -276,7 +276,7 @@ TEST_CASE("Check boolean PropertyTablePropertyView") { gsl::span(data.data(), data.size())); for (int64_t i = 0; i < property.size(); ++i) { - REQUIRE(property.get(i) == bits[static_cast(i)]); + REQUIRE(property.getRaw(i) == bits[static_cast(i)]); } } @@ -332,7 +332,7 @@ TEST_CASE("Check string PropertyTablePropertyView") { PropertyComponentType::Uint32); for (int64_t i = 0; i < property.size(); ++i) { - REQUIRE(property.get(i) == strings[static_cast(i)]); + REQUIRE(property.getRaw(i) == strings[static_cast(i)]); } } @@ -900,7 +900,7 @@ TEST_CASE("Check fixed-length array of string") { size_t expectedIdx = 0; for (int64_t i = 0; i < property.size(); ++i) { - PropertyArrayView values = property.get(i); + PropertyArrayView values = property.getRaw(i); for (int64_t j = 0; j < values.size(); ++j) { std::string_view v = values[j]; REQUIRE(v == strings[expectedIdx]); @@ -980,7 +980,7 @@ TEST_CASE("Check variable-length string array PropertyTablePropertyView") { size_t expectedIdx = 0; for (int64_t i = 0; i < property.size(); ++i) { - PropertyArrayView values = property.get(i); + PropertyArrayView values = property.getRaw(i); for (int64_t j = 0; j < values.size(); ++j) { std::string_view v = values[j]; REQUIRE(v == strings[expectedIdx]); @@ -1016,7 +1016,7 @@ TEST_CASE("Check fixed-length boolean array PropertyTablePropertyView") { REQUIRE(property.size() == 2); REQUIRE(property.arrayCount() == classProperty.count); - PropertyArrayView val0 = property.get(0); + PropertyArrayView val0 = property.getRaw(0); REQUIRE(val0.size() == 12); REQUIRE(static_cast(val0[0]) == 1); REQUIRE(static_cast(val0[1]) == 1); @@ -1031,7 +1031,7 @@ TEST_CASE("Check fixed-length boolean array PropertyTablePropertyView") { REQUIRE(static_cast(val0[10]) == 0); REQUIRE(static_cast(val0[11]) == 1); - PropertyArrayView val1 = property.get(1); + PropertyArrayView val1 = property.getRaw(1); REQUIRE(static_cast(val1[0]) == 1); REQUIRE(static_cast(val1[1]) == 1); REQUIRE(static_cast(val1[2]) == 1); @@ -1075,13 +1075,13 @@ TEST_CASE("Check variable-length boolean array PropertyTablePropertyView") { REQUIRE(property.size() == 3); REQUIRE(property.arrayCount() == 0); - PropertyArrayView val0 = property.get(0); + PropertyArrayView val0 = property.getRaw(0); REQUIRE(val0.size() == 3); REQUIRE(static_cast(val0[0]) == 1); REQUIRE(static_cast(val0[1]) == 1); REQUIRE(static_cast(val0[2]) == 1); - PropertyArrayView val1 = property.get(1); + PropertyArrayView val1 = property.getRaw(1); REQUIRE(val1.size() == 9); REQUIRE(static_cast(val1[0]) == 1); REQUIRE(static_cast(val1[1]) == 0); @@ -1093,7 +1093,7 @@ TEST_CASE("Check variable-length boolean array PropertyTablePropertyView") { REQUIRE(static_cast(val1[7]) == 0); REQUIRE(static_cast(val1[8]) == 1); - PropertyArrayView val2 = property.get(2); + PropertyArrayView val2 = property.getRaw(2); REQUIRE(val2.size() == 16); REQUIRE(static_cast(val2[0]) == 1); REQUIRE(static_cast(val2[1]) == 1); diff --git a/CesiumGltf/test/TestPropertyTableView.cpp b/CesiumGltf/test/TestPropertyTableView.cpp index 4a625a350..b79cf2e9e 100644 --- a/CesiumGltf/test/TestPropertyTableView.cpp +++ b/CesiumGltf/test/TestPropertyTableView.cpp @@ -141,7 +141,7 @@ TEST_CASE("Test scalar PropertyTableProperty") { REQUIRE(uint32Property.size() > 0); for (int64_t i = 0; i < uint32Property.size(); ++i) { - REQUIRE(uint32Property.get(i) == values[static_cast(i)]); + REQUIRE(uint32Property.getRaw(i) == values[static_cast(i)]); } } @@ -295,7 +295,7 @@ TEST_CASE("Test vecN PropertyTableProperty") { REQUIRE(ivec3Property.size() > 0); for (int64_t i = 0; i < ivec3Property.size(); ++i) { - REQUIRE(ivec3Property.get(i) == values[static_cast(i)]); + REQUIRE(ivec3Property.getRaw(i) == values[static_cast(i)]); } } @@ -467,7 +467,7 @@ TEST_CASE("Test matN PropertyTableProperty") { REQUIRE(u32mat2x2Property.size() > 0); for (int64_t i = 0; i < u32mat2x2Property.size(); ++i) { - REQUIRE(u32mat2x2Property.get(i) == values[static_cast(i)]); + REQUIRE(u32mat2x2Property.getRaw(i) == values[static_cast(i)]); } } @@ -641,7 +641,7 @@ TEST_CASE("Test boolean PropertyTableProperty") { REQUIRE(boolProperty.size() == instanceCount); for (int64_t i = 0; i < boolProperty.size(); ++i) { bool expectedValue = expected[static_cast(i)]; - REQUIRE(boolProperty.get(i) == expectedValue); + REQUIRE(boolProperty.getRaw(i) == expectedValue); } } @@ -724,7 +724,7 @@ TEST_CASE("Test string PropertyTableProperty") { view.getPropertyView("TestClassProperty"); REQUIRE(stringProperty.status() == PropertyTablePropertyViewStatus::Valid); for (size_t i = 0; i < expected.size(); ++i) { - REQUIRE(stringProperty.get(static_cast(i)) == expected[i]); + REQUIRE(stringProperty.getRaw(static_cast(i)) == expected[i]); } } @@ -846,7 +846,7 @@ TEST_CASE("Test fixed-length scalar array") { REQUIRE(arrayProperty.status() == PropertyTablePropertyViewStatus::Valid); for (int64_t i = 0; i < arrayProperty.size(); ++i) { - PropertyArrayView member = arrayProperty.get(i); + PropertyArrayView member = arrayProperty.getRaw(i); for (int64_t j = 0; j < member.size(); ++j) { REQUIRE(member[j] == values[static_cast(i * 3 + j)]); } @@ -990,7 +990,7 @@ TEST_CASE("Test variable-length scalar array") { REQUIRE(property.status() == PropertyTablePropertyViewStatus::Valid); for (size_t i = 0; i < expected.size(); ++i) { PropertyArrayView valueMember = - property.get(static_cast(i)); + property.getRaw(static_cast(i)); REQUIRE(valueMember.size() == static_cast(expected[i].size())); for (size_t j = 0; j < expected[i].size(); ++j) { REQUIRE(expected[i][j] == valueMember[static_cast(j)]); @@ -1122,7 +1122,7 @@ TEST_CASE("Test fixed-length vecN array") { REQUIRE(arrayProperty.status() == PropertyTablePropertyViewStatus::Valid); for (int64_t i = 0; i < arrayProperty.size(); ++i) { - PropertyArrayView member = arrayProperty.get(i); + PropertyArrayView member = arrayProperty.getRaw(i); for (int64_t j = 0; j < member.size(); ++j) { REQUIRE(member[j] == values[static_cast(i * 2 + j)]); } @@ -1272,7 +1272,7 @@ TEST_CASE("Test variable-length vecN array") { REQUIRE(property.status() == PropertyTablePropertyViewStatus::Valid); for (size_t i = 0; i < expected.size(); ++i) { PropertyArrayView valueMember = - property.get(static_cast(i)); + property.getRaw(static_cast(i)); REQUIRE(valueMember.size() == static_cast(expected[i].size())); for (size_t j = 0; j < expected[i].size(); ++j) { REQUIRE(expected[i][j] == valueMember[static_cast(j)]); @@ -1423,7 +1423,7 @@ TEST_CASE("Test fixed-length matN array") { REQUIRE(arrayProperty.status() == PropertyTablePropertyViewStatus::Valid); for (int64_t i = 0; i < arrayProperty.size(); ++i) { - PropertyArrayView member = arrayProperty.get(i); + PropertyArrayView member = arrayProperty.getRaw(i); for (int64_t j = 0; j < member.size(); ++j) { REQUIRE(member[j] == values[static_cast(i * 2 + j)]); } @@ -1594,7 +1594,7 @@ TEST_CASE("Test variable-length matN array") { REQUIRE(property.status() == PropertyTablePropertyViewStatus::Valid); for (size_t i = 0; i < expected.size(); ++i) { PropertyArrayView valueMember = - property.get(static_cast(i)); + property.getRaw(static_cast(i)); REQUIRE(valueMember.size() == static_cast(expected[i].size())); for (size_t j = 0; j < expected[i].size(); ++j) { REQUIRE(expected[i][j] == valueMember[static_cast(j)]); @@ -1746,7 +1746,7 @@ TEST_CASE("Test fixed-length boolean array") { REQUIRE(boolArrayProperty.size() == propertyTable.count); REQUIRE(boolArrayProperty.size() > 0); for (int64_t i = 0; i < boolArrayProperty.size(); ++i) { - PropertyArrayView valueMember = boolArrayProperty.get(i); + PropertyArrayView valueMember = boolArrayProperty.getRaw(i); for (int64_t j = 0; j < valueMember.size(); ++j) { REQUIRE(valueMember[j] == expected[static_cast(i * 3 + j)]); } @@ -1872,7 +1872,7 @@ TEST_CASE("Test variable-length boolean array") { boolArrayProperty.status() == PropertyTablePropertyViewStatus::Valid); for (size_t i = 0; i < expected.size(); ++i) { PropertyArrayView arrayMember = - boolArrayProperty.get(static_cast(i)); + boolArrayProperty.getRaw(static_cast(i)); REQUIRE(arrayMember.size() == static_cast(expected[i].size())); for (size_t j = 0; j < expected[i].size(); ++j) { REQUIRE(expected[i][j] == arrayMember[static_cast(j)]); @@ -2028,17 +2028,17 @@ TEST_CASE("Test fixed-length arrays of strings") { REQUIRE(stringProperty.status() == PropertyTablePropertyViewStatus::Valid); REQUIRE(stringProperty.size() == 3); - PropertyArrayView v0 = stringProperty.get(0); + PropertyArrayView v0 = stringProperty.getRaw(0); REQUIRE(v0.size() == 2); REQUIRE(v0[0] == "What's up"); REQUIRE(v0[1] == "Breaking news!!! Aliens no longer attacks the US first"); - PropertyArrayView v1 = stringProperty.get(1); + PropertyArrayView v1 = stringProperty.getRaw(1); REQUIRE(v1.size() == 2); REQUIRE(v1[0] == "But they still abduct my cows! Those milk thiefs! 👽 🐮"); REQUIRE(v1[1] == "I'm not crazy. My mother had me tested 🤪"); - PropertyArrayView v2 = stringProperty.get(2); + PropertyArrayView v2 = stringProperty.getRaw(2); REQUIRE(v2.size() == 2); REQUIRE(v2[0] == "I love you, meat bags! ❤️"); REQUIRE(v2[1] == "Book in the freezer"); @@ -2198,7 +2198,7 @@ TEST_CASE("Test variable-length arrays of strings") { REQUIRE(stringProperty.status() == PropertyTablePropertyViewStatus::Valid); for (size_t i = 0; i < expected.size(); ++i) { PropertyArrayView stringArray = - stringProperty.get(static_cast(i)); + stringProperty.getRaw(static_cast(i)); for (size_t j = 0; j < expected[i].size(); ++j) { REQUIRE(stringArray[static_cast(j)] == expected[i][j]); } @@ -2472,7 +2472,7 @@ TEST_CASE("Test callback for scalar PropertyTableProperty") { for (int64_t i = 0; i < propertyValue.size(); ++i) { REQUIRE( - static_cast(propertyValue.get(i)) == + static_cast(propertyValue.getRaw(i)) == values[static_cast(i)]); } } else { @@ -2541,7 +2541,7 @@ TEST_CASE("Test callback for vecN PropertyTableProperty") { decltype(propertyValue)>) { for (int64_t i = 0; i < propertyValue.size(); ++i) { REQUIRE( - static_cast(propertyValue.get(i)) == + static_cast(propertyValue.getRaw(i)) == values[static_cast(i)]); } } else { @@ -2619,7 +2619,7 @@ TEST_CASE("Test callback for matN PropertyTableProperty") { decltype(propertyValue)>) { for (int64_t i = 0; i < propertyValue.size(); ++i) { REQUIRE( - static_cast(propertyValue.get(i)) == + static_cast(propertyValue.getRaw(i)) == values[static_cast(i)]); } } else { @@ -2702,7 +2702,7 @@ TEST_CASE("Test callback for boolean PropertyTableProperty") { decltype(propertyValue)>) { for (int64_t i = 0; i < propertyValue.size(); ++i) { REQUIRE( - static_cast(propertyValue.get(i)) == + static_cast(propertyValue.getRaw(i)) == expected[static_cast(i)]); } } else { @@ -2792,7 +2792,7 @@ TEST_CASE("Test callback for string PropertyTableProperty") { decltype(propertyValue)>) { for (int64_t i = 0; i < propertyValue.size(); ++i) { REQUIRE( - static_cast(propertyValue.get(i)) == + static_cast(propertyValue.getRaw(i)) == expected[static_cast(i)]); } } else { @@ -2861,7 +2861,7 @@ TEST_CASE("Test callback for scalar array PropertyTableProperty") { PropertyArrayView>, decltype(propertyValue)>) { for (int64_t i = 0; i < propertyValue.size(); ++i) { - PropertyArrayView member = propertyValue.get(i); + PropertyArrayView member = propertyValue.getRaw(i); for (int64_t j = 0; j < member.size(); ++j) { REQUIRE(member[j] == values[static_cast(i * 3 + j)]); } @@ -2938,7 +2938,7 @@ TEST_CASE("Test callback for vecN array PropertyTableProperty") { PropertyArrayView>, decltype(propertyValue)>) { for (int64_t i = 0; i < propertyValue.size(); ++i) { - PropertyArrayView member = propertyValue.get(i); + PropertyArrayView member = propertyValue.getRaw(i); for (int64_t j = 0; j < member.size(); ++j) { REQUIRE(member[j] == values[static_cast(i * 2 + j)]); } @@ -3029,7 +3029,7 @@ TEST_CASE("Test callback for matN array PropertyTableProperty") { PropertyArrayView>, decltype(propertyValue)>) { for (int64_t i = 0; i < propertyValue.size(); ++i) { - PropertyArrayView member = propertyValue.get(i); + PropertyArrayView member = propertyValue.getRaw(i); for (int64_t j = 0; j < member.size(); ++j) { REQUIRE(member[j] == values[static_cast(i * 2 + j)]); } @@ -3120,7 +3120,7 @@ TEST_CASE("Test callback for boolean array PropertyTableProperty") { PropertyTablePropertyView>, decltype(propertyValue)>) { for (int64_t i = 0; i < propertyValue.size(); ++i) { - PropertyArrayView member = propertyValue.get(i); + PropertyArrayView member = propertyValue.getRaw(i); for (int64_t j = 0; j < member.size(); ++j) { REQUIRE(member[j] == expected[static_cast(i * 3 + j)]); } @@ -3219,20 +3219,20 @@ TEST_CASE("Test callback for string array PropertyTableProperty") { PropertyTablePropertyView< PropertyArrayView>, decltype(propertyValue)>) { - PropertyArrayView v0 = propertyValue.get(0); + PropertyArrayView v0 = propertyValue.getRaw(0); REQUIRE(v0.size() == 2); REQUIRE(v0[0] == "What's up"); REQUIRE( v0[1] == "Breaking news!!! Aliens no longer attacks the US first"); - PropertyArrayView v1 = propertyValue.get(1); + PropertyArrayView v1 = propertyValue.getRaw(1); REQUIRE(v1.size() == 2); REQUIRE( v1[0] == "But they still abduct my cows! Those milk thiefs! 👽 🐮"); REQUIRE(v1[1] == "I'm not crazy. My mother had me tested 🤪"); - PropertyArrayView v2 = propertyValue.get(2); + PropertyArrayView v2 = propertyValue.getRaw(2); REQUIRE(v2.size() == 2); REQUIRE(v2[0] == "I love you, meat bags! ❤️"); REQUIRE(v2[1] == "Book in the freezer"); diff --git a/CesiumGltf/test/TestPropertyTypeTraits.cpp b/CesiumGltf/test/TestPropertyTypeTraits.cpp index a6f7bafcf..ff527cb7e 100644 --- a/CesiumGltf/test/TestPropertyTypeTraits.cpp +++ b/CesiumGltf/test/TestPropertyTypeTraits.cpp @@ -533,7 +533,6 @@ TEST_CASE("Test CanBeNormalized") { REQUIRE(CanBeNormalized::value); REQUIRE(CanBeNormalized::value); - REQUIRE(!CanBeNormalized::value); REQUIRE(!CanBeNormalized::value); REQUIRE(!CanBeNormalized::value); @@ -724,4 +723,16 @@ TEST_CASE("TypeToNormalizedType") { TypeToNormalizedType::type, ExpectedMat4Type>); } + + SECTION("Works for arrays") { + REQUIRE(std::is_same_v< + TypeToNormalizedType>::type, + PropertyArrayView>); + REQUIRE(std::is_same_v< + TypeToNormalizedType>::type, + PropertyArrayView>); + REQUIRE(std::is_same_v< + TypeToNormalizedType>::type, + PropertyArrayView>); + } } From 8f8cb9bb8cb632fd35d8c356e2f3eb194372e063 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Tue, 22 Aug 2023 22:15:47 +1000 Subject: [PATCH 193/421] Generate Reader classes, remove hand-written versions. --- .../include/Cesium3DTilesReader/AssetReader.h | 69 + .../Cesium3DTilesReader/AvailabilityReader.h | 69 + .../BoundingVolumeReader.h | 70 + .../Cesium3DTilesReader/BufferReader.h | 69 + .../Cesium3DTilesReader/BufferViewReader.h | 69 + .../Cesium3DTilesReader/ClassPropertyReader.h | 70 + .../include/Cesium3DTilesReader/ClassReader.h | 69 + .../ClassStatisticsReader.h | 70 + .../Cesium3DTilesReader/ContentReader.h | 69 + .../include/Cesium3DTilesReader/EnumReader.h | 69 + .../Cesium3DTilesReader/EnumValueReader.h | 69 + .../Extension3dTilesBoundingVolumeS2Reader.h | 75 + .../Cesium3DTilesReader/GroupMetadataReader.h | 70 + .../ImplicitTilingReader.h | 70 + .../MetadataEntityReader.h | 70 + .../Cesium3DTilesReader/PropertiesReader.h | 69 + .../PropertyStatisticsReader.h | 71 + .../PropertyTablePropertyReader.h | 71 + .../Cesium3DTilesReader/PropertyTableReader.h | 70 + .../Cesium3DTilesReader/SchemaReader.h | 69 + .../Cesium3DTilesReader/StatisticsReader.h | 69 + .../Cesium3DTilesReader/SubtreeReader.h | 69 + .../Cesium3DTilesReader/SubtreesReader.h | 69 + .../include/Cesium3DTilesReader/TileReader.h | 69 + .../Cesium3DTilesReader/TilesetReader.h | 69 + ...ension3dTilesBoundingVolumeS2JsonHandler.h | 45 +- .../generated/src/GeneratedJsonHandlers.cpp | 899 +++++ .../include/Cesium3DTilesReader/Readers.h | 30 - .../Cesium3DTilesReader/SchemaReader.h | 75 - .../Cesium3DTilesReader/SubtreeReader.h | 73 - .../Cesium3DTilesReader/TilesetReader.h | 72 - Cesium3DTilesReader/src/Readers.cpp | 43 - Cesium3DTilesReader/src/SchemaReader.cpp | 63 - Cesium3DTilesReader/src/SubtreeReader.cpp | 48 - Cesium3DTilesReader/src/TilesetReader.cpp | 48 - .../test/TestTilesetReader.cpp | 52 +- .../src/TilesetJsonLoader.cpp | 15 +- .../test/TestTilesetWriter.cpp | 11 +- .../include/CesiumGltfReader/AccessorReader.h | 69 + .../AccessorSparseIndicesReader.h | 71 + .../CesiumGltfReader/AccessorSparseReader.h | 70 + .../AccessorSparseValuesReader.h | 71 + .../CesiumGltfReader/AnimationChannelReader.h | 70 + .../AnimationChannelTargetReader.h | 71 + .../CesiumGltfReader/AnimationReader.h | 69 + .../CesiumGltfReader/AnimationSamplerReader.h | 70 + .../include/CesiumGltfReader/AssetReader.h | 69 + .../include/CesiumGltfReader/BufferReader.h | 69 + .../CesiumGltfReader/BufferViewReader.h | 69 + .../CameraOrthographicReader.h | 70 + .../CameraPerspectiveReader.h | 70 + .../include/CesiumGltfReader/CameraReader.h | 69 + .../CesiumGltfReader/ClassPropertyReader.h | 70 + .../include/CesiumGltfReader/ClassReader.h | 69 + .../CesiumGltfReader/ClassStatisticsReader.h | 70 + .../include/CesiumGltfReader/EnumReader.h | 69 + .../CesiumGltfReader/EnumValueReader.h | 69 + ...tensionBufferExtMeshoptCompressionReader.h | 75 + ...ionBufferViewExtMeshoptCompressionReader.h | 75 + .../ExtensionCesiumRTCReader.h | 70 + .../ExtensionCesiumTileEdgesReader.h | 72 + ...ensionExtInstanceFeaturesFeatureIdReader.h | 75 + .../ExtensionExtInstanceFeaturesReader.h | 73 + .../ExtensionExtMeshFeaturesFeatureIdReader.h | 75 + ...ionExtMeshFeaturesFeatureIdTextureReader.h | 75 + .../ExtensionExtMeshFeaturesReader.h | 72 + .../ExtensionExtMeshGpuInstancingReader.h | 73 + ...ExtStructuralMetadataClassPropertyReader.h | 75 + ...xtensionExtStructuralMetadataClassReader.h | 75 + ...ExtensionExtStructuralMetadataEnumReader.h | 75 + ...sionExtStructuralMetadataEnumValueReader.h | 75 + ...lMetadataPropertyAttributePropertyReader.h | 78 + ...tructuralMetadataPropertyAttributeReader.h | 76 + ...turalMetadataPropertyTablePropertyReader.h | 78 + ...ExtStructuralMetadataPropertyTableReader.h | 75 + ...ralMetadataPropertyTexturePropertyReader.h | 78 + ...tStructuralMetadataPropertyTextureReader.h | 75 + ...tensionExtStructuralMetadataSchemaReader.h | 75 + .../ExtensionKhrDracoMeshCompressionReader.h | 73 + .../ExtensionKhrMaterialsUnlitReader.h | 72 + .../ExtensionKhrTextureBasisuReader.h | 72 + .../ExtensionKhrTextureTransformReader.h | 73 + ...ionMeshPrimitiveExtFeatureMetadataReader.h | 75 + ...MeshPrimitiveExtStructuralMetadataReader.h | 75 + ...eKhrMaterialsVariantsMappingsValueReader.h | 78 + ...nMeshPrimitiveKhrMaterialsVariantsReader.h | 75 + .../ExtensionModelExtFeatureMetadataReader.h | 73 + ...xtensionModelExtStructuralMetadataReader.h | 75 + ...ExtensionModelKhrMaterialsVariantsReader.h | 75 + ...sionModelKhrMaterialsVariantsValueReader.h | 75 + .../ExtensionModelMaxarMeshVariantsReader.h | 73 + ...tensionModelMaxarMeshVariantsValueReader.h | 75 + ...NodeMaxarMeshVariantsMappingsValueReader.h | 75 + .../ExtensionNodeMaxarMeshVariantsReader.h | 73 + .../ExtensionTextureWebpReader.h | 71 + .../FeatureIDAttributeReader.h | 70 + .../CesiumGltfReader/FeatureIDTextureReader.h | 70 + .../CesiumGltfReader/FeatureIDsReader.h | 69 + .../FeatureTablePropertyReader.h | 71 + .../CesiumGltfReader/FeatureTableReader.h | 69 + .../CesiumGltfReader/FeatureTextureReader.h | 70 + .../include/CesiumGltfReader/ImageReader.h | 69 + .../MaterialNormalTextureInfoReader.h | 72 + .../MaterialOcclusionTextureInfoReader.h | 73 + .../MaterialPBRMetallicRoughnessReader.h | 73 + .../include/CesiumGltfReader/MaterialReader.h | 69 + .../CesiumGltfReader/MeshPrimitiveReader.h | 70 + .../include/CesiumGltfReader/MeshReader.h | 69 + .../include/CesiumGltfReader/ModelReader.h | 69 + .../include/CesiumGltfReader/NodeReader.h | 69 + .../PropertyStatisticsReader.h | 70 + .../include/CesiumGltfReader/SamplerReader.h | 69 + .../include/CesiumGltfReader/SceneReader.h | 69 + .../include/CesiumGltfReader/SchemaReader.h | 69 + .../include/CesiumGltfReader/SkinReader.h | 69 + .../CesiumGltfReader/StatisticsReader.h | 69 + .../CesiumGltfReader/TextureAccessorReader.h | 70 + .../CesiumGltfReader/TextureInfoReader.h | 69 + .../include/CesiumGltfReader/TextureReader.h | 69 + ...onBufferExtMeshoptCompressionJsonHandler.h | 45 +- ...fferViewExtMeshoptCompressionJsonHandler.h | 45 +- .../src/ExtensionCesiumRTCJsonHandler.h | 45 +- .../src/ExtensionCesiumTileEdgesJsonHandler.h | 45 +- .../ExtensionExtInstanceFeaturesJsonHandler.h | 45 +- .../src/ExtensionExtMeshFeaturesJsonHandler.h | 45 +- ...ExtensionExtMeshGpuInstancingJsonHandler.h | 45 +- ...ensionKhrDracoMeshCompressionJsonHandler.h | 45 +- .../ExtensionKhrMaterialsUnlitJsonHandler.h | 45 +- .../ExtensionKhrTextureBasisuJsonHandler.h | 45 +- .../ExtensionKhrTextureTransformJsonHandler.h | 45 +- ...shPrimitiveExtFeatureMetadataJsonHandler.h | 45 +- ...rimitiveExtStructuralMetadataJsonHandler.h | 45 +- ...PrimitiveKhrMaterialsVariantsJsonHandler.h | 45 +- ...ensionModelExtFeatureMetadataJsonHandler.h | 45 +- ...ionModelExtStructuralMetadataJsonHandler.h | 45 +- ...sionModelKhrMaterialsVariantsJsonHandler.h | 45 +- ...tensionModelMaxarMeshVariantsJsonHandler.h | 45 +- ...xtensionNodeMaxarMeshVariantsJsonHandler.h | 45 +- .../src/ExtensionTextureWebpJsonHandler.h | 45 +- .../generated/src/GeneratedJsonHandlers.cpp | 3300 ++++++++++++++++- .../CesiumJsonReader/IExtensionJsonHandler.h | 4 +- .../src/ExtensionsJsonHandler.cpp | 2 +- CesiumJsonReader/src/JsonReaderOptions.cpp | 2 + tools/generate-classes/NameFormatters.js | 4 +- tools/generate-classes/generate.js | 214 +- tools/generate-classes/resolveProperty.js | 8 +- 146 files changed, 11986 insertions(+), 1491 deletions(-) create mode 100644 Cesium3DTilesReader/generated/include/Cesium3DTilesReader/AssetReader.h create mode 100644 Cesium3DTilesReader/generated/include/Cesium3DTilesReader/AvailabilityReader.h create mode 100644 Cesium3DTilesReader/generated/include/Cesium3DTilesReader/BoundingVolumeReader.h create mode 100644 Cesium3DTilesReader/generated/include/Cesium3DTilesReader/BufferReader.h create mode 100644 Cesium3DTilesReader/generated/include/Cesium3DTilesReader/BufferViewReader.h create mode 100644 Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ClassPropertyReader.h create mode 100644 Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ClassReader.h create mode 100644 Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ClassStatisticsReader.h create mode 100644 Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ContentReader.h create mode 100644 Cesium3DTilesReader/generated/include/Cesium3DTilesReader/EnumReader.h create mode 100644 Cesium3DTilesReader/generated/include/Cesium3DTilesReader/EnumValueReader.h create mode 100644 Cesium3DTilesReader/generated/include/Cesium3DTilesReader/Extension3dTilesBoundingVolumeS2Reader.h create mode 100644 Cesium3DTilesReader/generated/include/Cesium3DTilesReader/GroupMetadataReader.h create mode 100644 Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ImplicitTilingReader.h create mode 100644 Cesium3DTilesReader/generated/include/Cesium3DTilesReader/MetadataEntityReader.h create mode 100644 Cesium3DTilesReader/generated/include/Cesium3DTilesReader/PropertiesReader.h create mode 100644 Cesium3DTilesReader/generated/include/Cesium3DTilesReader/PropertyStatisticsReader.h create mode 100644 Cesium3DTilesReader/generated/include/Cesium3DTilesReader/PropertyTablePropertyReader.h create mode 100644 Cesium3DTilesReader/generated/include/Cesium3DTilesReader/PropertyTableReader.h create mode 100644 Cesium3DTilesReader/generated/include/Cesium3DTilesReader/SchemaReader.h create mode 100644 Cesium3DTilesReader/generated/include/Cesium3DTilesReader/StatisticsReader.h create mode 100644 Cesium3DTilesReader/generated/include/Cesium3DTilesReader/SubtreeReader.h create mode 100644 Cesium3DTilesReader/generated/include/Cesium3DTilesReader/SubtreesReader.h create mode 100644 Cesium3DTilesReader/generated/include/Cesium3DTilesReader/TileReader.h create mode 100644 Cesium3DTilesReader/generated/include/Cesium3DTilesReader/TilesetReader.h delete mode 100644 Cesium3DTilesReader/include/Cesium3DTilesReader/Readers.h delete mode 100644 Cesium3DTilesReader/include/Cesium3DTilesReader/SchemaReader.h delete mode 100644 Cesium3DTilesReader/include/Cesium3DTilesReader/SubtreeReader.h delete mode 100644 Cesium3DTilesReader/include/Cesium3DTilesReader/TilesetReader.h delete mode 100644 Cesium3DTilesReader/src/Readers.cpp delete mode 100644 Cesium3DTilesReader/src/SchemaReader.cpp delete mode 100644 Cesium3DTilesReader/src/SubtreeReader.cpp delete mode 100644 Cesium3DTilesReader/src/TilesetReader.cpp create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/AccessorReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/AccessorSparseIndicesReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/AccessorSparseReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/AccessorSparseValuesReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/AnimationChannelReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/AnimationChannelTargetReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/AnimationReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/AnimationSamplerReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/AssetReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/BufferReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/BufferViewReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/CameraOrthographicReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/CameraPerspectiveReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/CameraReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ClassPropertyReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ClassReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ClassStatisticsReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/EnumReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/EnumValueReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionBufferExtMeshoptCompressionReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionBufferViewExtMeshoptCompressionReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionCesiumRTCReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionCesiumTileEdgesReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtInstanceFeaturesFeatureIdReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtInstanceFeaturesReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtMeshFeaturesFeatureIdReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtMeshFeaturesFeatureIdTextureReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtMeshFeaturesReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtMeshGpuInstancingReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataClassPropertyReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataClassReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataEnumReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataEnumValueReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyAttributePropertyReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyAttributeReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyTablePropertyReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyTableReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyTexturePropertyReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyTextureReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataSchemaReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionKhrDracoMeshCompressionReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionKhrMaterialsUnlitReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionKhrTextureBasisuReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionKhrTextureTransformReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionMeshPrimitiveExtFeatureMetadataReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionMeshPrimitiveExtStructuralMetadataReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionMeshPrimitiveKhrMaterialsVariantsReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelExtFeatureMetadataReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelExtStructuralMetadataReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelKhrMaterialsVariantsReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelKhrMaterialsVariantsValueReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelMaxarMeshVariantsReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelMaxarMeshVariantsValueReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionNodeMaxarMeshVariantsMappingsValueReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionNodeMaxarMeshVariantsReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionTextureWebpReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/FeatureIDAttributeReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/FeatureIDTextureReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/FeatureIDsReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/FeatureTablePropertyReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/FeatureTableReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/FeatureTextureReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ImageReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/MaterialNormalTextureInfoReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/MaterialOcclusionTextureInfoReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/MaterialPBRMetallicRoughnessReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/MaterialReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/MeshPrimitiveReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/MeshReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ModelReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/NodeReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/PropertyStatisticsReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/SamplerReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/SceneReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/SchemaReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/SkinReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/StatisticsReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/TextureAccessorReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/TextureInfoReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/TextureReader.h diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/AssetReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/AssetReader.h new file mode 100644 index 000000000..5e0ad3faa --- /dev/null +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/AssetReader.h @@ -0,0 +1,69 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace Cesium3DTiles { +struct Asset; +} + +namespace Cesium3DTilesReader { + +class CESIUM3DTILESREADER_API AssetReader { +public: + /** + * @brief Constructs a new instance. + */ + AssetReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Asset from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Asset from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Asset from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace Cesium3DTilesReader \ No newline at end of file diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/AvailabilityReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/AvailabilityReader.h new file mode 100644 index 000000000..e746647aa --- /dev/null +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/AvailabilityReader.h @@ -0,0 +1,69 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace Cesium3DTiles { +struct Availability; +} + +namespace Cesium3DTilesReader { + +class CESIUM3DTILESREADER_API AvailabilityReader { +public: + /** + * @brief Constructs a new instance. + */ + AvailabilityReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Availability from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Availability from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Availability from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace Cesium3DTilesReader \ No newline at end of file diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/BoundingVolumeReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/BoundingVolumeReader.h new file mode 100644 index 000000000..51266d2d3 --- /dev/null +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/BoundingVolumeReader.h @@ -0,0 +1,70 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace Cesium3DTiles { +struct BoundingVolume; +} + +namespace Cesium3DTilesReader { + +class CESIUM3DTILESREADER_API BoundingVolumeReader { +public: + /** + * @brief Constructs a new instance. + */ + BoundingVolumeReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of BoundingVolume from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of BoundingVolume from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of BoundingVolume from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace Cesium3DTilesReader \ No newline at end of file diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/BufferReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/BufferReader.h new file mode 100644 index 000000000..70ae16e3b --- /dev/null +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/BufferReader.h @@ -0,0 +1,69 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace Cesium3DTiles { +struct Buffer; +} + +namespace Cesium3DTilesReader { + +class CESIUM3DTILESREADER_API BufferReader { +public: + /** + * @brief Constructs a new instance. + */ + BufferReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Buffer from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Buffer from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Buffer from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace Cesium3DTilesReader \ No newline at end of file diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/BufferViewReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/BufferViewReader.h new file mode 100644 index 000000000..b06617adb --- /dev/null +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/BufferViewReader.h @@ -0,0 +1,69 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace Cesium3DTiles { +struct BufferView; +} + +namespace Cesium3DTilesReader { + +class CESIUM3DTILESREADER_API BufferViewReader { +public: + /** + * @brief Constructs a new instance. + */ + BufferViewReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of BufferView from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of BufferView from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of BufferView from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace Cesium3DTilesReader \ No newline at end of file diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ClassPropertyReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ClassPropertyReader.h new file mode 100644 index 000000000..632d5ac80 --- /dev/null +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ClassPropertyReader.h @@ -0,0 +1,70 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace Cesium3DTiles { +struct ClassProperty; +} + +namespace Cesium3DTilesReader { + +class CESIUM3DTILESREADER_API ClassPropertyReader { +public: + /** + * @brief Constructs a new instance. + */ + ClassPropertyReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ClassProperty from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ClassProperty from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of ClassProperty from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace Cesium3DTilesReader \ No newline at end of file diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ClassReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ClassReader.h new file mode 100644 index 000000000..5ee9c0194 --- /dev/null +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ClassReader.h @@ -0,0 +1,69 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace Cesium3DTiles { +struct Class; +} + +namespace Cesium3DTilesReader { + +class CESIUM3DTILESREADER_API ClassReader { +public: + /** + * @brief Constructs a new instance. + */ + ClassReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Class from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Class from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Class from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace Cesium3DTilesReader \ No newline at end of file diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ClassStatisticsReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ClassStatisticsReader.h new file mode 100644 index 000000000..69714777f --- /dev/null +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ClassStatisticsReader.h @@ -0,0 +1,70 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace Cesium3DTiles { +struct ClassStatistics; +} + +namespace Cesium3DTilesReader { + +class CESIUM3DTILESREADER_API ClassStatisticsReader { +public: + /** + * @brief Constructs a new instance. + */ + ClassStatisticsReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ClassStatistics from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ClassStatistics from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of ClassStatistics from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace Cesium3DTilesReader \ No newline at end of file diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ContentReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ContentReader.h new file mode 100644 index 000000000..519914dcd --- /dev/null +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ContentReader.h @@ -0,0 +1,69 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace Cesium3DTiles { +struct Content; +} + +namespace Cesium3DTilesReader { + +class CESIUM3DTILESREADER_API ContentReader { +public: + /** + * @brief Constructs a new instance. + */ + ContentReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Content from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Content from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Content from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace Cesium3DTilesReader \ No newline at end of file diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/EnumReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/EnumReader.h new file mode 100644 index 000000000..59be8c989 --- /dev/null +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/EnumReader.h @@ -0,0 +1,69 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace Cesium3DTiles { +struct Enum; +} + +namespace Cesium3DTilesReader { + +class CESIUM3DTILESREADER_API EnumReader { +public: + /** + * @brief Constructs a new instance. + */ + EnumReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Enum from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Enum from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Enum from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace Cesium3DTilesReader \ No newline at end of file diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/EnumValueReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/EnumValueReader.h new file mode 100644 index 000000000..fb88d8e44 --- /dev/null +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/EnumValueReader.h @@ -0,0 +1,69 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace Cesium3DTiles { +struct EnumValue; +} + +namespace Cesium3DTilesReader { + +class CESIUM3DTILESREADER_API EnumValueReader { +public: + /** + * @brief Constructs a new instance. + */ + EnumValueReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of EnumValue from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of EnumValue from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of EnumValue from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace Cesium3DTilesReader \ No newline at end of file diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/Extension3dTilesBoundingVolumeS2Reader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/Extension3dTilesBoundingVolumeS2Reader.h new file mode 100644 index 000000000..328f48714 --- /dev/null +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/Extension3dTilesBoundingVolumeS2Reader.h @@ -0,0 +1,75 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace Cesium3DTiles { +struct Extension3dTilesBoundingVolumeS2; +} + +namespace Cesium3DTilesReader { + +class CESIUM3DTILESREADER_API Extension3dTilesBoundingVolumeS2Reader { +public: + /** + * @brief Constructs a new instance. + */ + Extension3dTilesBoundingVolumeS2Reader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Extension3dTilesBoundingVolumeS2 from a byte + * buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + Cesium3DTiles::Extension3dTilesBoundingVolumeS2> + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Extension3dTilesBoundingVolumeS2 from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + Cesium3DTiles::Extension3dTilesBoundingVolumeS2> + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Extension3dTilesBoundingVolumeS2 from + * a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace Cesium3DTilesReader \ No newline at end of file diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/GroupMetadataReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/GroupMetadataReader.h new file mode 100644 index 000000000..0175b2e4a --- /dev/null +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/GroupMetadataReader.h @@ -0,0 +1,70 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace Cesium3DTiles { +struct GroupMetadata; +} + +namespace Cesium3DTilesReader { + +class CESIUM3DTILESREADER_API GroupMetadataReader { +public: + /** + * @brief Constructs a new instance. + */ + GroupMetadataReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of GroupMetadata from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of GroupMetadata from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of GroupMetadata from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace Cesium3DTilesReader \ No newline at end of file diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ImplicitTilingReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ImplicitTilingReader.h new file mode 100644 index 000000000..7bf251142 --- /dev/null +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ImplicitTilingReader.h @@ -0,0 +1,70 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace Cesium3DTiles { +struct ImplicitTiling; +} + +namespace Cesium3DTilesReader { + +class CESIUM3DTILESREADER_API ImplicitTilingReader { +public: + /** + * @brief Constructs a new instance. + */ + ImplicitTilingReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ImplicitTiling from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ImplicitTiling from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of ImplicitTiling from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace Cesium3DTilesReader \ No newline at end of file diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/MetadataEntityReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/MetadataEntityReader.h new file mode 100644 index 000000000..8b2e3001c --- /dev/null +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/MetadataEntityReader.h @@ -0,0 +1,70 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace Cesium3DTiles { +struct MetadataEntity; +} + +namespace Cesium3DTilesReader { + +class CESIUM3DTILESREADER_API MetadataEntityReader { +public: + /** + * @brief Constructs a new instance. + */ + MetadataEntityReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of MetadataEntity from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of MetadataEntity from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of MetadataEntity from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace Cesium3DTilesReader \ No newline at end of file diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/PropertiesReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/PropertiesReader.h new file mode 100644 index 000000000..34a9db015 --- /dev/null +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/PropertiesReader.h @@ -0,0 +1,69 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace Cesium3DTiles { +struct Properties; +} + +namespace Cesium3DTilesReader { + +class CESIUM3DTILESREADER_API PropertiesReader { +public: + /** + * @brief Constructs a new instance. + */ + PropertiesReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Properties from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Properties from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Properties from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace Cesium3DTilesReader \ No newline at end of file diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/PropertyStatisticsReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/PropertyStatisticsReader.h new file mode 100644 index 000000000..75268cf23 --- /dev/null +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/PropertyStatisticsReader.h @@ -0,0 +1,71 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace Cesium3DTiles { +struct PropertyStatistics; +} + +namespace Cesium3DTilesReader { + +class CESIUM3DTILESREADER_API PropertyStatisticsReader { +public: + /** + * @brief Constructs a new instance. + */ + PropertyStatisticsReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of PropertyStatistics from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of PropertyStatistics from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of PropertyStatistics from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace Cesium3DTilesReader \ No newline at end of file diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/PropertyTablePropertyReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/PropertyTablePropertyReader.h new file mode 100644 index 000000000..b064b82a2 --- /dev/null +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/PropertyTablePropertyReader.h @@ -0,0 +1,71 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace Cesium3DTiles { +struct PropertyTableProperty; +} + +namespace Cesium3DTilesReader { + +class CESIUM3DTILESREADER_API PropertyTablePropertyReader { +public: + /** + * @brief Constructs a new instance. + */ + PropertyTablePropertyReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of PropertyTableProperty from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of PropertyTableProperty from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of PropertyTableProperty from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace Cesium3DTilesReader \ No newline at end of file diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/PropertyTableReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/PropertyTableReader.h new file mode 100644 index 000000000..1610bc9d5 --- /dev/null +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/PropertyTableReader.h @@ -0,0 +1,70 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace Cesium3DTiles { +struct PropertyTable; +} + +namespace Cesium3DTilesReader { + +class CESIUM3DTILESREADER_API PropertyTableReader { +public: + /** + * @brief Constructs a new instance. + */ + PropertyTableReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of PropertyTable from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of PropertyTable from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of PropertyTable from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace Cesium3DTilesReader \ No newline at end of file diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/SchemaReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/SchemaReader.h new file mode 100644 index 000000000..6680c820a --- /dev/null +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/SchemaReader.h @@ -0,0 +1,69 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace Cesium3DTiles { +struct Schema; +} + +namespace Cesium3DTilesReader { + +class CESIUM3DTILESREADER_API SchemaReader { +public: + /** + * @brief Constructs a new instance. + */ + SchemaReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Schema from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Schema from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Schema from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace Cesium3DTilesReader \ No newline at end of file diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/StatisticsReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/StatisticsReader.h new file mode 100644 index 000000000..03c4c8ae6 --- /dev/null +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/StatisticsReader.h @@ -0,0 +1,69 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace Cesium3DTiles { +struct Statistics; +} + +namespace Cesium3DTilesReader { + +class CESIUM3DTILESREADER_API StatisticsReader { +public: + /** + * @brief Constructs a new instance. + */ + StatisticsReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Statistics from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Statistics from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Statistics from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace Cesium3DTilesReader \ No newline at end of file diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/SubtreeReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/SubtreeReader.h new file mode 100644 index 000000000..285096a69 --- /dev/null +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/SubtreeReader.h @@ -0,0 +1,69 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace Cesium3DTiles { +struct Subtree; +} + +namespace Cesium3DTilesReader { + +class CESIUM3DTILESREADER_API SubtreeReader { +public: + /** + * @brief Constructs a new instance. + */ + SubtreeReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Subtree from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Subtree from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Subtree from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace Cesium3DTilesReader \ No newline at end of file diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/SubtreesReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/SubtreesReader.h new file mode 100644 index 000000000..29da3eb2a --- /dev/null +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/SubtreesReader.h @@ -0,0 +1,69 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace Cesium3DTiles { +struct Subtrees; +} + +namespace Cesium3DTilesReader { + +class CESIUM3DTILESREADER_API SubtreesReader { +public: + /** + * @brief Constructs a new instance. + */ + SubtreesReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Subtrees from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Subtrees from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Subtrees from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace Cesium3DTilesReader \ No newline at end of file diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/TileReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/TileReader.h new file mode 100644 index 000000000..370c7971d --- /dev/null +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/TileReader.h @@ -0,0 +1,69 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace Cesium3DTiles { +struct Tile; +} + +namespace Cesium3DTilesReader { + +class CESIUM3DTILESREADER_API TileReader { +public: + /** + * @brief Constructs a new instance. + */ + TileReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Tile from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Tile from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Tile from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace Cesium3DTilesReader \ No newline at end of file diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/TilesetReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/TilesetReader.h new file mode 100644 index 000000000..56dc53bd2 --- /dev/null +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/TilesetReader.h @@ -0,0 +1,69 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace Cesium3DTiles { +struct Tileset; +} + +namespace Cesium3DTilesReader { + +class CESIUM3DTILESREADER_API TilesetReader { +public: + /** + * @brief Constructs a new instance. + */ + TilesetReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Tileset from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Tileset from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Tileset from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace Cesium3DTilesReader \ No newline at end of file diff --git a/Cesium3DTilesReader/generated/src/Extension3dTilesBoundingVolumeS2JsonHandler.h b/Cesium3DTilesReader/generated/src/Extension3dTilesBoundingVolumeS2JsonHandler.h index dcfee86c0..d8f237521 100644 --- a/Cesium3DTilesReader/generated/src/Extension3dTilesBoundingVolumeS2JsonHandler.h +++ b/Cesium3DTilesReader/generated/src/Extension3dTilesBoundingVolumeS2JsonHandler.h @@ -34,50 +34,7 @@ class Extension3dTilesBoundingVolumeS2JsonHandler CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) override; - virtual IJsonHandler* readNull() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readNull(); - }; - virtual IJsonHandler* readBool(bool b) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readBool(b); - } - virtual IJsonHandler* readInt32(int32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt32(i); - } - virtual IJsonHandler* readUint32(uint32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint32(i); - } - virtual IJsonHandler* readInt64(int64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt64(i); - } - virtual IJsonHandler* readUint64(uint64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint64(i); - } - virtual IJsonHandler* readDouble(double d) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readDouble(d); - } - virtual IJsonHandler* readString(const std::string_view& str) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readString(str); - } - virtual IJsonHandler* readObjectStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectStart(); - } - virtual IJsonHandler* readObjectEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectEnd(); - } - virtual IJsonHandler* readArrayStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayStart(); - } - virtual IJsonHandler* readArrayEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayEnd(); - } - virtual void reportWarning( - const std::string& warning, - std::vector&& context = - std::vector()) override { - CesiumJsonReader::ExtensibleObjectJsonHandler::reportWarning( - warning, - std::move(context)); - } + virtual IJsonHandler& getHandler() override { return *this; } protected: IJsonHandler* readObjectKeyExtension3dTilesBoundingVolumeS2( diff --git a/Cesium3DTilesReader/generated/src/GeneratedJsonHandlers.cpp b/Cesium3DTilesReader/generated/src/GeneratedJsonHandlers.cpp index 9fec23d8c..f4c59b3f5 100644 --- a/Cesium3DTilesReader/generated/src/GeneratedJsonHandlers.cpp +++ b/Cesium3DTilesReader/generated/src/GeneratedJsonHandlers.cpp @@ -1,8 +1,12 @@ // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "Extension3dTilesBoundingVolumeS2JsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -65,12 +69,58 @@ CesiumJsonReader::IJsonHandler* Extension3dTilesBoundingVolumeS2JsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +Extension3dTilesBoundingVolumeS2Reader:: + Extension3dTilesBoundingVolumeS2Reader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +Extension3dTilesBoundingVolumeS2Reader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +Extension3dTilesBoundingVolumeS2Reader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult< + Cesium3DTiles::Extension3dTilesBoundingVolumeS2> +Extension3dTilesBoundingVolumeS2Reader::readFromJson( + const gsl::span& data) const { + Extension3dTilesBoundingVolumeS2JsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult< + Cesium3DTiles::Extension3dTilesBoundingVolumeS2> +Extension3dTilesBoundingVolumeS2Reader::readFromJson( + const rapidjson::Value& value) const { + Extension3dTilesBoundingVolumeS2JsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +Extension3dTilesBoundingVolumeS2Reader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + Cesium3DTiles::Extension3dTilesBoundingVolumeS2, + Extension3dTilesBoundingVolumeS2JsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace Cesium3DTilesReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "StatisticsJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -110,12 +160,47 @@ CesiumJsonReader::IJsonHandler* StatisticsJsonHandler::readObjectKeyStatistics( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +StatisticsReader::StatisticsReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& StatisticsReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +StatisticsReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +StatisticsReader::readFromJson(const gsl::span& data) const { + StatisticsJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +StatisticsReader::readFromJson(const rapidjson::Value& value) const { + StatisticsJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +StatisticsReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader:: + ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace Cesium3DTilesReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ClassStatisticsJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -159,12 +244,51 @@ ClassStatisticsJsonHandler::readObjectKeyClassStatistics( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ClassStatisticsReader::ClassStatisticsReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& ClassStatisticsReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ClassStatisticsReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +ClassStatisticsReader::readFromJson( + const gsl::span& data) const { + ClassStatisticsJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +ClassStatisticsReader::readFromJson(const rapidjson::Value& value) const { + ClassStatisticsJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +ClassStatisticsReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + Cesium3DTiles::ClassStatistics, + ClassStatisticsJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace Cesium3DTilesReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "PropertyStatisticsJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -229,12 +353,52 @@ PropertyStatisticsJsonHandler::readObjectKeyPropertyStatistics( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +PropertyStatisticsReader::PropertyStatisticsReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& PropertyStatisticsReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +PropertyStatisticsReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +PropertyStatisticsReader::readFromJson( + const gsl::span& data) const { + PropertyStatisticsJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +PropertyStatisticsReader::readFromJson(const rapidjson::Value& value) const { + PropertyStatisticsJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +PropertyStatisticsReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + Cesium3DTiles::PropertyStatistics, + PropertyStatisticsJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace Cesium3DTilesReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "SchemaJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -289,12 +453,45 @@ CesiumJsonReader::IJsonHandler* SchemaJsonHandler::readObjectKeySchema( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +SchemaReader::SchemaReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& SchemaReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& SchemaReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +SchemaReader::readFromJson(const gsl::span& data) const { + SchemaJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +SchemaReader::readFromJson(const rapidjson::Value& value) const { + SchemaJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +SchemaReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace Cesium3DTilesReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "EnumJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -343,12 +540,45 @@ CesiumJsonReader::IJsonHandler* EnumJsonHandler::readObjectKeyEnum( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +EnumReader::EnumReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& EnumReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& EnumReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +EnumReader::readFromJson(const gsl::span& data) const { + EnumJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +EnumReader::readFromJson(const rapidjson::Value& value) const { + EnumJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +EnumReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace Cesium3DTilesReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "EnumValueJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -394,12 +624,46 @@ CesiumJsonReader::IJsonHandler* EnumValueJsonHandler::readObjectKeyEnumValue( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +EnumValueReader::EnumValueReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& EnumValueReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& EnumValueReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +EnumValueReader::readFromJson(const gsl::span& data) const { + EnumValueJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +EnumValueReader::readFromJson(const rapidjson::Value& value) const { + EnumValueJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +EnumValueReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader:: + ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace Cesium3DTilesReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ClassJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -445,12 +709,45 @@ CesiumJsonReader::IJsonHandler* ClassJsonHandler::readObjectKeyClass( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ClassReader::ClassReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& ClassReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& ClassReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +ClassReader::readFromJson(const gsl::span& data) const { + ClassJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +ClassReader::readFromJson(const rapidjson::Value& value) const { + ClassJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +ClassReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace Cesium3DTilesReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ClassPropertyJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -536,12 +833,50 @@ ClassPropertyJsonHandler::readObjectKeyClassProperty( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ClassPropertyReader::ClassPropertyReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& ClassPropertyReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ClassPropertyReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +ClassPropertyReader::readFromJson( + const gsl::span& data) const { + ClassPropertyJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +ClassPropertyReader::readFromJson(const rapidjson::Value& value) const { + ClassPropertyJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +ClassPropertyReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader:: + ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace Cesium3DTilesReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "SubtreeJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -620,12 +955,45 @@ CesiumJsonReader::IJsonHandler* SubtreeJsonHandler::readObjectKeySubtree( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +SubtreeReader::SubtreeReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& SubtreeReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& SubtreeReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +SubtreeReader::readFromJson(const gsl::span& data) const { + SubtreeJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +SubtreeReader::readFromJson(const rapidjson::Value& value) const { + SubtreeJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +SubtreeReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace Cesium3DTilesReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "MetadataEntityJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -669,12 +1037,50 @@ MetadataEntityJsonHandler::readObjectKeyMetadataEntity( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +MetadataEntityReader::MetadataEntityReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& MetadataEntityReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +MetadataEntityReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +MetadataEntityReader::readFromJson( + const gsl::span& data) const { + MetadataEntityJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +MetadataEntityReader::readFromJson(const rapidjson::Value& value) const { + MetadataEntityJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +MetadataEntityReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader:: + ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace Cesium3DTilesReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "AvailabilityJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -721,12 +1127,47 @@ AvailabilityJsonHandler::readObjectKeyAvailability( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +AvailabilityReader::AvailabilityReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& AvailabilityReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +AvailabilityReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +AvailabilityReader::readFromJson(const gsl::span& data) const { + AvailabilityJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +AvailabilityReader::readFromJson(const rapidjson::Value& value) const { + AvailabilityJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +AvailabilityReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader:: + ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace Cesium3DTilesReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "PropertyTableJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -776,12 +1217,50 @@ PropertyTableJsonHandler::readObjectKeyPropertyTable( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +PropertyTableReader::PropertyTableReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& PropertyTableReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +PropertyTableReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +PropertyTableReader::readFromJson( + const gsl::span& data) const { + PropertyTableJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +PropertyTableReader::readFromJson(const rapidjson::Value& value) const { + PropertyTableJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +PropertyTableReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader:: + ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace Cesium3DTilesReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "PropertyTablePropertyJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -852,12 +1331,53 @@ PropertyTablePropertyJsonHandler::readObjectKeyPropertyTableProperty( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +PropertyTablePropertyReader::PropertyTablePropertyReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& PropertyTablePropertyReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +PropertyTablePropertyReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +PropertyTablePropertyReader::readFromJson( + const gsl::span& data) const { + PropertyTablePropertyJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +PropertyTablePropertyReader::readFromJson(const rapidjson::Value& value) const { + PropertyTablePropertyJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +PropertyTablePropertyReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + Cesium3DTiles::PropertyTableProperty, + PropertyTablePropertyJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace Cesium3DTilesReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "BufferViewJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -906,12 +1426,47 @@ CesiumJsonReader::IJsonHandler* BufferViewJsonHandler::readObjectKeyBufferView( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +BufferViewReader::BufferViewReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& BufferViewReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +BufferViewReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +BufferViewReader::readFromJson(const gsl::span& data) const { + BufferViewJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +BufferViewReader::readFromJson(const rapidjson::Value& value) const { + BufferViewJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +BufferViewReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader:: + ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace Cesium3DTilesReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "BufferJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -957,12 +1512,45 @@ CesiumJsonReader::IJsonHandler* BufferJsonHandler::readObjectKeyBuffer( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +BufferReader::BufferReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& BufferReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& BufferReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +BufferReader::readFromJson(const gsl::span& data) const { + BufferJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +BufferReader::readFromJson(const rapidjson::Value& value) const { + BufferJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +BufferReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace Cesium3DTilesReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "TilesetJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -1035,12 +1623,45 @@ CesiumJsonReader::IJsonHandler* TilesetJsonHandler::readObjectKeyTileset( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +TilesetReader::TilesetReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& TilesetReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& TilesetReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +TilesetReader::readFromJson(const gsl::span& data) const { + TilesetJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +TilesetReader::readFromJson(const rapidjson::Value& value) const { + TilesetJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +TilesetReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace Cesium3DTilesReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "TileJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -1110,12 +1731,45 @@ CesiumJsonReader::IJsonHandler* TileJsonHandler::readObjectKeyTile( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +TileReader::TileReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& TileReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& TileReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +TileReader::readFromJson(const gsl::span& data) const { + TileJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +TileReader::readFromJson(const rapidjson::Value& value) const { + TileJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +TileReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace Cesium3DTilesReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ImplicitTilingJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -1171,12 +1825,50 @@ ImplicitTilingJsonHandler::readObjectKeyImplicitTiling( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ImplicitTilingReader::ImplicitTilingReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& ImplicitTilingReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ImplicitTilingReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +ImplicitTilingReader::readFromJson( + const gsl::span& data) const { + ImplicitTilingJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +ImplicitTilingReader::readFromJson(const rapidjson::Value& value) const { + ImplicitTilingJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +ImplicitTilingReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader:: + ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace Cesium3DTilesReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "SubtreesJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -1215,12 +1907,46 @@ CesiumJsonReader::IJsonHandler* SubtreesJsonHandler::readObjectKeySubtrees( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +SubtreesReader::SubtreesReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& SubtreesReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& SubtreesReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +SubtreesReader::readFromJson(const gsl::span& data) const { + SubtreesJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +SubtreesReader::readFromJson(const rapidjson::Value& value) const { + SubtreesJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +SubtreesReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader:: + ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace Cesium3DTilesReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ContentJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -1269,12 +1995,45 @@ CesiumJsonReader::IJsonHandler* ContentJsonHandler::readObjectKeyContent( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ContentReader::ContentReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& ContentReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& ContentReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +ContentReader::readFromJson(const gsl::span& data) const { + ContentJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +ContentReader::readFromJson(const rapidjson::Value& value) const { + ContentJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +ContentReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace Cesium3DTilesReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "BoundingVolumeJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -1321,12 +2080,50 @@ BoundingVolumeJsonHandler::readObjectKeyBoundingVolume( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +BoundingVolumeReader::BoundingVolumeReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& BoundingVolumeReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +BoundingVolumeReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +BoundingVolumeReader::readFromJson( + const gsl::span& data) const { + BoundingVolumeJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +BoundingVolumeReader::readFromJson(const rapidjson::Value& value) const { + BoundingVolumeJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +BoundingVolumeReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader:: + ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace Cesium3DTilesReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "GroupMetadataJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -1365,12 +2162,50 @@ GroupMetadataJsonHandler::readObjectKeyGroupMetadata( return this->readObjectKeyMetadataEntity(objectType, str, *this->_pObject); } +GroupMetadataReader::GroupMetadataReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& GroupMetadataReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +GroupMetadataReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +GroupMetadataReader::readFromJson( + const gsl::span& data) const { + GroupMetadataJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +GroupMetadataReader::readFromJson(const rapidjson::Value& value) const { + GroupMetadataJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +GroupMetadataReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader:: + ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace Cesium3DTilesReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "PropertiesJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -1413,12 +2248,47 @@ CesiumJsonReader::IJsonHandler* PropertiesJsonHandler::readObjectKeyProperties( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +PropertiesReader::PropertiesReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& PropertiesReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +PropertiesReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +PropertiesReader::readFromJson(const gsl::span& data) const { + PropertiesJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +PropertiesReader::readFromJson(const rapidjson::Value& value) const { + PropertiesJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +PropertiesReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader:: + ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace Cesium3DTilesReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "AssetJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -1461,4 +2331,33 @@ CesiumJsonReader::IJsonHandler* AssetJsonHandler::readObjectKeyAsset( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +AssetReader::AssetReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& AssetReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& AssetReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +AssetReader::readFromJson(const gsl::span& data) const { + AssetJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +AssetReader::readFromJson(const rapidjson::Value& value) const { + AssetJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +AssetReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace Cesium3DTilesReader diff --git a/Cesium3DTilesReader/include/Cesium3DTilesReader/Readers.h b/Cesium3DTilesReader/include/Cesium3DTilesReader/Readers.h deleted file mode 100644 index ee09263eb..000000000 --- a/Cesium3DTilesReader/include/Cesium3DTilesReader/Readers.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -namespace Cesium3DTilesReader { - -CesiumJsonReader::ReadJsonResult readSchema( - const rapidjson::Value& value, - const CesiumJsonReader::JsonReaderOptions& options = {}); - -CesiumJsonReader::ReadJsonResult -readMetadataEntity( - const rapidjson::Value& value, - const CesiumJsonReader::JsonReaderOptions& options = {}); - -CesiumJsonReader::ReadJsonResult -readGroupMetadata( - const rapidjson::Value& value, - const CesiumJsonReader::JsonReaderOptions& options = {}); - -CesiumJsonReader::ReadJsonResult> -readGroupMetadataArray( - const rapidjson::Value& value, - const CesiumJsonReader::JsonReaderOptions& options = {}); - -} // namespace Cesium3DTilesReader diff --git a/Cesium3DTilesReader/include/Cesium3DTilesReader/SchemaReader.h b/Cesium3DTilesReader/include/Cesium3DTilesReader/SchemaReader.h deleted file mode 100644 index c02339414..000000000 --- a/Cesium3DTilesReader/include/Cesium3DTilesReader/SchemaReader.h +++ /dev/null @@ -1,75 +0,0 @@ -#pragma once - -#include "Cesium3DTilesReader/Library.h" - -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include - -namespace Cesium3DTilesReader { - -/** - * @brief The result of reading a schema with - * {@link SchemaReader::readSchema}. - */ -struct CESIUM3DTILESREADER_API SchemaReaderResult { - /** - * @brief The read schema, or std::nullopt if the schema could not be read. - */ - std::optional schema; - - /** - * @brief Errors, if any, that occurred during the load process. - */ - std::vector errors; - - /** - * @brief Warnings, if any, that occurred during the load process. - */ - std::vector warnings; -}; - -/** - * @brief Reads schemas. - */ -class CESIUM3DTILESREADER_API SchemaReader { -public: - /** - * @brief Constructs a new instance. - */ - SchemaReader(); - - /** - * @brief Gets the options controlling how the JSON is read. - */ - CesiumJsonReader::JsonReaderOptions& getOptions(); - - /** - * @brief Gets the options controlling how the JSON is read. - */ - const CesiumJsonReader::JsonReaderOptions& getOptions() const; - - /** - * @brief Reads a schema. - * - * @param data The buffer from which to read the schema. - * @return The result of reading the schame. - */ - SchemaReaderResult readSchema(const gsl::span& data) const; - - SchemaReaderResult readSchema(const rapidjson::Value& value) const; - -private: - CesiumJsonReader::JsonReaderOptions _context; -}; - -} // namespace Cesium3DTilesReader diff --git a/Cesium3DTilesReader/include/Cesium3DTilesReader/SubtreeReader.h b/Cesium3DTilesReader/include/Cesium3DTilesReader/SubtreeReader.h deleted file mode 100644 index 05f3c35bd..000000000 --- a/Cesium3DTilesReader/include/Cesium3DTilesReader/SubtreeReader.h +++ /dev/null @@ -1,73 +0,0 @@ -#pragma once - -#include "Cesium3DTilesReader/Library.h" - -#include -#include - -#include - -#include -#include -#include -#include -#include -#include - -namespace Cesium3DTilesReader { - -/** - * @brief The result of reading a subtree with - * {@link SubtreeReader::readSubtree}. - */ -struct CESIUM3DTILESREADER_API SubtreeReaderResult { - /** - * @brief The read subtree, or std::nullopt if the subtree could not be read. - */ - std::optional subtree; - - /** - * @brief Errors, if any, that occurred during the load process. - */ - std::vector errors; - - /** - * @brief Warnings, if any, that occurred during the load process. - */ - std::vector warnings; -}; - -/** - * @brief Reads subtrees. - */ -class CESIUM3DTILESREADER_API SubtreeReader { -public: - /** - * @brief Constructs a new instance. - */ - SubtreeReader(); - - /** - * @brief Gets the options controlling how the JSON is read. - */ - CesiumJsonReader::JsonReaderOptions& getOptions(); - - /** - * @brief Gets the options controlling how the JSON is read. - */ - const CesiumJsonReader::JsonReaderOptions& getOptions() const; - - /** - * @brief Reads a subtree. - * - * @param data The buffer from which to read the subtree. - * @param options Options for how to read the subtree. - * @return The result of reading the subtree. - */ - SubtreeReaderResult readSubtree(const gsl::span& data) const; - -private: - CesiumJsonReader::JsonReaderOptions _context; -}; - -} // namespace Cesium3DTilesReader diff --git a/Cesium3DTilesReader/include/Cesium3DTilesReader/TilesetReader.h b/Cesium3DTilesReader/include/Cesium3DTilesReader/TilesetReader.h deleted file mode 100644 index ba2741cd7..000000000 --- a/Cesium3DTilesReader/include/Cesium3DTilesReader/TilesetReader.h +++ /dev/null @@ -1,72 +0,0 @@ -#pragma once - -#include "Cesium3DTilesReader/Library.h" - -#include -#include - -#include - -#include -#include -#include -#include -#include -#include - -namespace Cesium3DTilesReader { - -/** - * @brief The result of reading a tileset with - * {@link TilesetReader::readTileset}. - */ -struct CESIUM3DTILESREADER_API TilesetReaderResult { - /** - * @brief The read tileset, or std::nullopt if the tileset could not be read. - */ - std::optional tileset; - - /** - * @brief Errors, if any, that occurred during the load process. - */ - std::vector errors; - - /** - * @brief Warnings, if any, that occurred during the load process. - */ - std::vector warnings; -}; - -/** - * @brief Reads tilesets. - */ -class CESIUM3DTILESREADER_API TilesetReader { -public: - /** - * @brief Constructs a new instance. - */ - TilesetReader(); - - /** - * @brief Gets the options controlling how the JSON is read. - */ - CesiumJsonReader::JsonReaderOptions& getOptions(); - - /** - * @brief Gets the options controlling how the JSON is read. - */ - const CesiumJsonReader::JsonReaderOptions& getOptions() const; - - /** - * @brief Reads a tileset. - * - * @param data The buffer from which to read the tileset. - * @return The result of reading the tileset. - */ - TilesetReaderResult readTileset(const gsl::span& data) const; - -private: - CesiumJsonReader::JsonReaderOptions _context; -}; - -} // namespace Cesium3DTilesReader diff --git a/Cesium3DTilesReader/src/Readers.cpp b/Cesium3DTilesReader/src/Readers.cpp deleted file mode 100644 index 4c0fcb08f..000000000 --- a/Cesium3DTilesReader/src/Readers.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include "GroupMetadataJsonHandler.h" -#include "MetadataEntityJsonHandler.h" -#include "SchemaJsonHandler.h" - -#include -#include - -namespace Cesium3DTilesReader { - -CesiumJsonReader::ReadJsonResult readSchema( - const rapidjson::Value& value, - const CesiumJsonReader::JsonReaderOptions& options) { - SchemaJsonHandler handler(options); - return CesiumJsonReader::JsonReader::readJson(value, handler); -} - -CesiumJsonReader::ReadJsonResult -readMetadataEntity( - const rapidjson::Value& value, - const CesiumJsonReader::JsonReaderOptions& options) { - MetadataEntityJsonHandler handler(options); - return CesiumJsonReader::JsonReader::readJson(value, handler); -} - -CesiumJsonReader::ReadJsonResult -readGroupMetadata( - const rapidjson::Value& value, - const CesiumJsonReader::JsonReaderOptions& options) { - GroupMetadataJsonHandler handler(options); - return CesiumJsonReader::JsonReader::readJson(value, handler); -} - -CesiumJsonReader::ReadJsonResult> -readGroupMetadataArray( - const rapidjson::Value& value, - const CesiumJsonReader::JsonReaderOptions& options) { - CesiumJsonReader:: - ArrayJsonHandler - handler(options); - return CesiumJsonReader::JsonReader::readJson(value, handler); -} - -} // namespace Cesium3DTilesReader diff --git a/Cesium3DTilesReader/src/SchemaReader.cpp b/Cesium3DTilesReader/src/SchemaReader.cpp deleted file mode 100644 index 28d726032..000000000 --- a/Cesium3DTilesReader/src/SchemaReader.cpp +++ /dev/null @@ -1,63 +0,0 @@ -#include "Cesium3DTilesReader/SchemaReader.h" - -#include "SchemaJsonHandler.h" -#include "registerExtensions.h" - -#include -#include - -namespace Cesium3DTilesReader { - -namespace { - -SchemaReaderResult readSchemaJson( - const CesiumJsonReader::JsonReaderOptions& context, - const gsl::span& data) { - - CESIUM_TRACE("Cesium3DTilesReader::SchemaReader::readSchemaJson"); - - SchemaJsonHandler schemaHandler(context); - CesiumJsonReader::ReadJsonResult jsonResult = - CesiumJsonReader::JsonReader::readJson(data, schemaHandler); - - return SchemaReaderResult{ - std::move(jsonResult.value), - std::move(jsonResult.errors), - std::move(jsonResult.warnings)}; -} - -} // namespace - -SchemaReader::SchemaReader() { registerExtensions(this->_context); } - -CesiumJsonReader::JsonReaderOptions& SchemaReader::getOptions() { - return this->_context; -} - -const CesiumJsonReader::JsonReaderOptions& SchemaReader::getOptions() const { - return this->_context; -} - -SchemaReaderResult -SchemaReader::readSchema(const gsl::span& data) const { - const CesiumJsonReader::JsonReaderOptions& context = this->getOptions(); - SchemaReaderResult result = readSchemaJson(context, data); - - return result; -} - -SchemaReaderResult -SchemaReader::readSchema(const rapidjson::Value& value) const { - CESIUM_TRACE("Cesium3DTilesReader::SchemaReader::readSchemaValue"); - const CesiumJsonReader::JsonReaderOptions& context = this->getOptions(); - SchemaJsonHandler schemaHandler(context); - CesiumJsonReader::ReadJsonResult jsonResult = - CesiumJsonReader::JsonReader::readJson(value, schemaHandler); - - return SchemaReaderResult{ - std::move(jsonResult.value), - std::move(jsonResult.errors), - std::move(jsonResult.warnings)}; -} - -} // namespace Cesium3DTilesReader diff --git a/Cesium3DTilesReader/src/SubtreeReader.cpp b/Cesium3DTilesReader/src/SubtreeReader.cpp deleted file mode 100644 index c121abf22..000000000 --- a/Cesium3DTilesReader/src/SubtreeReader.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include "Cesium3DTilesReader/SubtreeReader.h" - -#include "SubtreeJsonHandler.h" -#include "registerExtensions.h" - -#include -#include - -namespace Cesium3DTilesReader { - -namespace { - -SubtreeReaderResult readSubtreeJson( - const CesiumJsonReader::JsonReaderOptions& context, - const gsl::span& data) { - - CESIUM_TRACE("Cesium3DTilesReader::SubtreeReader::readSubtreeJson"); - - SubtreeJsonHandler subtreeHandler(context); - CesiumJsonReader::ReadJsonResult jsonResult = - CesiumJsonReader::JsonReader::readJson(data, subtreeHandler); - - return SubtreeReaderResult{ - std::move(jsonResult.value), - std::move(jsonResult.errors), - std::move(jsonResult.warnings)}; -} - -} // namespace - -SubtreeReader::SubtreeReader() { registerExtensions(this->_context); } - -CesiumJsonReader::JsonReaderOptions& SubtreeReader::getOptions() { - return this->_context; -} - -const CesiumJsonReader::JsonReaderOptions& SubtreeReader::getOptions() const { - return this->_context; -} - -SubtreeReaderResult -SubtreeReader::readSubtree(const gsl::span& data) const { - const CesiumJsonReader::JsonReaderOptions& context = this->getOptions(); - SubtreeReaderResult result = readSubtreeJson(context, data); - - return result; -} -} // namespace Cesium3DTilesReader diff --git a/Cesium3DTilesReader/src/TilesetReader.cpp b/Cesium3DTilesReader/src/TilesetReader.cpp deleted file mode 100644 index ba6ab2942..000000000 --- a/Cesium3DTilesReader/src/TilesetReader.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include "Cesium3DTilesReader/TilesetReader.h" - -#include "TilesetJsonHandler.h" -#include "registerExtensions.h" - -#include -#include - -namespace Cesium3DTilesReader { - -namespace { - -TilesetReaderResult readTilesetJson( - const CesiumJsonReader::JsonReaderOptions& context, - const gsl::span& data) { - - CESIUM_TRACE("Cesium3DTilesReader::TilesetReader::readTilesetJson"); - - TilesetJsonHandler tilesetHandler(context); - CesiumJsonReader::ReadJsonResult jsonResult = - CesiumJsonReader::JsonReader::readJson(data, tilesetHandler); - - return TilesetReaderResult{ - std::move(jsonResult.value), - std::move(jsonResult.errors), - std::move(jsonResult.warnings)}; -} - -} // namespace - -TilesetReader::TilesetReader() { registerExtensions(this->_context); } - -CesiumJsonReader::JsonReaderOptions& TilesetReader::getOptions() { - return this->_context; -} - -const CesiumJsonReader::JsonReaderOptions& TilesetReader::getOptions() const { - return this->_context; -} - -TilesetReaderResult -TilesetReader::readTileset(const gsl::span& data) const { - const CesiumJsonReader::JsonReaderOptions& context = this->getOptions(); - TilesetReaderResult result = readTilesetJson(context, data); - - return result; -} -} // namespace Cesium3DTilesReader diff --git a/Cesium3DTilesReader/test/TestTilesetReader.cpp b/Cesium3DTilesReader/test/TestTilesetReader.cpp index 83a9c7acf..aca84cdd1 100644 --- a/Cesium3DTilesReader/test/TestTilesetReader.cpp +++ b/Cesium3DTilesReader/test/TestTilesetReader.cpp @@ -1,6 +1,5 @@ -#include "Cesium3DTilesReader/TilesetReader.h" - #include +#include #include #include @@ -34,10 +33,10 @@ TEST_CASE("Reads tileset JSON") { tilesetFile /= "tileset.json"; std::vector data = readFile(tilesetFile); Cesium3DTilesReader::TilesetReader reader; - Cesium3DTilesReader::TilesetReaderResult result = reader.readTileset(data); - REQUIRE(result.tileset); + auto result = reader.readFromJson(data); + REQUIRE(result.value); - const Cesium3DTiles::Tileset& tileset = result.tileset.value(); + const Cesium3DTiles::Tileset& tileset = result.value.value(); REQUIRE(tileset.asset.version == "1.0"); REQUIRE(tileset.geometricError == 494.50961650991815); @@ -146,13 +145,13 @@ TEST_CASE("Reads extras") { )"; Cesium3DTilesReader::TilesetReader reader; - Cesium3DTilesReader::TilesetReaderResult result = reader.readTileset( + auto result = reader.readFromJson( gsl::span(reinterpret_cast(s.c_str()), s.size())); REQUIRE(result.errors.empty()); REQUIRE(result.warnings.empty()); - REQUIRE(result.tileset.has_value()); + REQUIRE(result.value.has_value()); - Cesium3DTiles::Tileset& tileset = result.tileset.value(); + Cesium3DTiles::Tileset& tileset = result.value.value(); auto ait = tileset.extras.find("A"); REQUIRE(ait != tileset.extras.end()); @@ -224,13 +223,13 @@ TEST_CASE("Reads 3DTILES_content_gltf") { )"; Cesium3DTilesReader::TilesetReader reader; - Cesium3DTilesReader::TilesetReaderResult result = reader.readTileset( + auto result = reader.readFromJson( gsl::span(reinterpret_cast(s.c_str()), s.size())); REQUIRE(result.errors.empty()); REQUIRE(result.warnings.empty()); - REQUIRE(result.tileset.has_value()); + REQUIRE(result.value.has_value()); - Cesium3DTiles::Tileset& tileset = result.tileset.value(); + Cesium3DTiles::Tileset& tileset = result.value.value(); CHECK(tileset.asset.version == "1.0"); const std::vector tilesetExtensionUsed{ @@ -274,17 +273,15 @@ TEST_CASE("Reads custom extension") { )"; Cesium3DTilesReader::TilesetReader reader; - Cesium3DTilesReader::TilesetReaderResult withCustomExt = reader.readTileset( + auto withCustomExt = reader.readFromJson( gsl::span(reinterpret_cast(s.c_str()), s.size())); REQUIRE(withCustomExt.errors.empty()); - REQUIRE(withCustomExt.tileset.has_value()); + REQUIRE(withCustomExt.value.has_value()); - REQUIRE(withCustomExt.tileset->extensions.size() == 2); + REQUIRE(withCustomExt.value->extensions.size() == 2); - CesiumUtility::JsonValue* pA = - withCustomExt.tileset->getGenericExtension("A"); - CesiumUtility::JsonValue* pB = - withCustomExt.tileset->getGenericExtension("B"); + CesiumUtility::JsonValue* pA = withCustomExt.value->getGenericExtension("A"); + CesiumUtility::JsonValue* pB = withCustomExt.value->getGenericExtension("B"); REQUIRE(pA != nullptr); REQUIRE(pB != nullptr); @@ -303,11 +300,10 @@ TEST_CASE("Reads custom extension") { "B", CesiumJsonReader::ExtensionState::Disabled); - Cesium3DTilesReader::TilesetReaderResult withoutCustomExt = - reader.readTileset( - gsl::span(reinterpret_cast(s.c_str()), s.size())); + auto withoutCustomExt = reader.readFromJson( + gsl::span(reinterpret_cast(s.c_str()), s.size())); - auto& zeroExtensions = withoutCustomExt.tileset->extensions; + auto& zeroExtensions = withoutCustomExt.value->extensions; REQUIRE(zeroExtensions.empty()); } @@ -318,13 +314,13 @@ TEST_CASE("Reads tileset JSON with unknown properties") { tilesetFile /= "tileset-with-unsupported-properties.json"; std::vector data = readFile(tilesetFile); Cesium3DTilesReader::TilesetReader reader; - Cesium3DTilesReader::TilesetReaderResult result = reader.readTileset(data); + auto result = reader.readFromJson(data); CHECK(result.errors.empty()); CHECK(result.warnings.empty()); - REQUIRE(result.tileset); + REQUIRE(result.value); const CesiumUtility::JsonValue::Object& unknownProperties = - result.tileset->asset.unknownProperties; + result.value->asset.unknownProperties; auto itString = unknownProperties.find("someString"); REQUIRE(itString != unknownProperties.end()); @@ -383,12 +379,12 @@ TEST_CASE("Reads tileset JSON with unknown properties and ignores them when " std::vector data = readFile(tilesetFile); Cesium3DTilesReader::TilesetReader reader; reader.getOptions().setCaptureUnknownProperties(false); - Cesium3DTilesReader::TilesetReaderResult result = reader.readTileset(data); + auto result = reader.readFromJson(data); CHECK(result.errors.empty()); CHECK(result.warnings.empty()); - REQUIRE(result.tileset); + REQUIRE(result.value); const CesiumUtility::JsonValue::Object& unknownProperties = - result.tileset->asset.unknownProperties; + result.value->asset.unknownProperties; CHECK(unknownProperties.empty()); } diff --git a/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp b/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp index 282d8e1d7..6113bdda0 100644 --- a/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp +++ b/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp @@ -4,7 +4,9 @@ #include "ImplicitQuadtreeLoader.h" #include "logTileLoadResult.h" -#include +#include +#include +#include #include #include #include @@ -627,7 +629,8 @@ void parseTilesetMetadata( TileExternalContent& externalContent) { auto schemaIt = tilesetJson.FindMember("schema"); if (schemaIt != tilesetJson.MemberEnd()) { - auto schemaResult = Cesium3DTilesReader::readSchema(schemaIt->value); + Cesium3DTilesReader::SchemaReader schemaReader; + auto schemaResult = schemaReader.readFromJson(schemaIt->value); if (schemaResult.value) { externalContent.schema = std::move(*schemaResult.value); } @@ -640,8 +643,8 @@ void parseTilesetMetadata( const auto metadataIt = tilesetJson.FindMember("metadata"); if (metadataIt != tilesetJson.MemberEnd()) { - auto metadataResult = - Cesium3DTilesReader::readMetadataEntity(metadataIt->value); + Cesium3DTilesReader::MetadataEntityReader metadataReader; + auto metadataResult = metadataReader.readFromJson(metadataIt->value); if (metadataResult.value) { externalContent.metadata = std::move(*metadataResult.value); } @@ -649,8 +652,8 @@ void parseTilesetMetadata( const auto groupsIt = tilesetJson.FindMember("groups"); if (groupsIt != tilesetJson.MemberEnd()) { - auto groupsResult = - Cesium3DTilesReader::readGroupMetadataArray(groupsIt->value); + Cesium3DTilesReader::GroupMetadataReader groupMetadataReader; + auto groupsResult = groupMetadataReader.readArrayFromJson(groupsIt->value); if (groupsResult.value) { externalContent.groups = std::move(*groupsResult.value); } diff --git a/Cesium3DTilesWriter/test/TestTilesetWriter.cpp b/Cesium3DTilesWriter/test/TestTilesetWriter.cpp index f2213d3e8..5aa913f6f 100644 --- a/Cesium3DTilesWriter/test/TestTilesetWriter.cpp +++ b/Cesium3DTilesWriter/test/TestTilesetWriter.cpp @@ -11,15 +11,14 @@ namespace { void check(const std::string& input, const std::string& expectedOutput) { Cesium3DTilesReader::TilesetReader reader; - Cesium3DTilesReader::TilesetReaderResult readResult = - reader.readTileset(gsl::span( - reinterpret_cast(input.c_str()), - input.size())); + auto readResult = reader.readFromJson(gsl::span( + reinterpret_cast(input.c_str()), + input.size())); REQUIRE(readResult.errors.empty()); REQUIRE(readResult.warnings.empty()); - REQUIRE(readResult.tileset.has_value()); + REQUIRE(readResult.value.has_value()); - Cesium3DTiles::Tileset& tileset = readResult.tileset.value(); + Cesium3DTiles::Tileset& tileset = readResult.value.value(); Cesium3DTilesWriter::TilesetWriter writer; Cesium3DTilesWriter::TilesetWriterResult writeResult = diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/AccessorReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/AccessorReader.h new file mode 100644 index 000000000..03e48111d --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/AccessorReader.h @@ -0,0 +1,69 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct Accessor; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API AccessorReader { +public: + /** + * @brief Constructs a new instance. + */ + AccessorReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Accessor from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Accessor from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Accessor from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/AccessorSparseIndicesReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/AccessorSparseIndicesReader.h new file mode 100644 index 000000000..f7cda6278 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/AccessorSparseIndicesReader.h @@ -0,0 +1,71 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct AccessorSparseIndices; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API AccessorSparseIndicesReader { +public: + /** + * @brief Constructs a new instance. + */ + AccessorSparseIndicesReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of AccessorSparseIndices from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of AccessorSparseIndices from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of AccessorSparseIndices from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/AccessorSparseReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/AccessorSparseReader.h new file mode 100644 index 000000000..3c80ae661 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/AccessorSparseReader.h @@ -0,0 +1,70 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct AccessorSparse; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API AccessorSparseReader { +public: + /** + * @brief Constructs a new instance. + */ + AccessorSparseReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of AccessorSparse from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of AccessorSparse from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of AccessorSparse from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/AccessorSparseValuesReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/AccessorSparseValuesReader.h new file mode 100644 index 000000000..5fa6060a4 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/AccessorSparseValuesReader.h @@ -0,0 +1,71 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct AccessorSparseValues; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API AccessorSparseValuesReader { +public: + /** + * @brief Constructs a new instance. + */ + AccessorSparseValuesReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of AccessorSparseValues from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of AccessorSparseValues from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of AccessorSparseValues from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/AnimationChannelReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/AnimationChannelReader.h new file mode 100644 index 000000000..7a7eba6c9 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/AnimationChannelReader.h @@ -0,0 +1,70 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct AnimationChannel; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API AnimationChannelReader { +public: + /** + * @brief Constructs a new instance. + */ + AnimationChannelReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of AnimationChannel from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of AnimationChannel from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of AnimationChannel from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/AnimationChannelTargetReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/AnimationChannelTargetReader.h new file mode 100644 index 000000000..71a7e60f6 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/AnimationChannelTargetReader.h @@ -0,0 +1,71 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct AnimationChannelTarget; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API AnimationChannelTargetReader { +public: + /** + * @brief Constructs a new instance. + */ + AnimationChannelTargetReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of AnimationChannelTarget from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of AnimationChannelTarget from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of AnimationChannelTarget from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/AnimationReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/AnimationReader.h new file mode 100644 index 000000000..2079961e3 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/AnimationReader.h @@ -0,0 +1,69 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct Animation; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API AnimationReader { +public: + /** + * @brief Constructs a new instance. + */ + AnimationReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Animation from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Animation from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Animation from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/AnimationSamplerReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/AnimationSamplerReader.h new file mode 100644 index 000000000..0be957233 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/AnimationSamplerReader.h @@ -0,0 +1,70 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct AnimationSampler; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API AnimationSamplerReader { +public: + /** + * @brief Constructs a new instance. + */ + AnimationSamplerReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of AnimationSampler from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of AnimationSampler from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of AnimationSampler from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/AssetReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/AssetReader.h new file mode 100644 index 000000000..b44103a8d --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/AssetReader.h @@ -0,0 +1,69 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct Asset; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API AssetReader { +public: + /** + * @brief Constructs a new instance. + */ + AssetReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Asset from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Asset from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Asset from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/BufferReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/BufferReader.h new file mode 100644 index 000000000..226c3b582 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/BufferReader.h @@ -0,0 +1,69 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct Buffer; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API BufferReader { +public: + /** + * @brief Constructs a new instance. + */ + BufferReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Buffer from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Buffer from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Buffer from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/BufferViewReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/BufferViewReader.h new file mode 100644 index 000000000..494e1cd8f --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/BufferViewReader.h @@ -0,0 +1,69 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct BufferView; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API BufferViewReader { +public: + /** + * @brief Constructs a new instance. + */ + BufferViewReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of BufferView from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of BufferView from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of BufferView from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/CameraOrthographicReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/CameraOrthographicReader.h new file mode 100644 index 000000000..f7fb6c42f --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/CameraOrthographicReader.h @@ -0,0 +1,70 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct CameraOrthographic; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API CameraOrthographicReader { +public: + /** + * @brief Constructs a new instance. + */ + CameraOrthographicReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of CameraOrthographic from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of CameraOrthographic from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of CameraOrthographic from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/CameraPerspectiveReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/CameraPerspectiveReader.h new file mode 100644 index 000000000..384a8fa2d --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/CameraPerspectiveReader.h @@ -0,0 +1,70 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct CameraPerspective; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API CameraPerspectiveReader { +public: + /** + * @brief Constructs a new instance. + */ + CameraPerspectiveReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of CameraPerspective from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of CameraPerspective from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of CameraPerspective from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/CameraReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/CameraReader.h new file mode 100644 index 000000000..a98f5d62c --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/CameraReader.h @@ -0,0 +1,69 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct Camera; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API CameraReader { +public: + /** + * @brief Constructs a new instance. + */ + CameraReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Camera from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Camera from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Camera from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ClassPropertyReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ClassPropertyReader.h new file mode 100644 index 000000000..7e7b8eb1b --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ClassPropertyReader.h @@ -0,0 +1,70 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ClassProperty; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API ClassPropertyReader { +public: + /** + * @brief Constructs a new instance. + */ + ClassPropertyReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ClassProperty from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ClassProperty from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of ClassProperty from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ClassReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ClassReader.h new file mode 100644 index 000000000..06063a254 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ClassReader.h @@ -0,0 +1,69 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct Class; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API ClassReader { +public: + /** + * @brief Constructs a new instance. + */ + ClassReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Class from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Class from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Class from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ClassStatisticsReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ClassStatisticsReader.h new file mode 100644 index 000000000..c28d44c67 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ClassStatisticsReader.h @@ -0,0 +1,70 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ClassStatistics; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API ClassStatisticsReader { +public: + /** + * @brief Constructs a new instance. + */ + ClassStatisticsReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ClassStatistics from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ClassStatistics from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of ClassStatistics from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/EnumReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/EnumReader.h new file mode 100644 index 000000000..f2b7c1b02 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/EnumReader.h @@ -0,0 +1,69 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct Enum; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API EnumReader { +public: + /** + * @brief Constructs a new instance. + */ + EnumReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Enum from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Enum from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Enum from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/EnumValueReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/EnumValueReader.h new file mode 100644 index 000000000..e8d587abe --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/EnumValueReader.h @@ -0,0 +1,69 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct EnumValue; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API EnumValueReader { +public: + /** + * @brief Constructs a new instance. + */ + EnumValueReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of EnumValue from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of EnumValue from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of EnumValue from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionBufferExtMeshoptCompressionReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionBufferExtMeshoptCompressionReader.h new file mode 100644 index 000000000..c1078ed5e --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionBufferExtMeshoptCompressionReader.h @@ -0,0 +1,75 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionBufferExtMeshoptCompression; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API ExtensionBufferExtMeshoptCompressionReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionBufferExtMeshoptCompressionReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionBufferExtMeshoptCompression from a + * byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionBufferExtMeshoptCompression> + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionBufferExtMeshoptCompression from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionBufferExtMeshoptCompression> + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of ExtensionBufferExtMeshoptCompression + * from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionBufferViewExtMeshoptCompressionReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionBufferViewExtMeshoptCompressionReader.h new file mode 100644 index 000000000..9e8592426 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionBufferViewExtMeshoptCompressionReader.h @@ -0,0 +1,75 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionBufferViewExtMeshoptCompression; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API ExtensionBufferViewExtMeshoptCompressionReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionBufferViewExtMeshoptCompressionReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionBufferViewExtMeshoptCompression from a + * byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionBufferViewExtMeshoptCompression> + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionBufferViewExtMeshoptCompression from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionBufferViewExtMeshoptCompression> + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of + * ExtensionBufferViewExtMeshoptCompression from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionCesiumRTCReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionCesiumRTCReader.h new file mode 100644 index 000000000..369bba9cb --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionCesiumRTCReader.h @@ -0,0 +1,70 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionCesiumRTC; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API ExtensionCesiumRTCReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionCesiumRTCReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionCesiumRTC from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionCesiumRTC from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of ExtensionCesiumRTC from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionCesiumTileEdgesReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionCesiumTileEdgesReader.h new file mode 100644 index 000000000..089890b00 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionCesiumTileEdgesReader.h @@ -0,0 +1,72 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionCesiumTileEdges; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API ExtensionCesiumTileEdgesReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionCesiumTileEdgesReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionCesiumTileEdges from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionCesiumTileEdges from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of ExtensionCesiumTileEdges from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtInstanceFeaturesFeatureIdReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtInstanceFeaturesFeatureIdReader.h new file mode 100644 index 000000000..96beedab1 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtInstanceFeaturesFeatureIdReader.h @@ -0,0 +1,75 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionExtInstanceFeaturesFeatureId; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API ExtensionExtInstanceFeaturesFeatureIdReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionExtInstanceFeaturesFeatureIdReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionExtInstanceFeaturesFeatureId from a + * byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtInstanceFeaturesFeatureId> + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionExtInstanceFeaturesFeatureId from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtInstanceFeaturesFeatureId> + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of ExtensionExtInstanceFeaturesFeatureId + * from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtInstanceFeaturesReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtInstanceFeaturesReader.h new file mode 100644 index 000000000..43763102c --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtInstanceFeaturesReader.h @@ -0,0 +1,73 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionExtInstanceFeatures; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API ExtensionExtInstanceFeaturesReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionExtInstanceFeaturesReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionExtInstanceFeatures from a byte + * buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionExtInstanceFeatures from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of ExtensionExtInstanceFeatures from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtMeshFeaturesFeatureIdReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtMeshFeaturesFeatureIdReader.h new file mode 100644 index 000000000..5bc9b0443 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtMeshFeaturesFeatureIdReader.h @@ -0,0 +1,75 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionExtMeshFeaturesFeatureId; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API ExtensionExtMeshFeaturesFeatureIdReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionExtMeshFeaturesFeatureIdReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionExtMeshFeaturesFeatureId from a byte + * buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtMeshFeaturesFeatureId> + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionExtMeshFeaturesFeatureId from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtMeshFeaturesFeatureId> + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of ExtensionExtMeshFeaturesFeatureId + * from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtMeshFeaturesFeatureIdTextureReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtMeshFeaturesFeatureIdTextureReader.h new file mode 100644 index 000000000..273ae1a0a --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtMeshFeaturesFeatureIdTextureReader.h @@ -0,0 +1,75 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionExtMeshFeaturesFeatureIdTexture; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API ExtensionExtMeshFeaturesFeatureIdTextureReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionExtMeshFeaturesFeatureIdTextureReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionExtMeshFeaturesFeatureIdTexture from a + * byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtMeshFeaturesFeatureIdTexture> + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionExtMeshFeaturesFeatureIdTexture from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtMeshFeaturesFeatureIdTexture> + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of + * ExtensionExtMeshFeaturesFeatureIdTexture from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtMeshFeaturesReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtMeshFeaturesReader.h new file mode 100644 index 000000000..55dd544d2 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtMeshFeaturesReader.h @@ -0,0 +1,72 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionExtMeshFeatures; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API ExtensionExtMeshFeaturesReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionExtMeshFeaturesReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionExtMeshFeatures from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionExtMeshFeatures from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of ExtensionExtMeshFeatures from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtMeshGpuInstancingReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtMeshGpuInstancingReader.h new file mode 100644 index 000000000..f7d26c075 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtMeshGpuInstancingReader.h @@ -0,0 +1,73 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionExtMeshGpuInstancing; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API ExtensionExtMeshGpuInstancingReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionExtMeshGpuInstancingReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionExtMeshGpuInstancing from a byte + * buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionExtMeshGpuInstancing from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of ExtensionExtMeshGpuInstancing from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataClassPropertyReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataClassPropertyReader.h new file mode 100644 index 000000000..dacf03a40 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataClassPropertyReader.h @@ -0,0 +1,75 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionExtStructuralMetadataClassProperty; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API ExtensionExtStructuralMetadataClassPropertyReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionExtStructuralMetadataClassPropertyReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionExtStructuralMetadataClassProperty + * from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataClassProperty> + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionExtStructuralMetadataClassProperty + * from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataClassProperty> + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of + * ExtensionExtStructuralMetadataClassProperty from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataClassReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataClassReader.h new file mode 100644 index 000000000..75c1a2dc2 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataClassReader.h @@ -0,0 +1,75 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionExtStructuralMetadataClass; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API ExtensionExtStructuralMetadataClassReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionExtStructuralMetadataClassReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionExtStructuralMetadataClass from a byte + * buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataClass> + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionExtStructuralMetadataClass from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataClass> + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of ExtensionExtStructuralMetadataClass + * from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataEnumReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataEnumReader.h new file mode 100644 index 000000000..4eb19c05a --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataEnumReader.h @@ -0,0 +1,75 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionExtStructuralMetadataEnum; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API ExtensionExtStructuralMetadataEnumReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionExtStructuralMetadataEnumReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionExtStructuralMetadataEnum from a byte + * buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataEnum> + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionExtStructuralMetadataEnum from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataEnum> + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of ExtensionExtStructuralMetadataEnum + * from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataEnumValueReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataEnumValueReader.h new file mode 100644 index 000000000..cb261c358 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataEnumValueReader.h @@ -0,0 +1,75 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionExtStructuralMetadataEnumValue; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API ExtensionExtStructuralMetadataEnumValueReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionExtStructuralMetadataEnumValueReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionExtStructuralMetadataEnumValue from a + * byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataEnumValue> + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionExtStructuralMetadataEnumValue from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataEnumValue> + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of + * ExtensionExtStructuralMetadataEnumValue from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyAttributePropertyReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyAttributePropertyReader.h new file mode 100644 index 000000000..9c339b405 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyAttributePropertyReader.h @@ -0,0 +1,78 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionExtStructuralMetadataPropertyAttributeProperty; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API + ExtensionExtStructuralMetadataPropertyAttributePropertyReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionExtStructuralMetadataPropertyAttributePropertyReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of + * ExtensionExtStructuralMetadataPropertyAttributeProperty from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataPropertyAttributeProperty> + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of + * ExtensionExtStructuralMetadataPropertyAttributeProperty from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataPropertyAttributeProperty> + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of + * ExtensionExtStructuralMetadataPropertyAttributeProperty from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyAttributeReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyAttributeReader.h new file mode 100644 index 000000000..4bc012c7b --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyAttributeReader.h @@ -0,0 +1,76 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionExtStructuralMetadataPropertyAttribute; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API + ExtensionExtStructuralMetadataPropertyAttributeReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionExtStructuralMetadataPropertyAttributeReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionExtStructuralMetadataPropertyAttribute + * from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataPropertyAttribute> + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionExtStructuralMetadataPropertyAttribute + * from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataPropertyAttribute> + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of + * ExtensionExtStructuralMetadataPropertyAttribute from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyTablePropertyReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyTablePropertyReader.h new file mode 100644 index 000000000..fb3a8dec9 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyTablePropertyReader.h @@ -0,0 +1,78 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionExtStructuralMetadataPropertyTableProperty; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API + ExtensionExtStructuralMetadataPropertyTablePropertyReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionExtStructuralMetadataPropertyTablePropertyReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of + * ExtensionExtStructuralMetadataPropertyTableProperty from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataPropertyTableProperty> + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of + * ExtensionExtStructuralMetadataPropertyTableProperty from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataPropertyTableProperty> + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of + * ExtensionExtStructuralMetadataPropertyTableProperty from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyTableReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyTableReader.h new file mode 100644 index 000000000..0f013603d --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyTableReader.h @@ -0,0 +1,75 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionExtStructuralMetadataPropertyTable; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API ExtensionExtStructuralMetadataPropertyTableReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionExtStructuralMetadataPropertyTableReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionExtStructuralMetadataPropertyTable + * from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataPropertyTable> + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionExtStructuralMetadataPropertyTable + * from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataPropertyTable> + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of + * ExtensionExtStructuralMetadataPropertyTable from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyTexturePropertyReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyTexturePropertyReader.h new file mode 100644 index 000000000..49d864d3f --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyTexturePropertyReader.h @@ -0,0 +1,78 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionExtStructuralMetadataPropertyTextureProperty; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API + ExtensionExtStructuralMetadataPropertyTexturePropertyReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionExtStructuralMetadataPropertyTexturePropertyReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of + * ExtensionExtStructuralMetadataPropertyTextureProperty from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataPropertyTextureProperty> + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of + * ExtensionExtStructuralMetadataPropertyTextureProperty from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataPropertyTextureProperty> + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of + * ExtensionExtStructuralMetadataPropertyTextureProperty from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyTextureReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyTextureReader.h new file mode 100644 index 000000000..0fb2e4a06 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyTextureReader.h @@ -0,0 +1,75 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionExtStructuralMetadataPropertyTexture; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API ExtensionExtStructuralMetadataPropertyTextureReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionExtStructuralMetadataPropertyTextureReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionExtStructuralMetadataPropertyTexture + * from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataPropertyTexture> + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionExtStructuralMetadataPropertyTexture + * from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataPropertyTexture> + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of + * ExtensionExtStructuralMetadataPropertyTexture from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataSchemaReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataSchemaReader.h new file mode 100644 index 000000000..54d022826 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataSchemaReader.h @@ -0,0 +1,75 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionExtStructuralMetadataSchema; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API ExtensionExtStructuralMetadataSchemaReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionExtStructuralMetadataSchemaReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionExtStructuralMetadataSchema from a + * byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataSchema> + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionExtStructuralMetadataSchema from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataSchema> + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of ExtensionExtStructuralMetadataSchema + * from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionKhrDracoMeshCompressionReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionKhrDracoMeshCompressionReader.h new file mode 100644 index 000000000..d798d8154 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionKhrDracoMeshCompressionReader.h @@ -0,0 +1,73 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionKhrDracoMeshCompression; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API ExtensionKhrDracoMeshCompressionReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionKhrDracoMeshCompressionReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionKhrDracoMeshCompression from a byte + * buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionKhrDracoMeshCompression from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of ExtensionKhrDracoMeshCompression from + * a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionKhrMaterialsUnlitReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionKhrMaterialsUnlitReader.h new file mode 100644 index 000000000..643c529d8 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionKhrMaterialsUnlitReader.h @@ -0,0 +1,72 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionKhrMaterialsUnlit; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API ExtensionKhrMaterialsUnlitReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionKhrMaterialsUnlitReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionKhrMaterialsUnlit from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionKhrMaterialsUnlit from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of ExtensionKhrMaterialsUnlit from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionKhrTextureBasisuReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionKhrTextureBasisuReader.h new file mode 100644 index 000000000..c5dcb9ac2 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionKhrTextureBasisuReader.h @@ -0,0 +1,72 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionKhrTextureBasisu; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API ExtensionKhrTextureBasisuReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionKhrTextureBasisuReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionKhrTextureBasisu from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionKhrTextureBasisu from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of ExtensionKhrTextureBasisu from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionKhrTextureTransformReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionKhrTextureTransformReader.h new file mode 100644 index 000000000..90ba7cdf0 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionKhrTextureTransformReader.h @@ -0,0 +1,73 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionKhrTextureTransform; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API ExtensionKhrTextureTransformReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionKhrTextureTransformReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionKhrTextureTransform from a byte + * buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionKhrTextureTransform from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of ExtensionKhrTextureTransform from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionMeshPrimitiveExtFeatureMetadataReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionMeshPrimitiveExtFeatureMetadataReader.h new file mode 100644 index 000000000..465f37e5c --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionMeshPrimitiveExtFeatureMetadataReader.h @@ -0,0 +1,75 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionMeshPrimitiveExtFeatureMetadata; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API ExtensionMeshPrimitiveExtFeatureMetadataReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionMeshPrimitiveExtFeatureMetadataReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionMeshPrimitiveExtFeatureMetadata from a + * byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionMeshPrimitiveExtFeatureMetadata> + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionMeshPrimitiveExtFeatureMetadata from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionMeshPrimitiveExtFeatureMetadata> + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of + * ExtensionMeshPrimitiveExtFeatureMetadata from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionMeshPrimitiveExtStructuralMetadataReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionMeshPrimitiveExtStructuralMetadataReader.h new file mode 100644 index 000000000..328005207 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionMeshPrimitiveExtStructuralMetadataReader.h @@ -0,0 +1,75 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionMeshPrimitiveExtStructuralMetadata; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API ExtensionMeshPrimitiveExtStructuralMetadataReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionMeshPrimitiveExtStructuralMetadataReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionMeshPrimitiveExtStructuralMetadata + * from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionMeshPrimitiveExtStructuralMetadata> + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionMeshPrimitiveExtStructuralMetadata + * from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionMeshPrimitiveExtStructuralMetadata> + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of + * ExtensionMeshPrimitiveExtStructuralMetadata from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueReader.h new file mode 100644 index 000000000..9ebfc2493 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueReader.h @@ -0,0 +1,78 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValue; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API + ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of + * ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValue from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValue> + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of + * ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValue from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValue> + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of + * ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValue from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionMeshPrimitiveKhrMaterialsVariantsReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionMeshPrimitiveKhrMaterialsVariantsReader.h new file mode 100644 index 000000000..817f9d444 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionMeshPrimitiveKhrMaterialsVariantsReader.h @@ -0,0 +1,75 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionMeshPrimitiveKhrMaterialsVariants; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API ExtensionMeshPrimitiveKhrMaterialsVariantsReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionMeshPrimitiveKhrMaterialsVariantsReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionMeshPrimitiveKhrMaterialsVariants from + * a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionMeshPrimitiveKhrMaterialsVariants> + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionMeshPrimitiveKhrMaterialsVariants from + * a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionMeshPrimitiveKhrMaterialsVariants> + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of + * ExtensionMeshPrimitiveKhrMaterialsVariants from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelExtFeatureMetadataReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelExtFeatureMetadataReader.h new file mode 100644 index 000000000..cfdda0e1c --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelExtFeatureMetadataReader.h @@ -0,0 +1,73 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionModelExtFeatureMetadata; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API ExtensionModelExtFeatureMetadataReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionModelExtFeatureMetadataReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionModelExtFeatureMetadata from a byte + * buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionModelExtFeatureMetadata from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of ExtensionModelExtFeatureMetadata from + * a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelExtStructuralMetadataReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelExtStructuralMetadataReader.h new file mode 100644 index 000000000..8cb1c1d85 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelExtStructuralMetadataReader.h @@ -0,0 +1,75 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionModelExtStructuralMetadata; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API ExtensionModelExtStructuralMetadataReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionModelExtStructuralMetadataReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionModelExtStructuralMetadata from a byte + * buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionModelExtStructuralMetadata> + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionModelExtStructuralMetadata from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionModelExtStructuralMetadata> + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of ExtensionModelExtStructuralMetadata + * from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelKhrMaterialsVariantsReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelKhrMaterialsVariantsReader.h new file mode 100644 index 000000000..3d04a7e16 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelKhrMaterialsVariantsReader.h @@ -0,0 +1,75 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionModelKhrMaterialsVariants; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API ExtensionModelKhrMaterialsVariantsReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionModelKhrMaterialsVariantsReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionModelKhrMaterialsVariants from a byte + * buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionModelKhrMaterialsVariants> + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionModelKhrMaterialsVariants from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionModelKhrMaterialsVariants> + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of ExtensionModelKhrMaterialsVariants + * from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelKhrMaterialsVariantsValueReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelKhrMaterialsVariantsValueReader.h new file mode 100644 index 000000000..bd13a86da --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelKhrMaterialsVariantsValueReader.h @@ -0,0 +1,75 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionModelKhrMaterialsVariantsValue; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API ExtensionModelKhrMaterialsVariantsValueReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionModelKhrMaterialsVariantsValueReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionModelKhrMaterialsVariantsValue from a + * byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionModelKhrMaterialsVariantsValue> + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionModelKhrMaterialsVariantsValue from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionModelKhrMaterialsVariantsValue> + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of + * ExtensionModelKhrMaterialsVariantsValue from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelMaxarMeshVariantsReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelMaxarMeshVariantsReader.h new file mode 100644 index 000000000..5872bb206 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelMaxarMeshVariantsReader.h @@ -0,0 +1,73 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionModelMaxarMeshVariants; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API ExtensionModelMaxarMeshVariantsReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionModelMaxarMeshVariantsReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionModelMaxarMeshVariants from a byte + * buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionModelMaxarMeshVariants from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of ExtensionModelMaxarMeshVariants from + * a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelMaxarMeshVariantsValueReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelMaxarMeshVariantsValueReader.h new file mode 100644 index 000000000..2baa15b55 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelMaxarMeshVariantsValueReader.h @@ -0,0 +1,75 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionModelMaxarMeshVariantsValue; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API ExtensionModelMaxarMeshVariantsValueReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionModelMaxarMeshVariantsValueReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionModelMaxarMeshVariantsValue from a + * byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionModelMaxarMeshVariantsValue> + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionModelMaxarMeshVariantsValue from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionModelMaxarMeshVariantsValue> + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of ExtensionModelMaxarMeshVariantsValue + * from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionNodeMaxarMeshVariantsMappingsValueReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionNodeMaxarMeshVariantsMappingsValueReader.h new file mode 100644 index 000000000..14d3aac22 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionNodeMaxarMeshVariantsMappingsValueReader.h @@ -0,0 +1,75 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionNodeMaxarMeshVariantsMappingsValue; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API ExtensionNodeMaxarMeshVariantsMappingsValueReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionNodeMaxarMeshVariantsMappingsValueReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionNodeMaxarMeshVariantsMappingsValue + * from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionNodeMaxarMeshVariantsMappingsValue> + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionNodeMaxarMeshVariantsMappingsValue + * from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionNodeMaxarMeshVariantsMappingsValue> + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of + * ExtensionNodeMaxarMeshVariantsMappingsValue from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionNodeMaxarMeshVariantsReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionNodeMaxarMeshVariantsReader.h new file mode 100644 index 000000000..dbe3a8b5b --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionNodeMaxarMeshVariantsReader.h @@ -0,0 +1,73 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionNodeMaxarMeshVariants; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API ExtensionNodeMaxarMeshVariantsReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionNodeMaxarMeshVariantsReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionNodeMaxarMeshVariants from a byte + * buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionNodeMaxarMeshVariants from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of ExtensionNodeMaxarMeshVariants from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionTextureWebpReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionTextureWebpReader.h new file mode 100644 index 000000000..0a3abc8b4 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionTextureWebpReader.h @@ -0,0 +1,71 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionTextureWebp; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API ExtensionTextureWebpReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionTextureWebpReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionTextureWebp from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionTextureWebp from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of ExtensionTextureWebp from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureIDAttributeReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureIDAttributeReader.h new file mode 100644 index 000000000..0cb2ba4f9 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureIDAttributeReader.h @@ -0,0 +1,70 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct FeatureIDAttribute; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API FeatureIDAttributeReader { +public: + /** + * @brief Constructs a new instance. + */ + FeatureIDAttributeReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of FeatureIDAttribute from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of FeatureIDAttribute from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of FeatureIDAttribute from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureIDTextureReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureIDTextureReader.h new file mode 100644 index 000000000..fdea6446c --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureIDTextureReader.h @@ -0,0 +1,70 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct FeatureIDTexture; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API FeatureIDTextureReader { +public: + /** + * @brief Constructs a new instance. + */ + FeatureIDTextureReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of FeatureIDTexture from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of FeatureIDTexture from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of FeatureIDTexture from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureIDsReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureIDsReader.h new file mode 100644 index 000000000..0d14eb1bd --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureIDsReader.h @@ -0,0 +1,69 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct FeatureIDs; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API FeatureIDsReader { +public: + /** + * @brief Constructs a new instance. + */ + FeatureIDsReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of FeatureIDs from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of FeatureIDs from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of FeatureIDs from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureTablePropertyReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureTablePropertyReader.h new file mode 100644 index 000000000..1ec56faf0 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureTablePropertyReader.h @@ -0,0 +1,71 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct FeatureTableProperty; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API FeatureTablePropertyReader { +public: + /** + * @brief Constructs a new instance. + */ + FeatureTablePropertyReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of FeatureTableProperty from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of FeatureTableProperty from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of FeatureTableProperty from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureTableReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureTableReader.h new file mode 100644 index 000000000..5773ae465 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureTableReader.h @@ -0,0 +1,69 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct FeatureTable; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API FeatureTableReader { +public: + /** + * @brief Constructs a new instance. + */ + FeatureTableReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of FeatureTable from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of FeatureTable from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of FeatureTable from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureTextureReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureTextureReader.h new file mode 100644 index 000000000..5d36807d4 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureTextureReader.h @@ -0,0 +1,70 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct FeatureTexture; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API FeatureTextureReader { +public: + /** + * @brief Constructs a new instance. + */ + FeatureTextureReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of FeatureTexture from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of FeatureTexture from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of FeatureTexture from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ImageReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ImageReader.h new file mode 100644 index 000000000..8772a1f15 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ImageReader.h @@ -0,0 +1,69 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct Image; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API ImageReader { +public: + /** + * @brief Constructs a new instance. + */ + ImageReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Image from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Image from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Image from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/MaterialNormalTextureInfoReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/MaterialNormalTextureInfoReader.h new file mode 100644 index 000000000..199facc46 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/MaterialNormalTextureInfoReader.h @@ -0,0 +1,72 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct MaterialNormalTextureInfo; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API MaterialNormalTextureInfoReader { +public: + /** + * @brief Constructs a new instance. + */ + MaterialNormalTextureInfoReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of MaterialNormalTextureInfo from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of MaterialNormalTextureInfo from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of MaterialNormalTextureInfo from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/MaterialOcclusionTextureInfoReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/MaterialOcclusionTextureInfoReader.h new file mode 100644 index 000000000..26bf4786a --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/MaterialOcclusionTextureInfoReader.h @@ -0,0 +1,73 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct MaterialOcclusionTextureInfo; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API MaterialOcclusionTextureInfoReader { +public: + /** + * @brief Constructs a new instance. + */ + MaterialOcclusionTextureInfoReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of MaterialOcclusionTextureInfo from a byte + * buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of MaterialOcclusionTextureInfo from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of MaterialOcclusionTextureInfo from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/MaterialPBRMetallicRoughnessReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/MaterialPBRMetallicRoughnessReader.h new file mode 100644 index 000000000..33226b68f --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/MaterialPBRMetallicRoughnessReader.h @@ -0,0 +1,73 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct MaterialPBRMetallicRoughness; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API MaterialPBRMetallicRoughnessReader { +public: + /** + * @brief Constructs a new instance. + */ + MaterialPBRMetallicRoughnessReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of MaterialPBRMetallicRoughness from a byte + * buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of MaterialPBRMetallicRoughness from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of MaterialPBRMetallicRoughness from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/MaterialReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/MaterialReader.h new file mode 100644 index 000000000..9f4b896f0 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/MaterialReader.h @@ -0,0 +1,69 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct Material; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API MaterialReader { +public: + /** + * @brief Constructs a new instance. + */ + MaterialReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Material from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Material from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Material from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/MeshPrimitiveReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/MeshPrimitiveReader.h new file mode 100644 index 000000000..84298f0e6 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/MeshPrimitiveReader.h @@ -0,0 +1,70 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct MeshPrimitive; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API MeshPrimitiveReader { +public: + /** + * @brief Constructs a new instance. + */ + MeshPrimitiveReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of MeshPrimitive from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of MeshPrimitive from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of MeshPrimitive from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/MeshReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/MeshReader.h new file mode 100644 index 000000000..a70941373 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/MeshReader.h @@ -0,0 +1,69 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct Mesh; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API MeshReader { +public: + /** + * @brief Constructs a new instance. + */ + MeshReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Mesh from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Mesh from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Mesh from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ModelReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ModelReader.h new file mode 100644 index 000000000..385afea2d --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ModelReader.h @@ -0,0 +1,69 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct Model; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API ModelReader { +public: + /** + * @brief Constructs a new instance. + */ + ModelReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Model from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Model from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Model from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/NodeReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/NodeReader.h new file mode 100644 index 000000000..6091f0ec4 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/NodeReader.h @@ -0,0 +1,69 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct Node; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API NodeReader { +public: + /** + * @brief Constructs a new instance. + */ + NodeReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Node from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Node from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Node from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/PropertyStatisticsReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/PropertyStatisticsReader.h new file mode 100644 index 000000000..ced8713f2 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/PropertyStatisticsReader.h @@ -0,0 +1,70 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct PropertyStatistics; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API PropertyStatisticsReader { +public: + /** + * @brief Constructs a new instance. + */ + PropertyStatisticsReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of PropertyStatistics from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of PropertyStatistics from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of PropertyStatistics from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/SamplerReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/SamplerReader.h new file mode 100644 index 000000000..b551ffd7d --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/SamplerReader.h @@ -0,0 +1,69 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct Sampler; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API SamplerReader { +public: + /** + * @brief Constructs a new instance. + */ + SamplerReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Sampler from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Sampler from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Sampler from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/SceneReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/SceneReader.h new file mode 100644 index 000000000..fe65364d2 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/SceneReader.h @@ -0,0 +1,69 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct Scene; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API SceneReader { +public: + /** + * @brief Constructs a new instance. + */ + SceneReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Scene from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Scene from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Scene from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/SchemaReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/SchemaReader.h new file mode 100644 index 000000000..1e3daec0d --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/SchemaReader.h @@ -0,0 +1,69 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct Schema; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API SchemaReader { +public: + /** + * @brief Constructs a new instance. + */ + SchemaReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Schema from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Schema from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Schema from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/SkinReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/SkinReader.h new file mode 100644 index 000000000..2fd1ce417 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/SkinReader.h @@ -0,0 +1,69 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct Skin; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API SkinReader { +public: + /** + * @brief Constructs a new instance. + */ + SkinReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Skin from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Skin from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Skin from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/StatisticsReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/StatisticsReader.h new file mode 100644 index 000000000..a410d4763 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/StatisticsReader.h @@ -0,0 +1,69 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct Statistics; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API StatisticsReader { +public: + /** + * @brief Constructs a new instance. + */ + StatisticsReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Statistics from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Statistics from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Statistics from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/TextureAccessorReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/TextureAccessorReader.h new file mode 100644 index 000000000..0026b9b33 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/TextureAccessorReader.h @@ -0,0 +1,70 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct TextureAccessor; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API TextureAccessorReader { +public: + /** + * @brief Constructs a new instance. + */ + TextureAccessorReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of TextureAccessor from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of TextureAccessor from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of TextureAccessor from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/TextureInfoReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/TextureInfoReader.h new file mode 100644 index 000000000..5ac9e133e --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/TextureInfoReader.h @@ -0,0 +1,69 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct TextureInfo; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API TextureInfoReader { +public: + /** + * @brief Constructs a new instance. + */ + TextureInfoReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of TextureInfo from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of TextureInfo from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of TextureInfo from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/TextureReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/TextureReader.h new file mode 100644 index 000000000..e625b3c60 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/TextureReader.h @@ -0,0 +1,69 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct Texture; +} + +namespace CesiumGltfReader { + +class CESIUMGLTFREADER_API TextureReader { +public: + /** + * @brief Constructs a new instance. + */ + TextureReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Texture from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Texture from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Texture from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/src/ExtensionBufferExtMeshoptCompressionJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionBufferExtMeshoptCompressionJsonHandler.h index 7c4871195..5bed9a46e 100644 --- a/CesiumGltfReader/generated/src/ExtensionBufferExtMeshoptCompressionJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionBufferExtMeshoptCompressionJsonHandler.h @@ -32,50 +32,7 @@ class ExtensionBufferExtMeshoptCompressionJsonHandler CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) override; - virtual IJsonHandler* readNull() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readNull(); - }; - virtual IJsonHandler* readBool(bool b) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readBool(b); - } - virtual IJsonHandler* readInt32(int32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt32(i); - } - virtual IJsonHandler* readUint32(uint32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint32(i); - } - virtual IJsonHandler* readInt64(int64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt64(i); - } - virtual IJsonHandler* readUint64(uint64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint64(i); - } - virtual IJsonHandler* readDouble(double d) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readDouble(d); - } - virtual IJsonHandler* readString(const std::string_view& str) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readString(str); - } - virtual IJsonHandler* readObjectStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectStart(); - } - virtual IJsonHandler* readObjectEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectEnd(); - } - virtual IJsonHandler* readArrayStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayStart(); - } - virtual IJsonHandler* readArrayEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayEnd(); - } - virtual void reportWarning( - const std::string& warning, - std::vector&& context = - std::vector()) override { - CesiumJsonReader::ExtensibleObjectJsonHandler::reportWarning( - warning, - std::move(context)); - } + virtual IJsonHandler& getHandler() override { return *this; } protected: IJsonHandler* readObjectKeyExtensionBufferExtMeshoptCompression( diff --git a/CesiumGltfReader/generated/src/ExtensionBufferViewExtMeshoptCompressionJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionBufferViewExtMeshoptCompressionJsonHandler.h index c8783361e..775513949 100644 --- a/CesiumGltfReader/generated/src/ExtensionBufferViewExtMeshoptCompressionJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionBufferViewExtMeshoptCompressionJsonHandler.h @@ -33,50 +33,7 @@ class ExtensionBufferViewExtMeshoptCompressionJsonHandler CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) override; - virtual IJsonHandler* readNull() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readNull(); - }; - virtual IJsonHandler* readBool(bool b) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readBool(b); - } - virtual IJsonHandler* readInt32(int32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt32(i); - } - virtual IJsonHandler* readUint32(uint32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint32(i); - } - virtual IJsonHandler* readInt64(int64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt64(i); - } - virtual IJsonHandler* readUint64(uint64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint64(i); - } - virtual IJsonHandler* readDouble(double d) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readDouble(d); - } - virtual IJsonHandler* readString(const std::string_view& str) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readString(str); - } - virtual IJsonHandler* readObjectStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectStart(); - } - virtual IJsonHandler* readObjectEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectEnd(); - } - virtual IJsonHandler* readArrayStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayStart(); - } - virtual IJsonHandler* readArrayEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayEnd(); - } - virtual void reportWarning( - const std::string& warning, - std::vector&& context = - std::vector()) override { - CesiumJsonReader::ExtensibleObjectJsonHandler::reportWarning( - warning, - std::move(context)); - } + virtual IJsonHandler& getHandler() override { return *this; } protected: IJsonHandler* readObjectKeyExtensionBufferViewExtMeshoptCompression( diff --git a/CesiumGltfReader/generated/src/ExtensionCesiumRTCJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionCesiumRTCJsonHandler.h index 84ffc1482..2626df960 100644 --- a/CesiumGltfReader/generated/src/ExtensionCesiumRTCJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionCesiumRTCJsonHandler.h @@ -32,50 +32,7 @@ class ExtensionCesiumRTCJsonHandler CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) override; - virtual IJsonHandler* readNull() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readNull(); - }; - virtual IJsonHandler* readBool(bool b) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readBool(b); - } - virtual IJsonHandler* readInt32(int32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt32(i); - } - virtual IJsonHandler* readUint32(uint32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint32(i); - } - virtual IJsonHandler* readInt64(int64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt64(i); - } - virtual IJsonHandler* readUint64(uint64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint64(i); - } - virtual IJsonHandler* readDouble(double d) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readDouble(d); - } - virtual IJsonHandler* readString(const std::string_view& str) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readString(str); - } - virtual IJsonHandler* readObjectStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectStart(); - } - virtual IJsonHandler* readObjectEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectEnd(); - } - virtual IJsonHandler* readArrayStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayStart(); - } - virtual IJsonHandler* readArrayEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayEnd(); - } - virtual void reportWarning( - const std::string& warning, - std::vector&& context = - std::vector()) override { - CesiumJsonReader::ExtensibleObjectJsonHandler::reportWarning( - warning, - std::move(context)); - } + virtual IJsonHandler& getHandler() override { return *this; } protected: IJsonHandler* readObjectKeyExtensionCesiumRTC( diff --git a/CesiumGltfReader/generated/src/ExtensionCesiumTileEdgesJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionCesiumTileEdgesJsonHandler.h index d08365fdc..c3588e144 100644 --- a/CesiumGltfReader/generated/src/ExtensionCesiumTileEdgesJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionCesiumTileEdgesJsonHandler.h @@ -32,50 +32,7 @@ class ExtensionCesiumTileEdgesJsonHandler CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) override; - virtual IJsonHandler* readNull() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readNull(); - }; - virtual IJsonHandler* readBool(bool b) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readBool(b); - } - virtual IJsonHandler* readInt32(int32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt32(i); - } - virtual IJsonHandler* readUint32(uint32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint32(i); - } - virtual IJsonHandler* readInt64(int64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt64(i); - } - virtual IJsonHandler* readUint64(uint64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint64(i); - } - virtual IJsonHandler* readDouble(double d) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readDouble(d); - } - virtual IJsonHandler* readString(const std::string_view& str) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readString(str); - } - virtual IJsonHandler* readObjectStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectStart(); - } - virtual IJsonHandler* readObjectEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectEnd(); - } - virtual IJsonHandler* readArrayStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayStart(); - } - virtual IJsonHandler* readArrayEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayEnd(); - } - virtual void reportWarning( - const std::string& warning, - std::vector&& context = - std::vector()) override { - CesiumJsonReader::ExtensibleObjectJsonHandler::reportWarning( - warning, - std::move(context)); - } + virtual IJsonHandler& getHandler() override { return *this; } protected: IJsonHandler* readObjectKeyExtensionCesiumTileEdges( diff --git a/CesiumGltfReader/generated/src/ExtensionExtInstanceFeaturesJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionExtInstanceFeaturesJsonHandler.h index c03c4fada..4d59d63a3 100644 --- a/CesiumGltfReader/generated/src/ExtensionExtInstanceFeaturesJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionExtInstanceFeaturesJsonHandler.h @@ -34,50 +34,7 @@ class ExtensionExtInstanceFeaturesJsonHandler CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) override; - virtual IJsonHandler* readNull() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readNull(); - }; - virtual IJsonHandler* readBool(bool b) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readBool(b); - } - virtual IJsonHandler* readInt32(int32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt32(i); - } - virtual IJsonHandler* readUint32(uint32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint32(i); - } - virtual IJsonHandler* readInt64(int64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt64(i); - } - virtual IJsonHandler* readUint64(uint64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint64(i); - } - virtual IJsonHandler* readDouble(double d) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readDouble(d); - } - virtual IJsonHandler* readString(const std::string_view& str) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readString(str); - } - virtual IJsonHandler* readObjectStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectStart(); - } - virtual IJsonHandler* readObjectEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectEnd(); - } - virtual IJsonHandler* readArrayStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayStart(); - } - virtual IJsonHandler* readArrayEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayEnd(); - } - virtual void reportWarning( - const std::string& warning, - std::vector&& context = - std::vector()) override { - CesiumJsonReader::ExtensibleObjectJsonHandler::reportWarning( - warning, - std::move(context)); - } + virtual IJsonHandler& getHandler() override { return *this; } protected: IJsonHandler* readObjectKeyExtensionExtInstanceFeatures( diff --git a/CesiumGltfReader/generated/src/ExtensionExtMeshFeaturesJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionExtMeshFeaturesJsonHandler.h index e39bf64f5..cc2ede28d 100644 --- a/CesiumGltfReader/generated/src/ExtensionExtMeshFeaturesJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionExtMeshFeaturesJsonHandler.h @@ -34,50 +34,7 @@ class ExtensionExtMeshFeaturesJsonHandler CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) override; - virtual IJsonHandler* readNull() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readNull(); - }; - virtual IJsonHandler* readBool(bool b) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readBool(b); - } - virtual IJsonHandler* readInt32(int32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt32(i); - } - virtual IJsonHandler* readUint32(uint32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint32(i); - } - virtual IJsonHandler* readInt64(int64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt64(i); - } - virtual IJsonHandler* readUint64(uint64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint64(i); - } - virtual IJsonHandler* readDouble(double d) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readDouble(d); - } - virtual IJsonHandler* readString(const std::string_view& str) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readString(str); - } - virtual IJsonHandler* readObjectStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectStart(); - } - virtual IJsonHandler* readObjectEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectEnd(); - } - virtual IJsonHandler* readArrayStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayStart(); - } - virtual IJsonHandler* readArrayEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayEnd(); - } - virtual void reportWarning( - const std::string& warning, - std::vector&& context = - std::vector()) override { - CesiumJsonReader::ExtensibleObjectJsonHandler::reportWarning( - warning, - std::move(context)); - } + virtual IJsonHandler& getHandler() override { return *this; } protected: IJsonHandler* readObjectKeyExtensionExtMeshFeatures( diff --git a/CesiumGltfReader/generated/src/ExtensionExtMeshGpuInstancingJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionExtMeshGpuInstancingJsonHandler.h index 5c0414671..afc99ea04 100644 --- a/CesiumGltfReader/generated/src/ExtensionExtMeshGpuInstancingJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionExtMeshGpuInstancingJsonHandler.h @@ -33,50 +33,7 @@ class ExtensionExtMeshGpuInstancingJsonHandler CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) override; - virtual IJsonHandler* readNull() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readNull(); - }; - virtual IJsonHandler* readBool(bool b) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readBool(b); - } - virtual IJsonHandler* readInt32(int32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt32(i); - } - virtual IJsonHandler* readUint32(uint32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint32(i); - } - virtual IJsonHandler* readInt64(int64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt64(i); - } - virtual IJsonHandler* readUint64(uint64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint64(i); - } - virtual IJsonHandler* readDouble(double d) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readDouble(d); - } - virtual IJsonHandler* readString(const std::string_view& str) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readString(str); - } - virtual IJsonHandler* readObjectStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectStart(); - } - virtual IJsonHandler* readObjectEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectEnd(); - } - virtual IJsonHandler* readArrayStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayStart(); - } - virtual IJsonHandler* readArrayEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayEnd(); - } - virtual void reportWarning( - const std::string& warning, - std::vector&& context = - std::vector()) override { - CesiumJsonReader::ExtensibleObjectJsonHandler::reportWarning( - warning, - std::move(context)); - } + virtual IJsonHandler& getHandler() override { return *this; } protected: IJsonHandler* readObjectKeyExtensionExtMeshGpuInstancing( diff --git a/CesiumGltfReader/generated/src/ExtensionKhrDracoMeshCompressionJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionKhrDracoMeshCompressionJsonHandler.h index e3835e421..a6f3cb67a 100644 --- a/CesiumGltfReader/generated/src/ExtensionKhrDracoMeshCompressionJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionKhrDracoMeshCompressionJsonHandler.h @@ -34,50 +34,7 @@ class ExtensionKhrDracoMeshCompressionJsonHandler CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) override; - virtual IJsonHandler* readNull() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readNull(); - }; - virtual IJsonHandler* readBool(bool b) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readBool(b); - } - virtual IJsonHandler* readInt32(int32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt32(i); - } - virtual IJsonHandler* readUint32(uint32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint32(i); - } - virtual IJsonHandler* readInt64(int64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt64(i); - } - virtual IJsonHandler* readUint64(uint64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint64(i); - } - virtual IJsonHandler* readDouble(double d) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readDouble(d); - } - virtual IJsonHandler* readString(const std::string_view& str) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readString(str); - } - virtual IJsonHandler* readObjectStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectStart(); - } - virtual IJsonHandler* readObjectEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectEnd(); - } - virtual IJsonHandler* readArrayStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayStart(); - } - virtual IJsonHandler* readArrayEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayEnd(); - } - virtual void reportWarning( - const std::string& warning, - std::vector&& context = - std::vector()) override { - CesiumJsonReader::ExtensibleObjectJsonHandler::reportWarning( - warning, - std::move(context)); - } + virtual IJsonHandler& getHandler() override { return *this; } protected: IJsonHandler* readObjectKeyExtensionKhrDracoMeshCompression( diff --git a/CesiumGltfReader/generated/src/ExtensionKhrMaterialsUnlitJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionKhrMaterialsUnlitJsonHandler.h index c143b4169..5125caf45 100644 --- a/CesiumGltfReader/generated/src/ExtensionKhrMaterialsUnlitJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionKhrMaterialsUnlitJsonHandler.h @@ -31,50 +31,7 @@ class ExtensionKhrMaterialsUnlitJsonHandler CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) override; - virtual IJsonHandler* readNull() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readNull(); - }; - virtual IJsonHandler* readBool(bool b) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readBool(b); - } - virtual IJsonHandler* readInt32(int32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt32(i); - } - virtual IJsonHandler* readUint32(uint32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint32(i); - } - virtual IJsonHandler* readInt64(int64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt64(i); - } - virtual IJsonHandler* readUint64(uint64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint64(i); - } - virtual IJsonHandler* readDouble(double d) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readDouble(d); - } - virtual IJsonHandler* readString(const std::string_view& str) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readString(str); - } - virtual IJsonHandler* readObjectStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectStart(); - } - virtual IJsonHandler* readObjectEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectEnd(); - } - virtual IJsonHandler* readArrayStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayStart(); - } - virtual IJsonHandler* readArrayEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayEnd(); - } - virtual void reportWarning( - const std::string& warning, - std::vector&& context = - std::vector()) override { - CesiumJsonReader::ExtensibleObjectJsonHandler::reportWarning( - warning, - std::move(context)); - } + virtual IJsonHandler& getHandler() override { return *this; } protected: IJsonHandler* readObjectKeyExtensionKhrMaterialsUnlit( diff --git a/CesiumGltfReader/generated/src/ExtensionKhrTextureBasisuJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionKhrTextureBasisuJsonHandler.h index fe66ed324..0b3cd768c 100644 --- a/CesiumGltfReader/generated/src/ExtensionKhrTextureBasisuJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionKhrTextureBasisuJsonHandler.h @@ -32,50 +32,7 @@ class ExtensionKhrTextureBasisuJsonHandler CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) override; - virtual IJsonHandler* readNull() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readNull(); - }; - virtual IJsonHandler* readBool(bool b) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readBool(b); - } - virtual IJsonHandler* readInt32(int32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt32(i); - } - virtual IJsonHandler* readUint32(uint32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint32(i); - } - virtual IJsonHandler* readInt64(int64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt64(i); - } - virtual IJsonHandler* readUint64(uint64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint64(i); - } - virtual IJsonHandler* readDouble(double d) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readDouble(d); - } - virtual IJsonHandler* readString(const std::string_view& str) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readString(str); - } - virtual IJsonHandler* readObjectStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectStart(); - } - virtual IJsonHandler* readObjectEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectEnd(); - } - virtual IJsonHandler* readArrayStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayStart(); - } - virtual IJsonHandler* readArrayEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayEnd(); - } - virtual void reportWarning( - const std::string& warning, - std::vector&& context = - std::vector()) override { - CesiumJsonReader::ExtensibleObjectJsonHandler::reportWarning( - warning, - std::move(context)); - } + virtual IJsonHandler& getHandler() override { return *this; } protected: IJsonHandler* readObjectKeyExtensionKhrTextureBasisu( diff --git a/CesiumGltfReader/generated/src/ExtensionKhrTextureTransformJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionKhrTextureTransformJsonHandler.h index 11585ed94..6741affa9 100644 --- a/CesiumGltfReader/generated/src/ExtensionKhrTextureTransformJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionKhrTextureTransformJsonHandler.h @@ -34,50 +34,7 @@ class ExtensionKhrTextureTransformJsonHandler CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) override; - virtual IJsonHandler* readNull() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readNull(); - }; - virtual IJsonHandler* readBool(bool b) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readBool(b); - } - virtual IJsonHandler* readInt32(int32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt32(i); - } - virtual IJsonHandler* readUint32(uint32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint32(i); - } - virtual IJsonHandler* readInt64(int64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt64(i); - } - virtual IJsonHandler* readUint64(uint64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint64(i); - } - virtual IJsonHandler* readDouble(double d) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readDouble(d); - } - virtual IJsonHandler* readString(const std::string_view& str) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readString(str); - } - virtual IJsonHandler* readObjectStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectStart(); - } - virtual IJsonHandler* readObjectEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectEnd(); - } - virtual IJsonHandler* readArrayStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayStart(); - } - virtual IJsonHandler* readArrayEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayEnd(); - } - virtual void reportWarning( - const std::string& warning, - std::vector&& context = - std::vector()) override { - CesiumJsonReader::ExtensibleObjectJsonHandler::reportWarning( - warning, - std::move(context)); - } + virtual IJsonHandler& getHandler() override { return *this; } protected: IJsonHandler* readObjectKeyExtensionKhrTextureTransform( diff --git a/CesiumGltfReader/generated/src/ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler.h index 93ec30a16..b4da79e6e 100644 --- a/CesiumGltfReader/generated/src/ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler.h @@ -36,50 +36,7 @@ class ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) override; - virtual IJsonHandler* readNull() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readNull(); - }; - virtual IJsonHandler* readBool(bool b) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readBool(b); - } - virtual IJsonHandler* readInt32(int32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt32(i); - } - virtual IJsonHandler* readUint32(uint32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint32(i); - } - virtual IJsonHandler* readInt64(int64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt64(i); - } - virtual IJsonHandler* readUint64(uint64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint64(i); - } - virtual IJsonHandler* readDouble(double d) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readDouble(d); - } - virtual IJsonHandler* readString(const std::string_view& str) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readString(str); - } - virtual IJsonHandler* readObjectStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectStart(); - } - virtual IJsonHandler* readObjectEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectEnd(); - } - virtual IJsonHandler* readArrayStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayStart(); - } - virtual IJsonHandler* readArrayEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayEnd(); - } - virtual void reportWarning( - const std::string& warning, - std::vector&& context = - std::vector()) override { - CesiumJsonReader::ExtensibleObjectJsonHandler::reportWarning( - warning, - std::move(context)); - } + virtual IJsonHandler& getHandler() override { return *this; } protected: IJsonHandler* readObjectKeyExtensionMeshPrimitiveExtFeatureMetadata( diff --git a/CesiumGltfReader/generated/src/ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler.h index cc3da651e..e2ab51dbb 100644 --- a/CesiumGltfReader/generated/src/ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler.h @@ -33,50 +33,7 @@ class ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) override; - virtual IJsonHandler* readNull() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readNull(); - }; - virtual IJsonHandler* readBool(bool b) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readBool(b); - } - virtual IJsonHandler* readInt32(int32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt32(i); - } - virtual IJsonHandler* readUint32(uint32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint32(i); - } - virtual IJsonHandler* readInt64(int64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt64(i); - } - virtual IJsonHandler* readUint64(uint64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint64(i); - } - virtual IJsonHandler* readDouble(double d) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readDouble(d); - } - virtual IJsonHandler* readString(const std::string_view& str) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readString(str); - } - virtual IJsonHandler* readObjectStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectStart(); - } - virtual IJsonHandler* readObjectEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectEnd(); - } - virtual IJsonHandler* readArrayStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayStart(); - } - virtual IJsonHandler* readArrayEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayEnd(); - } - virtual void reportWarning( - const std::string& warning, - std::vector&& context = - std::vector()) override { - CesiumJsonReader::ExtensibleObjectJsonHandler::reportWarning( - warning, - std::move(context)); - } + virtual IJsonHandler& getHandler() override { return *this; } protected: IJsonHandler* readObjectKeyExtensionMeshPrimitiveExtStructuralMetadata( diff --git a/CesiumGltfReader/generated/src/ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler.h index 28c93e0e3..2f9ee7e52 100644 --- a/CesiumGltfReader/generated/src/ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler.h @@ -34,50 +34,7 @@ class ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) override; - virtual IJsonHandler* readNull() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readNull(); - }; - virtual IJsonHandler* readBool(bool b) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readBool(b); - } - virtual IJsonHandler* readInt32(int32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt32(i); - } - virtual IJsonHandler* readUint32(uint32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint32(i); - } - virtual IJsonHandler* readInt64(int64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt64(i); - } - virtual IJsonHandler* readUint64(uint64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint64(i); - } - virtual IJsonHandler* readDouble(double d) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readDouble(d); - } - virtual IJsonHandler* readString(const std::string_view& str) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readString(str); - } - virtual IJsonHandler* readObjectStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectStart(); - } - virtual IJsonHandler* readObjectEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectEnd(); - } - virtual IJsonHandler* readArrayStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayStart(); - } - virtual IJsonHandler* readArrayEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayEnd(); - } - virtual void reportWarning( - const std::string& warning, - std::vector&& context = - std::vector()) override { - CesiumJsonReader::ExtensibleObjectJsonHandler::reportWarning( - warning, - std::move(context)); - } + virtual IJsonHandler& getHandler() override { return *this; } protected: IJsonHandler* readObjectKeyExtensionMeshPrimitiveKhrMaterialsVariants( diff --git a/CesiumGltfReader/generated/src/ExtensionModelExtFeatureMetadataJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionModelExtFeatureMetadataJsonHandler.h index 1fc825848..6dc96fa48 100644 --- a/CesiumGltfReader/generated/src/ExtensionModelExtFeatureMetadataJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionModelExtFeatureMetadataJsonHandler.h @@ -38,50 +38,7 @@ class ExtensionModelExtFeatureMetadataJsonHandler CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) override; - virtual IJsonHandler* readNull() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readNull(); - }; - virtual IJsonHandler* readBool(bool b) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readBool(b); - } - virtual IJsonHandler* readInt32(int32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt32(i); - } - virtual IJsonHandler* readUint32(uint32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint32(i); - } - virtual IJsonHandler* readInt64(int64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt64(i); - } - virtual IJsonHandler* readUint64(uint64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint64(i); - } - virtual IJsonHandler* readDouble(double d) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readDouble(d); - } - virtual IJsonHandler* readString(const std::string_view& str) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readString(str); - } - virtual IJsonHandler* readObjectStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectStart(); - } - virtual IJsonHandler* readObjectEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectEnd(); - } - virtual IJsonHandler* readArrayStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayStart(); - } - virtual IJsonHandler* readArrayEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayEnd(); - } - virtual void reportWarning( - const std::string& warning, - std::vector&& context = - std::vector()) override { - CesiumJsonReader::ExtensibleObjectJsonHandler::reportWarning( - warning, - std::move(context)); - } + virtual IJsonHandler& getHandler() override { return *this; } protected: IJsonHandler* readObjectKeyExtensionModelExtFeatureMetadata( diff --git a/CesiumGltfReader/generated/src/ExtensionModelExtStructuralMetadataJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionModelExtStructuralMetadataJsonHandler.h index bce764b8f..0176d6421 100644 --- a/CesiumGltfReader/generated/src/ExtensionModelExtStructuralMetadataJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionModelExtStructuralMetadataJsonHandler.h @@ -38,50 +38,7 @@ class ExtensionModelExtStructuralMetadataJsonHandler CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) override; - virtual IJsonHandler* readNull() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readNull(); - }; - virtual IJsonHandler* readBool(bool b) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readBool(b); - } - virtual IJsonHandler* readInt32(int32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt32(i); - } - virtual IJsonHandler* readUint32(uint32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint32(i); - } - virtual IJsonHandler* readInt64(int64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt64(i); - } - virtual IJsonHandler* readUint64(uint64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint64(i); - } - virtual IJsonHandler* readDouble(double d) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readDouble(d); - } - virtual IJsonHandler* readString(const std::string_view& str) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readString(str); - } - virtual IJsonHandler* readObjectStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectStart(); - } - virtual IJsonHandler* readObjectEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectEnd(); - } - virtual IJsonHandler* readArrayStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayStart(); - } - virtual IJsonHandler* readArrayEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayEnd(); - } - virtual void reportWarning( - const std::string& warning, - std::vector&& context = - std::vector()) override { - CesiumJsonReader::ExtensibleObjectJsonHandler::reportWarning( - warning, - std::move(context)); - } + virtual IJsonHandler& getHandler() override { return *this; } protected: IJsonHandler* readObjectKeyExtensionModelExtStructuralMetadata( diff --git a/CesiumGltfReader/generated/src/ExtensionModelKhrMaterialsVariantsJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionModelKhrMaterialsVariantsJsonHandler.h index 4c967b4b5..e1c5cac88 100644 --- a/CesiumGltfReader/generated/src/ExtensionModelKhrMaterialsVariantsJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionModelKhrMaterialsVariantsJsonHandler.h @@ -34,50 +34,7 @@ class ExtensionModelKhrMaterialsVariantsJsonHandler CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) override; - virtual IJsonHandler* readNull() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readNull(); - }; - virtual IJsonHandler* readBool(bool b) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readBool(b); - } - virtual IJsonHandler* readInt32(int32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt32(i); - } - virtual IJsonHandler* readUint32(uint32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint32(i); - } - virtual IJsonHandler* readInt64(int64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt64(i); - } - virtual IJsonHandler* readUint64(uint64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint64(i); - } - virtual IJsonHandler* readDouble(double d) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readDouble(d); - } - virtual IJsonHandler* readString(const std::string_view& str) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readString(str); - } - virtual IJsonHandler* readObjectStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectStart(); - } - virtual IJsonHandler* readObjectEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectEnd(); - } - virtual IJsonHandler* readArrayStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayStart(); - } - virtual IJsonHandler* readArrayEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayEnd(); - } - virtual void reportWarning( - const std::string& warning, - std::vector&& context = - std::vector()) override { - CesiumJsonReader::ExtensibleObjectJsonHandler::reportWarning( - warning, - std::move(context)); - } + virtual IJsonHandler& getHandler() override { return *this; } protected: IJsonHandler* readObjectKeyExtensionModelKhrMaterialsVariants( diff --git a/CesiumGltfReader/generated/src/ExtensionModelMaxarMeshVariantsJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionModelMaxarMeshVariantsJsonHandler.h index c98b19818..303da2a86 100644 --- a/CesiumGltfReader/generated/src/ExtensionModelMaxarMeshVariantsJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionModelMaxarMeshVariantsJsonHandler.h @@ -35,50 +35,7 @@ class ExtensionModelMaxarMeshVariantsJsonHandler CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) override; - virtual IJsonHandler* readNull() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readNull(); - }; - virtual IJsonHandler* readBool(bool b) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readBool(b); - } - virtual IJsonHandler* readInt32(int32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt32(i); - } - virtual IJsonHandler* readUint32(uint32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint32(i); - } - virtual IJsonHandler* readInt64(int64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt64(i); - } - virtual IJsonHandler* readUint64(uint64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint64(i); - } - virtual IJsonHandler* readDouble(double d) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readDouble(d); - } - virtual IJsonHandler* readString(const std::string_view& str) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readString(str); - } - virtual IJsonHandler* readObjectStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectStart(); - } - virtual IJsonHandler* readObjectEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectEnd(); - } - virtual IJsonHandler* readArrayStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayStart(); - } - virtual IJsonHandler* readArrayEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayEnd(); - } - virtual void reportWarning( - const std::string& warning, - std::vector&& context = - std::vector()) override { - CesiumJsonReader::ExtensibleObjectJsonHandler::reportWarning( - warning, - std::move(context)); - } + virtual IJsonHandler& getHandler() override { return *this; } protected: IJsonHandler* readObjectKeyExtensionModelMaxarMeshVariants( diff --git a/CesiumGltfReader/generated/src/ExtensionNodeMaxarMeshVariantsJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionNodeMaxarMeshVariantsJsonHandler.h index 16bb452ce..9f73a30e8 100644 --- a/CesiumGltfReader/generated/src/ExtensionNodeMaxarMeshVariantsJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionNodeMaxarMeshVariantsJsonHandler.h @@ -34,50 +34,7 @@ class ExtensionNodeMaxarMeshVariantsJsonHandler CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) override; - virtual IJsonHandler* readNull() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readNull(); - }; - virtual IJsonHandler* readBool(bool b) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readBool(b); - } - virtual IJsonHandler* readInt32(int32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt32(i); - } - virtual IJsonHandler* readUint32(uint32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint32(i); - } - virtual IJsonHandler* readInt64(int64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt64(i); - } - virtual IJsonHandler* readUint64(uint64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint64(i); - } - virtual IJsonHandler* readDouble(double d) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readDouble(d); - } - virtual IJsonHandler* readString(const std::string_view& str) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readString(str); - } - virtual IJsonHandler* readObjectStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectStart(); - } - virtual IJsonHandler* readObjectEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectEnd(); - } - virtual IJsonHandler* readArrayStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayStart(); - } - virtual IJsonHandler* readArrayEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayEnd(); - } - virtual void reportWarning( - const std::string& warning, - std::vector&& context = - std::vector()) override { - CesiumJsonReader::ExtensibleObjectJsonHandler::reportWarning( - warning, - std::move(context)); - } + virtual IJsonHandler& getHandler() override { return *this; } protected: IJsonHandler* readObjectKeyExtensionNodeMaxarMeshVariants( diff --git a/CesiumGltfReader/generated/src/ExtensionTextureWebpJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionTextureWebpJsonHandler.h index 04ed382e3..8ffe0739f 100644 --- a/CesiumGltfReader/generated/src/ExtensionTextureWebpJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionTextureWebpJsonHandler.h @@ -32,50 +32,7 @@ class ExtensionTextureWebpJsonHandler CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) override; - virtual IJsonHandler* readNull() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readNull(); - }; - virtual IJsonHandler* readBool(bool b) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readBool(b); - } - virtual IJsonHandler* readInt32(int32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt32(i); - } - virtual IJsonHandler* readUint32(uint32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint32(i); - } - virtual IJsonHandler* readInt64(int64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt64(i); - } - virtual IJsonHandler* readUint64(uint64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint64(i); - } - virtual IJsonHandler* readDouble(double d) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readDouble(d); - } - virtual IJsonHandler* readString(const std::string_view& str) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readString(str); - } - virtual IJsonHandler* readObjectStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectStart(); - } - virtual IJsonHandler* readObjectEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectEnd(); - } - virtual IJsonHandler* readArrayStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayStart(); - } - virtual IJsonHandler* readArrayEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayEnd(); - } - virtual void reportWarning( - const std::string& warning, - std::vector&& context = - std::vector()) override { - CesiumJsonReader::ExtensibleObjectJsonHandler::reportWarning( - warning, - std::move(context)); - } + virtual IJsonHandler& getHandler() override { return *this; } protected: IJsonHandler* readObjectKeyExtensionTextureWebp( diff --git a/CesiumGltfReader/generated/src/GeneratedJsonHandlers.cpp b/CesiumGltfReader/generated/src/GeneratedJsonHandlers.cpp index 660088e7a..832291018 100644 --- a/CesiumGltfReader/generated/src/GeneratedJsonHandlers.cpp +++ b/CesiumGltfReader/generated/src/GeneratedJsonHandlers.cpp @@ -1,8 +1,12 @@ // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionCesiumRTCJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -54,12 +58,52 @@ ExtensionCesiumRTCJsonHandler::readObjectKeyExtensionCesiumRTC( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionCesiumRTCReader::ExtensionCesiumRTCReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& ExtensionCesiumRTCReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionCesiumRTCReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +ExtensionCesiumRTCReader::readFromJson( + const gsl::span& data) const { + ExtensionCesiumRTCJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +ExtensionCesiumRTCReader::readFromJson(const rapidjson::Value& value) const { + ExtensionCesiumRTCJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +ExtensionCesiumRTCReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionCesiumRTC, + ExtensionCesiumRTCJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionCesiumTileEdgesJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -123,12 +167,55 @@ ExtensionCesiumTileEdgesJsonHandler::readObjectKeyExtensionCesiumTileEdges( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionCesiumTileEdgesReader::ExtensionCesiumTileEdgesReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionCesiumTileEdgesReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionCesiumTileEdgesReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +ExtensionCesiumTileEdgesReader::readFromJson( + const gsl::span& data) const { + ExtensionCesiumTileEdgesJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +ExtensionCesiumTileEdgesReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionCesiumTileEdgesJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionCesiumTileEdgesReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionCesiumTileEdges, + ExtensionCesiumTileEdgesJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionModelExtFeatureMetadataJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -200,12 +287,56 @@ CesiumJsonReader::IJsonHandler* ExtensionModelExtFeatureMetadataJsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionModelExtFeatureMetadataReader:: + ExtensionModelExtFeatureMetadataReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionModelExtFeatureMetadataReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionModelExtFeatureMetadataReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +ExtensionModelExtFeatureMetadataReader::readFromJson( + const gsl::span& data) const { + ExtensionModelExtFeatureMetadataJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +ExtensionModelExtFeatureMetadataReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionModelExtFeatureMetadataJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionModelExtFeatureMetadataReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionModelExtFeatureMetadata, + ExtensionModelExtFeatureMetadataJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -280,12 +411,58 @@ ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionMeshPrimitiveExtFeatureMetadataReader:: + ExtensionMeshPrimitiveExtFeatureMetadataReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionMeshPrimitiveExtFeatureMetadataReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionMeshPrimitiveExtFeatureMetadataReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionMeshPrimitiveExtFeatureMetadata> +ExtensionMeshPrimitiveExtFeatureMetadataReader::readFromJson( + const gsl::span& data) const { + ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionMeshPrimitiveExtFeatureMetadata> +ExtensionMeshPrimitiveExtFeatureMetadataReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionMeshPrimitiveExtFeatureMetadataReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionMeshPrimitiveExtFeatureMetadata, + ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionExtInstanceFeaturesJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -341,12 +518,55 @@ CesiumJsonReader::IJsonHandler* ExtensionExtInstanceFeaturesJsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionExtInstanceFeaturesReader::ExtensionExtInstanceFeaturesReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionExtInstanceFeaturesReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionExtInstanceFeaturesReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +ExtensionExtInstanceFeaturesReader::readFromJson( + const gsl::span& data) const { + ExtensionExtInstanceFeaturesJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +ExtensionExtInstanceFeaturesReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionExtInstanceFeaturesJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionExtInstanceFeaturesReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionExtInstanceFeatures, + ExtensionExtInstanceFeaturesJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionExtMeshFeaturesJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -401,12 +621,55 @@ ExtensionExtMeshFeaturesJsonHandler::readObjectKeyExtensionExtMeshFeatures( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionExtMeshFeaturesReader::ExtensionExtMeshFeaturesReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionExtMeshFeaturesReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionExtMeshFeaturesReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +ExtensionExtMeshFeaturesReader::readFromJson( + const gsl::span& data) const { + ExtensionExtMeshFeaturesJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +ExtensionExtMeshFeaturesReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionExtMeshFeaturesJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionExtMeshFeaturesReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionExtMeshFeatures, + ExtensionExtMeshFeaturesJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionExtMeshGpuInstancingJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -461,12 +724,55 @@ CesiumJsonReader::IJsonHandler* ExtensionExtMeshGpuInstancingJsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionExtMeshGpuInstancingReader::ExtensionExtMeshGpuInstancingReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionExtMeshGpuInstancingReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionExtMeshGpuInstancingReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +ExtensionExtMeshGpuInstancingReader::readFromJson( + const gsl::span& data) const { + ExtensionExtMeshGpuInstancingJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +ExtensionExtMeshGpuInstancingReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionExtMeshGpuInstancingJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionExtMeshGpuInstancingReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionExtMeshGpuInstancing, + ExtensionExtMeshGpuInstancingJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionBufferExtMeshoptCompressionJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -523,12 +829,58 @@ ExtensionBufferExtMeshoptCompressionJsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionBufferExtMeshoptCompressionReader:: + ExtensionBufferExtMeshoptCompressionReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionBufferExtMeshoptCompressionReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionBufferExtMeshoptCompressionReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionBufferExtMeshoptCompression> +ExtensionBufferExtMeshoptCompressionReader::readFromJson( + const gsl::span& data) const { + ExtensionBufferExtMeshoptCompressionJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionBufferExtMeshoptCompression> +ExtensionBufferExtMeshoptCompressionReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionBufferExtMeshoptCompressionJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionBufferExtMeshoptCompressionReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionBufferExtMeshoptCompression, + ExtensionBufferExtMeshoptCompressionJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionBufferViewExtMeshoptCompressionJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -606,12 +958,58 @@ ExtensionBufferViewExtMeshoptCompressionJsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionBufferViewExtMeshoptCompressionReader:: + ExtensionBufferViewExtMeshoptCompressionReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionBufferViewExtMeshoptCompressionReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionBufferViewExtMeshoptCompressionReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionBufferViewExtMeshoptCompression> +ExtensionBufferViewExtMeshoptCompressionReader::readFromJson( + const gsl::span& data) const { + ExtensionBufferViewExtMeshoptCompressionJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionBufferViewExtMeshoptCompression> +ExtensionBufferViewExtMeshoptCompressionReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionBufferViewExtMeshoptCompressionJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionBufferViewExtMeshoptCompressionReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionBufferViewExtMeshoptCompression, + ExtensionBufferViewExtMeshoptCompressionJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionModelExtStructuralMetadataJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -686,12 +1084,58 @@ CesiumJsonReader::IJsonHandler* ExtensionModelExtStructuralMetadataJsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionModelExtStructuralMetadataReader:: + ExtensionModelExtStructuralMetadataReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionModelExtStructuralMetadataReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionModelExtStructuralMetadataReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionModelExtStructuralMetadata> +ExtensionModelExtStructuralMetadataReader::readFromJson( + const gsl::span& data) const { + ExtensionModelExtStructuralMetadataJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionModelExtStructuralMetadata> +ExtensionModelExtStructuralMetadataReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionModelExtStructuralMetadataJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionModelExtStructuralMetadataReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionModelExtStructuralMetadata, + ExtensionModelExtStructuralMetadataJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -760,12 +1204,60 @@ ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionMeshPrimitiveExtStructuralMetadataReader:: + ExtensionMeshPrimitiveExtStructuralMetadataReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionMeshPrimitiveExtStructuralMetadataReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionMeshPrimitiveExtStructuralMetadataReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionMeshPrimitiveExtStructuralMetadata> +ExtensionMeshPrimitiveExtStructuralMetadataReader::readFromJson( + const gsl::span& data) const { + ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler handler( + this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionMeshPrimitiveExtStructuralMetadata> +ExtensionMeshPrimitiveExtStructuralMetadataReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler handler( + this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionMeshPrimitiveExtStructuralMetadataReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionMeshPrimitiveExtStructuralMetadata, + ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionKhrDracoMeshCompressionJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -825,12 +1317,56 @@ CesiumJsonReader::IJsonHandler* ExtensionKhrDracoMeshCompressionJsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionKhrDracoMeshCompressionReader:: + ExtensionKhrDracoMeshCompressionReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionKhrDracoMeshCompressionReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionKhrDracoMeshCompressionReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +ExtensionKhrDracoMeshCompressionReader::readFromJson( + const gsl::span& data) const { + ExtensionKhrDracoMeshCompressionJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +ExtensionKhrDracoMeshCompressionReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionKhrDracoMeshCompressionJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionKhrDracoMeshCompressionReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionKhrDracoMeshCompression, + ExtensionKhrDracoMeshCompressionJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionKhrMaterialsUnlitJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -883,12 +1419,55 @@ ExtensionKhrMaterialsUnlitJsonHandler::readObjectKeyExtensionKhrMaterialsUnlit( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionKhrMaterialsUnlitReader::ExtensionKhrMaterialsUnlitReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionKhrMaterialsUnlitReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionKhrMaterialsUnlitReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +ExtensionKhrMaterialsUnlitReader::readFromJson( + const gsl::span& data) const { + ExtensionKhrMaterialsUnlitJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +ExtensionKhrMaterialsUnlitReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionKhrMaterialsUnlitJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionKhrMaterialsUnlitReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionKhrMaterialsUnlit, + ExtensionKhrMaterialsUnlitJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionModelKhrMaterialsVariantsJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -945,12 +1524,56 @@ CesiumJsonReader::IJsonHandler* ExtensionModelKhrMaterialsVariantsJsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionModelKhrMaterialsVariantsReader:: + ExtensionModelKhrMaterialsVariantsReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionModelKhrMaterialsVariantsReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionModelKhrMaterialsVariantsReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +ExtensionModelKhrMaterialsVariantsReader::readFromJson( + const gsl::span& data) const { + ExtensionModelKhrMaterialsVariantsJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +ExtensionModelKhrMaterialsVariantsReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionModelKhrMaterialsVariantsJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionModelKhrMaterialsVariantsReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionModelKhrMaterialsVariants, + ExtensionModelKhrMaterialsVariantsJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -1010,12 +1633,58 @@ ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionMeshPrimitiveKhrMaterialsVariantsReader:: + ExtensionMeshPrimitiveKhrMaterialsVariantsReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionMeshPrimitiveKhrMaterialsVariantsReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionMeshPrimitiveKhrMaterialsVariantsReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionMeshPrimitiveKhrMaterialsVariants> +ExtensionMeshPrimitiveKhrMaterialsVariantsReader::readFromJson( + const gsl::span& data) const { + ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionMeshPrimitiveKhrMaterialsVariants> +ExtensionMeshPrimitiveKhrMaterialsVariantsReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionMeshPrimitiveKhrMaterialsVariantsReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionMeshPrimitiveKhrMaterialsVariants, + ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionKhrTextureBasisuJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -1069,12 +1738,55 @@ ExtensionKhrTextureBasisuJsonHandler::readObjectKeyExtensionKhrTextureBasisu( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionKhrTextureBasisuReader::ExtensionKhrTextureBasisuReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionKhrTextureBasisuReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionKhrTextureBasisuReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +ExtensionKhrTextureBasisuReader::readFromJson( + const gsl::span& data) const { + ExtensionKhrTextureBasisuJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +ExtensionKhrTextureBasisuReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionKhrTextureBasisuJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionKhrTextureBasisuReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionKhrTextureBasisu, + ExtensionKhrTextureBasisuJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionModelMaxarMeshVariantsJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -1133,12 +1845,55 @@ CesiumJsonReader::IJsonHandler* ExtensionModelMaxarMeshVariantsJsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionModelMaxarMeshVariantsReader::ExtensionModelMaxarMeshVariantsReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionModelMaxarMeshVariantsReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionModelMaxarMeshVariantsReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +ExtensionModelMaxarMeshVariantsReader::readFromJson( + const gsl::span& data) const { + ExtensionModelMaxarMeshVariantsJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +ExtensionModelMaxarMeshVariantsReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionModelMaxarMeshVariantsJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionModelMaxarMeshVariantsReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionModelMaxarMeshVariants, + ExtensionModelMaxarMeshVariantsJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionNodeMaxarMeshVariantsJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -1194,12 +1949,55 @@ CesiumJsonReader::IJsonHandler* ExtensionNodeMaxarMeshVariantsJsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionNodeMaxarMeshVariantsReader::ExtensionNodeMaxarMeshVariantsReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionNodeMaxarMeshVariantsReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionNodeMaxarMeshVariantsReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +ExtensionNodeMaxarMeshVariantsReader::readFromJson( + const gsl::span& data) const { + ExtensionNodeMaxarMeshVariantsJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +ExtensionNodeMaxarMeshVariantsReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionNodeMaxarMeshVariantsJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionNodeMaxarMeshVariantsReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionNodeMaxarMeshVariants, + ExtensionNodeMaxarMeshVariantsJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionKhrTextureTransformJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -1264,12 +2062,55 @@ CesiumJsonReader::IJsonHandler* ExtensionKhrTextureTransformJsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionKhrTextureTransformReader::ExtensionKhrTextureTransformReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionKhrTextureTransformReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionKhrTextureTransformReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +ExtensionKhrTextureTransformReader::readFromJson( + const gsl::span& data) const { + ExtensionKhrTextureTransformJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +ExtensionKhrTextureTransformReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionKhrTextureTransformJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionKhrTextureTransformReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionKhrTextureTransform, + ExtensionKhrTextureTransformJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionTextureWebpJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -1321,12 +2162,52 @@ ExtensionTextureWebpJsonHandler::readObjectKeyExtensionTextureWebp( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionTextureWebpReader::ExtensionTextureWebpReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& ExtensionTextureWebpReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionTextureWebpReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +ExtensionTextureWebpReader::readFromJson( + const gsl::span& data) const { + ExtensionTextureWebpJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +ExtensionTextureWebpReader::readFromJson(const rapidjson::Value& value) const { + ExtensionTextureWebpJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +ExtensionTextureWebpReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionTextureWebp, + ExtensionTextureWebpJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionNodeMaxarMeshVariantsMappingsValueJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -1376,12 +2257,60 @@ ExtensionNodeMaxarMeshVariantsMappingsValueJsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionNodeMaxarMeshVariantsMappingsValueReader:: + ExtensionNodeMaxarMeshVariantsMappingsValueReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionNodeMaxarMeshVariantsMappingsValueReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionNodeMaxarMeshVariantsMappingsValueReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionNodeMaxarMeshVariantsMappingsValue> +ExtensionNodeMaxarMeshVariantsMappingsValueReader::readFromJson( + const gsl::span& data) const { + ExtensionNodeMaxarMeshVariantsMappingsValueJsonHandler handler( + this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionNodeMaxarMeshVariantsMappingsValue> +ExtensionNodeMaxarMeshVariantsMappingsValueReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionNodeMaxarMeshVariantsMappingsValueJsonHandler handler( + this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionNodeMaxarMeshVariantsMappingsValueReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionNodeMaxarMeshVariantsMappingsValue, + ExtensionNodeMaxarMeshVariantsMappingsValueJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionModelMaxarMeshVariantsValueJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -1424,12 +2353,58 @@ ExtensionModelMaxarMeshVariantsValueJsonHandler:: return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } -} // namespace CesiumGltfReader -// This file was generated by generate-classes. -// DO NOT EDIT THIS FILE! -#include "ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueJsonHandler.h" +ExtensionModelMaxarMeshVariantsValueReader:: + ExtensionModelMaxarMeshVariantsValueReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionModelMaxarMeshVariantsValueReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionModelMaxarMeshVariantsValueReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionModelMaxarMeshVariantsValue> +ExtensionModelMaxarMeshVariantsValueReader::readFromJson( + const gsl::span& data) const { + ExtensionModelMaxarMeshVariantsValueJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionModelMaxarMeshVariantsValue> +ExtensionModelMaxarMeshVariantsValueReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionModelMaxarMeshVariantsValueJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionModelMaxarMeshVariantsValueReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionModelMaxarMeshVariantsValue, + ExtensionModelMaxarMeshVariantsValueJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +} // namespace CesiumGltfReader +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#include "ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -1483,12 +2458,61 @@ ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueJsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueReader:: + ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueReader::getOptions() + const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValue> +ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueReader::readFromJson( + const gsl::span& data) const { + ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueJsonHandler handler( + this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValue> +ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueJsonHandler handler( + this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueReader:: + readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValue, + ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionModelKhrMaterialsVariantsValueJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -1531,12 +2555,58 @@ ExtensionModelKhrMaterialsVariantsValueJsonHandler:: return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } +ExtensionModelKhrMaterialsVariantsValueReader:: + ExtensionModelKhrMaterialsVariantsValueReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionModelKhrMaterialsVariantsValueReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionModelKhrMaterialsVariantsValueReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionModelKhrMaterialsVariantsValue> +ExtensionModelKhrMaterialsVariantsValueReader::readFromJson( + const gsl::span& data) const { + ExtensionModelKhrMaterialsVariantsValueJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionModelKhrMaterialsVariantsValue> +ExtensionModelKhrMaterialsVariantsValueReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionModelKhrMaterialsVariantsValueJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionModelKhrMaterialsVariantsValueReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionModelKhrMaterialsVariantsValue, + ExtensionModelKhrMaterialsVariantsValueJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionExtStructuralMetadataPropertyAttributeJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -1586,12 +2656,60 @@ ExtensionExtStructuralMetadataPropertyAttributeJsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionExtStructuralMetadataPropertyAttributeReader:: + ExtensionExtStructuralMetadataPropertyAttributeReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionExtStructuralMetadataPropertyAttributeReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionExtStructuralMetadataPropertyAttributeReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataPropertyAttribute> +ExtensionExtStructuralMetadataPropertyAttributeReader::readFromJson( + const gsl::span& data) const { + ExtensionExtStructuralMetadataPropertyAttributeJsonHandler handler( + this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataPropertyAttribute> +ExtensionExtStructuralMetadataPropertyAttributeReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionExtStructuralMetadataPropertyAttributeJsonHandler handler( + this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionExtStructuralMetadataPropertyAttributeReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionExtStructuralMetadataPropertyAttribute, + ExtensionExtStructuralMetadataPropertyAttributeJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionExtStructuralMetadataPropertyAttributePropertyJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -1651,12 +2769,61 @@ ExtensionExtStructuralMetadataPropertyAttributePropertyJsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionExtStructuralMetadataPropertyAttributePropertyReader:: + ExtensionExtStructuralMetadataPropertyAttributePropertyReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionExtStructuralMetadataPropertyAttributePropertyReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionExtStructuralMetadataPropertyAttributePropertyReader::getOptions() + const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataPropertyAttributeProperty> +ExtensionExtStructuralMetadataPropertyAttributePropertyReader::readFromJson( + const gsl::span& data) const { + ExtensionExtStructuralMetadataPropertyAttributePropertyJsonHandler handler( + this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataPropertyAttributeProperty> +ExtensionExtStructuralMetadataPropertyAttributePropertyReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionExtStructuralMetadataPropertyAttributePropertyJsonHandler handler( + this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +ExtensionExtStructuralMetadataPropertyAttributePropertyReader:: + readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionExtStructuralMetadataPropertyAttributeProperty, + ExtensionExtStructuralMetadataPropertyAttributePropertyJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionExtStructuralMetadataPropertyTextureJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -1706,12 +2873,60 @@ ExtensionExtStructuralMetadataPropertyTextureJsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionExtStructuralMetadataPropertyTextureReader:: + ExtensionExtStructuralMetadataPropertyTextureReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionExtStructuralMetadataPropertyTextureReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionExtStructuralMetadataPropertyTextureReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataPropertyTexture> +ExtensionExtStructuralMetadataPropertyTextureReader::readFromJson( + const gsl::span& data) const { + ExtensionExtStructuralMetadataPropertyTextureJsonHandler handler( + this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataPropertyTexture> +ExtensionExtStructuralMetadataPropertyTextureReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionExtStructuralMetadataPropertyTextureJsonHandler handler( + this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionExtStructuralMetadataPropertyTextureReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionExtStructuralMetadataPropertyTexture, + ExtensionExtStructuralMetadataPropertyTextureJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionExtStructuralMetadataPropertyTexturePropertyJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -1770,12 +2985,61 @@ ExtensionExtStructuralMetadataPropertyTexturePropertyJsonHandler:: return this->readObjectKeyTextureInfo(objectType, str, *this->_pObject); } +ExtensionExtStructuralMetadataPropertyTexturePropertyReader:: + ExtensionExtStructuralMetadataPropertyTexturePropertyReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionExtStructuralMetadataPropertyTexturePropertyReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionExtStructuralMetadataPropertyTexturePropertyReader::getOptions() + const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataPropertyTextureProperty> +ExtensionExtStructuralMetadataPropertyTexturePropertyReader::readFromJson( + const gsl::span& data) const { + ExtensionExtStructuralMetadataPropertyTexturePropertyJsonHandler handler( + this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataPropertyTextureProperty> +ExtensionExtStructuralMetadataPropertyTexturePropertyReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionExtStructuralMetadataPropertyTexturePropertyJsonHandler handler( + this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +ExtensionExtStructuralMetadataPropertyTexturePropertyReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionExtStructuralMetadataPropertyTextureProperty, + ExtensionExtStructuralMetadataPropertyTexturePropertyJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "TextureInfoJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -1819,12 +3083,47 @@ TextureInfoJsonHandler::readObjectKeyTextureInfo( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +TextureInfoReader::TextureInfoReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& TextureInfoReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +TextureInfoReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +TextureInfoReader::readFromJson(const gsl::span& data) const { + TextureInfoJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +TextureInfoReader::readFromJson(const rapidjson::Value& value) const { + TextureInfoJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +TextureInfoReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader:: + ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionExtStructuralMetadataPropertyTableJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -1877,12 +3176,60 @@ ExtensionExtStructuralMetadataPropertyTableJsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionExtStructuralMetadataPropertyTableReader:: + ExtensionExtStructuralMetadataPropertyTableReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionExtStructuralMetadataPropertyTableReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionExtStructuralMetadataPropertyTableReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataPropertyTable> +ExtensionExtStructuralMetadataPropertyTableReader::readFromJson( + const gsl::span& data) const { + ExtensionExtStructuralMetadataPropertyTableJsonHandler handler( + this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataPropertyTable> +ExtensionExtStructuralMetadataPropertyTableReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionExtStructuralMetadataPropertyTableJsonHandler handler( + this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionExtStructuralMetadataPropertyTableReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionExtStructuralMetadataPropertyTable, + ExtensionExtStructuralMetadataPropertyTableJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionExtStructuralMetadataPropertyTablePropertyJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -1956,12 +3303,60 @@ ExtensionExtStructuralMetadataPropertyTablePropertyJsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionExtStructuralMetadataPropertyTablePropertyReader:: + ExtensionExtStructuralMetadataPropertyTablePropertyReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionExtStructuralMetadataPropertyTablePropertyReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionExtStructuralMetadataPropertyTablePropertyReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataPropertyTableProperty> +ExtensionExtStructuralMetadataPropertyTablePropertyReader::readFromJson( + const gsl::span& data) const { + ExtensionExtStructuralMetadataPropertyTablePropertyJsonHandler handler( + this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataPropertyTableProperty> +ExtensionExtStructuralMetadataPropertyTablePropertyReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionExtStructuralMetadataPropertyTablePropertyJsonHandler handler( + this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +ExtensionExtStructuralMetadataPropertyTablePropertyReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionExtStructuralMetadataPropertyTableProperty, + ExtensionExtStructuralMetadataPropertyTablePropertyJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionExtStructuralMetadataSchemaJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -2020,12 +3415,58 @@ ExtensionExtStructuralMetadataSchemaJsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionExtStructuralMetadataSchemaReader:: + ExtensionExtStructuralMetadataSchemaReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionExtStructuralMetadataSchemaReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionExtStructuralMetadataSchemaReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataSchema> +ExtensionExtStructuralMetadataSchemaReader::readFromJson( + const gsl::span& data) const { + ExtensionExtStructuralMetadataSchemaJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataSchema> +ExtensionExtStructuralMetadataSchemaReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionExtStructuralMetadataSchemaJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionExtStructuralMetadataSchemaReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionExtStructuralMetadataSchema, + ExtensionExtStructuralMetadataSchemaJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionExtStructuralMetadataEnumJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -2077,12 +3518,56 @@ CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataEnumJsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionExtStructuralMetadataEnumReader:: + ExtensionExtStructuralMetadataEnumReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionExtStructuralMetadataEnumReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionExtStructuralMetadataEnumReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +ExtensionExtStructuralMetadataEnumReader::readFromJson( + const gsl::span& data) const { + ExtensionExtStructuralMetadataEnumJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +ExtensionExtStructuralMetadataEnumReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionExtStructuralMetadataEnumJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionExtStructuralMetadataEnumReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionExtStructuralMetadataEnum, + ExtensionExtStructuralMetadataEnumJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionExtStructuralMetadataEnumValueJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -2132,12 +3617,58 @@ ExtensionExtStructuralMetadataEnumValueJsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionExtStructuralMetadataEnumValueReader:: + ExtensionExtStructuralMetadataEnumValueReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionExtStructuralMetadataEnumValueReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionExtStructuralMetadataEnumValueReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataEnumValue> +ExtensionExtStructuralMetadataEnumValueReader::readFromJson( + const gsl::span& data) const { + ExtensionExtStructuralMetadataEnumValueJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataEnumValue> +ExtensionExtStructuralMetadataEnumValueReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionExtStructuralMetadataEnumValueJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionExtStructuralMetadataEnumValueReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionExtStructuralMetadataEnumValue, + ExtensionExtStructuralMetadataEnumValueJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionExtStructuralMetadataClassJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -2186,12 +3717,58 @@ CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataClassJsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionExtStructuralMetadataClassReader:: + ExtensionExtStructuralMetadataClassReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionExtStructuralMetadataClassReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionExtStructuralMetadataClassReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataClass> +ExtensionExtStructuralMetadataClassReader::readFromJson( + const gsl::span& data) const { + ExtensionExtStructuralMetadataClassJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataClass> +ExtensionExtStructuralMetadataClassReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionExtStructuralMetadataClassJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionExtStructuralMetadataClassReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionExtStructuralMetadataClass, + ExtensionExtStructuralMetadataClassJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionExtStructuralMetadataClassPropertyJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -2280,12 +3857,60 @@ ExtensionExtStructuralMetadataClassPropertyJsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionExtStructuralMetadataClassPropertyReader:: + ExtensionExtStructuralMetadataClassPropertyReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionExtStructuralMetadataClassPropertyReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionExtStructuralMetadataClassPropertyReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataClassProperty> +ExtensionExtStructuralMetadataClassPropertyReader::readFromJson( + const gsl::span& data) const { + ExtensionExtStructuralMetadataClassPropertyJsonHandler handler( + this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataClassProperty> +ExtensionExtStructuralMetadataClassPropertyReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionExtStructuralMetadataClassPropertyJsonHandler handler( + this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionExtStructuralMetadataClassPropertyReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionExtStructuralMetadataClassProperty, + ExtensionExtStructuralMetadataClassPropertyJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionExtMeshFeaturesFeatureIdJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -2343,12 +3968,56 @@ CesiumJsonReader::IJsonHandler* ExtensionExtMeshFeaturesFeatureIdJsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionExtMeshFeaturesFeatureIdReader:: + ExtensionExtMeshFeaturesFeatureIdReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionExtMeshFeaturesFeatureIdReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionExtMeshFeaturesFeatureIdReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +ExtensionExtMeshFeaturesFeatureIdReader::readFromJson( + const gsl::span& data) const { + ExtensionExtMeshFeaturesFeatureIdJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +ExtensionExtMeshFeaturesFeatureIdReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionExtMeshFeaturesFeatureIdJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionExtMeshFeaturesFeatureIdReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionExtMeshFeaturesFeatureId, + ExtensionExtMeshFeaturesFeatureIdJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionExtMeshFeaturesFeatureIdTextureJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -2391,12 +4060,58 @@ ExtensionExtMeshFeaturesFeatureIdTextureJsonHandler:: return this->readObjectKeyTextureInfo(objectType, str, *this->_pObject); } +ExtensionExtMeshFeaturesFeatureIdTextureReader:: + ExtensionExtMeshFeaturesFeatureIdTextureReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionExtMeshFeaturesFeatureIdTextureReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionExtMeshFeaturesFeatureIdTextureReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtMeshFeaturesFeatureIdTexture> +ExtensionExtMeshFeaturesFeatureIdTextureReader::readFromJson( + const gsl::span& data) const { + ExtensionExtMeshFeaturesFeatureIdTextureJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtMeshFeaturesFeatureIdTexture> +ExtensionExtMeshFeaturesFeatureIdTextureReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionExtMeshFeaturesFeatureIdTextureJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionExtMeshFeaturesFeatureIdTextureReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionExtMeshFeaturesFeatureIdTexture, + ExtensionExtMeshFeaturesFeatureIdTextureJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionExtInstanceFeaturesFeatureIdJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -2452,12 +4167,58 @@ ExtensionExtInstanceFeaturesFeatureIdJsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionExtInstanceFeaturesFeatureIdReader:: + ExtensionExtInstanceFeaturesFeatureIdReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionExtInstanceFeaturesFeatureIdReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionExtInstanceFeaturesFeatureIdReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtInstanceFeaturesFeatureId> +ExtensionExtInstanceFeaturesFeatureIdReader::readFromJson( + const gsl::span& data) const { + ExtensionExtInstanceFeaturesFeatureIdJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtInstanceFeaturesFeatureId> +ExtensionExtInstanceFeaturesFeatureIdReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionExtInstanceFeaturesFeatureIdJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionExtInstanceFeaturesFeatureIdReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionExtInstanceFeaturesFeatureId, + ExtensionExtInstanceFeaturesFeatureIdJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "FeatureIDTextureJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -2501,12 +4262,51 @@ FeatureIDTextureJsonHandler::readObjectKeyFeatureIDTexture( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +FeatureIDTextureReader::FeatureIDTextureReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& FeatureIDTextureReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +FeatureIDTextureReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +FeatureIDTextureReader::readFromJson( + const gsl::span& data) const { + FeatureIDTextureJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +FeatureIDTextureReader::readFromJson(const rapidjson::Value& value) const { + FeatureIDTextureJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +FeatureIDTextureReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::FeatureIDTexture, + FeatureIDTextureJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "TextureAccessorJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -2550,12 +4350,50 @@ TextureAccessorJsonHandler::readObjectKeyTextureAccessor( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +TextureAccessorReader::TextureAccessorReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& TextureAccessorReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +TextureAccessorReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +TextureAccessorReader::readFromJson( + const gsl::span& data) const { + TextureAccessorJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +TextureAccessorReader::readFromJson(const rapidjson::Value& value) const { + TextureAccessorJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +TextureAccessorReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader:: + ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "FeatureIDAttributeJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -2599,12 +4437,52 @@ FeatureIDAttributeJsonHandler::readObjectKeyFeatureIDAttribute( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +FeatureIDAttributeReader::FeatureIDAttributeReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& FeatureIDAttributeReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +FeatureIDAttributeReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +FeatureIDAttributeReader::readFromJson( + const gsl::span& data) const { + FeatureIDAttributeJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +FeatureIDAttributeReader::readFromJson(const rapidjson::Value& value) const { + FeatureIDAttributeJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +FeatureIDAttributeReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::FeatureIDAttribute, + FeatureIDAttributeJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "FeatureIDsJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -2650,12 +4528,47 @@ CesiumJsonReader::IJsonHandler* FeatureIDsJsonHandler::readObjectKeyFeatureIDs( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } -} // namespace CesiumGltfReader +FeatureIDsReader::FeatureIDsReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& FeatureIDsReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +FeatureIDsReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +FeatureIDsReader::readFromJson(const gsl::span& data) const { + FeatureIDsJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +FeatureIDsReader::readFromJson(const rapidjson::Value& value) const { + FeatureIDsJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +FeatureIDsReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader:: + ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +} // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "FeatureTextureJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -2699,12 +4612,50 @@ FeatureTextureJsonHandler::readObjectKeyFeatureTexture( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +FeatureTextureReader::FeatureTextureReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& FeatureTextureReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +FeatureTextureReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +FeatureTextureReader::readFromJson( + const gsl::span& data) const { + FeatureTextureJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +FeatureTextureReader::readFromJson(const rapidjson::Value& value) const { + FeatureTextureJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +FeatureTextureReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader:: + ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "FeatureTableJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -2751,12 +4702,47 @@ FeatureTableJsonHandler::readObjectKeyFeatureTable( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +FeatureTableReader::FeatureTableReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& FeatureTableReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +FeatureTableReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +FeatureTableReader::readFromJson(const gsl::span& data) const { + FeatureTableJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +FeatureTableReader::readFromJson(const rapidjson::Value& value) const { + FeatureTableJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +FeatureTableReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader:: + ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "FeatureTablePropertyJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -2812,12 +4798,52 @@ FeatureTablePropertyJsonHandler::readObjectKeyFeatureTableProperty( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +FeatureTablePropertyReader::FeatureTablePropertyReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& FeatureTablePropertyReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +FeatureTablePropertyReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +FeatureTablePropertyReader::readFromJson( + const gsl::span& data) const { + FeatureTablePropertyJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +FeatureTablePropertyReader::readFromJson(const rapidjson::Value& value) const { + FeatureTablePropertyJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +FeatureTablePropertyReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::FeatureTableProperty, + FeatureTablePropertyJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "StatisticsJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -2857,12 +4883,47 @@ CesiumJsonReader::IJsonHandler* StatisticsJsonHandler::readObjectKeyStatistics( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +StatisticsReader::StatisticsReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& StatisticsReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +StatisticsReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +StatisticsReader::readFromJson(const gsl::span& data) const { + StatisticsJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +StatisticsReader::readFromJson(const rapidjson::Value& value) const { + StatisticsJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +StatisticsReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader:: + ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ClassStatisticsJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -2906,12 +4967,50 @@ ClassStatisticsJsonHandler::readObjectKeyClassStatistics( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ClassStatisticsReader::ClassStatisticsReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& ClassStatisticsReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ClassStatisticsReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +ClassStatisticsReader::readFromJson( + const gsl::span& data) const { + ClassStatisticsJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +ClassStatisticsReader::readFromJson(const rapidjson::Value& value) const { + ClassStatisticsJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +ClassStatisticsReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader:: + ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "PropertyStatisticsJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -2976,12 +5075,52 @@ PropertyStatisticsJsonHandler::readObjectKeyPropertyStatistics( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +PropertyStatisticsReader::PropertyStatisticsReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& PropertyStatisticsReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +PropertyStatisticsReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +PropertyStatisticsReader::readFromJson( + const gsl::span& data) const { + PropertyStatisticsJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +PropertyStatisticsReader::readFromJson(const rapidjson::Value& value) const { + PropertyStatisticsJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +PropertyStatisticsReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::PropertyStatistics, + PropertyStatisticsJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "SchemaJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -3033,12 +5172,45 @@ CesiumJsonReader::IJsonHandler* SchemaJsonHandler::readObjectKeySchema( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +SchemaReader::SchemaReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& SchemaReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& SchemaReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +SchemaReader::readFromJson(const gsl::span& data) const { + SchemaJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +SchemaReader::readFromJson(const rapidjson::Value& value) const { + SchemaJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +SchemaReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "EnumJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -3087,12 +5259,45 @@ CesiumJsonReader::IJsonHandler* EnumJsonHandler::readObjectKeyEnum( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +EnumReader::EnumReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& EnumReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& EnumReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +EnumReader::readFromJson(const gsl::span& data) const { + EnumJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +EnumReader::readFromJson(const rapidjson::Value& value) const { + EnumJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +EnumReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler handler( + this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "EnumValueJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -3138,12 +5343,46 @@ CesiumJsonReader::IJsonHandler* EnumValueJsonHandler::readObjectKeyEnumValue( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +EnumValueReader::EnumValueReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& EnumValueReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& EnumValueReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +EnumValueReader::readFromJson(const gsl::span& data) const { + EnumValueJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +EnumValueReader::readFromJson(const rapidjson::Value& value) const { + EnumValueJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +EnumValueReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader:: + ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ClassJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -3189,12 +5428,45 @@ CesiumJsonReader::IJsonHandler* ClassJsonHandler::readObjectKeyClass( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ClassReader::ClassReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& ClassReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& ClassReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +ClassReader::readFromJson(const gsl::span& data) const { + ClassJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +ClassReader::readFromJson(const rapidjson::Value& value) const { + ClassJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +ClassReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ClassPropertyJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -3268,12 +5540,50 @@ ClassPropertyJsonHandler::readObjectKeyClassProperty( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ClassPropertyReader::ClassPropertyReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& ClassPropertyReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ClassPropertyReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +ClassPropertyReader::readFromJson( + const gsl::span& data) const { + ClassPropertyJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +ClassPropertyReader::readFromJson(const rapidjson::Value& value) const { + ClassPropertyJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +ClassPropertyReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader:: + ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ModelJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -3364,12 +5674,45 @@ CesiumJsonReader::IJsonHandler* ModelJsonHandler::readObjectKeyModel( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ModelReader::ModelReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& ModelReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& ModelReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +ModelReader::readFromJson(const gsl::span& data) const { + ModelJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +ModelReader::readFromJson(const rapidjson::Value& value) const { + ModelJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +ModelReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "TextureJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -3412,12 +5755,45 @@ CesiumJsonReader::IJsonHandler* TextureJsonHandler::readObjectKeyTexture( return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } +TextureReader::TextureReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& TextureReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& TextureReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +TextureReader::readFromJson(const gsl::span& data) const { + TextureJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +TextureReader::readFromJson(const rapidjson::Value& value) const { + TextureJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +TextureReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "SkinJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -3466,12 +5842,45 @@ CesiumJsonReader::IJsonHandler* SkinJsonHandler::readObjectKeySkin( return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } +SkinReader::SkinReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& SkinReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& SkinReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +SkinReader::readFromJson(const gsl::span& data) const { + SkinJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +SkinReader::readFromJson(const rapidjson::Value& value) const { + SkinJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +SkinReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler handler( + this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "SceneJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -3510,12 +5919,45 @@ CesiumJsonReader::IJsonHandler* SceneJsonHandler::readObjectKeyScene( return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } +SceneReader::SceneReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& SceneReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& SceneReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +SceneReader::readFromJson(const gsl::span& data) const { + SceneJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +SceneReader::readFromJson(const rapidjson::Value& value) const { + SceneJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +SceneReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "SamplerJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -3564,12 +6006,45 @@ CesiumJsonReader::IJsonHandler* SamplerJsonHandler::readObjectKeySampler( return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } +SamplerReader::SamplerReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& SamplerReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& SamplerReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +SamplerReader::readFromJson(const gsl::span& data) const { + SamplerJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +SamplerReader::readFromJson(const rapidjson::Value& value) const { + SamplerJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +SamplerReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "NodeJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -3633,12 +6108,45 @@ CesiumJsonReader::IJsonHandler* NodeJsonHandler::readObjectKeyNode( return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } +NodeReader::NodeReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& NodeReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& NodeReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +NodeReader::readFromJson(const gsl::span& data) const { + NodeJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +NodeReader::readFromJson(const rapidjson::Value& value) const { + NodeJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +NodeReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler handler( + this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "MeshJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -3681,12 +6189,45 @@ CesiumJsonReader::IJsonHandler* MeshJsonHandler::readObjectKeyMesh( return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } +MeshReader::MeshReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& MeshReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& MeshReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +MeshReader::readFromJson(const gsl::span& data) const { + MeshJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +MeshReader::readFromJson(const rapidjson::Value& value) const { + MeshJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +MeshReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler handler( + this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "MeshPrimitiveJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -3739,12 +6280,50 @@ MeshPrimitiveJsonHandler::readObjectKeyMeshPrimitive( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +MeshPrimitiveReader::MeshPrimitiveReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& MeshPrimitiveReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +MeshPrimitiveReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +MeshPrimitiveReader::readFromJson( + const gsl::span& data) const { + MeshPrimitiveJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +MeshPrimitiveReader::readFromJson(const rapidjson::Value& value) const { + MeshPrimitiveJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +MeshPrimitiveReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader:: + ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "MaterialJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -3814,12 +6393,45 @@ CesiumJsonReader::IJsonHandler* MaterialJsonHandler::readObjectKeyMaterial( return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } +MaterialReader::MaterialReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& MaterialReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& MaterialReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +MaterialReader::readFromJson(const gsl::span& data) const { + MaterialJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +MaterialReader::readFromJson(const rapidjson::Value& value) const { + MaterialJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +MaterialReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "MaterialOcclusionTextureInfoJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -3861,12 +6473,55 @@ CesiumJsonReader::IJsonHandler* MaterialOcclusionTextureInfoJsonHandler:: return this->readObjectKeyTextureInfo(objectType, str, *this->_pObject); } +MaterialOcclusionTextureInfoReader::MaterialOcclusionTextureInfoReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +MaterialOcclusionTextureInfoReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +MaterialOcclusionTextureInfoReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +MaterialOcclusionTextureInfoReader::readFromJson( + const gsl::span& data) const { + MaterialOcclusionTextureInfoJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +MaterialOcclusionTextureInfoReader::readFromJson( + const rapidjson::Value& value) const { + MaterialOcclusionTextureInfoJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +MaterialOcclusionTextureInfoReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::MaterialOcclusionTextureInfo, + MaterialOcclusionTextureInfoJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "MaterialNormalTextureInfoJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -3907,12 +6562,55 @@ MaterialNormalTextureInfoJsonHandler::readObjectKeyMaterialNormalTextureInfo( return this->readObjectKeyTextureInfo(objectType, str, *this->_pObject); } +MaterialNormalTextureInfoReader::MaterialNormalTextureInfoReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +MaterialNormalTextureInfoReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +MaterialNormalTextureInfoReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +MaterialNormalTextureInfoReader::readFromJson( + const gsl::span& data) const { + MaterialNormalTextureInfoJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +MaterialNormalTextureInfoReader::readFromJson( + const rapidjson::Value& value) const { + MaterialNormalTextureInfoJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +MaterialNormalTextureInfoReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::MaterialNormalTextureInfo, + MaterialNormalTextureInfoJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "MaterialPBRMetallicRoughnessJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -3979,12 +6677,55 @@ CesiumJsonReader::IJsonHandler* MaterialPBRMetallicRoughnessJsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +MaterialPBRMetallicRoughnessReader::MaterialPBRMetallicRoughnessReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +MaterialPBRMetallicRoughnessReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +MaterialPBRMetallicRoughnessReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +MaterialPBRMetallicRoughnessReader::readFromJson( + const gsl::span& data) const { + MaterialPBRMetallicRoughnessJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +MaterialPBRMetallicRoughnessReader::readFromJson( + const rapidjson::Value& value) const { + MaterialPBRMetallicRoughnessJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +MaterialPBRMetallicRoughnessReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::MaterialPBRMetallicRoughness, + MaterialPBRMetallicRoughnessJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ImageJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -4030,12 +6771,45 @@ CesiumJsonReader::IJsonHandler* ImageJsonHandler::readObjectKeyImage( return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } +ImageReader::ImageReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& ImageReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& ImageReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +ImageReader::readFromJson(const gsl::span& data) const { + ImageJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +ImageReader::readFromJson(const rapidjson::Value& value) const { + ImageJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +ImageReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "CameraJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -4081,12 +6855,45 @@ CesiumJsonReader::IJsonHandler* CameraJsonHandler::readObjectKeyCamera( return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } +CameraReader::CameraReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& CameraReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& CameraReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +CameraReader::readFromJson(const gsl::span& data) const { + CameraJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +CameraReader::readFromJson(const rapidjson::Value& value) const { + CameraJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +CameraReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "CameraPerspectiveJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -4136,12 +6943,52 @@ CameraPerspectiveJsonHandler::readObjectKeyCameraPerspective( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +CameraPerspectiveReader::CameraPerspectiveReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& CameraPerspectiveReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +CameraPerspectiveReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +CameraPerspectiveReader::readFromJson( + const gsl::span& data) const { + CameraPerspectiveJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +CameraPerspectiveReader::readFromJson(const rapidjson::Value& value) const { + CameraPerspectiveJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +CameraPerspectiveReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::CameraPerspective, + CameraPerspectiveJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "CameraOrthographicJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -4191,12 +7038,52 @@ CameraOrthographicJsonHandler::readObjectKeyCameraOrthographic( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +CameraOrthographicReader::CameraOrthographicReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& CameraOrthographicReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +CameraOrthographicReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +CameraOrthographicReader::readFromJson( + const gsl::span& data) const { + CameraOrthographicJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +CameraOrthographicReader::readFromJson(const rapidjson::Value& value) const { + CameraOrthographicJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +CameraOrthographicReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::CameraOrthographic, + CameraOrthographicJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "BufferViewJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -4248,12 +7135,47 @@ CesiumJsonReader::IJsonHandler* BufferViewJsonHandler::readObjectKeyBufferView( return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } +BufferViewReader::BufferViewReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& BufferViewReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +BufferViewReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +BufferViewReader::readFromJson(const gsl::span& data) const { + BufferViewJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +BufferViewReader::readFromJson(const rapidjson::Value& value) const { + BufferViewJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +BufferViewReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader:: + ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "BufferJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -4296,12 +7218,45 @@ CesiumJsonReader::IJsonHandler* BufferJsonHandler::readObjectKeyBuffer( return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } +BufferReader::BufferReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& BufferReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& BufferReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +BufferReader::readFromJson(const gsl::span& data) const { + BufferJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +BufferReader::readFromJson(const rapidjson::Value& value) const { + BufferJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +BufferReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "AssetJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -4350,12 +7305,45 @@ CesiumJsonReader::IJsonHandler* AssetJsonHandler::readObjectKeyAsset( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +AssetReader::AssetReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& AssetReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& AssetReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +AssetReader::readFromJson(const gsl::span& data) const { + AssetJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +AssetReader::readFromJson(const rapidjson::Value& value) const { + AssetJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +AssetReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "AnimationJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -4398,12 +7386,46 @@ CesiumJsonReader::IJsonHandler* AnimationJsonHandler::readObjectKeyAnimation( return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } +AnimationReader::AnimationReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& AnimationReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& AnimationReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +AnimationReader::readFromJson(const gsl::span& data) const { + AnimationJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +AnimationReader::readFromJson(const rapidjson::Value& value) const { + AnimationJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +AnimationReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader:: + ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "AnimationSamplerJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -4450,12 +7472,51 @@ AnimationSamplerJsonHandler::readObjectKeyAnimationSampler( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +AnimationSamplerReader::AnimationSamplerReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& AnimationSamplerReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +AnimationSamplerReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +AnimationSamplerReader::readFromJson( + const gsl::span& data) const { + AnimationSamplerJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +AnimationSamplerReader::readFromJson(const rapidjson::Value& value) const { + AnimationSamplerJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +AnimationSamplerReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::AnimationSampler, + AnimationSamplerJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "AnimationChannelJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -4499,12 +7560,51 @@ AnimationChannelJsonHandler::readObjectKeyAnimationChannel( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +AnimationChannelReader::AnimationChannelReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& AnimationChannelReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +AnimationChannelReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +AnimationChannelReader::readFromJson( + const gsl::span& data) const { + AnimationChannelJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +AnimationChannelReader::readFromJson(const rapidjson::Value& value) const { + AnimationChannelJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +AnimationChannelReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::AnimationChannel, + AnimationChannelJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "AnimationChannelTargetJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -4548,12 +7648,55 @@ AnimationChannelTargetJsonHandler::readObjectKeyAnimationChannelTarget( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +AnimationChannelTargetReader::AnimationChannelTargetReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +AnimationChannelTargetReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +AnimationChannelTargetReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +AnimationChannelTargetReader::readFromJson( + const gsl::span& data) const { + AnimationChannelTargetJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +AnimationChannelTargetReader::readFromJson( + const rapidjson::Value& value) const { + AnimationChannelTargetJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +AnimationChannelTargetReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::AnimationChannelTarget, + AnimationChannelTargetJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "AccessorJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -4617,12 +7760,45 @@ CesiumJsonReader::IJsonHandler* AccessorJsonHandler::readObjectKeyAccessor( return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } +AccessorReader::AccessorReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& AccessorReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& AccessorReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +AccessorReader::readFromJson(const gsl::span& data) const { + AccessorJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +AccessorReader::readFromJson(const rapidjson::Value& value) const { + AccessorJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +AccessorReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "AccessorSparseJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -4669,12 +7845,50 @@ AccessorSparseJsonHandler::readObjectKeyAccessorSparse( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +AccessorSparseReader::AccessorSparseReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& AccessorSparseReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +AccessorSparseReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +AccessorSparseReader::readFromJson( + const gsl::span& data) const { + AccessorSparseJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +AccessorSparseReader::readFromJson(const rapidjson::Value& value) const { + AccessorSparseJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +AccessorSparseReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader:: + ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "AccessorSparseValuesJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -4718,12 +7932,52 @@ AccessorSparseValuesJsonHandler::readObjectKeyAccessorSparseValues( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +AccessorSparseValuesReader::AccessorSparseValuesReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& AccessorSparseValuesReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +AccessorSparseValuesReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +AccessorSparseValuesReader::readFromJson( + const gsl::span& data) const { + AccessorSparseValuesJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +AccessorSparseValuesReader::readFromJson(const rapidjson::Value& value) const { + AccessorSparseValuesJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +AccessorSparseValuesReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::AccessorSparseValues, + AccessorSparseValuesJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "AccessorSparseIndicesJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -4770,4 +8024,40 @@ AccessorSparseIndicesJsonHandler::readObjectKeyAccessorSparseIndices( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +AccessorSparseIndicesReader::AccessorSparseIndicesReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& AccessorSparseIndicesReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +AccessorSparseIndicesReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +AccessorSparseIndicesReader::readFromJson( + const gsl::span& data) const { + AccessorSparseIndicesJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +AccessorSparseIndicesReader::readFromJson(const rapidjson::Value& value) const { + AccessorSparseIndicesJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +AccessorSparseIndicesReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::AccessorSparseIndices, + AccessorSparseIndicesJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader diff --git a/CesiumJsonReader/include/CesiumJsonReader/IExtensionJsonHandler.h b/CesiumJsonReader/include/CesiumJsonReader/IExtensionJsonHandler.h index c640ff1c6..1e193eb6c 100644 --- a/CesiumJsonReader/include/CesiumJsonReader/IExtensionJsonHandler.h +++ b/CesiumJsonReader/include/CesiumJsonReader/IExtensionJsonHandler.h @@ -9,12 +9,14 @@ namespace CesiumJsonReader { -class IExtensionJsonHandler : public IJsonHandler { +class IExtensionJsonHandler { public: + virtual ~IExtensionJsonHandler() noexcept = 0 {} virtual void reset( IJsonHandler* pParentHandler, CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) = 0; + virtual IJsonHandler& getHandler() = 0; }; } // namespace CesiumJsonReader diff --git a/CesiumJsonReader/src/ExtensionsJsonHandler.cpp b/CesiumJsonReader/src/ExtensionsJsonHandler.cpp index 2b3ef0e28..18535961a 100644 --- a/CesiumJsonReader/src/ExtensionsJsonHandler.cpp +++ b/CesiumJsonReader/src/ExtensionsJsonHandler.cpp @@ -21,7 +21,7 @@ ExtensionsJsonHandler::readObjectKey(const std::string_view& str) { this->_context.createExtensionHandler(str, this->_objectType); if (this->_currentExtensionHandler) { this->_currentExtensionHandler->reset(this, *this->_pObject, str); - return this->_currentExtensionHandler.get(); + return &this->_currentExtensionHandler->getHandler(); } else { return this->ignoreAndContinue(); } diff --git a/CesiumJsonReader/src/JsonReaderOptions.cpp b/CesiumJsonReader/src/JsonReaderOptions.cpp index ad894ba0c..ead57754d 100644 --- a/CesiumJsonReader/src/JsonReaderOptions.cpp +++ b/CesiumJsonReader/src/JsonReaderOptions.cpp @@ -25,6 +25,8 @@ class AnyExtensionJsonHandler : public JsonObjectJsonHandler, &std::any_cast(value)); } + virtual IJsonHandler& getHandler() override { return *this; } + virtual IJsonHandler* readNull() override { return JsonObjectJsonHandler::readNull(); }; diff --git a/tools/generate-classes/NameFormatters.js b/tools/generate-classes/NameFormatters.js index 5cafe07d5..4a2a49516 100644 --- a/tools/generate-classes/NameFormatters.js +++ b/tools/generate-classes/NameFormatters.js @@ -34,7 +34,7 @@ const NameFormatters = { } }, - getReaderIncludeFromName: function getReaderIncludeFromName( + getJsonHandlerIncludeFromName: function getJsonHandlerIncludeFromName( name, readerNamespace ) { @@ -54,7 +54,7 @@ const NameFormatters = { } }, - getReaderName: function getReaderName(name, readerNamespace) { + getJsonHandlerName: function getJsonHandlerName(name, readerNamespace) { const pieces = name.match(qualifiedTypeNameRegex); if (pieces && pieces.groups && pieces.groups.namespace) { const namespace = NameFormatters.getReaderNamespace( diff --git a/tools/generate-classes/generate.js b/tools/generate-classes/generate.js index cce3b7720..9037a4b1e 100644 --- a/tools/generate-classes/generate.js +++ b/tools/generate-classes/generate.js @@ -122,39 +122,42 @@ function generate(options, schema, writers) { ); fs.writeFileSync(headerOutputPath, unindent(header), "utf-8"); - let readerHeaders = lodash.uniq([ - NameFormatters.getReaderIncludeFromName(base, readerNamespace), + let jsonHandlerHeaders = lodash.uniq([ + NameFormatters.getJsonHandlerIncludeFromName(base, readerNamespace), `<${namespace}/${name}.h>`, ...lodash.flatten(properties.map((property) => property.readerHeaders)), ]); // Prevent header from including itself for recursive types like Tile - readerHeaders = readerHeaders.filter((readerHeader) => { + jsonHandlerHeaders = jsonHandlerHeaders.filter((readerHeader) => { return readerHeader !== `"${name}JsonHandler.h"`; }); - readerHeaders.sort(); + jsonHandlerHeaders.sort(); const readerLocalTypes = lodash.uniq( lodash.flatten(properties.map((property) => property.readerLocalTypes)) ); - const baseReader = NameFormatters.getReaderName(base, readerNamespace); + const baseJsonHandler = NameFormatters.getJsonHandlerName( + base, + readerNamespace + ); // prettier-ignore - const readerHeader = ` + const jsonHandlerHeader = ` // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #pragma once - ${readerHeaders.map((header) => `#include ${header}`).join("\n")} + ${jsonHandlerHeaders.map((header) => `#include ${header}`).join("\n")} namespace CesiumJsonReader { class JsonReaderOptions; } namespace ${readerNamespace} { - class ${name}JsonHandler : public ${baseReader}${thisConfig.extensionName ? `, public CesiumJsonReader::IExtensionJsonHandler` : ""} { + class ${name}JsonHandler : public ${baseJsonHandler}${thisConfig.extensionName ? `, public CesiumJsonReader::IExtensionJsonHandler` : ""} { public: using ValueType = ${namespace}::${name}; @@ -168,45 +171,7 @@ function generate(options, schema, writers) { ${thisConfig.extensionName ? ` virtual void reset(IJsonHandler* pParentHandler, CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) override; - virtual IJsonHandler* readNull() override { - return ${baseReader}::readNull(); - }; - virtual IJsonHandler* readBool(bool b) override { - return ${baseReader}::readBool(b); - } - virtual IJsonHandler* readInt32(int32_t i) override { - return ${baseReader}::readInt32(i); - } - virtual IJsonHandler* readUint32(uint32_t i) override { - return ${baseReader}::readUint32(i); - } - virtual IJsonHandler* readInt64(int64_t i) override { - return ${baseReader}::readInt64(i); - } - virtual IJsonHandler* readUint64(uint64_t i) override { - return ${baseReader}::readUint64(i); - } - virtual IJsonHandler* readDouble(double d) override { - return ${baseReader}::readDouble(d); - } - virtual IJsonHandler* readString(const std::string_view& str) override { - return ${baseReader}::readString(str); - } - virtual IJsonHandler* readObjectStart() override { - return ${baseReader}::readObjectStart(); - } - virtual IJsonHandler* readObjectEnd() override { - return ${baseReader}::readObjectEnd(); - } - virtual IJsonHandler* readArrayStart() override { - return ${baseReader}::readArrayStart(); - } - virtual IJsonHandler* readArrayEnd() override { - return ${baseReader}::readArrayEnd(); - } - virtual void reportWarning(const std::string& warning, std::vector&& context = std::vector()) override { - ${baseReader}::reportWarning(warning, std::move(context)); - } + virtual IJsonHandler& getHandler() override { return *this; } ` : ""} protected: @@ -225,25 +190,113 @@ function generate(options, schema, writers) { } `; - const readerHeaderOutputDir = path.join(readerOutputDir, "generated", "src"); + const jsonHandlerHeaderOutputDir = path.join( + readerOutputDir, + "generated", + "src" + ); + fs.mkdirSync(jsonHandlerHeaderOutputDir, { recursive: true }); + + const jsonHandlerHeaderOutputPath = path.join( + jsonHandlerHeaderOutputDir, + name + "JsonHandler.h" + ); + fs.writeFileSync( + jsonHandlerHeaderOutputPath, + unindent(jsonHandlerHeader), + "utf-8" + ); + + const readerHeader = ` + // This file was generated by generate-classes. + // DO NOT EDIT THIS FILE! + #pragma once + + #include <${readerNamespace}/Library.h> + #include + #include + #include <${namespace}/${name}.h> + #include + #include + #include + + namespace ${namespace} { + struct ${name}; + } + + namespace ${readerNamespace} { + + class ${readerNamespace.toUpperCase()}_API ${name}Reader { + public: + /** + * @brief Constructs a new instance. + */ + ${name}Reader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ${name} from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult<${namespace}::${name}> readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ${name} from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult<${namespace}::${name}> readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of ${name} from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> readArrayFromJson(const rapidjson::Value& value) const; + + private: + CesiumJsonReader::JsonReaderOptions _options; + }; + + } // namespace ${readerNamespace}`; + + const readerHeaderOutputDir = path.join( + readerOutputDir, + "generated", + "include", + readerNamespace + ); fs.mkdirSync(readerHeaderOutputDir, { recursive: true }); const readerHeaderOutputPath = path.join( readerHeaderOutputDir, - name + "JsonHandler.h" + name + "Reader.h" ); fs.writeFileSync(readerHeaderOutputPath, unindent(readerHeader), "utf-8"); - const readerLocalTypesImpl = lodash.uniq( + const jsonHandlerLocalTypesImpl = lodash.uniq( lodash.flatten(properties.map((property) => property.readerLocalTypesImpl)) ); - const readerHeadersImpl = lodash.uniq([ + const jsonHandlerHeadersImpl = lodash.uniq([ ...lodash.flatten(properties.map((property) => property.readerHeadersImpl)), ]); - readerHeadersImpl.sort(); + jsonHandlerHeadersImpl.sort(); - function generateReaderOptionsInitializerList(properties, varName) { + function generateJsonHandlerOptionsInitializerList(properties, varName) { const initializerList = properties .filter((p) => p.readerType.toLowerCase().indexOf("jsonhandler") != -1) .map( @@ -256,22 +309,26 @@ function generate(options, schema, writers) { return initializerList == "" ? "" : ", " + initializerList; } // prettier-ignore - const readerImpl = ` + const jsonHandlerImpl = ` // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "${name}JsonHandler.h" #include <${namespace}/${name}.h> - ${readerHeadersImpl.map((header) => `#include ${header}`).join("\n")} + ${jsonHandlerHeadersImpl.map((header) => `#include ${header}`).join("\n")} + #include <${readerNamespace}/${name}Reader.h> + #include + #include + #include "registerExtensions.h" #include #include namespace ${readerNamespace} { - ${name}JsonHandler::${name}JsonHandler(const CesiumJsonReader::JsonReaderOptions& options) noexcept : ${baseReader}(options)${generateReaderOptionsInitializerList(properties, 'options')} {} + ${name}JsonHandler::${name}JsonHandler(const CesiumJsonReader::JsonReaderOptions& options) noexcept : ${baseJsonHandler}(options)${generateJsonHandlerOptionsInitializerList(properties, 'options')} {} void ${name}JsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, ${namespace}::${name}* pObject) { - ${baseReader}::reset(pParentHandler, pObject); + ${baseJsonHandler}::reset(pParentHandler, pObject); this->_pObject = pObject; } @@ -305,7 +362,32 @@ function generate(options, schema, writers) { return this->readObjectKey${NameFormatters.removeNamespace(base)}(objectType, str, *this->_pObject); } - ${indent(readerLocalTypesImpl.join("\n\n"), 8)} + ${name}Reader::${name}Reader() { registerExtensions(this->_options); } + + CesiumJsonReader::JsonReaderOptions& ${name}Reader::getOptions() { + return this->_options; + } + + const CesiumJsonReader::JsonReaderOptions& ${name}Reader::getOptions() const { + return this->_options; + } + + CesiumJsonReader::ReadJsonResult<${namespace}::${name}> ${name}Reader::readFromJson(const gsl::span& data) const { + ${name}JsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); + } + + CesiumJsonReader::ReadJsonResult<${namespace}::${name}> ${name}Reader::readFromJson(const rapidjson::Value& value) const { + ${name}JsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); + } + + CesiumJsonReader::ReadJsonResult> ${name}Reader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler<${namespace}::${name}, ${name}JsonHandler> handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); + } + + ${indent(jsonHandlerLocalTypesImpl.join("\n\n"), 8)} } // namespace ${readerNamespace} `; @@ -427,16 +509,24 @@ function generate(options, schema, writers) { if (options.oneHandlerFile) { const readerSourceOutputPath = path.join( - readerHeaderOutputDir, + jsonHandlerHeaderOutputDir, "GeneratedJsonHandlers.cpp" ); - fs.appendFileSync(readerSourceOutputPath, unindent(readerImpl), "utf-8"); + fs.appendFileSync( + readerSourceOutputPath, + unindent(jsonHandlerImpl), + "utf-8" + ); } else { const readerSourceOutputPath = path.join( - readerHeaderOutputDir, + jsonHandlerHeaderOutputDir, name + "JsonHandler.cpp" ); - fs.writeFileSync(readerSourceOutputPath, unindent(readerImpl), "utf-8"); + fs.writeFileSync( + readerSourceOutputPath, + unindent(jsonHandlerImpl), + "utf-8" + ); } const schemas = lodash.flatten( diff --git a/tools/generate-classes/resolveProperty.js b/tools/generate-classes/resolveProperty.js index f39f817e2..66e70f776 100644 --- a/tools/generate-classes/resolveProperty.js +++ b/tools/generate-classes/resolveProperty.js @@ -115,9 +115,9 @@ function resolveProperty( NameFormatters.getIncludeFromName(type, namespace), ...(makeOptional ? [""] : []), ], - readerType: NameFormatters.getReaderName(type, readerNamespace), + readerType: NameFormatters.getJsonHandlerName(type, readerNamespace), readerHeaders: [ - NameFormatters.getReaderIncludeFromName(type, readerNamespace), + NameFormatters.getJsonHandlerIncludeFromName(type, readerNamespace), ], schemas: [schema], }; @@ -198,9 +198,9 @@ function resolveProperty( NameFormatters.getIncludeFromName(type, namespace), ...(makeOptional ? [""] : []), ], - readerType: NameFormatters.getReaderName(type, readerNamespace), + readerType: NameFormatters.getJsonHandlerName(type, readerNamespace), readerHeaders: [ - NameFormatters.getReaderIncludeFromName(type, readerNamespace), + NameFormatters.getJsonHandlerIncludeFromName(type, readerNamespace), ], schemas: [itemSchema], }; From cfaaf4d737fc9d3bd62b2f0fb35201d418822e8f Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Tue, 22 Aug 2023 22:23:04 +1000 Subject: [PATCH 194/421] Add doc for readers. --- .../generated/include/Cesium3DTilesReader/AssetReader.h | 3 +++ .../generated/include/Cesium3DTilesReader/AvailabilityReader.h | 3 +++ .../include/Cesium3DTilesReader/BoundingVolumeReader.h | 3 +++ .../generated/include/Cesium3DTilesReader/BufferReader.h | 3 +++ .../generated/include/Cesium3DTilesReader/BufferViewReader.h | 3 +++ .../include/Cesium3DTilesReader/ClassPropertyReader.h | 3 +++ .../generated/include/Cesium3DTilesReader/ClassReader.h | 3 +++ .../include/Cesium3DTilesReader/ClassStatisticsReader.h | 3 +++ .../generated/include/Cesium3DTilesReader/ContentReader.h | 3 +++ .../generated/include/Cesium3DTilesReader/EnumReader.h | 3 +++ .../generated/include/Cesium3DTilesReader/EnumValueReader.h | 3 +++ .../Extension3dTilesBoundingVolumeS2Reader.h | 3 +++ .../include/Cesium3DTilesReader/GroupMetadataReader.h | 3 +++ .../include/Cesium3DTilesReader/ImplicitTilingReader.h | 3 +++ .../include/Cesium3DTilesReader/MetadataEntityReader.h | 3 +++ .../generated/include/Cesium3DTilesReader/PropertiesReader.h | 3 +++ .../include/Cesium3DTilesReader/PropertyStatisticsReader.h | 3 +++ .../include/Cesium3DTilesReader/PropertyTablePropertyReader.h | 3 +++ .../include/Cesium3DTilesReader/PropertyTableReader.h | 3 +++ .../generated/include/Cesium3DTilesReader/SchemaReader.h | 3 +++ .../generated/include/Cesium3DTilesReader/StatisticsReader.h | 3 +++ .../generated/include/Cesium3DTilesReader/SubtreeReader.h | 3 +++ .../generated/include/Cesium3DTilesReader/SubtreesReader.h | 3 +++ .../generated/include/Cesium3DTilesReader/TileReader.h | 3 +++ .../generated/include/Cesium3DTilesReader/TilesetReader.h | 3 +++ .../generated/include/CesiumGltfReader/AccessorReader.h | 3 +++ .../include/CesiumGltfReader/AccessorSparseIndicesReader.h | 3 +++ .../generated/include/CesiumGltfReader/AccessorSparseReader.h | 3 +++ .../include/CesiumGltfReader/AccessorSparseValuesReader.h | 3 +++ .../include/CesiumGltfReader/AnimationChannelReader.h | 3 +++ .../include/CesiumGltfReader/AnimationChannelTargetReader.h | 3 +++ .../generated/include/CesiumGltfReader/AnimationReader.h | 3 +++ .../include/CesiumGltfReader/AnimationSamplerReader.h | 3 +++ .../generated/include/CesiumGltfReader/AssetReader.h | 3 +++ .../generated/include/CesiumGltfReader/BufferReader.h | 3 +++ .../generated/include/CesiumGltfReader/BufferViewReader.h | 3 +++ .../include/CesiumGltfReader/CameraOrthographicReader.h | 3 +++ .../include/CesiumGltfReader/CameraPerspectiveReader.h | 3 +++ .../generated/include/CesiumGltfReader/CameraReader.h | 3 +++ .../generated/include/CesiumGltfReader/ClassPropertyReader.h | 3 +++ .../generated/include/CesiumGltfReader/ClassReader.h | 3 +++ .../generated/include/CesiumGltfReader/ClassStatisticsReader.h | 3 +++ .../generated/include/CesiumGltfReader/EnumReader.h | 3 +++ .../generated/include/CesiumGltfReader/EnumValueReader.h | 3 +++ .../ExtensionBufferExtMeshoptCompressionReader.h | 3 +++ .../ExtensionBufferViewExtMeshoptCompressionReader.h | 3 +++ .../include/CesiumGltfReader/ExtensionCesiumRTCReader.h | 3 +++ .../include/CesiumGltfReader/ExtensionCesiumTileEdgesReader.h | 3 +++ .../ExtensionExtInstanceFeaturesFeatureIdReader.h | 3 +++ .../CesiumGltfReader/ExtensionExtInstanceFeaturesReader.h | 3 +++ .../CesiumGltfReader/ExtensionExtMeshFeaturesFeatureIdReader.h | 3 +++ .../ExtensionExtMeshFeaturesFeatureIdTextureReader.h | 3 +++ .../include/CesiumGltfReader/ExtensionExtMeshFeaturesReader.h | 3 +++ .../CesiumGltfReader/ExtensionExtMeshGpuInstancingReader.h | 3 +++ .../ExtensionExtStructuralMetadataClassPropertyReader.h | 3 +++ .../ExtensionExtStructuralMetadataClassReader.h | 3 +++ .../ExtensionExtStructuralMetadataEnumReader.h | 3 +++ .../ExtensionExtStructuralMetadataEnumValueReader.h | 3 +++ ...nsionExtStructuralMetadataPropertyAttributePropertyReader.h | 3 +++ .../ExtensionExtStructuralMetadataPropertyAttributeReader.h | 3 +++ ...ExtensionExtStructuralMetadataPropertyTablePropertyReader.h | 3 +++ .../ExtensionExtStructuralMetadataPropertyTableReader.h | 3 +++ ...tensionExtStructuralMetadataPropertyTexturePropertyReader.h | 3 +++ .../ExtensionExtStructuralMetadataPropertyTextureReader.h | 3 +++ .../ExtensionExtStructuralMetadataSchemaReader.h | 3 +++ .../CesiumGltfReader/ExtensionKhrDracoMeshCompressionReader.h | 3 +++ .../CesiumGltfReader/ExtensionKhrMaterialsUnlitReader.h | 3 +++ .../include/CesiumGltfReader/ExtensionKhrTextureBasisuReader.h | 3 +++ .../CesiumGltfReader/ExtensionKhrTextureTransformReader.h | 3 +++ .../ExtensionMeshPrimitiveExtFeatureMetadataReader.h | 3 +++ .../ExtensionMeshPrimitiveExtStructuralMetadataReader.h | 3 +++ ...nsionMeshPrimitiveKhrMaterialsVariantsMappingsValueReader.h | 3 +++ .../ExtensionMeshPrimitiveKhrMaterialsVariantsReader.h | 3 +++ .../CesiumGltfReader/ExtensionModelExtFeatureMetadataReader.h | 3 +++ .../ExtensionModelExtStructuralMetadataReader.h | 3 +++ .../ExtensionModelKhrMaterialsVariantsReader.h | 3 +++ .../ExtensionModelKhrMaterialsVariantsValueReader.h | 3 +++ .../CesiumGltfReader/ExtensionModelMaxarMeshVariantsReader.h | 3 +++ .../ExtensionModelMaxarMeshVariantsValueReader.h | 3 +++ .../ExtensionNodeMaxarMeshVariantsMappingsValueReader.h | 3 +++ .../CesiumGltfReader/ExtensionNodeMaxarMeshVariantsReader.h | 3 +++ .../include/CesiumGltfReader/ExtensionTextureWebpReader.h | 3 +++ .../include/CesiumGltfReader/FeatureIDAttributeReader.h | 3 +++ .../include/CesiumGltfReader/FeatureIDTextureReader.h | 3 +++ .../generated/include/CesiumGltfReader/FeatureIDsReader.h | 3 +++ .../include/CesiumGltfReader/FeatureTablePropertyReader.h | 3 +++ .../generated/include/CesiumGltfReader/FeatureTableReader.h | 3 +++ .../generated/include/CesiumGltfReader/FeatureTextureReader.h | 3 +++ .../generated/include/CesiumGltfReader/ImageReader.h | 3 +++ .../include/CesiumGltfReader/MaterialNormalTextureInfoReader.h | 3 +++ .../CesiumGltfReader/MaterialOcclusionTextureInfoReader.h | 3 +++ .../CesiumGltfReader/MaterialPBRMetallicRoughnessReader.h | 3 +++ .../generated/include/CesiumGltfReader/MaterialReader.h | 3 +++ .../generated/include/CesiumGltfReader/MeshPrimitiveReader.h | 3 +++ .../generated/include/CesiumGltfReader/MeshReader.h | 3 +++ .../generated/include/CesiumGltfReader/ModelReader.h | 3 +++ .../generated/include/CesiumGltfReader/NodeReader.h | 3 +++ .../include/CesiumGltfReader/PropertyStatisticsReader.h | 3 +++ .../generated/include/CesiumGltfReader/SamplerReader.h | 3 +++ .../generated/include/CesiumGltfReader/SceneReader.h | 3 +++ .../generated/include/CesiumGltfReader/SchemaReader.h | 3 +++ .../generated/include/CesiumGltfReader/SkinReader.h | 3 +++ .../generated/include/CesiumGltfReader/StatisticsReader.h | 3 +++ .../generated/include/CesiumGltfReader/TextureAccessorReader.h | 3 +++ .../generated/include/CesiumGltfReader/TextureInfoReader.h | 3 +++ .../generated/include/CesiumGltfReader/TextureReader.h | 3 +++ tools/generate-classes/generate.js | 3 +++ 107 files changed, 321 insertions(+) diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/AssetReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/AssetReader.h index 5e0ad3faa..9abdcd5d5 100644 --- a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/AssetReader.h +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/AssetReader.h @@ -18,6 +18,9 @@ struct Asset; namespace Cesium3DTilesReader { +/** + * @brief Reads {@link Asset} instances from JSON. + */ class CESIUM3DTILESREADER_API AssetReader { public: /** diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/AvailabilityReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/AvailabilityReader.h index e746647aa..e3cd4845f 100644 --- a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/AvailabilityReader.h +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/AvailabilityReader.h @@ -18,6 +18,9 @@ struct Availability; namespace Cesium3DTilesReader { +/** + * @brief Reads {@link Availability} instances from JSON. + */ class CESIUM3DTILESREADER_API AvailabilityReader { public: /** diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/BoundingVolumeReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/BoundingVolumeReader.h index 51266d2d3..7de46c70e 100644 --- a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/BoundingVolumeReader.h +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/BoundingVolumeReader.h @@ -18,6 +18,9 @@ struct BoundingVolume; namespace Cesium3DTilesReader { +/** + * @brief Reads {@link BoundingVolume} instances from JSON. + */ class CESIUM3DTILESREADER_API BoundingVolumeReader { public: /** diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/BufferReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/BufferReader.h index 70ae16e3b..7a735cca7 100644 --- a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/BufferReader.h +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/BufferReader.h @@ -18,6 +18,9 @@ struct Buffer; namespace Cesium3DTilesReader { +/** + * @brief Reads {@link Buffer} instances from JSON. + */ class CESIUM3DTILESREADER_API BufferReader { public: /** diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/BufferViewReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/BufferViewReader.h index b06617adb..3813e1a97 100644 --- a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/BufferViewReader.h +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/BufferViewReader.h @@ -18,6 +18,9 @@ struct BufferView; namespace Cesium3DTilesReader { +/** + * @brief Reads {@link BufferView} instances from JSON. + */ class CESIUM3DTILESREADER_API BufferViewReader { public: /** diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ClassPropertyReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ClassPropertyReader.h index 632d5ac80..20d68bfcf 100644 --- a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ClassPropertyReader.h +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ClassPropertyReader.h @@ -18,6 +18,9 @@ struct ClassProperty; namespace Cesium3DTilesReader { +/** + * @brief Reads {@link ClassProperty} instances from JSON. + */ class CESIUM3DTILESREADER_API ClassPropertyReader { public: /** diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ClassReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ClassReader.h index 5ee9c0194..c3020b056 100644 --- a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ClassReader.h +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ClassReader.h @@ -18,6 +18,9 @@ struct Class; namespace Cesium3DTilesReader { +/** + * @brief Reads {@link Class} instances from JSON. + */ class CESIUM3DTILESREADER_API ClassReader { public: /** diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ClassStatisticsReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ClassStatisticsReader.h index 69714777f..dd11e3a32 100644 --- a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ClassStatisticsReader.h +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ClassStatisticsReader.h @@ -18,6 +18,9 @@ struct ClassStatistics; namespace Cesium3DTilesReader { +/** + * @brief Reads {@link ClassStatistics} instances from JSON. + */ class CESIUM3DTILESREADER_API ClassStatisticsReader { public: /** diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ContentReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ContentReader.h index 519914dcd..deddd1921 100644 --- a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ContentReader.h +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ContentReader.h @@ -18,6 +18,9 @@ struct Content; namespace Cesium3DTilesReader { +/** + * @brief Reads {@link Content} instances from JSON. + */ class CESIUM3DTILESREADER_API ContentReader { public: /** diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/EnumReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/EnumReader.h index 59be8c989..7ea040745 100644 --- a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/EnumReader.h +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/EnumReader.h @@ -18,6 +18,9 @@ struct Enum; namespace Cesium3DTilesReader { +/** + * @brief Reads {@link Enum} instances from JSON. + */ class CESIUM3DTILESREADER_API EnumReader { public: /** diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/EnumValueReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/EnumValueReader.h index fb88d8e44..96ef662d0 100644 --- a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/EnumValueReader.h +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/EnumValueReader.h @@ -18,6 +18,9 @@ struct EnumValue; namespace Cesium3DTilesReader { +/** + * @brief Reads {@link EnumValue} instances from JSON. + */ class CESIUM3DTILESREADER_API EnumValueReader { public: /** diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/Extension3dTilesBoundingVolumeS2Reader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/Extension3dTilesBoundingVolumeS2Reader.h index 328f48714..e5dcb3560 100644 --- a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/Extension3dTilesBoundingVolumeS2Reader.h +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/Extension3dTilesBoundingVolumeS2Reader.h @@ -18,6 +18,9 @@ struct Extension3dTilesBoundingVolumeS2; namespace Cesium3DTilesReader { +/** + * @brief Reads {@link Extension3dTilesBoundingVolumeS2} instances from JSON. + */ class CESIUM3DTILESREADER_API Extension3dTilesBoundingVolumeS2Reader { public: /** diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/GroupMetadataReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/GroupMetadataReader.h index 0175b2e4a..60163eb62 100644 --- a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/GroupMetadataReader.h +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/GroupMetadataReader.h @@ -18,6 +18,9 @@ struct GroupMetadata; namespace Cesium3DTilesReader { +/** + * @brief Reads {@link GroupMetadata} instances from JSON. + */ class CESIUM3DTILESREADER_API GroupMetadataReader { public: /** diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ImplicitTilingReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ImplicitTilingReader.h index 7bf251142..416c6442e 100644 --- a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ImplicitTilingReader.h +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ImplicitTilingReader.h @@ -18,6 +18,9 @@ struct ImplicitTiling; namespace Cesium3DTilesReader { +/** + * @brief Reads {@link ImplicitTiling} instances from JSON. + */ class CESIUM3DTILESREADER_API ImplicitTilingReader { public: /** diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/MetadataEntityReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/MetadataEntityReader.h index 8b2e3001c..d203c2795 100644 --- a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/MetadataEntityReader.h +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/MetadataEntityReader.h @@ -18,6 +18,9 @@ struct MetadataEntity; namespace Cesium3DTilesReader { +/** + * @brief Reads {@link MetadataEntity} instances from JSON. + */ class CESIUM3DTILESREADER_API MetadataEntityReader { public: /** diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/PropertiesReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/PropertiesReader.h index 34a9db015..7e60dadc3 100644 --- a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/PropertiesReader.h +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/PropertiesReader.h @@ -18,6 +18,9 @@ struct Properties; namespace Cesium3DTilesReader { +/** + * @brief Reads {@link Properties} instances from JSON. + */ class CESIUM3DTILESREADER_API PropertiesReader { public: /** diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/PropertyStatisticsReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/PropertyStatisticsReader.h index 75268cf23..dd1fc3737 100644 --- a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/PropertyStatisticsReader.h +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/PropertyStatisticsReader.h @@ -18,6 +18,9 @@ struct PropertyStatistics; namespace Cesium3DTilesReader { +/** + * @brief Reads {@link PropertyStatistics} instances from JSON. + */ class CESIUM3DTILESREADER_API PropertyStatisticsReader { public: /** diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/PropertyTablePropertyReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/PropertyTablePropertyReader.h index b064b82a2..c67ed85d6 100644 --- a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/PropertyTablePropertyReader.h +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/PropertyTablePropertyReader.h @@ -18,6 +18,9 @@ struct PropertyTableProperty; namespace Cesium3DTilesReader { +/** + * @brief Reads {@link PropertyTableProperty} instances from JSON. + */ class CESIUM3DTILESREADER_API PropertyTablePropertyReader { public: /** diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/PropertyTableReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/PropertyTableReader.h index 1610bc9d5..13ee8ab76 100644 --- a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/PropertyTableReader.h +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/PropertyTableReader.h @@ -18,6 +18,9 @@ struct PropertyTable; namespace Cesium3DTilesReader { +/** + * @brief Reads {@link PropertyTable} instances from JSON. + */ class CESIUM3DTILESREADER_API PropertyTableReader { public: /** diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/SchemaReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/SchemaReader.h index 6680c820a..b99ae26c2 100644 --- a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/SchemaReader.h +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/SchemaReader.h @@ -18,6 +18,9 @@ struct Schema; namespace Cesium3DTilesReader { +/** + * @brief Reads {@link Schema} instances from JSON. + */ class CESIUM3DTILESREADER_API SchemaReader { public: /** diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/StatisticsReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/StatisticsReader.h index 03c4c8ae6..808b52f03 100644 --- a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/StatisticsReader.h +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/StatisticsReader.h @@ -18,6 +18,9 @@ struct Statistics; namespace Cesium3DTilesReader { +/** + * @brief Reads {@link Statistics} instances from JSON. + */ class CESIUM3DTILESREADER_API StatisticsReader { public: /** diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/SubtreeReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/SubtreeReader.h index 285096a69..4d3d5c808 100644 --- a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/SubtreeReader.h +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/SubtreeReader.h @@ -18,6 +18,9 @@ struct Subtree; namespace Cesium3DTilesReader { +/** + * @brief Reads {@link Subtree} instances from JSON. + */ class CESIUM3DTILESREADER_API SubtreeReader { public: /** diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/SubtreesReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/SubtreesReader.h index 29da3eb2a..21a2bda57 100644 --- a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/SubtreesReader.h +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/SubtreesReader.h @@ -18,6 +18,9 @@ struct Subtrees; namespace Cesium3DTilesReader { +/** + * @brief Reads {@link Subtrees} instances from JSON. + */ class CESIUM3DTILESREADER_API SubtreesReader { public: /** diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/TileReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/TileReader.h index 370c7971d..96b083a27 100644 --- a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/TileReader.h +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/TileReader.h @@ -18,6 +18,9 @@ struct Tile; namespace Cesium3DTilesReader { +/** + * @brief Reads {@link Tile} instances from JSON. + */ class CESIUM3DTILESREADER_API TileReader { public: /** diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/TilesetReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/TilesetReader.h index 56dc53bd2..aa066c793 100644 --- a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/TilesetReader.h +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/TilesetReader.h @@ -18,6 +18,9 @@ struct Tileset; namespace Cesium3DTilesReader { +/** + * @brief Reads {@link Tileset} instances from JSON. + */ class CESIUM3DTILESREADER_API TilesetReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/AccessorReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/AccessorReader.h index 03e48111d..804f1d620 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/AccessorReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/AccessorReader.h @@ -18,6 +18,9 @@ struct Accessor; namespace CesiumGltfReader { +/** + * @brief Reads {@link Accessor} instances from JSON. + */ class CESIUMGLTFREADER_API AccessorReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/AccessorSparseIndicesReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/AccessorSparseIndicesReader.h index f7cda6278..64381e2a3 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/AccessorSparseIndicesReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/AccessorSparseIndicesReader.h @@ -18,6 +18,9 @@ struct AccessorSparseIndices; namespace CesiumGltfReader { +/** + * @brief Reads {@link AccessorSparseIndices} instances from JSON. + */ class CESIUMGLTFREADER_API AccessorSparseIndicesReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/AccessorSparseReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/AccessorSparseReader.h index 3c80ae661..d52c8261c 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/AccessorSparseReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/AccessorSparseReader.h @@ -18,6 +18,9 @@ struct AccessorSparse; namespace CesiumGltfReader { +/** + * @brief Reads {@link AccessorSparse} instances from JSON. + */ class CESIUMGLTFREADER_API AccessorSparseReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/AccessorSparseValuesReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/AccessorSparseValuesReader.h index 5fa6060a4..59f524297 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/AccessorSparseValuesReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/AccessorSparseValuesReader.h @@ -18,6 +18,9 @@ struct AccessorSparseValues; namespace CesiumGltfReader { +/** + * @brief Reads {@link AccessorSparseValues} instances from JSON. + */ class CESIUMGLTFREADER_API AccessorSparseValuesReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/AnimationChannelReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/AnimationChannelReader.h index 7a7eba6c9..f1bc4e980 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/AnimationChannelReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/AnimationChannelReader.h @@ -18,6 +18,9 @@ struct AnimationChannel; namespace CesiumGltfReader { +/** + * @brief Reads {@link AnimationChannel} instances from JSON. + */ class CESIUMGLTFREADER_API AnimationChannelReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/AnimationChannelTargetReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/AnimationChannelTargetReader.h index 71a7e60f6..23b1ea78b 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/AnimationChannelTargetReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/AnimationChannelTargetReader.h @@ -18,6 +18,9 @@ struct AnimationChannelTarget; namespace CesiumGltfReader { +/** + * @brief Reads {@link AnimationChannelTarget} instances from JSON. + */ class CESIUMGLTFREADER_API AnimationChannelTargetReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/AnimationReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/AnimationReader.h index 2079961e3..db5c3c9c0 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/AnimationReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/AnimationReader.h @@ -18,6 +18,9 @@ struct Animation; namespace CesiumGltfReader { +/** + * @brief Reads {@link Animation} instances from JSON. + */ class CESIUMGLTFREADER_API AnimationReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/AnimationSamplerReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/AnimationSamplerReader.h index 0be957233..0ebf280a6 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/AnimationSamplerReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/AnimationSamplerReader.h @@ -18,6 +18,9 @@ struct AnimationSampler; namespace CesiumGltfReader { +/** + * @brief Reads {@link AnimationSampler} instances from JSON. + */ class CESIUMGLTFREADER_API AnimationSamplerReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/AssetReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/AssetReader.h index b44103a8d..9df7561a8 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/AssetReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/AssetReader.h @@ -18,6 +18,9 @@ struct Asset; namespace CesiumGltfReader { +/** + * @brief Reads {@link Asset} instances from JSON. + */ class CESIUMGLTFREADER_API AssetReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/BufferReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/BufferReader.h index 226c3b582..eb4998d01 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/BufferReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/BufferReader.h @@ -18,6 +18,9 @@ struct Buffer; namespace CesiumGltfReader { +/** + * @brief Reads {@link Buffer} instances from JSON. + */ class CESIUMGLTFREADER_API BufferReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/BufferViewReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/BufferViewReader.h index 494e1cd8f..af00165cb 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/BufferViewReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/BufferViewReader.h @@ -18,6 +18,9 @@ struct BufferView; namespace CesiumGltfReader { +/** + * @brief Reads {@link BufferView} instances from JSON. + */ class CESIUMGLTFREADER_API BufferViewReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/CameraOrthographicReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/CameraOrthographicReader.h index f7fb6c42f..9c8527379 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/CameraOrthographicReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/CameraOrthographicReader.h @@ -18,6 +18,9 @@ struct CameraOrthographic; namespace CesiumGltfReader { +/** + * @brief Reads {@link CameraOrthographic} instances from JSON. + */ class CESIUMGLTFREADER_API CameraOrthographicReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/CameraPerspectiveReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/CameraPerspectiveReader.h index 384a8fa2d..0c17d7113 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/CameraPerspectiveReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/CameraPerspectiveReader.h @@ -18,6 +18,9 @@ struct CameraPerspective; namespace CesiumGltfReader { +/** + * @brief Reads {@link CameraPerspective} instances from JSON. + */ class CESIUMGLTFREADER_API CameraPerspectiveReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/CameraReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/CameraReader.h index a98f5d62c..39a2facd2 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/CameraReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/CameraReader.h @@ -18,6 +18,9 @@ struct Camera; namespace CesiumGltfReader { +/** + * @brief Reads {@link Camera} instances from JSON. + */ class CESIUMGLTFREADER_API CameraReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ClassPropertyReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ClassPropertyReader.h index 7e7b8eb1b..162c5ff96 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/ClassPropertyReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ClassPropertyReader.h @@ -18,6 +18,9 @@ struct ClassProperty; namespace CesiumGltfReader { +/** + * @brief Reads {@link ClassProperty} instances from JSON. + */ class CESIUMGLTFREADER_API ClassPropertyReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ClassReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ClassReader.h index 06063a254..852ce802a 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/ClassReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ClassReader.h @@ -18,6 +18,9 @@ struct Class; namespace CesiumGltfReader { +/** + * @brief Reads {@link Class} instances from JSON. + */ class CESIUMGLTFREADER_API ClassReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ClassStatisticsReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ClassStatisticsReader.h index c28d44c67..746b16067 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/ClassStatisticsReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ClassStatisticsReader.h @@ -18,6 +18,9 @@ struct ClassStatistics; namespace CesiumGltfReader { +/** + * @brief Reads {@link ClassStatistics} instances from JSON. + */ class CESIUMGLTFREADER_API ClassStatisticsReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/EnumReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/EnumReader.h index f2b7c1b02..388273a98 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/EnumReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/EnumReader.h @@ -18,6 +18,9 @@ struct Enum; namespace CesiumGltfReader { +/** + * @brief Reads {@link Enum} instances from JSON. + */ class CESIUMGLTFREADER_API EnumReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/EnumValueReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/EnumValueReader.h index e8d587abe..149c8cf47 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/EnumValueReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/EnumValueReader.h @@ -18,6 +18,9 @@ struct EnumValue; namespace CesiumGltfReader { +/** + * @brief Reads {@link EnumValue} instances from JSON. + */ class CESIUMGLTFREADER_API EnumValueReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionBufferExtMeshoptCompressionReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionBufferExtMeshoptCompressionReader.h index c1078ed5e..32d905978 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionBufferExtMeshoptCompressionReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionBufferExtMeshoptCompressionReader.h @@ -18,6 +18,9 @@ struct ExtensionBufferExtMeshoptCompression; namespace CesiumGltfReader { +/** + * @brief Reads {@link ExtensionBufferExtMeshoptCompression} instances from JSON. + */ class CESIUMGLTFREADER_API ExtensionBufferExtMeshoptCompressionReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionBufferViewExtMeshoptCompressionReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionBufferViewExtMeshoptCompressionReader.h index 9e8592426..da7ff77c8 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionBufferViewExtMeshoptCompressionReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionBufferViewExtMeshoptCompressionReader.h @@ -18,6 +18,9 @@ struct ExtensionBufferViewExtMeshoptCompression; namespace CesiumGltfReader { +/** + * @brief Reads {@link ExtensionBufferViewExtMeshoptCompression} instances from JSON. + */ class CESIUMGLTFREADER_API ExtensionBufferViewExtMeshoptCompressionReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionCesiumRTCReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionCesiumRTCReader.h index 369bba9cb..f524c7906 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionCesiumRTCReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionCesiumRTCReader.h @@ -18,6 +18,9 @@ struct ExtensionCesiumRTC; namespace CesiumGltfReader { +/** + * @brief Reads {@link ExtensionCesiumRTC} instances from JSON. + */ class CESIUMGLTFREADER_API ExtensionCesiumRTCReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionCesiumTileEdgesReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionCesiumTileEdgesReader.h index 089890b00..b9df268d2 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionCesiumTileEdgesReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionCesiumTileEdgesReader.h @@ -18,6 +18,9 @@ struct ExtensionCesiumTileEdges; namespace CesiumGltfReader { +/** + * @brief Reads {@link ExtensionCesiumTileEdges} instances from JSON. + */ class CESIUMGLTFREADER_API ExtensionCesiumTileEdgesReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtInstanceFeaturesFeatureIdReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtInstanceFeaturesFeatureIdReader.h index 96beedab1..a3fa2919b 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtInstanceFeaturesFeatureIdReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtInstanceFeaturesFeatureIdReader.h @@ -18,6 +18,9 @@ struct ExtensionExtInstanceFeaturesFeatureId; namespace CesiumGltfReader { +/** + * @brief Reads {@link ExtensionExtInstanceFeaturesFeatureId} instances from JSON. + */ class CESIUMGLTFREADER_API ExtensionExtInstanceFeaturesFeatureIdReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtInstanceFeaturesReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtInstanceFeaturesReader.h index 43763102c..88cae65fa 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtInstanceFeaturesReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtInstanceFeaturesReader.h @@ -18,6 +18,9 @@ struct ExtensionExtInstanceFeatures; namespace CesiumGltfReader { +/** + * @brief Reads {@link ExtensionExtInstanceFeatures} instances from JSON. + */ class CESIUMGLTFREADER_API ExtensionExtInstanceFeaturesReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtMeshFeaturesFeatureIdReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtMeshFeaturesFeatureIdReader.h index 5bc9b0443..e3f51fd79 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtMeshFeaturesFeatureIdReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtMeshFeaturesFeatureIdReader.h @@ -18,6 +18,9 @@ struct ExtensionExtMeshFeaturesFeatureId; namespace CesiumGltfReader { +/** + * @brief Reads {@link ExtensionExtMeshFeaturesFeatureId} instances from JSON. + */ class CESIUMGLTFREADER_API ExtensionExtMeshFeaturesFeatureIdReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtMeshFeaturesFeatureIdTextureReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtMeshFeaturesFeatureIdTextureReader.h index 273ae1a0a..36b324965 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtMeshFeaturesFeatureIdTextureReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtMeshFeaturesFeatureIdTextureReader.h @@ -18,6 +18,9 @@ struct ExtensionExtMeshFeaturesFeatureIdTexture; namespace CesiumGltfReader { +/** + * @brief Reads {@link ExtensionExtMeshFeaturesFeatureIdTexture} instances from JSON. + */ class CESIUMGLTFREADER_API ExtensionExtMeshFeaturesFeatureIdTextureReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtMeshFeaturesReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtMeshFeaturesReader.h index 55dd544d2..8d369d631 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtMeshFeaturesReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtMeshFeaturesReader.h @@ -18,6 +18,9 @@ struct ExtensionExtMeshFeatures; namespace CesiumGltfReader { +/** + * @brief Reads {@link ExtensionExtMeshFeatures} instances from JSON. + */ class CESIUMGLTFREADER_API ExtensionExtMeshFeaturesReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtMeshGpuInstancingReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtMeshGpuInstancingReader.h index f7d26c075..507301ac9 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtMeshGpuInstancingReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtMeshGpuInstancingReader.h @@ -18,6 +18,9 @@ struct ExtensionExtMeshGpuInstancing; namespace CesiumGltfReader { +/** + * @brief Reads {@link ExtensionExtMeshGpuInstancing} instances from JSON. + */ class CESIUMGLTFREADER_API ExtensionExtMeshGpuInstancingReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataClassPropertyReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataClassPropertyReader.h index dacf03a40..f1ba1e071 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataClassPropertyReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataClassPropertyReader.h @@ -18,6 +18,9 @@ struct ExtensionExtStructuralMetadataClassProperty; namespace CesiumGltfReader { +/** + * @brief Reads {@link ExtensionExtStructuralMetadataClassProperty} instances from JSON. + */ class CESIUMGLTFREADER_API ExtensionExtStructuralMetadataClassPropertyReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataClassReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataClassReader.h index 75c1a2dc2..cbdac90cd 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataClassReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataClassReader.h @@ -18,6 +18,9 @@ struct ExtensionExtStructuralMetadataClass; namespace CesiumGltfReader { +/** + * @brief Reads {@link ExtensionExtStructuralMetadataClass} instances from JSON. + */ class CESIUMGLTFREADER_API ExtensionExtStructuralMetadataClassReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataEnumReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataEnumReader.h index 4eb19c05a..e64155cf9 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataEnumReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataEnumReader.h @@ -18,6 +18,9 @@ struct ExtensionExtStructuralMetadataEnum; namespace CesiumGltfReader { +/** + * @brief Reads {@link ExtensionExtStructuralMetadataEnum} instances from JSON. + */ class CESIUMGLTFREADER_API ExtensionExtStructuralMetadataEnumReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataEnumValueReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataEnumValueReader.h index cb261c358..4b960a26c 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataEnumValueReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataEnumValueReader.h @@ -18,6 +18,9 @@ struct ExtensionExtStructuralMetadataEnumValue; namespace CesiumGltfReader { +/** + * @brief Reads {@link ExtensionExtStructuralMetadataEnumValue} instances from JSON. + */ class CESIUMGLTFREADER_API ExtensionExtStructuralMetadataEnumValueReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyAttributePropertyReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyAttributePropertyReader.h index 9c339b405..bcbc21953 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyAttributePropertyReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyAttributePropertyReader.h @@ -18,6 +18,9 @@ struct ExtensionExtStructuralMetadataPropertyAttributeProperty; namespace CesiumGltfReader { +/** + * @brief Reads {@link ExtensionExtStructuralMetadataPropertyAttributeProperty} instances from JSON. + */ class CESIUMGLTFREADER_API ExtensionExtStructuralMetadataPropertyAttributePropertyReader { public: diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyAttributeReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyAttributeReader.h index 4bc012c7b..ae043c249 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyAttributeReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyAttributeReader.h @@ -18,6 +18,9 @@ struct ExtensionExtStructuralMetadataPropertyAttribute; namespace CesiumGltfReader { +/** + * @brief Reads {@link ExtensionExtStructuralMetadataPropertyAttribute} instances from JSON. + */ class CESIUMGLTFREADER_API ExtensionExtStructuralMetadataPropertyAttributeReader { public: diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyTablePropertyReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyTablePropertyReader.h index fb3a8dec9..7a9d09c39 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyTablePropertyReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyTablePropertyReader.h @@ -18,6 +18,9 @@ struct ExtensionExtStructuralMetadataPropertyTableProperty; namespace CesiumGltfReader { +/** + * @brief Reads {@link ExtensionExtStructuralMetadataPropertyTableProperty} instances from JSON. + */ class CESIUMGLTFREADER_API ExtensionExtStructuralMetadataPropertyTablePropertyReader { public: diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyTableReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyTableReader.h index 0f013603d..7c0407433 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyTableReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyTableReader.h @@ -18,6 +18,9 @@ struct ExtensionExtStructuralMetadataPropertyTable; namespace CesiumGltfReader { +/** + * @brief Reads {@link ExtensionExtStructuralMetadataPropertyTable} instances from JSON. + */ class CESIUMGLTFREADER_API ExtensionExtStructuralMetadataPropertyTableReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyTexturePropertyReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyTexturePropertyReader.h index 49d864d3f..33d2db6d5 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyTexturePropertyReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyTexturePropertyReader.h @@ -18,6 +18,9 @@ struct ExtensionExtStructuralMetadataPropertyTextureProperty; namespace CesiumGltfReader { +/** + * @brief Reads {@link ExtensionExtStructuralMetadataPropertyTextureProperty} instances from JSON. + */ class CESIUMGLTFREADER_API ExtensionExtStructuralMetadataPropertyTexturePropertyReader { public: diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyTextureReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyTextureReader.h index 0fb2e4a06..24e512639 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyTextureReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyTextureReader.h @@ -18,6 +18,9 @@ struct ExtensionExtStructuralMetadataPropertyTexture; namespace CesiumGltfReader { +/** + * @brief Reads {@link ExtensionExtStructuralMetadataPropertyTexture} instances from JSON. + */ class CESIUMGLTFREADER_API ExtensionExtStructuralMetadataPropertyTextureReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataSchemaReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataSchemaReader.h index 54d022826..1ee96e3d8 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataSchemaReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataSchemaReader.h @@ -18,6 +18,9 @@ struct ExtensionExtStructuralMetadataSchema; namespace CesiumGltfReader { +/** + * @brief Reads {@link ExtensionExtStructuralMetadataSchema} instances from JSON. + */ class CESIUMGLTFREADER_API ExtensionExtStructuralMetadataSchemaReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionKhrDracoMeshCompressionReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionKhrDracoMeshCompressionReader.h index d798d8154..51ac2ee70 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionKhrDracoMeshCompressionReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionKhrDracoMeshCompressionReader.h @@ -18,6 +18,9 @@ struct ExtensionKhrDracoMeshCompression; namespace CesiumGltfReader { +/** + * @brief Reads {@link ExtensionKhrDracoMeshCompression} instances from JSON. + */ class CESIUMGLTFREADER_API ExtensionKhrDracoMeshCompressionReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionKhrMaterialsUnlitReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionKhrMaterialsUnlitReader.h index 643c529d8..43307034d 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionKhrMaterialsUnlitReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionKhrMaterialsUnlitReader.h @@ -18,6 +18,9 @@ struct ExtensionKhrMaterialsUnlit; namespace CesiumGltfReader { +/** + * @brief Reads {@link ExtensionKhrMaterialsUnlit} instances from JSON. + */ class CESIUMGLTFREADER_API ExtensionKhrMaterialsUnlitReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionKhrTextureBasisuReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionKhrTextureBasisuReader.h index c5dcb9ac2..dcf213fe8 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionKhrTextureBasisuReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionKhrTextureBasisuReader.h @@ -18,6 +18,9 @@ struct ExtensionKhrTextureBasisu; namespace CesiumGltfReader { +/** + * @brief Reads {@link ExtensionKhrTextureBasisu} instances from JSON. + */ class CESIUMGLTFREADER_API ExtensionKhrTextureBasisuReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionKhrTextureTransformReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionKhrTextureTransformReader.h index 90ba7cdf0..7559901b5 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionKhrTextureTransformReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionKhrTextureTransformReader.h @@ -18,6 +18,9 @@ struct ExtensionKhrTextureTransform; namespace CesiumGltfReader { +/** + * @brief Reads {@link ExtensionKhrTextureTransform} instances from JSON. + */ class CESIUMGLTFREADER_API ExtensionKhrTextureTransformReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionMeshPrimitiveExtFeatureMetadataReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionMeshPrimitiveExtFeatureMetadataReader.h index 465f37e5c..03d8de9ab 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionMeshPrimitiveExtFeatureMetadataReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionMeshPrimitiveExtFeatureMetadataReader.h @@ -18,6 +18,9 @@ struct ExtensionMeshPrimitiveExtFeatureMetadata; namespace CesiumGltfReader { +/** + * @brief Reads {@link ExtensionMeshPrimitiveExtFeatureMetadata} instances from JSON. + */ class CESIUMGLTFREADER_API ExtensionMeshPrimitiveExtFeatureMetadataReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionMeshPrimitiveExtStructuralMetadataReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionMeshPrimitiveExtStructuralMetadataReader.h index 328005207..09c94d295 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionMeshPrimitiveExtStructuralMetadataReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionMeshPrimitiveExtStructuralMetadataReader.h @@ -18,6 +18,9 @@ struct ExtensionMeshPrimitiveExtStructuralMetadata; namespace CesiumGltfReader { +/** + * @brief Reads {@link ExtensionMeshPrimitiveExtStructuralMetadata} instances from JSON. + */ class CESIUMGLTFREADER_API ExtensionMeshPrimitiveExtStructuralMetadataReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueReader.h index 9ebfc2493..321da8d86 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueReader.h @@ -18,6 +18,9 @@ struct ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValue; namespace CesiumGltfReader { +/** + * @brief Reads {@link ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValue} instances from JSON. + */ class CESIUMGLTFREADER_API ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueReader { public: diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionMeshPrimitiveKhrMaterialsVariantsReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionMeshPrimitiveKhrMaterialsVariantsReader.h index 817f9d444..4bdfa1a06 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionMeshPrimitiveKhrMaterialsVariantsReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionMeshPrimitiveKhrMaterialsVariantsReader.h @@ -18,6 +18,9 @@ struct ExtensionMeshPrimitiveKhrMaterialsVariants; namespace CesiumGltfReader { +/** + * @brief Reads {@link ExtensionMeshPrimitiveKhrMaterialsVariants} instances from JSON. + */ class CESIUMGLTFREADER_API ExtensionMeshPrimitiveKhrMaterialsVariantsReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelExtFeatureMetadataReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelExtFeatureMetadataReader.h index cfdda0e1c..1ffdfe190 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelExtFeatureMetadataReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelExtFeatureMetadataReader.h @@ -18,6 +18,9 @@ struct ExtensionModelExtFeatureMetadata; namespace CesiumGltfReader { +/** + * @brief Reads {@link ExtensionModelExtFeatureMetadata} instances from JSON. + */ class CESIUMGLTFREADER_API ExtensionModelExtFeatureMetadataReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelExtStructuralMetadataReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelExtStructuralMetadataReader.h index 8cb1c1d85..b3aa0e002 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelExtStructuralMetadataReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelExtStructuralMetadataReader.h @@ -18,6 +18,9 @@ struct ExtensionModelExtStructuralMetadata; namespace CesiumGltfReader { +/** + * @brief Reads {@link ExtensionModelExtStructuralMetadata} instances from JSON. + */ class CESIUMGLTFREADER_API ExtensionModelExtStructuralMetadataReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelKhrMaterialsVariantsReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelKhrMaterialsVariantsReader.h index 3d04a7e16..72320b8db 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelKhrMaterialsVariantsReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelKhrMaterialsVariantsReader.h @@ -18,6 +18,9 @@ struct ExtensionModelKhrMaterialsVariants; namespace CesiumGltfReader { +/** + * @brief Reads {@link ExtensionModelKhrMaterialsVariants} instances from JSON. + */ class CESIUMGLTFREADER_API ExtensionModelKhrMaterialsVariantsReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelKhrMaterialsVariantsValueReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelKhrMaterialsVariantsValueReader.h index bd13a86da..6f6e83962 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelKhrMaterialsVariantsValueReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelKhrMaterialsVariantsValueReader.h @@ -18,6 +18,9 @@ struct ExtensionModelKhrMaterialsVariantsValue; namespace CesiumGltfReader { +/** + * @brief Reads {@link ExtensionModelKhrMaterialsVariantsValue} instances from JSON. + */ class CESIUMGLTFREADER_API ExtensionModelKhrMaterialsVariantsValueReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelMaxarMeshVariantsReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelMaxarMeshVariantsReader.h index 5872bb206..89b5af2da 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelMaxarMeshVariantsReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelMaxarMeshVariantsReader.h @@ -18,6 +18,9 @@ struct ExtensionModelMaxarMeshVariants; namespace CesiumGltfReader { +/** + * @brief Reads {@link ExtensionModelMaxarMeshVariants} instances from JSON. + */ class CESIUMGLTFREADER_API ExtensionModelMaxarMeshVariantsReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelMaxarMeshVariantsValueReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelMaxarMeshVariantsValueReader.h index 2baa15b55..d89c5206c 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelMaxarMeshVariantsValueReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelMaxarMeshVariantsValueReader.h @@ -18,6 +18,9 @@ struct ExtensionModelMaxarMeshVariantsValue; namespace CesiumGltfReader { +/** + * @brief Reads {@link ExtensionModelMaxarMeshVariantsValue} instances from JSON. + */ class CESIUMGLTFREADER_API ExtensionModelMaxarMeshVariantsValueReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionNodeMaxarMeshVariantsMappingsValueReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionNodeMaxarMeshVariantsMappingsValueReader.h index 14d3aac22..071bd32dd 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionNodeMaxarMeshVariantsMappingsValueReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionNodeMaxarMeshVariantsMappingsValueReader.h @@ -18,6 +18,9 @@ struct ExtensionNodeMaxarMeshVariantsMappingsValue; namespace CesiumGltfReader { +/** + * @brief Reads {@link ExtensionNodeMaxarMeshVariantsMappingsValue} instances from JSON. + */ class CESIUMGLTFREADER_API ExtensionNodeMaxarMeshVariantsMappingsValueReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionNodeMaxarMeshVariantsReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionNodeMaxarMeshVariantsReader.h index dbe3a8b5b..28558d042 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionNodeMaxarMeshVariantsReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionNodeMaxarMeshVariantsReader.h @@ -18,6 +18,9 @@ struct ExtensionNodeMaxarMeshVariants; namespace CesiumGltfReader { +/** + * @brief Reads {@link ExtensionNodeMaxarMeshVariants} instances from JSON. + */ class CESIUMGLTFREADER_API ExtensionNodeMaxarMeshVariantsReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionTextureWebpReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionTextureWebpReader.h index 0a3abc8b4..a97af321a 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionTextureWebpReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionTextureWebpReader.h @@ -18,6 +18,9 @@ struct ExtensionTextureWebp; namespace CesiumGltfReader { +/** + * @brief Reads {@link ExtensionTextureWebp} instances from JSON. + */ class CESIUMGLTFREADER_API ExtensionTextureWebpReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureIDAttributeReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureIDAttributeReader.h index 0cb2ba4f9..bc31b3743 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureIDAttributeReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureIDAttributeReader.h @@ -18,6 +18,9 @@ struct FeatureIDAttribute; namespace CesiumGltfReader { +/** + * @brief Reads {@link FeatureIDAttribute} instances from JSON. + */ class CESIUMGLTFREADER_API FeatureIDAttributeReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureIDTextureReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureIDTextureReader.h index fdea6446c..b8fda0b54 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureIDTextureReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureIDTextureReader.h @@ -18,6 +18,9 @@ struct FeatureIDTexture; namespace CesiumGltfReader { +/** + * @brief Reads {@link FeatureIDTexture} instances from JSON. + */ class CESIUMGLTFREADER_API FeatureIDTextureReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureIDsReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureIDsReader.h index 0d14eb1bd..caf4779ff 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureIDsReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureIDsReader.h @@ -18,6 +18,9 @@ struct FeatureIDs; namespace CesiumGltfReader { +/** + * @brief Reads {@link FeatureIDs} instances from JSON. + */ class CESIUMGLTFREADER_API FeatureIDsReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureTablePropertyReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureTablePropertyReader.h index 1ec56faf0..4183569ff 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureTablePropertyReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureTablePropertyReader.h @@ -18,6 +18,9 @@ struct FeatureTableProperty; namespace CesiumGltfReader { +/** + * @brief Reads {@link FeatureTableProperty} instances from JSON. + */ class CESIUMGLTFREADER_API FeatureTablePropertyReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureTableReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureTableReader.h index 5773ae465..764a98583 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureTableReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureTableReader.h @@ -18,6 +18,9 @@ struct FeatureTable; namespace CesiumGltfReader { +/** + * @brief Reads {@link FeatureTable} instances from JSON. + */ class CESIUMGLTFREADER_API FeatureTableReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureTextureReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureTextureReader.h index 5d36807d4..1504abef4 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureTextureReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureTextureReader.h @@ -18,6 +18,9 @@ struct FeatureTexture; namespace CesiumGltfReader { +/** + * @brief Reads {@link FeatureTexture} instances from JSON. + */ class CESIUMGLTFREADER_API FeatureTextureReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ImageReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ImageReader.h index 8772a1f15..8dc8a4fcc 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/ImageReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ImageReader.h @@ -18,6 +18,9 @@ struct Image; namespace CesiumGltfReader { +/** + * @brief Reads {@link Image} instances from JSON. + */ class CESIUMGLTFREADER_API ImageReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/MaterialNormalTextureInfoReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/MaterialNormalTextureInfoReader.h index 199facc46..a51503dcc 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/MaterialNormalTextureInfoReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/MaterialNormalTextureInfoReader.h @@ -18,6 +18,9 @@ struct MaterialNormalTextureInfo; namespace CesiumGltfReader { +/** + * @brief Reads {@link MaterialNormalTextureInfo} instances from JSON. + */ class CESIUMGLTFREADER_API MaterialNormalTextureInfoReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/MaterialOcclusionTextureInfoReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/MaterialOcclusionTextureInfoReader.h index 26bf4786a..a39dd089c 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/MaterialOcclusionTextureInfoReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/MaterialOcclusionTextureInfoReader.h @@ -18,6 +18,9 @@ struct MaterialOcclusionTextureInfo; namespace CesiumGltfReader { +/** + * @brief Reads {@link MaterialOcclusionTextureInfo} instances from JSON. + */ class CESIUMGLTFREADER_API MaterialOcclusionTextureInfoReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/MaterialPBRMetallicRoughnessReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/MaterialPBRMetallicRoughnessReader.h index 33226b68f..7eda8c603 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/MaterialPBRMetallicRoughnessReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/MaterialPBRMetallicRoughnessReader.h @@ -18,6 +18,9 @@ struct MaterialPBRMetallicRoughness; namespace CesiumGltfReader { +/** + * @brief Reads {@link MaterialPBRMetallicRoughness} instances from JSON. + */ class CESIUMGLTFREADER_API MaterialPBRMetallicRoughnessReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/MaterialReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/MaterialReader.h index 9f4b896f0..133cc0a53 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/MaterialReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/MaterialReader.h @@ -18,6 +18,9 @@ struct Material; namespace CesiumGltfReader { +/** + * @brief Reads {@link Material} instances from JSON. + */ class CESIUMGLTFREADER_API MaterialReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/MeshPrimitiveReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/MeshPrimitiveReader.h index 84298f0e6..db5c35b6b 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/MeshPrimitiveReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/MeshPrimitiveReader.h @@ -18,6 +18,9 @@ struct MeshPrimitive; namespace CesiumGltfReader { +/** + * @brief Reads {@link MeshPrimitive} instances from JSON. + */ class CESIUMGLTFREADER_API MeshPrimitiveReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/MeshReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/MeshReader.h index a70941373..05587f7ae 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/MeshReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/MeshReader.h @@ -18,6 +18,9 @@ struct Mesh; namespace CesiumGltfReader { +/** + * @brief Reads {@link Mesh} instances from JSON. + */ class CESIUMGLTFREADER_API MeshReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ModelReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ModelReader.h index 385afea2d..637a21481 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/ModelReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ModelReader.h @@ -18,6 +18,9 @@ struct Model; namespace CesiumGltfReader { +/** + * @brief Reads {@link Model} instances from JSON. + */ class CESIUMGLTFREADER_API ModelReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/NodeReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/NodeReader.h index 6091f0ec4..c2c55438d 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/NodeReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/NodeReader.h @@ -18,6 +18,9 @@ struct Node; namespace CesiumGltfReader { +/** + * @brief Reads {@link Node} instances from JSON. + */ class CESIUMGLTFREADER_API NodeReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/PropertyStatisticsReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/PropertyStatisticsReader.h index ced8713f2..2e9374d15 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/PropertyStatisticsReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/PropertyStatisticsReader.h @@ -18,6 +18,9 @@ struct PropertyStatistics; namespace CesiumGltfReader { +/** + * @brief Reads {@link PropertyStatistics} instances from JSON. + */ class CESIUMGLTFREADER_API PropertyStatisticsReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/SamplerReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/SamplerReader.h index b551ffd7d..99f6e8815 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/SamplerReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/SamplerReader.h @@ -18,6 +18,9 @@ struct Sampler; namespace CesiumGltfReader { +/** + * @brief Reads {@link Sampler} instances from JSON. + */ class CESIUMGLTFREADER_API SamplerReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/SceneReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/SceneReader.h index fe65364d2..162d5e201 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/SceneReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/SceneReader.h @@ -18,6 +18,9 @@ struct Scene; namespace CesiumGltfReader { +/** + * @brief Reads {@link Scene} instances from JSON. + */ class CESIUMGLTFREADER_API SceneReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/SchemaReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/SchemaReader.h index 1e3daec0d..3cda72932 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/SchemaReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/SchemaReader.h @@ -18,6 +18,9 @@ struct Schema; namespace CesiumGltfReader { +/** + * @brief Reads {@link Schema} instances from JSON. + */ class CESIUMGLTFREADER_API SchemaReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/SkinReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/SkinReader.h index 2fd1ce417..1402f486a 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/SkinReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/SkinReader.h @@ -18,6 +18,9 @@ struct Skin; namespace CesiumGltfReader { +/** + * @brief Reads {@link Skin} instances from JSON. + */ class CESIUMGLTFREADER_API SkinReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/StatisticsReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/StatisticsReader.h index a410d4763..e8f42bf53 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/StatisticsReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/StatisticsReader.h @@ -18,6 +18,9 @@ struct Statistics; namespace CesiumGltfReader { +/** + * @brief Reads {@link Statistics} instances from JSON. + */ class CESIUMGLTFREADER_API StatisticsReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/TextureAccessorReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/TextureAccessorReader.h index 0026b9b33..ced1e7ef9 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/TextureAccessorReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/TextureAccessorReader.h @@ -18,6 +18,9 @@ struct TextureAccessor; namespace CesiumGltfReader { +/** + * @brief Reads {@link TextureAccessor} instances from JSON. + */ class CESIUMGLTFREADER_API TextureAccessorReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/TextureInfoReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/TextureInfoReader.h index 5ac9e133e..313d48dc8 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/TextureInfoReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/TextureInfoReader.h @@ -18,6 +18,9 @@ struct TextureInfo; namespace CesiumGltfReader { +/** + * @brief Reads {@link TextureInfo} instances from JSON. + */ class CESIUMGLTFREADER_API TextureInfoReader { public: /** diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/TextureReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/TextureReader.h index e625b3c60..b1832c762 100644 --- a/CesiumGltfReader/generated/include/CesiumGltfReader/TextureReader.h +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/TextureReader.h @@ -18,6 +18,9 @@ struct Texture; namespace CesiumGltfReader { +/** + * @brief Reads {@link Texture} instances from JSON. + */ class CESIUMGLTFREADER_API TextureReader { public: /** diff --git a/tools/generate-classes/generate.js b/tools/generate-classes/generate.js index 9037a4b1e..4cd3dd03d 100644 --- a/tools/generate-classes/generate.js +++ b/tools/generate-classes/generate.js @@ -226,6 +226,9 @@ function generate(options, schema, writers) { namespace ${readerNamespace} { + /** + * @brief Reads {@link ${name}} instances from JSON. + */ class ${readerNamespace.toUpperCase()}_API ${name}Reader { public: /** From 38001698e8ecf44fd19dc87e42124dc5173c1091 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Tue, 22 Aug 2023 12:43:48 -0400 Subject: [PATCH 195/421] Fix offset / scale etc. to only work for fixed-length arrays --- ...gradeBatchTableToExtStructuralMetadata.cpp | 8 +- .../CesiumGltf/PropertyTablePropertyView.h | 476 ++++++------ .../CesiumGltf/PropertyTransformations.h | 40 +- CesiumGltf/include/CesiumGltf/PropertyView.h | 680 ++++++------------ .../test/TestPropertyTablePropertyView.cpp | 254 ++++++- CesiumGltf/test/TestPropertyView.cpp | 229 +++++- 6 files changed, 939 insertions(+), 748 deletions(-) diff --git a/Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp b/Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp index 46dc79b45..8a8a7509f 100644 --- a/Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp +++ b/Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp @@ -49,16 +49,16 @@ static void checkNonArrayProperty( for (int64_t i = 0; i < propertyView.size(); ++i) { if constexpr (std::is_same_v) { REQUIRE(Math::equalsEpsilon( - static_cast(propertyView.get(i)), + static_cast(propertyView.getRaw(i)), static_cast(expected[static_cast(i)]), Math::Epsilon6)); } else if constexpr ( std::is_same_v || std::is_same_v) { - REQUIRE(propertyView.get(i) == Approx(expected[static_cast(i)])); + REQUIRE(propertyView.getRaw(i) == Approx(expected[static_cast(i)])); } else { REQUIRE( - static_cast(propertyView.get(i)) == + static_cast(propertyView.getRaw(i)) == expected[static_cast(i)]); } } @@ -92,7 +92,7 @@ static void checkArrayProperty( REQUIRE(propertyView.size() == static_cast(expectedTotalInstances)); for (size_t i = 0; i < expectedTotalInstances; ++i) { PropertyArrayView value = - propertyView.get(static_cast(i)); + propertyView.getRaw(static_cast(i)); if (expectedCount > 0) { REQUIRE(value.size() == expectedCount); } diff --git a/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h index 440e3dc43..e4fe2e9de 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h @@ -1,6 +1,7 @@ #pragma once #include "CesiumGltf/PropertyArrayView.h" +#include "CesiumGltf/PropertyTransformations.h" #include "CesiumGltf/PropertyTypeTraits.h" #include "CesiumGltf/PropertyView.h" @@ -130,6 +131,23 @@ class PropertyTablePropertyViewStatus : public PropertyViewStatus { static const PropertyViewStatusType ErrorStringOffsetOutOfBounds = 29; }; +namespace { +int64_t getOffsetTypeSize(PropertyComponentType offsetType) noexcept { + switch (offsetType) { + case PropertyComponentType::Uint8: + return sizeof(uint8_t); + case PropertyComponentType::Uint16: + return sizeof(uint16_t); + case PropertyComponentType::Uint32: + return sizeof(uint32_t); + case PropertyComponentType::Uint64: + return sizeof(uint64_t); + default: + return 0; + } +} +} // namespace + template class PropertyTablePropertyView; @@ -248,13 +266,14 @@ class PropertyTablePropertyView * offset and scale, they will be applied to the value before the value is * returned. * - * If this property has a defined "no data" value, and the retrieved element + * If this property has a specified "no data" value, and the retrieved element * is equal to that value, then this will return the property's specified * default value. If the property did not provide a default value, this * returns std::nullopt. * * @param index The element index - * @return The value of the element + * @return The value of the element, or std::nullopt if it matches the "no + * data" value */ std::optional get(int64_t index) const noexcept { ElementType value = getRaw(index); @@ -276,8 +295,8 @@ class PropertyTablePropertyView * @brief Get the raw value of an element of the {@link PropertyTable}, * without offset or scale applied. * - * If this property has a "no data" value defined, the raw value will still be - * returned, even if it equals the "no data" value. + * If this property has a specified "no data" value, the raw value will still + * be returned, even if it equals the "no data" value. * * @param index The element index * @return The value of the element @@ -433,21 +452,6 @@ class PropertyTablePropertyView return PropertyArrayView(buffer, currentOffset % 8, totalBits); } - static int64_t getOffsetTypeSize(PropertyComponentType offsetType) noexcept { - switch (offsetType) { - case PropertyComponentType::Uint8: - return sizeof(uint8_t); - case PropertyComponentType::Uint16: - return sizeof(uint16_t); - case PropertyComponentType::Uint32: - return sizeof(uint32_t); - case PropertyComponentType::Uint64: - return sizeof(uint64_t); - default: - return 0; - } - } - gsl::span _values; int64_t _size; @@ -474,220 +478,222 @@ class PropertyTablePropertyView * the integer scalar types, or PropertyArrayView with T as one of the * aforementioned types. */ -// template -// class PropertyTablePropertyView -// : public PropertyView { -// private: -// using NormalizedType = typename TypeToNormalizedType::type; -// -// public: -// /** -// * @brief Constructs an invalid instance for a non-existent property. -// */ -// PropertyTablePropertyView() -// : PropertyView(), -// _values{}, -// _size{0}, -// _arrayOffsets{}, -// _arrayOffsetType{PropertyComponentType::None}, -// _arrayOffsetTypeSize{0} {} -// -// /** -// * @brief Constructs an invalid instance for an erroneous property. -// * -// * @param status The value of {@link PropertyTablePropertyViewStatus} indicating the error with the property. -// */ -// PropertyTablePropertyView(PropertyViewStatusType status) -// : PropertyView(status), -// _values{}, -// _size{0}, -// _arrayOffsets{}, -// _arrayOffsetType{PropertyComponentType::None}, -// _arrayOffsetTypeSize{0} { -// assert( -// this->_status != PropertyTablePropertyViewStatus::Valid && -// "An empty property view should not be constructed with a valid -// status"); -// } -// -// /** -// * @brief Construct an instance pointing to non-array data specified by a {@link PropertyTableProperty}. -// * -// * @param property The {@link PropertyTableProperty} -// * @param classProperty The {@link ClassProperty} this property conforms to. -// * @param size The number of elements in the property table specified by {@link PropertyTable::count} -// * @param value The raw buffer specified by {@link PropertyTableProperty::values} -// */ -// PropertyTablePropertyView( -// const PropertyTableProperty& property, -// const ClassProperty& classProperty, -// int64_t size, -// gsl::span values) noexcept -// : PropertyView(classProperty, property), -// _values{values}, -// _size{ -// this->_status == PropertyTablePropertyViewStatus::Valid ? size : -// 0}, -// _arrayOffsets{}, -// _arrayOffsetType{PropertyComponentType::None}, -// _arrayOffsetTypeSize{0} {} -// -// /** -// * @brief Construct an instance pointing to the data specified by a {@link PropertyTableProperty}. -// * -// * -// * @param property The {@link PropertyTableProperty} -// * @param classProperty The {@link ClassProperty} this property conforms to. -// * @param size The number of elements in the property table specified by {@link PropertyTable::count} -// * @param values The raw buffer specified by {@link PropertyTableProperty::values} -// * @param arrayOffsets The raw buffer specified by {@link PropertyTableProperty::arrayOffsets} -// * @param offsetType The offset type of arrayOffsets specified by {@link PropertyTableProperty::arrayOffsetType} -// */ -// PropertyTablePropertyView( -// const PropertyTableProperty& property, -// const ClassProperty& classProperty, -// int64_t size, -// gsl::span values, -// gsl::span arrayOffsets, -// PropertyComponentType arrayOffsetType) noexcept -// : PropertyView(classProperty, property), -// _values{values}, -// _size{ -// this->_status == PropertyTablePropertyViewStatus::Valid ? size : -// 0}, -// _arrayOffsets{arrayOffsets}, -// _arrayOffsetType{arrayOffsetType}, -// _arrayOffsetTypeSize{getOffsetTypeSize(arrayOffsetType)} {} -// -// /** -// * @brief Get the value of an element of the {@link PropertyTable}, -// * with all value transforms applied. This means the returned value will be -// * normalized. If the property specifies an offset and scale, they will be -// * applied to the value as well. -// * -// * If this property has a "no data" value defined, and the returned this -// returns the "default value". -// * -// * @param index The element index -// * @return The value of the element -// */ -// std::optional get(int64_t index) const noexcept { -// assert( -// this->_status == PropertyTablePropertyViewStatus::Valid && -// "Check the status() first to make sure view is valid"); -// assert( -// size() > 0 && -// "Check the size() of the view to make sure it's not empty"); -// assert(index >= 0 && "index must be non-negative"); -// assert(index < size() && "index must be less than size"); -// -// if constexpr (IsMetadataNumeric::value) { -// ElementType value = getValue(index); -// if (value == this->noData()) { -// -// } -// } -// -// if constexpr (IsMetadataNumericArray::value) { -// return getArrayValues::type>( -// index); -// } -// } -// -// /** -// * @brief Get the raw value of an element of the {@link PropertyTable}, -// * without offset, scale, or normalization applied. -// * -// * If this property has a "no data" value defined, the raw value will still -// be -// * returned, even if it equals the "no data" value. -// * -// * @param index The element index -// * @return The value of the element -// */ -// ElementType getRaw(int64_t index) const noexcept { -// assert( -// this->_status == PropertyTablePropertyViewStatus::Valid && -// "Check the status() first to make sure view is valid"); -// assert( -// size() > 0 && -// "Check the size() of the view to make sure it's not empty"); -// assert(index >= 0 && "index must be non-negative"); -// assert(index < size() && "index must be less than size"); -// -// if constexpr (IsMetadataNumeric::value) { -// return getValue(index); -// } -// -// if constexpr (IsMetadataNumericArray::value) { -// return getArrayValues::type>( -// index); -// } -// } -// -// /** -// * @brief Get the number of elements in this -// * PropertyTablePropertyView. If the view is valid, this returns -// * {@link PropertyTable::count}. Otherwise, this returns 0. -// * -// * @return The number of elements in this PropertyTablePropertyView. -// */ -// int64_t size() const noexcept { -// return this->_status == PropertyTablePropertyViewStatus::Valid ? _size : -// 0; -// } -// -// private: -// ElementType getValue(int64_t index) const noexcept { -// ElementType value = -// reinterpret_cast(_values.data())[index]; -// } -// -// template -// PropertyArrayView getArrayValues(int64_t index) const noexcept { -// size_t count = static_cast(this->arrayCount()); -// // Handle fixed-length arrays -// if (count > 0) { -// size_t arraySize = count * sizeof(T); -// const gsl::span values( -// _values.data() + index * arraySize, -// arraySize); -// return PropertyArrayView{values}; -// } -// -// // Handle variable-length arrays -// const size_t currentOffset = -// getOffsetFromOffsetsBuffer(index, _arrayOffsets, _arrayOffsetType); -// const size_t nextOffset = -// getOffsetFromOffsetsBuffer(index + 1, _arrayOffsets, -// _arrayOffsetType); -// const gsl::span values( -// _values.data() + currentOffset, -// nextOffset - currentOffset); -// return PropertyArrayView{values}; -// } -// -// static int64_t getOffsetTypeSize(PropertyComponentType offsetType) noexcept -// { -// switch (offsetType) { -// case PropertyComponentType::Uint8: -// return sizeof(uint8_t); -// case PropertyComponentType::Uint16: -// return sizeof(uint16_t); -// case PropertyComponentType::Uint32: -// return sizeof(uint32_t); -// case PropertyComponentType::Uint64: -// return sizeof(uint64_t); -// default: -// return 0; -// } -// } -// -// gsl::span _values; -// int64_t _size; -// -// gsl::span _arrayOffsets; -// PropertyComponentType _arrayOffsetType; -// int64_t _arrayOffsetTypeSize; -//}; +template +class PropertyTablePropertyView + : public PropertyView { +private: + using NormalizedType = typename TypeToNormalizedType::type; + +public: + /** + * @brief Constructs an invalid instance for a non-existent property. + */ + PropertyTablePropertyView() + : PropertyView(), + _values{}, + _size{0}, + _arrayOffsets{}, + _arrayOffsetType{PropertyComponentType::None}, + _arrayOffsetTypeSize{0} {} + + /** + * @brief Constructs an invalid instance for an erroneous property. + * + * @param status The value of {@link PropertyTablePropertyViewStatus} indicating the error with the property. + */ + PropertyTablePropertyView(PropertyViewStatusType status) + : PropertyView(status), + _values{}, + _size{0}, + _arrayOffsets{}, + _arrayOffsetType{PropertyComponentType::None}, + _arrayOffsetTypeSize{0} { + assert( + this->_status != PropertyTablePropertyViewStatus::Valid && + "An empty property view should not be constructed with a valid status"); + } + + /** + * @brief Construct an instance pointing to non-array data specified by a {@link PropertyTableProperty}. + * + * @param property The {@link PropertyTableProperty} + * @param classProperty The {@link ClassProperty} this property conforms to. + * @param size The number of elements in the property table specified by {@link PropertyTable::count} + * @param value The raw buffer specified by {@link PropertyTableProperty::values} + */ + PropertyTablePropertyView( + const PropertyTableProperty& property, + const ClassProperty& classProperty, + int64_t size, + gsl::span values) noexcept + : PropertyView(classProperty, property), + _values{values}, + _size{ + this->_status == PropertyTablePropertyViewStatus::Valid ? size : 0}, + _arrayOffsets{}, + _arrayOffsetType{PropertyComponentType::None}, + _arrayOffsetTypeSize{0} {} + + /** + * @brief Construct an instance pointing to the data specified by a {@link PropertyTableProperty}. + * + * + * @param property The {@link PropertyTableProperty} + * @param classProperty The {@link ClassProperty} this property conforms to. + * @param size The number of elements in the property table specified by {@link PropertyTable::count} + * @param values The raw buffer specified by {@link PropertyTableProperty::values} + * @param arrayOffsets The raw buffer specified by {@link PropertyTableProperty::arrayOffsets} + * @param offsetType The offset type of arrayOffsets specified by {@link PropertyTableProperty::arrayOffsetType} + */ + PropertyTablePropertyView( + const PropertyTableProperty& property, + const ClassProperty& classProperty, + int64_t size, + gsl::span values, + gsl::span arrayOffsets, + PropertyComponentType arrayOffsetType) noexcept + : PropertyView(classProperty, property), + _values{values}, + _size{ + this->_status == PropertyTablePropertyViewStatus::Valid ? size : 0}, + _arrayOffsets{arrayOffsets}, + _arrayOffsetType{arrayOffsetType}, + _arrayOffsetTypeSize{getOffsetTypeSize(arrayOffsetType)} {} + + /** + * @brief Get the value of an element of the {@link PropertyTable}, + * with normalization and other value transforms applied. In other words, the + * value will be normalized, then transformed by the property's offset + * and scale, if they are defined. + * + * If this property has a specified "no data" value, and the retrieved element + * is equal to that value, then this will return the property's specified + * default value. If the property did not provide a default value, this + * returns std::nullopt. + * + * @param index The element index + * @return The value of the element, or std::nullopt if it matches the "no + * data" value + */ + std::optional get(int64_t index) const noexcept { + assert( + this->_status == PropertyTablePropertyViewStatus::Valid && + "Check the status() first to make sure view is valid"); + assert( + size() > 0 && + "Check the size() of the view to make sure it's not empty"); + assert(index >= 0 && "index must be non-negative"); + assert(index < size() && "index must be less than size"); + + ElementType value = getRaw(index); + if (this->noData() && value == *(this->noData())) { + return this->defaultValue(); + } + + if constexpr (IsMetadataNumeric::value) { + return applyOffsetAndScale( + normalize(value), + this->offset(), + this->scale()); + } + + if constexpr (IsMetadataNumericArray::value) { + const auto offset = this->offset(); + const auto scale = this->scale(); + int64_t offsetSize = offset ? offset->size() : 0; + int64_t scaleSize = scale ? scale->size() : 0; + std::vector result(static_cast(value.size())); + for (int64_t i = 0; i < value.size(); i++) { + result[i] = value[i]; + + if (i < scaleSize) { + result = applyScale(result[i], scale[i]); + } + + if (i < offsetSize) { + result[i] = result[i] + offset[i]; + } + } + + return PropertyArrayView(std::move(result)); + } + } + + /** + * @brief Get the raw value of an element of the {@link PropertyTable}, + * without offset, scale, or normalization applied. + * + * If this property has a specified "no data" value, the raw value will still + * be returned, even if it equals the "no data" value. + * + * @param index The element index + * @return The value of the element + */ + ElementType getRaw(int64_t index) const noexcept { + assert( + this->_status == PropertyTablePropertyViewStatus::Valid && + "Check the status() first to make sure view is valid"); + assert( + size() > 0 && + "Check the size() of the view to make sure it's not empty"); + assert(index >= 0 && "index must be non-negative"); + assert(index < size() && "index must be less than size"); + + if constexpr (IsMetadataNumeric::value) { + return getValue(index); + } + + if constexpr (IsMetadataNumericArray::value) { + return getArrayValues::type>( + index); + } + } + + /** + * @brief Get the number of elements in this + * PropertyTablePropertyView. If the view is valid, this returns + * {@link PropertyTable::count}. Otherwise, this returns 0. + * + * @return The number of elements in this PropertyTablePropertyView. + */ + int64_t size() const noexcept { + return this->_status == PropertyTablePropertyViewStatus::Valid ? _size : 0; + } + +private: + ElementType getValue(int64_t index) const noexcept { + return reinterpret_cast(_values.data())[index]; + } + + template + PropertyArrayView getArrayValues(int64_t index) const noexcept { + size_t count = static_cast(this->arrayCount()); + // Handle fixed-length arrays + if (count > 0) { + size_t arraySize = count * sizeof(T); + const gsl::span values( + _values.data() + index * arraySize, + arraySize); + return PropertyArrayView{values}; + } + + // Handle variable-length arrays + const size_t currentOffset = + getOffsetFromOffsetsBuffer(index, _arrayOffsets, _arrayOffsetType); + const size_t nextOffset = + getOffsetFromOffsetsBuffer(index + 1, _arrayOffsets, _arrayOffsetType); + const gsl::span values( + _values.data() + currentOffset, + nextOffset - currentOffset); + return PropertyArrayView{values}; + } + + gsl::span _values; + int64_t _size; + + gsl::span _arrayOffsets; + PropertyComponentType _arrayOffsetType; + int64_t _arrayOffsetTypeSize; +}; + } // namespace CesiumGltf diff --git a/CesiumGltf/include/CesiumGltf/PropertyTransformations.h b/CesiumGltf/include/CesiumGltf/PropertyTransformations.h index 6282400d1..e95b5ac1a 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTransformations.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTransformations.h @@ -10,30 +10,21 @@ #include namespace CesiumGltf { -template < - typename TSigned, - std::enable_if_t< - IsMetadataInteger::value && std::is_signed_v>> -double normalize(TSigned value) { - double max = static_cast(std::numeric_limits::max()); - return std::max(static_cast(value) / max, -1.0); -} - -template < - typename TUnsigned, - std::enable_if_t< - IsMetadataInteger::value && std::is_unsigned_v>> -double normalize(TUnsigned value) { - double max = static_cast(std::numeric_limits::max()); - return static_cast(value) / max; +template +double normalize(T value) { + constexpr double max = static_cast(std::numeric_limits::max()); + if constexpr (std::is_signed_v) { + return std::max(static_cast(value) / max, -1.0); + } else { + return static_cast(value) / max; + } } template < glm::length_t N, typename TSigned, glm::qualifier Q, - std::enable_if_t< - IsMetadataInteger::value && std::is_signed_v>> + std::enable_if_t>> glm::vec normalize(glm::vec value) { double max = static_cast(std::numeric_limits::max()); return glm::max(static_cast>(value) / max, -1.0); @@ -43,8 +34,7 @@ template < glm::length_t N, typename TUnsigned, glm::qualifier Q, - std::enable_if_t< - IsMetadataInteger::value && std::is_unsigned_v>> + std::enable_if_t>> glm::vec normalize(glm::vec value) { double max = static_cast(std::numeric_limits::max()); return static_cast>(value) / max; @@ -54,8 +44,7 @@ template < glm::length_t N, typename TSigned, glm::qualifier Q, - std::enable_if_t< - IsMetadataInteger::value && std::is_signed_v>> + std::enable_if_t>> glm::mat normalize(glm::mat value) { double max = static_cast(std::numeric_limits::max()); return glm::max(static_cast>(value) / max, -1.0); @@ -65,8 +54,7 @@ template < glm::length_t N, typename TUnsigned, glm::qualifier Q, - std::enable_if_t< - IsMetadataInteger::value && std::is_unsigned_v>> + std::enable_if_t>> glm::mat normalize(glm::mat value) { double max = static_cast(std::numeric_limits::max()); return static_cast>(value) / max; @@ -94,11 +82,11 @@ T applyOffsetAndScale( const std::optional& scale) { T result = value; if (scale) { - result = applyScale(result, scale); + result = applyScale(result, *scale); } if (offset) { - result += offset; + result += *offset; } return result; diff --git a/CesiumGltf/include/CesiumGltf/PropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyView.h index fe9c3ca3d..896189662 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyView.h @@ -220,6 +220,14 @@ getMatN(const CesiumUtility::JsonValue& jsonValue) { return result; } +template +int64_t getCount(std::optional>& buffer) { + if (!buffer) { + return 0; + } + + return static_cast(buffer->size() / sizeof(ElementType)); +} } // namespace /** @@ -276,56 +284,9 @@ template class PropertyView { return; } - if (classProperty.offset) { - // Only floating point types can specify an offset. - switch (TypeToPropertyType::component) { - case PropertyComponentType::Float32: - case PropertyComponentType::Float64: - _offset = getValue(*classProperty.offset); - if (_offset) { - break; - } - // If it does not break here, something went wrong. - [[fallthrough]]; - default: - _status = PropertyViewStatus::ErrorInvalidOffset; - return; - } - } - - if (classProperty.scale) { - // Only floating point types can specify a scale. - switch (TypeToPropertyType::component) { - case PropertyComponentType::Float32: - case PropertyComponentType::Float64: - _scale = getValue(*classProperty.scale); - if (_scale) { - break; - } - // If it does not break here, something went wrong. - [[fallthrough]]; - default: - _status = PropertyViewStatus::ErrorInvalidScale; - return; - } - } - - if (classProperty.max) { - _max = getValue(*classProperty.max); - if (!_scale) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidMax; - return; - } - } - - if (classProperty.min) { - _min = getValue(*classProperty.min); - if (!_scale) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidMin; - return; - } + getNumericPropertyValues(classProperty); + if (_status != PropertyViewStatus::Valid) { + return; } if (classProperty.noData) { @@ -384,58 +345,7 @@ template class PropertyView { } // If the property has its own values, override the class-provided values. - - if (property.offset) { - // Only floating point types can specify an offset. - switch (TypeToPropertyType::component) { - case PropertyComponentType::Float32: - case PropertyComponentType::Float64: - _offset = getValue(*property.offset); - if (_offset) { - break; - } - // If it does not break here, something went wrong. - [[fallthrough]]; - default: - _status = PropertyViewStatus::ErrorInvalidOffset; - return; - } - } - - if (property.scale) { - // Only floating point types can specify a scale. - switch (TypeToPropertyType::component) { - case PropertyComponentType::Float32: - case PropertyComponentType::Float64: - _scale = getValue(*property.scale); - if (_scale) { - break; - } - // If it does not break here, something went wrong. - [[fallthrough]]; - default: - _status = PropertyViewStatus::ErrorInvalidScale; - return; - } - } - - if (property.max) { - _max = getValue(*property.max); - if (!_scale) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidMax; - return; - } - } - - if (property.min) { - _min = getValue(*property.min); - if (!_scale) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidMin; - return; - } - } + getNumericPropertyValues(property); } /** @@ -451,58 +361,7 @@ template class PropertyView { } // If the property has its own values, override the class-provided values. - - if (property.offset) { - // Only floating point types can specify an offset. - switch (TypeToPropertyType::component) { - case PropertyComponentType::Float32: - case PropertyComponentType::Float64: - _offset = getValue(*property.offset); - if (_offset) { - break; - } - // If it does not break here, something went wrong. - [[fallthrough]]; - default: - _status = PropertyViewStatus::ErrorInvalidOffset; - return; - } - } - - if (property.scale) { - // Only floating point types can specify a scale. - switch (TypeToPropertyType::component) { - case PropertyComponentType::Float32: - case PropertyComponentType::Float64: - _scale = getValue(*property.scale); - if (_scale) { - break; - } - // If it does not break here, something went wrong. - [[fallthrough]]; - default: - _status = PropertyViewStatus::ErrorInvalidScale; - return; - } - } - - if (property.max) { - _max = getValue(*property.max); - if (!_scale) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidMax; - return; - } - } - - if (property.min) { - _min = getValue(*property.min); - if (!_scale) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidMin; - return; - } - } + getNumericPropertyValues(property); } public: @@ -596,11 +455,64 @@ template class PropertyView { protected: PropertyViewStatusType _status; - ElementType applyValueTransforms(ElementType value) { - if (_offset || _scale) { - return applyOffsetAndScale(value, _offset, _scale); - } - return value; + using PropertyDefinitionType = std:: + variant; + void getNumericPropertyValues(const PropertyDefinitionType& inProperty) { + std::visit( + [this](auto property) { + if (property.offset) { + // Only floating point types can specify an offset. + switch (TypeToPropertyType::component) { + case PropertyComponentType::Float32: + case PropertyComponentType::Float64: + this->_offset = getValue(*property.offset); + if (this->_offset) { + break; + } + // If it does not break here, something went wrong. + [[fallthrough]]; + default: + this->_status = PropertyViewStatus::ErrorInvalidOffset; + return; + } + } + + if (property.scale) { + // Only floating point types can specify a scale. + switch (TypeToPropertyType::component) { + case PropertyComponentType::Float32: + case PropertyComponentType::Float64: + this->_scale = getValue(*property.scale); + if (this->_scale) { + break; + } + // If it does not break here, something went wrong. + [[fallthrough]]; + default: + this->_status = PropertyViewStatus::ErrorInvalidScale; + return; + } + } + + if (property.max) { + this->_max = getValue(*property.max); + if (!this->_max) { + // The value was specified but something went wrong. + this->_status = PropertyViewStatus::ErrorInvalidMax; + return; + } + } + + if (property.min) { + this->_min = getValue(*property.min); + if (!this->_min) { + // The value was specified but something went wrong. + this->_status = PropertyViewStatus::ErrorInvalidMin; + return; + } + } + }, + inProperty); } private: @@ -941,14 +853,6 @@ class PropertyView< protected: PropertyViewStatusType _status; - NormalizedType applyValueTransforms(ElementType value) { - NormalizedType result = normalize(value); - if (_offset || _scale) { - return applyOffsetAndScale(result, _offset, _scale); - } - return result; - } - private: bool _required; @@ -1336,60 +1240,9 @@ class PropertyView> { return; } - if (classProperty.offset) { - // Only floating point types can specify an offset. - switch (TypeToPropertyType::component) { - case PropertyComponentType::Float32: - case PropertyComponentType::Float64: - _offset = getArrayValue(*classProperty.offset); - if (_offset && - (_count == 0 || _offset->size() == static_cast(_count))) { - break; - } - // If it does not break here, something went wrong. - [[fallthrough]]; - default: - _status = PropertyViewStatus::ErrorInvalidOffset; - return; - } - } - - if (classProperty.scale) { - // Only floating point types can specify an offset. - switch (TypeToPropertyType::component) { - case PropertyComponentType::Float32: - case PropertyComponentType::Float64: - _scale = getArrayValue(*classProperty.scale); - if (_scale && - (_count == 0 || _scale->size() == static_cast(_count))) { - break; - } - // If it does not break here, something went wrong. - [[fallthrough]]; - default: - _status = PropertyViewStatus::ErrorInvalidScale; - return; - } - } - - if (classProperty.max) { - _max = getArrayValue(*classProperty.max); - if (!_max || - (_count > 0 && _max->size() != static_cast(_count))) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidMax; - return; - } - } - - if (classProperty.min) { - _min = getArrayValue(*classProperty.min); - if (!_min || - (_count > 0 && _min->size() != static_cast(_count))) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidMin; - return; - } + getNumericPropertyValues(classProperty); + if (_status != PropertyViewStatus::Valid) { + return; } if (classProperty.noData) { @@ -1446,60 +1299,7 @@ class PropertyView> { } // If the property has its own values, override the class-provided values. - - if (property.offset) { - // Only floating point types can specify an offset. - switch (TypeToPropertyType::component) { - case PropertyComponentType::Float32: - case PropertyComponentType::Float64: - _offset = getArrayValue(*property.offset); - if (_offset && - (_count == 0 || _offset->size() == static_cast(_count))) { - break; - } - // If it does not break here, something went wrong. - [[fallthrough]]; - default: - _status = PropertyViewStatus::ErrorInvalidOffset; - return; - } - } - - if (property.scale) { - // Only floating point types can specify an offset. - switch (TypeToPropertyType::component) { - case PropertyComponentType::Float32: - case PropertyComponentType::Float64: - _scale = getArrayValue(*property.scale); - if (_scale && - (_count == 0 || _scale->size() == static_cast(_count))) { - break; - } - // If it does not break here, something went wrong. - [[fallthrough]]; - default: - _status = PropertyViewStatus::ErrorInvalidScale; - return; - } - } - - if (property.max) { - _max = getArrayValue(*property.max); - if (!_scale) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidMax; - return; - } - } - - if (property.min) { - _min = getArrayValue(*property.min); - if (!_scale) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidMin; - return; - } - } + getNumericPropertyValues(property); } /** @@ -1515,59 +1315,7 @@ class PropertyView> { } // If the property has its own values, override the class-provided values. - if (property.offset) { - // Only floating point types can specify an offset. - switch (TypeToPropertyType::component) { - case PropertyComponentType::Float32: - case PropertyComponentType::Float64: - _offset = getArrayValue(*property.offset); - if (_offset && - (_count == 0 || _offset->size() == static_cast(_count))) { - break; - } - // If it does not break here, something went wrong. - [[fallthrough]]; - default: - _status = PropertyViewStatus::ErrorInvalidOffset; - return; - } - } - - if (property.scale) { - // Only floating point types can specify an offset. - switch (TypeToPropertyType::component) { - case PropertyComponentType::Float32: - case PropertyComponentType::Float64: - _scale = getArrayValue(*property.scale); - if (_scale && - (_count == 0 || _scale->size() == static_cast(_count))) { - break; - } - // If it does not break here, something went wrong. - [[fallthrough]]; - default: - _status = PropertyViewStatus::ErrorInvalidScale; - return; - } - } - - if (property.max) { - _max = getArrayValue(*property.max); - if (!_scale) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidMax; - return; - } - } - - if (property.min) { - _min = getArrayValue(*property.min); - if (!_scale) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidMin; - return; - } - } + getNumericPropertyValues(property); } public: @@ -1667,14 +1415,6 @@ class PropertyView> { protected: PropertyViewStatusType _status; - PropertyArrayView - applyValueTransforms(const PropertyArrayView& value) { - if (_offset || _scale) { - return applyOffsetAndScale(value, _offset, _scale); - } - return value; - } - private: int64_t _count; @@ -1687,6 +1427,78 @@ class PropertyView> { std::optional> _noData; std::optional> _defaultValue; + using PropertyDefinitionType = std:: + variant; + void getNumericPropertyValues(const PropertyDefinitionType& inProperty) { + std::visit( + [this](auto property) { + if (property.offset) { + // Only floating point types can specify an offset. + switch (TypeToPropertyType::component) { + case PropertyComponentType::Float32: + case PropertyComponentType::Float64: + if (this->_count > 0) { + this->_offset = getArrayValue(*property.offset); + } + if (this->_offset && + getCount(this->_offset) == this->_count) { + break; + } + // If it does not break here, something went wrong. + [[fallthrough]]; + default: + this->_status = PropertyViewStatus::ErrorInvalidOffset; + return; + } + } + + if (property.scale) { + // Only floating point types can specify an offset. + switch (TypeToPropertyType::component) { + case PropertyComponentType::Float32: + case PropertyComponentType::Float64: + if (_count > 0) { + this->_scale = getArrayValue(*property.scale); + } + if (this->_scale && + getCount(this->_scale) == this->_count) { + break; + } + // If it does not break here, something went wrong. + [[fallthrough]]; + default: + this->_status = PropertyViewStatus::ErrorInvalidScale; + return; + } + } + + if (property.max) { + if (this->_count > 0) { + this->_max = getArrayValue(*property.max); + } + if (!this->_max || + getCount(this->_max) != this->_count) { + // The value was specified but something went wrong. + this->_status = PropertyViewStatus::ErrorInvalidMax; + return; + } + } + + if (property.min) { + if (this->_count > 0) { + this->_min = getArrayValue(*property.min); + } + if (!this->_min || + getCount(this->_min) != this->_count) { + // The value was specified but something went wrong. + this->_status = PropertyViewStatus::ErrorInvalidMin; + return; + } + } + }, + inProperty); + } + static std::optional> getArrayValue(const CesiumUtility::JsonValue& jsonValue) { if (!jsonValue.isArray()) { @@ -1795,44 +1607,9 @@ class PropertyView< return; } - if (classProperty.offset) { - _offset = getArrayValue(*classProperty.offset); - if (!_offset || - (_count > 0 && _offset->size() != static_cast(_count))) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidOffset; - return; - } - } - - if (classProperty.scale) { - _scale = getArrayValue(*classProperty.scale); - if (!_scale || - (_count > 0 && _scale->size() != static_cast(_count))) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidScale; - return; - } - } - - if (classProperty.max) { - _max = getArrayValue(*classProperty.max); - if (!_max || - (_count > 0 && _max->size() != static_cast(_count))) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidMax; - return; - } - } - - if (classProperty.min) { - _min = getArrayValue(*classProperty.min); - if (!_min || - (_count > 0 && _min->size() != static_cast(_count))) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidMin; - return; - } + getNumericPropertyValues(classProperty); + if (_status != PropertyViewStatus::Valid) { + return; } if (classProperty.noData) { @@ -1889,46 +1666,7 @@ class PropertyView< } // If the property has its own values, override the class-provided values. - - if (property.offset) { - _offset = getArrayValue(*property.offset); - if (!_offset || - (_count > 0 && _offset->size() != static_cast(_count))) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidOffset; - return; - } - } - - if (property.scale) { - _scale = getArrayValue(*property.scale); - if (!_scale || - (_count > 0 || _scale->size() != static_cast(_count))) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidScale; - return; - } - } - - if (property.max) { - _max = getArrayValue(*property.max); - if (!_max || - (_count > 0 && _max->size() != static_cast(_count))) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidMax; - return; - } - } - - if (property.min) { - _min = getArrayValue(*property.min); - if (!_min || - (_count > 0 && _min->size() != static_cast(_count))) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidMin; - return; - } - } + getNumericPropertyValues(property); } /** @@ -1945,45 +1683,7 @@ class PropertyView< // If the property has its own values, override the class-provided values. - if (property.offset) { - _offset = getArrayValue(*property.offset); - if (!_offset || - (_count > 0 && _offset->size() != static_cast(_count))) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidOffset; - return; - } - } - - if (property.scale) { - _scale = getArrayValue(*property.scale); - if (!_scale || - (_count > 0 || _scale->size() != static_cast(_count))) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidScale; - return; - } - } - - if (property.max) { - _max = getArrayValue(*property.max); - if (!_max || - (_count > 0 && _max->size() != static_cast(_count))) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidMax; - return; - } - } - - if (property.min) { - _min = getArrayValue(*property.min); - if (!_min || - (_count > 0 && _min->size() != static_cast(_count))) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidMin; - return; - } - } + getNumericPropertyValues(property); } public: @@ -2084,28 +1784,6 @@ class PropertyView< protected: PropertyViewStatusType _status; - PropertyArrayView - applyValueTransforms(const PropertyArrayView& value) { - auto offset = this->offset(); - auto scale = this->scale(); - int64_t offsetSize = offset ? offset.size() : 0; - int64_t scaleSize = scale ? scale.size() : 0; - std::vector result(static_cast(value.size())); - for (int64_t i = 0; i < value.size(); i++) { - result[i] = normalize(value[i]); - - if (i < scaleSize) { - result = applyScale(result[i], scale[i]); - } - - if (i < offsetSize) { - result[i] = result[i] + offset[i]; - } - } - - return PropertyArrayView(std::move(result)); - } - private: int64_t _count; @@ -2118,6 +1796,58 @@ class PropertyView< std::optional> _noData; std::optional> _defaultValue; + using PropertyDefinitionType = std:: + variant; + void getNumericPropertyValues(const PropertyDefinitionType& inProperty) { + std::visit( + [this](auto property) { + if (property.offset) { + if (_count > 0) { + _offset = getArrayValue(*property.offset); + } + if (!_offset || getCount(_offset) != _count) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidOffset; + return; + } + } + + if (property.scale) { + if (_count > 0) { + _scale = getArrayValue(*property.scale); + } + if (!_scale || getCount(_scale) != _count) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidScale; + return; + } + } + + if (property.max) { + if (_count > 0) { + _max = getArrayValue(*property.max); + } + if (!_max || getCount(_max) != _count) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidMax; + return; + } + } + + if (property.min) { + if (_count > 0) { + _min = getArrayValue(*property.min); + } + if (!_min || getCount(_min) != _count) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidMin; + return; + } + } + }, + inProperty); + } + template static std::optional> getArrayValue(const CesiumUtility::JsonValue& jsonValue) { diff --git a/CesiumGltf/test/TestPropertyTablePropertyView.cpp b/CesiumGltf/test/TestPropertyTablePropertyView.cpp index 22c24b4d1..8e875aba5 100644 --- a/CesiumGltf/test/TestPropertyTablePropertyView.cpp +++ b/CesiumGltf/test/TestPropertyTablePropertyView.cpp @@ -35,6 +35,116 @@ template static void checkNumeric(const std::vector& expected) { for (int64_t i = 0; i < property.size(); ++i) { REQUIRE(property.getRaw(i) == expected[static_cast(i)]); + REQUIRE(property.get(i)); + REQUIRE(*property.get(i) == property.getRaw(i)); + } +} + +template +static void checkNumeric( + const std::vector& values, + const std::vector>& expected, + const std::optional offset = std::nullopt, + const std::optional scale = std::nullopt, + const std::optional noData = std::nullopt, + const std::optional defaultValue = std::nullopt) { + std::vector data; + data.resize(values.size() * sizeof(T)); + std::memcpy(data.data(), values.data(), data.size()); + + PropertyTableProperty propertyTableProperty; + ClassProperty classProperty; + classProperty.type = + convertPropertyTypeToString(TypeToPropertyType::value); + + PropertyComponentType componentType = TypeToPropertyType::component; + if (componentType != PropertyComponentType::None) { + classProperty.componentType = + convertPropertyComponentTypeToString(componentType); + } + + if (offset) { + classProperty.offset = *offset; + } + + if (scale) { + classProperty.scale = *scale; + } + + if (noData) { + classProperty.noData = *noData; + } + + if (defaultValue) { + classProperty.defaultProperty = *defaultValue; + } + + PropertyTablePropertyView property( + propertyTableProperty, + classProperty, + static_cast(expected.size()), + gsl::span(data.data(), data.size())); + + for (int64_t i = 0; i < property.size(); ++i) { + REQUIRE(property.getRaw(i) == values[static_cast(i)]); + if constexpr (IsMetadataFloating::value) { + REQUIRE(property.get(i)); + REQUIRE(*property.get(i) == Approx(*expected[static_cast(i)])); + } else { + REQUIRE(property.get(i) == expected[static_cast(i)]); + } + } +} + +template ::type> +static void checkNormalizedNumeric( + const std::vector& values, + const std::vector>& expected, + const std::optional offset = std::nullopt, + const std::optional scale = std::nullopt, + const std::optional noData = std::nullopt, + const std::optional defaultValue = std::nullopt) { + std::vector data; + data.resize(values.size() * sizeof(T)); + std::memcpy(data.data(), values.data(), data.size()); + + PropertyTableProperty propertyTableProperty; + ClassProperty classProperty; + classProperty.type = + convertPropertyTypeToString(TypeToPropertyType::value); + + PropertyComponentType componentType = TypeToPropertyType::component; + classProperty.componentType = + convertPropertyComponentTypeToString(componentType); + + classProperty.normalized = true; + + if (offset) { + classProperty.offset = *offset; + } + + if (scale) { + classProperty.scale = *scale; + } + + if (noData) { + classProperty.noData = *noData; + } + + if (defaultValue) { + classProperty.defaultProperty = *defaultValue; + } + + PropertyTablePropertyView property( + propertyTableProperty, + classProperty, + static_cast(expected.size()), + gsl::span(data.data(), data.size())); + + for (int64_t i = 0; i < property.size(); ++i) { + REQUIRE(property.getRaw(i) == values[static_cast(i)]); + REQUIRE(property.get(i)); + REQUIRE(*property.get(i) == Approx(*expected[static_cast(i)])); } } @@ -142,22 +252,22 @@ static void checkFixedLengthArray( } TEST_CASE("Check scalar PropertyTablePropertyView") { - SECTION("Uint8 Scalar") { + SECTION("Uint8") { std::vector data{12, 33, 56, 67}; checkNumeric(data); } - SECTION("Int32 Scalar") { + SECTION("Int32") { std::vector data{111222, -11133, -56000, 670000}; checkNumeric(data); } - SECTION("Float Scalar") { + SECTION("Float") { std::vector data{12.3333f, -12.44555f, -5.6111f, 6.7421f}; checkNumeric(data); } - SECTION("Double Scalar") { + SECTION("Double") { std::vector data{ 12222.3302121, -12000.44555, @@ -165,6 +275,142 @@ TEST_CASE("Check scalar PropertyTablePropertyView") { 6.7421}; checkNumeric(data); } + + SECTION("Normalized Uint8") { + std::vector values{0, 64, 128, 255}; + std::vector> expected{ + 0.0, + 64.0 / 255.0, + 128.0 / 255.0, + 1.0}; + checkNormalizedNumeric(values, expected); + } + + SECTION("Normalized Int16") { + std::vector values{-32768, 0, 16384, 32767}; + std::vector> expected{ + -1.0, + 0.0, + 16384.0 / 32767.0, + 1.0}; + checkNormalizedNumeric(values, expected); + } + + SECTION("Float with Offset / Scale") { + std::vector values{12.5f, -12.5f, -5.0f, 6.75f}; + std::optional offset = 1.0f; + std::optional scale = 2.0f; + std::vector> expected{26.0f, -24.0f, -9.0f, 14.5f}; + checkNumeric(values, expected, offset, scale); + } + + SECTION("Normalized Uint8 with Offset and Scale") { + std::vector values{0, 64, 128, 255}; + std::optional offset = 1.0; + std::optional scale = 2.0; + std::vector> expected{ + 1.0, + 1 + 2 * (64.0 / 255.0), + 1 + 2 * (128.0 / 255.0), + 3.0}; + checkNormalizedNumeric(values, expected, offset, scale); + } + + SECTION("Int16 with NoData") { + std::vector values{-1, 3, 7, -1}; + std::optional noData = -1; + std::vector> expected{ + std::nullopt, + 3, + 7, + std::nullopt}; + checkNumeric( + values, + expected, + std::nullopt, + std::nullopt, + noData, + std::nullopt); + }; + + SECTION("Int16 with NoData and Default") { + std::vector values{-1, 3, 7, -1}; + std::optional noData = -1; + std::optional defaultValue = 0; + std::vector> expected{0, 3, 7, 0}; + checkNumeric( + values, + expected, + std::nullopt, + std::nullopt, + noData, + defaultValue); + } + + SECTION("Normalized Uint8 with all properties") { + std::vector values{0, 64, 128, 255}; + std::optional offset = 1.0; + std::optional scale = 2.0; + std::optional noData = 0; + std::optional defaultValue = 10.0; + std::vector> expected{ + 10.0, + 1 + 2 * (64.0 / 255.0), + 1 + 2 * (128.0 / 255.0), + 3.0}; + checkNormalizedNumeric( + values, + expected, + offset, + scale, + noData, + defaultValue); + } + + SECTION("Overrides class property values") { + std::vector values{1.0f, 3.0f, 2.0f, 4.0f}; + std::vector data; + data.resize(values.size() * sizeof(float)); + std::memcpy(data.data(), values.data(), data.size()); + + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::SCALAR; + classProperty.componentType = ClassProperty::ComponentType::FLOAT32; + classProperty.offset = 0.0f; + classProperty.scale = 1.0f; + classProperty.min = 1.0f; + classProperty.max = 4.0f; + + PropertyTableProperty propertyTableProperty; + propertyTableProperty.offset = 1.0f; + propertyTableProperty.scale = 2.0f; + propertyTableProperty.min = 3.0f; + propertyTableProperty.max = 9.0f; + + PropertyTablePropertyView property( + propertyTableProperty, + classProperty, + static_cast(values.size()), + gsl::span(data.data(), data.size())); + + REQUIRE(property.offset()); + REQUIRE(*property.offset() == 1.0f); + REQUIRE(property.scale()); + REQUIRE(*property.scale() == 2.0f); + REQUIRE(property.min()); + REQUIRE(*property.min() == 3.0f); + REQUIRE(property.max()); + REQUIRE(*property.max() == 9.0f); + + std::vector expected{3.0, 7.0f, 5.0f, 9.0f}; + for (int64_t i = 0; i < property.size(); ++i) { + REQUIRE(property.getRaw(i) == values[static_cast(i)]); + + std::optional transformedValue = property.get(i); + REQUIRE(transformedValue); + REQUIRE(*transformedValue == Approx(expected[static_cast(i)])); + } + } } TEST_CASE("Check vecN PropertyTablePropertyView") { diff --git a/CesiumGltf/test/TestPropertyView.cpp b/CesiumGltf/test/TestPropertyView.cpp index c9cf11553..8aa81a9a9 100644 --- a/CesiumGltf/test/TestPropertyView.cpp +++ b/CesiumGltf/test/TestPropertyView.cpp @@ -507,7 +507,7 @@ TEST_CASE("VecN PropertyView") { view = PropertyView(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMin); - classProperty.max = {10, 5}; + classProperty.max = {0, 500}; view = PropertyView(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMax); } @@ -901,8 +901,8 @@ TEST_CASE("MatN PropertyView") { // clang-format off classProperty.min = { - -29, -40, - -55, -43}; + -29, -240, + -155, -43}; // clang-format on view = PropertyView(classProperty); REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMin); @@ -1424,6 +1424,7 @@ TEST_CASE("Scalar Array PropertyView") { classProperty.type = ClassProperty::Type::SCALAR; classProperty.componentType = ClassProperty::ComponentType::FLOAT32; classProperty.array = true; + classProperty.count = 2; classProperty.offset = JsonValue::Array{5.0f, 10.0f}; classProperty.scale = JsonValue::Array{2.0f, 1.0f}; classProperty.max = JsonValue::Array{10.0f, 20.0f}; @@ -1462,6 +1463,7 @@ TEST_CASE("Scalar Array PropertyView") { classProperty.type = ClassProperty::Type::SCALAR; classProperty.componentType = ClassProperty::ComponentType::UINT8; classProperty.array = true; + classProperty.count = 2; classProperty.required = false; classProperty.noData = {0, 1}; classProperty.defaultProperty = {2, 3}; @@ -1483,11 +1485,36 @@ TEST_CASE("Scalar Array PropertyView") { REQUIRE(value[1] == 3); } + SECTION("Reports errors for defined properties on variable-length arrays") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::SCALAR; + classProperty.componentType = ClassProperty::ComponentType::FLOAT32; + classProperty.array = true; + classProperty.count = 0; + classProperty.min = {0, 0}; + + PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMin); + + classProperty.max = {5, 4}; + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMax); + + classProperty.scale = {1, 1}; + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidScale); + + classProperty.offset = {0, 2}; + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidOffset); + } + SECTION("Reports errors for incorrectly defined properties") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::SCALAR; classProperty.componentType = ClassProperty::ComponentType::UINT8; classProperty.array = true; + classProperty.count = 2; classProperty.required = true; classProperty.defaultProperty = {2, 3}; @@ -1512,6 +1539,7 @@ TEST_CASE("Scalar Array PropertyView") { classProperty.type = ClassProperty::Type::SCALAR; classProperty.componentType = ClassProperty::ComponentType::UINT8; classProperty.array = true; + classProperty.count = 2; classProperty.defaultProperty = {256, 256}; PropertyView> view(classProperty); @@ -1543,6 +1571,7 @@ TEST_CASE("Scalar Array PropertyView") { classProperty.type = ClassProperty::Type::SCALAR; classProperty.componentType = ClassProperty::ComponentType::UINT8; classProperty.array = true; + classProperty.count = 2; classProperty.defaultProperty = "[256, 256]"; PropertyView> view(classProperty); @@ -1643,6 +1672,7 @@ TEST_CASE("Scalar Array PropertyView (normalized)") { classProperty.type = ClassProperty::Type::SCALAR; classProperty.componentType = ClassProperty::ComponentType::INT16; classProperty.array = true; + classProperty.count = 2; classProperty.normalized = true; classProperty.offset = JsonValue::Array{5.0, 10.0}; classProperty.scale = JsonValue::Array{2.0, 1.0}; @@ -1704,11 +1734,38 @@ TEST_CASE("Scalar Array PropertyView (normalized)") { REQUIRE(defaultValue[1] == 3.5); } + SECTION("Reports errors for defined properties on variable-length arrays") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::SCALAR; + classProperty.componentType = ClassProperty::ComponentType::UINT8; + classProperty.array = true; + classProperty.count = 0; + classProperty.normalized = true; + classProperty.min = {0, 0}; + + PropertyView, true> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMin); + + classProperty.max = {5, 4}; + view = PropertyView, true>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMax); + + classProperty.scale = {1, 1}; + view = PropertyView, true>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidScale); + + classProperty.offset = {0, 2}; + classProperty.offset = {0, 2}; + view = PropertyView, true>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidOffset); + } + SECTION("Reports errors for incorrectly defined properties") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::SCALAR; classProperty.componentType = ClassProperty::ComponentType::UINT8; classProperty.array = true; + classProperty.count = 2; classProperty.normalized = true; classProperty.required = true; classProperty.defaultProperty = {2, 3}; @@ -1726,6 +1783,7 @@ TEST_CASE("Scalar Array PropertyView (normalized)") { classProperty.type = ClassProperty::Type::SCALAR; classProperty.componentType = ClassProperty::ComponentType::UINT8; classProperty.array = true; + classProperty.count = 2; classProperty.normalized = true; classProperty.noData = {-1, 0}; @@ -1738,6 +1796,7 @@ TEST_CASE("Scalar Array PropertyView (normalized)") { classProperty.type = ClassProperty::Type::SCALAR; classProperty.componentType = ClassProperty::ComponentType::UINT8; classProperty.array = true; + classProperty.count = 2; classProperty.normalized = true; classProperty.defaultProperty = "[256, 256]"; @@ -1839,6 +1898,7 @@ TEST_CASE("VecN Array PropertyView") { classProperty.type = ClassProperty::Type::VEC3; classProperty.componentType = ClassProperty::ComponentType::FLOAT32; classProperty.array = true; + classProperty.count = 2; classProperty.offset = {{-1, 1, 2}, {4, 4, 0}}; classProperty.scale = {{2, 1, 3}, {8, 2, 3}}; classProperty.max = {{14, 28, 12}, {10, 5, 6}}; @@ -1898,6 +1958,30 @@ TEST_CASE("VecN Array PropertyView") { REQUIRE(value[1] == glm::vec2(5.0f, 6.0f)); } + SECTION("Reports errors for defined properties on variable-length arrays") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::VEC3; + classProperty.componentType = ClassProperty::ComponentType::FLOAT32; + classProperty.array = true; + classProperty.count = 0; + classProperty.min = {{-11, -12, -13}, {-2, -4, 6}}; + + PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMin); + + classProperty.max = {{14, 28, 12}, {10, 5, 6}}; + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMax); + + classProperty.scale = {{2, 1, 3}, {8, 2, 3}}; + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidScale); + + classProperty.offset = {{-1, 1, 2}, {4, 4, 0}}; + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidOffset); + } + SECTION("Reports errors for incorrectly defined properties") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::VEC2; @@ -2058,6 +2142,7 @@ TEST_CASE("VecN Array PropertyView (normalized)") { classProperty.type = ClassProperty::Type::VEC3; classProperty.componentType = ClassProperty::ComponentType::INT32; classProperty.array = true; + classProperty.count = 2; classProperty.normalized = true; classProperty.offset = {{-1, 1, 2}, {4, 4, 0}}; classProperty.scale = {{2, 1, 3}, {8, 2, 3}}; @@ -2119,11 +2204,37 @@ TEST_CASE("VecN Array PropertyView (normalized)") { REQUIRE(defaultValue[1] == glm::dvec2(5.0, 6.0)); } + SECTION("Reports errors for defined properties on variable-length arrays") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::VEC3; + classProperty.componentType = ClassProperty::ComponentType::INT32; + classProperty.array = true; + classProperty.count = 0; + classProperty.normalized = true; + classProperty.min = {{-11, -12, -13}, {-2, -4, 6}}; + + PropertyView, true> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMin); + + classProperty.max = {{14, 28, 12}, {10, 5, 6}}; + view = PropertyView, true>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMax); + + classProperty.scale = {{2, 1, 3}, {8, 2, 3}}; + view = PropertyView, true>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidScale); + + classProperty.offset = {{-1, 1, 2}, {4, 4, 0}}; + view = PropertyView, true>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidOffset); + } + SECTION("Reports errors for incorrectly defined properties") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::VEC2; classProperty.componentType = ClassProperty::ComponentType::INT32; classProperty.array = true; + classProperty.count = 2; classProperty.normalized = true; classProperty.required = true; classProperty.defaultProperty = {{3, 4}, {5, 6}}; @@ -2253,6 +2364,7 @@ TEST_CASE("MatN Array PropertyView") { classProperty.type = ClassProperty::Type::MAT2; classProperty.componentType = ClassProperty::ComponentType::FLOAT32; classProperty.array = true; + classProperty.count = 2; // clang-format off classProperty.offset = { {-1, 1, @@ -2313,6 +2425,7 @@ TEST_CASE("MatN Array PropertyView") { classProperty.type = ClassProperty::Type::MAT2; classProperty.componentType = ClassProperty::ComponentType::INT8; classProperty.array = true; + classProperty.count = 2; classProperty.required = false; // clang-format off classProperty.noData = { @@ -2346,11 +2459,63 @@ TEST_CASE("MatN Array PropertyView") { REQUIRE(value[1] == glm::i8mat2x2(2, 2, 2, 2)); } + SECTION("Reports errors for defined properties on variable-length arrays") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::MAT2; + classProperty.componentType = ClassProperty::ComponentType::FLOAT32; + classProperty.array = true; + classProperty.count = 0; + // clang-format off + classProperty.min = { + {0, 0, + 0, 0}, + {-1, -1, + -1, -1}, + }; + // clang-format on + PropertyView> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMin); + + // clang-format off + classProperty.max = { + {1, 1, + 1, 1}, + {2, 2, + 2, 2}, + }; + // clang-format on + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMax); + + // clang-format off + classProperty.scale = { + {1, 0, + 0, 1}, + {-1, 0, + 0, -1}, + }; + // clang-format on + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidScale); + + // clang-format off + classProperty.offset = { + {2, 2, + 1, 1}, + {0, 2, + 1, 2}, + }; + // clang-format on + view = PropertyView>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidOffset); + } + SECTION("Reports errors for incorrectly defined properties") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::MAT2; classProperty.componentType = ClassProperty::ComponentType::INT32; classProperty.array = true; + classProperty.count = 2; classProperty.required = true; // clang-format off classProperty.defaultProperty = { @@ -2403,6 +2568,7 @@ TEST_CASE("MatN Array PropertyView") { classProperty.type = ClassProperty::Type::MAT2; classProperty.componentType = ClassProperty::ComponentType::INT8; classProperty.array = true; + classProperty.count = 2; // clang-format off classProperty.defaultProperty = { @@ -2474,6 +2640,7 @@ TEST_CASE("MatN Array PropertyView") { classProperty.type = ClassProperty::Type::MAT2; classProperty.componentType = ClassProperty::ComponentType::INT8; classProperty.array = true; + classProperty.count = 2; classProperty.defaultProperty = {4, 1, 2, 0}; PropertyView> view(classProperty); @@ -2566,8 +2733,8 @@ TEST_CASE("MatN Array PropertyView (normalized)") { classProperty.type = ClassProperty::Type::MAT3; classProperty.componentType = ClassProperty::ComponentType::INT32; classProperty.array = true; - classProperty.normalized = true; classProperty.count = 5; + classProperty.normalized = true; PropertyView, true> view(classProperty); REQUIRE(view.status() == PropertyViewStatus::Valid); @@ -2579,6 +2746,7 @@ TEST_CASE("MatN Array PropertyView (normalized)") { classProperty.type = ClassProperty::Type::MAT2; classProperty.componentType = ClassProperty::ComponentType::INT32; classProperty.array = true; + classProperty.count = 2; classProperty.normalized = true; // clang-format off @@ -2641,6 +2809,7 @@ TEST_CASE("MatN Array PropertyView (normalized)") { classProperty.type = ClassProperty::Type::MAT2; classProperty.componentType = ClassProperty::ComponentType::INT8; classProperty.array = true; + classProperty.count = 2; classProperty.normalized = true; classProperty.required = false; // clang-format off @@ -2674,12 +2843,64 @@ TEST_CASE("MatN Array PropertyView (normalized)") { REQUIRE(defaultValue[0] == glm::dmat2(1, 1, 1, 1)); REQUIRE(defaultValue[1] == glm::dmat2(2, 2, 2, 2)); } + SECTION("Reports errors for defined properties on variable-length arrays") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::MAT2; + classProperty.componentType = ClassProperty::ComponentType::INT32; + classProperty.array = true; + classProperty.count = 0; + classProperty.normalized = true; + // clang-format off + classProperty.min = { + {0, 0, + 0, 0}, + {-1, -1, + -1, -1}, + }; + + // clang-format on + PropertyView, true> view(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMin); + + // clang-format off + classProperty.max = { + {1, 1, + 1, 1}, + {2, 2, + 2, 2}, + }; + // clang-format on + view = PropertyView, true>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidMax); + // clang-format off + classProperty.scale = { + {1, 0, + 0, 1}, + {-1, 0, + 0, -1}, + }; + // clang-format on + view = PropertyView, true>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidScale); + + // clang-format off + classProperty.offset = { + {2, 2, + 1, 1}, + {0, 2, + 1, 2}, + }; + // clang-format on + view = PropertyView, true>(classProperty); + REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidOffset); + } SECTION("Reports errors for incorrectly defined properties") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::MAT2; classProperty.componentType = ClassProperty::ComponentType::INT32; classProperty.array = true; + classProperty.count = 2; classProperty.normalized = true; classProperty.required = true; // clang-format off From d43d7138afe8efa37768230eb8d890d2882c12b0 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Tue, 22 Aug 2023 18:31:09 -0400 Subject: [PATCH 196/421] Start adding tests for PropertyTablePropertyView --- .../CesiumGltf/PropertyTablePropertyView.h | 68 +- .../CesiumGltf/PropertyTransformations.h | 154 +- .../include/CesiumGltf/PropertyTypeTraits.h | 2 + CesiumGltf/include/CesiumGltf/PropertyView.h | 14 +- .../test/TestPropertyTablePropertyView.cpp | 1383 +++++++++++++++-- CesiumGltf/test/TestPropertyTypeTraits.cpp | 120 +- 6 files changed, 1517 insertions(+), 224 deletions(-) diff --git a/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h index e4fe2e9de..c63f6e661 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h @@ -148,7 +148,7 @@ int64_t getOffsetTypeSize(PropertyComponentType offsetType) noexcept { } } // namespace -template +template class PropertyTablePropertyView; /** @@ -282,10 +282,12 @@ class PropertyTablePropertyView return this->defaultValue(); } - if constexpr ( - IsMetadataNumeric::value || - IsMetadataNumericArray::value) { - value = applyOffsetAndScale(value, this->offset(), this->scale()); + if constexpr (IsMetadataNumeric::value) { + value = transformValue(value, this->offset(), this->scale()); + } + + if constexpr (IsMetadataNumericArray::value) { + value = transformArray(value, this->offset(), this->scale()); } return value; @@ -590,32 +592,50 @@ class PropertyTablePropertyView return this->defaultValue(); } - if constexpr (IsMetadataNumeric::value) { - return applyOffsetAndScale( + if constexpr (IsMetadataScalar::value) { + return transformValue( normalize(value), this->offset(), this->scale()); } - if constexpr (IsMetadataNumericArray::value) { - const auto offset = this->offset(); - const auto scale = this->scale(); - int64_t offsetSize = offset ? offset->size() : 0; - int64_t scaleSize = scale ? scale->size() : 0; - std::vector result(static_cast(value.size())); - for (int64_t i = 0; i < value.size(); i++) { - result[i] = value[i]; - - if (i < scaleSize) { - result = applyScale(result[i], scale[i]); - } - - if (i < offsetSize) { - result[i] = result[i] + offset[i]; - } + if constexpr (IsMetadataVecN::value) { + constexpr glm::length_t N = ElementType::length(); + using T = typename ElementType::value_type; + using NormalizedT = typename NormalizedType::value_type; + return transformVecN( + normalize(value), + this->offset(), + this->scale()); + } + + if constexpr (IsMetadataMatN::value) { + constexpr glm::length_t N = ElementType::length(); + using T = typename ElementType::value_type; + using NormalizedT = typename NormalizedType::value_type; + return transformMatN( + normalize(value), + this->offset(), + this->scale()); + } + + if constexpr (IsMetadataArray::value) { + using ArrayElementType = typename MetadataArrayType::type; + if constexpr (IsMetadataScalar::value) { + return transformNormalizedArray( + value, + this->offset(), + this->scale()); } - return PropertyArrayView(std::move(result)); + if constexpr (IsMetadataVecN::value) { + constexpr glm::length_t N = ArrayElementType::length(); + using T = typename ArrayElementType::value_type; + return transformNormalizedVecNArray( + value, + this->offset(), + this->scale()); + } } } diff --git a/CesiumGltf/include/CesiumGltf/PropertyTransformations.h b/CesiumGltf/include/CesiumGltf/PropertyTransformations.h index e95b5ac1a..9716520a9 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTransformations.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTransformations.h @@ -10,8 +10,7 @@ #include namespace CesiumGltf { -template -double normalize(T value) { +template double normalize(T value) { constexpr double max = static_cast(std::numeric_limits::max()); if constexpr (std::is_signed_v) { return std::max(static_cast(value) / max, -1.0); @@ -20,44 +19,31 @@ double normalize(T value) { } } -template < - glm::length_t N, - typename TSigned, - glm::qualifier Q, - std::enable_if_t>> -glm::vec normalize(glm::vec value) { - double max = static_cast(std::numeric_limits::max()); - return glm::max(static_cast>(value) / max, -1.0); -} - -template < - glm::length_t N, - typename TUnsigned, - glm::qualifier Q, - std::enable_if_t>> -glm::vec normalize(glm::vec value) { - double max = static_cast(std::numeric_limits::max()); - return static_cast>(value) / max; -} - -template < - glm::length_t N, - typename TSigned, - glm::qualifier Q, - std::enable_if_t>> -glm::mat normalize(glm::mat value) { - double max = static_cast(std::numeric_limits::max()); - return glm::max(static_cast>(value) / max, -1.0); +template +glm::vec normalize(glm::vec value) { + constexpr double max = static_cast(std::numeric_limits::max()); + if constexpr (std::is_signed_v) { + return glm::max(static_cast>(value) / max, -1.0); + } else { + return static_cast>(value) / max; + } } -template < - glm::length_t N, - typename TUnsigned, - glm::qualifier Q, - std::enable_if_t>> -glm::mat normalize(glm::mat value) { - double max = static_cast(std::numeric_limits::max()); - return static_cast>(value) / max; +template +glm::mat normalize(glm::mat value) { + constexpr double max = static_cast(std::numeric_limits::max()); + // No max() implementation for matrices, so we have to write our own. + glm::mat result; + for (glm::length_t i = 0; i < N; i++) { + for (glm::length_t j = 0; j < N; j++) { + if constexpr (std::is_signed_v) { + result[i][j] = glm::max(static_cast(value[i][j]) / max, -1.0); + } else { + result[i][j] = static_cast(value[i][j]) / max; + } + } + } + return result; } template T applyScale(const T& value, const T& scale) { @@ -76,7 +62,7 @@ template T applyScale(const T& value, const T& scale) { } template -T applyOffsetAndScale( +T transformValue( const T& value, const std::optional& offset, const std::optional& scale) { @@ -92,27 +78,103 @@ T applyOffsetAndScale( return result; } +template +glm::vec transformVecN( + const glm::vec& value, + const std::optional>& offset, + const std::optional>& scale) { + glm::vec result = value; + if (scale) { + result = applyScale>(result, *scale); + } + + if (offset) { + result += *offset; + } + + return result; +} + +template +glm::mat transformMatN( + const glm::mat& value, + const std::optional>& offset, + const std::optional>& scale) { + glm::mat result = value; + if (scale) { + result = applyScale>(result, *scale); + } + + if (offset) { + result += *offset; + } + + return result; +} + template -PropertyArrayView applyOffsetAndScale( +PropertyArrayView transformArray( const PropertyArrayView& value, const std::optional>& offset, const std::optional>& scale) { - int64_t offsetSize = offset ? offset->size() : 0; - int64_t scaleSize = scale ? scale->size() : 0; std::vector result(static_cast(value.size())); for (int64_t i = 0; i < value.size(); i++) { result[i] = value[i]; - if (i < scaleSize) { - result = applyScale(result[i], scale[i]); + if (scale) { + result[i] = applyScale(result[i], (*scale)[i]); } - if (i < offsetSize) { - result[i] = result[i] + offset[i]; + if (offset) { + result[i] = result[i] + (*offset)[i]; } } return PropertyArrayView(std::move(result)); } +template < + typename T, + typename NormalizedType = typename TypeToNormalizedType::type> +PropertyArrayView transformNormalizedArray( + const PropertyArrayView& value, + const std::optional>& offset, + const std::optional>& scale) { + std::vector result(static_cast(value.size())); + for (int64_t i = 0; i < value.size(); i++) { + result[i] = normalize(value[i]); + + if (scale) { + result[i] = applyScale(result[i], (*scale)[i]); + } + + if (offset) { + result[i] = result[i] + (*offset)[i]; + } + } + + return PropertyArrayView(std::move(result)); +} + +template +PropertyArrayView> transformNormalizedVecNArray( + const PropertyArrayView>& value, + const std::optional>>& offset, + const std::optional>>& scale) { + std::vector> result(static_cast(value.size())); + for (int64_t i = 0; i < value.size(); i++) { + result[i] = normalize(value[i]); + + if (scale) { + result[i] = applyScale>(result[i], (*scale)[i]); + } + + if (offset) { + result[i] = result[i] + (*offset)[i]; + } + } + + return PropertyArrayView>(std::move(result)); +} + } // namespace CesiumGltf diff --git a/CesiumGltf/include/CesiumGltf/PropertyTypeTraits.h b/CesiumGltf/include/CesiumGltf/PropertyTypeTraits.h index eff9ab37a..286cb0609 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTypeTraits.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTypeTraits.h @@ -285,6 +285,8 @@ struct CanBeNormalized> : CanBeNormalized {}; template struct CanBeNormalized> : CanBeNormalized {}; +template +struct CanBeNormalized> : CanBeNormalized {}; /** * @brief Convert an integer numeric type to the corresponding representation as * a double type. Doubles are preferred over floats to maintain more precision. diff --git a/CesiumGltf/include/CesiumGltf/PropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyView.h index 896189662..469f8392d 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyView.h @@ -233,8 +233,7 @@ int64_t getCount(std::optional>& buffer) { /** * @brief Represents a metadata property in EXT_structural_metadata. */ -template -class PropertyView; +template class PropertyView; /** * @brief Represents a non-normalized metadata property in @@ -565,11 +564,7 @@ template class PropertyView { * @tparam ElementType The C++ type of the values in this property. Must have an * integer component type. */ -template -class PropertyView< - ElementType, - true, - std::enable_if_t::value>> { +template class PropertyView { private: using NormalizedType = typename TypeToNormalizedType::type; @@ -1562,10 +1557,7 @@ class PropertyView> { * property. Must have an integer component type. */ template -class PropertyView< - PropertyArrayView, - true, - std::enable_if_t::value>> { +class PropertyView, true> { private: using NormalizedType = typename TypeToNormalizedType::type; diff --git a/CesiumGltf/test/TestPropertyTablePropertyView.cpp b/CesiumGltf/test/TestPropertyTablePropertyView.cpp index 8e875aba5..8094f00c8 100644 --- a/CesiumGltf/test/TestPropertyTablePropertyView.cpp +++ b/CesiumGltf/test/TestPropertyTablePropertyView.cpp @@ -10,6 +10,31 @@ #include using namespace CesiumGltf; +using namespace CesiumUtility; + +#pragma region Utility + +template +JsonValue::Array getArrayFromVecN(glm::vec vecN) { + JsonValue::Array values(N); + for (glm::length_t i = 0; i < N; i++) { + values[i] = vecN[i]; + } + + return values; +} + +template +JsonValue::Array getArrayFromMatN(glm::mat matN) { + JsonValue::Array values(N * N); + for (glm::length_t i = 0; i < N; i++) { + for (glm::length_t j = 0; j < N; j++) { + values[N * i + j] = matN[i][j]; + } + } + + return values; +} template static void checkNumeric(const std::vector& expected) { std::vector data; @@ -33,6 +58,8 @@ template static void checkNumeric(const std::vector& expected) { static_cast(expected.size()), gsl::span(data.data(), data.size())); + REQUIRE(property.size() == static_cast(expected.size())); + for (int64_t i = 0; i < property.size(); ++i) { REQUIRE(property.getRaw(i) == expected[static_cast(i)]); REQUIRE(property.get(i)); @@ -41,7 +68,7 @@ template static void checkNumeric(const std::vector& expected) { } template -static void checkNumeric( +static void checkScalar( const std::vector& values, const std::vector>& expected, const std::optional offset = std::nullopt, @@ -58,10 +85,8 @@ static void checkNumeric( convertPropertyTypeToString(TypeToPropertyType::value); PropertyComponentType componentType = TypeToPropertyType::component; - if (componentType != PropertyComponentType::None) { - classProperty.componentType = - convertPropertyComponentTypeToString(componentType); - } + classProperty.componentType = + convertPropertyComponentTypeToString(componentType); if (offset) { classProperty.offset = *offset; @@ -85,6 +110,8 @@ static void checkNumeric( static_cast(expected.size()), gsl::span(data.data(), data.size())); + REQUIRE(property.size() == static_cast(expected.size())); + for (int64_t i = 0; i < property.size(); ++i) { REQUIRE(property.getRaw(i) == values[static_cast(i)]); if constexpr (IsMetadataFloating::value) { @@ -96,14 +123,116 @@ static void checkNumeric( } } -template ::type> -static void checkNormalizedNumeric( +template +static void checkVecN( + const std::vector>& values, + const std::vector>>& expected, + const std::optional> offset = std::nullopt, + const std::optional> scale = std::nullopt, + const std::optional> noData = std::nullopt, + const std::optional> defaultValue = std::nullopt) { + std::vector data; + data.resize(values.size() * sizeof(glm::vec)); + std::memcpy(data.data(), values.data(), data.size()); + + PropertyTableProperty propertyTableProperty; + ClassProperty classProperty; + classProperty.type = + convertPropertyTypeToString(TypeToPropertyType>::value); + + PropertyComponentType componentType = TypeToPropertyType::component; + classProperty.componentType = + convertPropertyComponentTypeToString(componentType); + + if (offset) { + classProperty.offset = getArrayFromVecN(*offset); + } + + if (scale) { + classProperty.scale = getArrayFromVecN(*scale); + } + + if (noData) { + classProperty.noData = getArrayFromVecN(*noData); + } + + if (defaultValue) { + classProperty.defaultProperty = getArrayFromVecN(*defaultValue); + } + + PropertyTablePropertyView> property( + propertyTableProperty, + classProperty, + static_cast(expected.size()), + gsl::span(data.data(), data.size())); + + REQUIRE(property.size() == static_cast(expected.size())); + + for (int64_t i = 0; i < property.size(); ++i) { + REQUIRE(property.getRaw(i) == values[static_cast(i)]); + REQUIRE(property.get(i) == expected[static_cast(i)]); + } +} + +template +static void checkMatN( + const std::vector>& values, + const std::vector>>& expected, + const std::optional> offset = std::nullopt, + const std::optional> scale = std::nullopt, + const std::optional> noData = std::nullopt, + const std::optional> defaultValue = std::nullopt) { + std::vector data; + data.resize(values.size() * sizeof(glm::mat)); + std::memcpy(data.data(), values.data(), data.size()); + + PropertyTableProperty propertyTableProperty; + ClassProperty classProperty; + classProperty.type = + convertPropertyTypeToString(TypeToPropertyType>::value); + + PropertyComponentType componentType = TypeToPropertyType::component; + classProperty.componentType = + convertPropertyComponentTypeToString(componentType); + + if (offset) { + classProperty.offset = getArrayFromMatN(*offset); + } + + if (scale) { + classProperty.scale = getArrayFromMatN(*scale); + } + + if (noData) { + classProperty.noData = getArrayFromMatN(*noData); + } + + if (defaultValue) { + classProperty.defaultProperty = getArrayFromMatN(*defaultValue); + } + + PropertyTablePropertyView> property( + propertyTableProperty, + classProperty, + static_cast(expected.size()), + gsl::span(data.data(), data.size())); + + REQUIRE(property.size() == static_cast(expected.size())); + + for (int64_t i = 0; i < property.size(); ++i) { + REQUIRE(property.getRaw(i) == values[static_cast(i)]); + REQUIRE(property.get(i) == expected[static_cast(i)]); + } +} + +template +static void checkNormalizedScalar( const std::vector& values, - const std::vector>& expected, - const std::optional offset = std::nullopt, - const std::optional scale = std::nullopt, + const std::vector>& expected, + const std::optional offset = std::nullopt, + const std::optional scale = std::nullopt, const std::optional noData = std::nullopt, - const std::optional defaultValue = std::nullopt) { + const std::optional defaultValue = std::nullopt) { std::vector data; data.resize(values.size() * sizeof(T)); std::memcpy(data.data(), values.data(), data.size()); @@ -141,10 +270,117 @@ static void checkNormalizedNumeric( static_cast(expected.size()), gsl::span(data.data(), data.size())); + REQUIRE(property.size() == static_cast(expected.size())); + for (int64_t i = 0; i < property.size(); ++i) { REQUIRE(property.getRaw(i) == values[static_cast(i)]); - REQUIRE(property.get(i)); - REQUIRE(*property.get(i) == Approx(*expected[static_cast(i)])); + REQUIRE(property.get(i) == expected[static_cast(i)]); + } +} + +template +static void checkNormalizedVecN( + const std::vector>& values, + const std::vector>>& expected, + const std::optional> offset = std::nullopt, + const std::optional> scale = std::nullopt, + const std::optional> noData = std::nullopt, + const std::optional> defaultValue = std::nullopt) { + std::vector data; + data.resize(values.size() * sizeof(glm::vec)); + std::memcpy(data.data(), values.data(), data.size()); + + PropertyTableProperty propertyTableProperty; + ClassProperty classProperty; + classProperty.type = + convertPropertyTypeToString(TypeToPropertyType>::value); + + PropertyComponentType componentType = TypeToPropertyType::component; + classProperty.componentType = + convertPropertyComponentTypeToString(componentType); + + classProperty.normalized = true; + + if (offset) { + classProperty.offset = getArrayFromVecN(*offset); + } + + if (scale) { + classProperty.scale = getArrayFromVecN(*scale); + } + + if (noData) { + classProperty.noData = getArrayFromVecN(*noData); + } + + if (defaultValue) { + classProperty.defaultProperty = getArrayFromVecN(*defaultValue); + } + + PropertyTablePropertyView, true> property( + propertyTableProperty, + classProperty, + static_cast(expected.size()), + gsl::span(data.data(), data.size())); + + REQUIRE(property.size() == static_cast(expected.size())); + + for (int64_t i = 0; i < property.size(); ++i) { + REQUIRE(property.getRaw(i) == values[static_cast(i)]); + REQUIRE(property.get(i) == expected[static_cast(i)]); + } +} + +template +static void checkNormalizedMatN( + const std::vector>& values, + const std::vector>>& expected, + const std::optional> offset = std::nullopt, + const std::optional> scale = std::nullopt, + const std::optional> noData = std::nullopt, + const std::optional> defaultValue = std::nullopt) { + std::vector data; + data.resize(values.size() * sizeof(glm::mat)); + std::memcpy(data.data(), values.data(), data.size()); + + PropertyTableProperty propertyTableProperty; + ClassProperty classProperty; + classProperty.type = + convertPropertyTypeToString(TypeToPropertyType>::value); + + PropertyComponentType componentType = TypeToPropertyType::component; + classProperty.componentType = + convertPropertyComponentTypeToString(componentType); + + classProperty.normalized = true; + + if (offset) { + classProperty.offset = getArrayFromMatN(*offset); + } + + if (scale) { + classProperty.scale = getArrayFromMatN(*scale); + } + + if (noData) { + classProperty.noData = getArrayFromMatN(*noData); + } + + if (defaultValue) { + classProperty.defaultProperty = getArrayFromMatN(*defaultValue); + } + + PropertyTablePropertyView, true> property( + propertyTableProperty, + classProperty, + static_cast(expected.size()), + gsl::span(data.data(), data.size())); + + REQUIRE(property.size() == static_cast(expected.size())); + + for (int64_t i = 0; i < property.size(); ++i) { + REQUIRE(property.getRaw(i) == values[static_cast(i)]); + REQUIRE(property.get(i) == expected[static_cast(i)]); } } @@ -207,11 +443,13 @@ static void checkVariableLengthArray( template static void checkFixedLengthArray( const std::vector& data, - int64_t fixedLengthArrayCount, - int64_t instanceCount) { + int64_t fixedLengthArrayCount) { + int64_t instanceCount = + static_cast(data.size()) / fixedLengthArrayCount; + std::vector buffer; buffer.resize(data.size() * sizeof(T)); - std::memcpy(buffer.data(), data.data(), data.size() * sizeof(T)); + std::memcpy(buffer.data(), data.data(), buffer.size()); PropertyTableProperty propertyTableProperty; ClassProperty classProperty; @@ -250,81 +488,229 @@ static void checkFixedLengthArray( REQUIRE(expectedIdx == data.size()); } +template +static void checkFixedLengthArrayWithProperties( + const std::vector& data, + int64_t fixedLengthArrayCount, + const std::vector>>& expected, + const std::optional offset = std::nullopt, + const std::optional scale = std::nullopt, + const std::optional noData = std::nullopt, + const std::optional defaultValue = std::nullopt) { + int64_t instanceCount = + static_cast(data.size()) / fixedLengthArrayCount; -TEST_CASE("Check scalar PropertyTablePropertyView") { - SECTION("Uint8") { - std::vector data{12, 33, 56, 67}; - checkNumeric(data); - } + std::vector buffer; + buffer.resize(data.size() * sizeof(T)); + std::memcpy(buffer.data(), data.data(), buffer.size()); - SECTION("Int32") { - std::vector data{111222, -11133, -56000, 670000}; - checkNumeric(data); - } + PropertyTableProperty propertyTableProperty; + ClassProperty classProperty; + classProperty.type = + convertPropertyTypeToString(TypeToPropertyType::value); - SECTION("Float") { - std::vector data{12.3333f, -12.44555f, -5.6111f, 6.7421f}; - checkNumeric(data); + PropertyComponentType componentType = TypeToPropertyType::component; + if (componentType != PropertyComponentType::None) { + classProperty.componentType = + convertPropertyComponentTypeToString(componentType); } - SECTION("Double") { - std::vector data{ - 12222.3302121, - -12000.44555, - -5000.6113111, - 6.7421}; - checkNumeric(data); - } + classProperty.array = true; + classProperty.count = fixedLengthArrayCount; + classProperty.offset = offset; + classProperty.scale = scale; + classProperty.noData = noData; + classProperty.defaultProperty = defaultValue; - SECTION("Normalized Uint8") { - std::vector values{0, 64, 128, 255}; - std::vector> expected{ - 0.0, - 64.0 / 255.0, - 128.0 / 255.0, - 1.0}; - checkNormalizedNumeric(values, expected); - } + PropertyTablePropertyView> property( + propertyTableProperty, + classProperty, + instanceCount, + gsl::span(buffer.data(), buffer.size()), + gsl::span(), + gsl::span(), + PropertyComponentType::None, + PropertyComponentType::None); - SECTION("Normalized Int16") { - std::vector values{-32768, 0, 16384, 32767}; - std::vector> expected{ - -1.0, - 0.0, - 16384.0 / 32767.0, - 1.0}; - checkNormalizedNumeric(values, expected); - } + REQUIRE(property.arrayCount() == fixedLengthArrayCount); - SECTION("Float with Offset / Scale") { - std::vector values{12.5f, -12.5f, -5.0f, 6.75f}; - std::optional offset = 1.0f; - std::optional scale = 2.0f; - std::vector> expected{26.0f, -24.0f, -9.0f, 14.5f}; - checkNumeric(values, expected, offset, scale); + // Check raw values first + size_t expectedIdx = 0; + for (int64_t i = 0; i < property.size(); ++i) { + PropertyArrayView rawValues = property.getRaw(i); + for (int64_t j = 0; j < rawValues.size(); ++j) { + REQUIRE(rawValues[j] == data[expectedIdx]); + ++expectedIdx; + } } - SECTION("Normalized Uint8 with Offset and Scale") { - std::vector values{0, 64, 128, 255}; - std::optional offset = 1.0; - std::optional scale = 2.0; - std::vector> expected{ - 1.0, - 1 + 2 * (64.0 / 255.0), - 1 + 2 * (128.0 / 255.0), - 3.0}; - checkNormalizedNumeric(values, expected, offset, scale); + REQUIRE(expectedIdx == data.size()); + + // Check values with properties applied + for (int64_t i = 0; i < property.size(); ++i) { + std::optional> maybeValues = property.get(i); + std::optional> expectedValues = + expected[static_cast(i)]; + REQUIRE(maybeValues.has_value() == expectedValues.has_value()); + if (!maybeValues) { + continue; + } + + auto values = *maybeValues; + for (int64_t j = 0; j < values.size(); ++j) { + REQUIRE(values[j] == (*expectedValues)[static_cast(j)]); + } } +} - SECTION("Int16 with NoData") { - std::vector values{-1, 3, 7, -1}; - std::optional noData = -1; - std::vector> expected{ - std::nullopt, +template ::type> +static void checkNormalizedFixedLengthArray( + const std::vector& data, + int64_t fixedLengthArrayCount, + const std::vector>>& expected, + const std::optional offset = std::nullopt, + const std::optional scale = std::nullopt, + const std::optional noData = std::nullopt, + const std::optional defaultValue = std::nullopt) { + int64_t instanceCount = + static_cast(data.size()) / fixedLengthArrayCount; + + std::vector buffer; + buffer.resize(data.size() * sizeof(T)); + std::memcpy(buffer.data(), data.data(), buffer.size()); + + PropertyTableProperty propertyTableProperty; + ClassProperty classProperty; + classProperty.type = + convertPropertyTypeToString(TypeToPropertyType::value); + + PropertyComponentType componentType = TypeToPropertyType::component; + classProperty.componentType = + convertPropertyComponentTypeToString(componentType); + + classProperty.array = true; + classProperty.count = fixedLengthArrayCount; + classProperty.normalized = true; + + classProperty.offset = offset; + classProperty.scale = scale; + classProperty.noData = noData; + classProperty.defaultProperty = defaultValue; + + PropertyTablePropertyView, true> property( + propertyTableProperty, + classProperty, + instanceCount, + gsl::span(buffer.data(), buffer.size()), + gsl::span(), + PropertyComponentType::None); + + REQUIRE(property.arrayCount() == fixedLengthArrayCount); + + // Check raw values first + size_t expectedIdx = 0; + for (int64_t i = 0; i < property.size(); ++i) { + PropertyArrayView rawValues = property.getRaw(i); + for (int64_t j = 0; j < rawValues.size(); ++j) { + REQUIRE(rawValues[j] == data[expectedIdx]); + ++expectedIdx; + } + } + + REQUIRE(expectedIdx == data.size()); + + // Check values with properties applied + for (int64_t i = 0; i < property.size(); ++i) { + std::optional> maybeValues = property.get(i); + if (!maybeValues) { + REQUIRE(!expected[static_cast(i)]); + } + + auto values = *maybeValues; + auto expectedValues = *expected[static_cast(i)]; + + for (int64_t j = 0; j < values.size(); ++j) { + REQUIRE(values[j] == expectedValues[static_cast(j)]); + } + } +} + +#pragma endregion + +TEST_CASE("Check scalar PropertyTablePropertyView") { + SECTION("Uint8") { + std::vector data{12, 33, 56, 67}; + checkNumeric(data); + } + + SECTION("Int32") { + std::vector data{111222, -11133, -56000, 670000}; + checkNumeric(data); + } + + SECTION("Float") { + std::vector data{12.3333f, -12.44555f, -5.6111f, 6.7421f}; + checkNumeric(data); + } + + SECTION("Double") { + std::vector data{ + 12222.3302121, + -12000.44555, + -5000.6113111, + 6.7421}; + checkNumeric(data); + } + + SECTION("Normalized Uint8") { + std::vector values{0, 64, 128, 255}; + std::vector> expected{ + 0.0, + 64.0 / 255.0, + 128.0 / 255.0, + 1.0}; + checkNormalizedScalar(values, expected); + } + + SECTION("Normalized Int16") { + std::vector values{-32768, 0, 16384, 32767}; + std::vector> expected{ + -1.0, + 0.0, + 16384.0 / 32767.0, + 1.0}; + checkNormalizedScalar(values, expected); + } + + SECTION("Float with Offset / Scale") { + std::vector values{12.5f, -12.5f, -5.0f, 6.75f}; + std::optional offset = 1.0f; + std::optional scale = 2.0f; + std::vector> expected{26.0f, -24.0f, -9.0f, 14.5f}; + checkScalar(values, expected, offset, scale); + } + + SECTION("Normalized Uint8 with Offset and Scale") { + std::vector values{0, 64, 128, 255}; + std::optional offset = 1.0; + std::optional scale = 2.0; + std::vector> expected{ + 1.0, + 1 + 2 * (64.0 / 255.0), + 1 + 2 * (128.0 / 255.0), + 3.0}; + checkNormalizedScalar(values, expected, offset, scale); + } + + SECTION("Int16 with NoData") { + std::vector values{-1, 3, 7, -1}; + std::optional noData = -1; + std::vector> expected{ + std::nullopt, 3, 7, std::nullopt}; - checkNumeric( + checkScalar( values, expected, std::nullopt, @@ -338,7 +724,7 @@ TEST_CASE("Check scalar PropertyTablePropertyView") { std::optional noData = -1; std::optional defaultValue = 0; std::vector> expected{0, 3, 7, 0}; - checkNumeric( + checkScalar( values, expected, std::nullopt, @@ -358,7 +744,7 @@ TEST_CASE("Check scalar PropertyTablePropertyView") { 1 + 2 * (64.0 / 255.0), 1 + 2 * (128.0 / 255.0), 3.0}; - checkNormalizedNumeric( + checkNormalizedScalar( values, expected, offset, @@ -440,6 +826,169 @@ TEST_CASE("Check vecN PropertyTablePropertyView") { glm::u8vec4(0, 0, 0, 1)}; checkNumeric(data); } + + SECTION("Normalized Uint8 Vec2") { + std::vector values{ + glm::u8vec2(0, 64), + glm::u8vec2(128, 255), + glm::u8vec2(255, 0)}; + std::vector> expected{ + glm::dvec2(0.0, 64.0 / 255.0), + glm::dvec2(128.0 / 255.0, 1.0), + glm::dvec2(1.0, 0.0)}; + checkNormalizedVecN(values, expected); + } + + SECTION("Normalized Int16 Vec2") { + std::vector values{ + glm::i16vec2(-32768, 0), + glm::i16vec2(16384, 32767), + glm::i16vec2(32767, -32768)}; + std::vector> expected{ + glm::dvec2(-1.0, 0.0), + glm::dvec2(16384.0 / 32767.0, 1.0), + glm::dvec2(1.0, -1.0), + }; + checkNormalizedVecN(values, expected); + } + + SECTION("Float Vec3 with Offset / Scale") { + std::vector values{ + glm::vec3(0.0f, -1.5f, -5.0f), + glm::vec3(6.5f, 2.0f, 4.0f), + glm::vec3(8.0f, -3.0f, 1.0f), + }; + std::optional offset = glm::vec3(1.0f, 2.0, 3.0f); + std::optional scale = glm::vec3(2.0f, 1.0f, 2.0f); + std::vector> expected{ + glm::vec3(1.0f, 0.5f, -7.0f), + glm::vec3(14.0f, 4.0f, 11.0f), + glm::vec3(17.0f, -1.0f, 5.0f), + }; + checkVecN(values, expected, offset, scale); + } + + SECTION("Normalized Uint8 Vec2 with Offset and Scale") { + std::vector values{ + glm::u8vec2(0, 64), + glm::u8vec2(128, 255), + glm::u8vec2(255, 0)}; + std::optional offset = glm::dvec2(0.0, 1.0); + std::optional scale = glm::dvec2(2.0, 1.0); + std::vector> expected{ + glm::dvec2(0.0, 1 + (64.0 / 255.0)), + glm::dvec2(2 * (128.0 / 255.0), 2.0), + glm::dvec2(2.0, 1.0)}; + checkNormalizedVecN(values, expected, offset, scale); + } + + SECTION("Int16 Vec2 with NoData") { + std::vector values{ + glm::i16vec2(-1, 3), + glm::i16vec2(-1, -1), + glm::i16vec2(7, 0)}; + std::optional noData = glm::i16vec2(-1, -1); + std::vector> expected{ + glm::i16vec2(-1, 3), + std::nullopt, + glm::i16vec2(7, 0)}; + checkVecN<2, glm::i16>( + values, + expected, + std::nullopt, + std::nullopt, + noData, + std::nullopt); + }; + + SECTION("Int16 Vec2 with NoData and Default") { + std::vector values{ + glm::i16vec2(-1, 3), + glm::i16vec2(-1, -1), + glm::i16vec2(7, 0)}; + std::optional noData = glm::i16vec2(-1, -1); + std::optional defaultValue = glm::i16vec2(0, 1); + std::vector> expected{ + glm::i16vec2(-1, 3), + glm::i16vec2(0, 1), + glm::i16vec2(7, 0)}; + checkVecN<2, glm::i16>( + values, + expected, + std::nullopt, + std::nullopt, + noData, + defaultValue); + }; + + SECTION("Normalized Uint8 Vec2 with all properties") { + std::vector values{ + glm::u8vec2(0, 64), + glm::u8vec2(128, 255), + glm::u8vec2(255, 0), + glm::u8vec2(0, 0)}; + std::optional offset = glm::dvec2(0.0, 1.0); + std::optional scale = glm::dvec2(2.0, 1.0); + std::optional noData = glm::u8vec2(0, 0); + std::optional defaultValue = glm::dvec2(5.0, 15.0); + std::vector> expected{ + glm::dvec2(0.0, 1 + (64.0 / 255.0)), + glm::dvec2(2 * (128.0 / 255.0), 2.0), + glm::dvec2(2.0, 1.0), + glm::dvec2(5.0, 15.0)}; + checkNormalizedVecN(values, expected, offset, scale, noData, defaultValue); + } + + SECTION("Overrides class property values") { + std::vector values{ + glm::vec2(1.0f, 3.0f), + glm::vec2(2.5f, 2.5f), + glm::vec2(2.0f, 4.0f)}; + std::vector data; + data.resize(values.size() * sizeof(glm::vec2)); + std::memcpy(data.data(), values.data(), data.size()); + + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::VEC2; + classProperty.componentType = ClassProperty::ComponentType::FLOAT32; + classProperty.offset = {0.0f, 0.0f}; + classProperty.scale = {1.0f, 1.0f}; + classProperty.min = {1.0f, 2.5f}; + classProperty.max = {2.5f, 4.0f}; + + PropertyTableProperty propertyTableProperty; + propertyTableProperty.offset = {1.0f, 0.5f}; + propertyTableProperty.scale = {2.0f, 1.0f}; + propertyTableProperty.min = {3.0f, 3.0f}; + propertyTableProperty.max = {6.0f, 4.5f}; + + PropertyTablePropertyView property( + propertyTableProperty, + classProperty, + static_cast(values.size()), + gsl::span(data.data(), data.size())); + + REQUIRE(property.offset()); + REQUIRE(*property.offset() == glm::vec2(1.0f, 0.5f)); + REQUIRE(property.scale()); + REQUIRE(*property.scale() == glm::vec2(2.0f, 1.0f)); + REQUIRE(property.min()); + REQUIRE(*property.min() == glm::vec2(3.0f, 3.0f)); + REQUIRE(property.max()); + REQUIRE(*property.max() == glm::vec2(6.0f, 4.5f)); + + std::vector expected{ + glm::vec2(3.0f, 3.5f), + glm::vec2(6.0f, 3.0f), + glm::vec2(5.0f, 4.5f)}; + for (int64_t i = 0; i < property.size(); ++i) { + REQUIRE(property.getRaw(i) == values[static_cast(i)]); + + std::optional transformedValue = property.get(i); + REQUIRE(transformedValue); + REQUIRE(*transformedValue == expected[static_cast(i)]); + } + } } TEST_CASE("Check matN PropertyTablePropertyView") { @@ -502,6 +1051,192 @@ TEST_CASE("Check matN PropertyTablePropertyView") { // clang-format on checkNumeric(data); } + + SECTION("Normalized Uint8 Mat2") { + std::vector values{ + glm::u8mat2x2(0, 64, 255, 255), + glm::u8mat2x2(255, 0, 128, 0)}; + std::vector> expected{ + glm::dmat2(0.0, 64.0 / 255.0, 1.0, 1.0), + glm::dmat2(1.0, 0.0, 128.0 / 255.0, 0.0)}; + checkNormalizedMatN(values, expected); + } + + SECTION("Normalized Int16 Mat2") { + std::vector values{ + glm::i16mat2x2(-32768, 0, 16384, 32767), + glm::i16mat2x2(0, 32767, 32767, -32768)}; + std::vector> expected{ + glm::dmat2(-1.0, 0.0, 16384.0 / 32767.0, 1.0), + glm::dmat2(0.0, 1.0, 1.0, -1.0), + }; + checkNormalizedMatN(values, expected); + } + + SECTION("Float Mat2 with Offset / Scale") { + std::vector values{ + glm::mat2(1.0f, 3.0f, 4.0f, 2.0f), + glm::mat2(6.5f, 2.0f, -2.0f, 0.0f), + glm::mat2(8.0f, -1.0f, -3.0f, 1.0f), + }; + std::optional offset = glm::mat2(1.0f, 2.0, 3.0f, 1.0f); + std::optional scale = glm::mat2(2.0f, 0.0f, 0.0f, 2.0f); + std::vector> expected{ + glm::mat2(3.0f, 2.0f, 3.0f, 5.0f), + glm::mat2(14.0f, 2.0f, 3.0f, 1.0f), + glm::mat2(17.0f, 2.0f, 3.0f, 3.0f), + }; + checkMatN(values, expected, offset, scale); + } + + SECTION("Normalized Uint8 Mat2 with Offset and Scale") { + std::vector values{ + glm::u8mat2x2(0, 64, 255, 255), + glm::u8mat2x2(255, 0, 128, 0)}; + std::optional offset = glm::dmat2(0.0, 1.0, 1.0, 0.0); + std::optional scale = glm::dmat2(2.0, 1.0, 0.0, 2.0); + std::vector> expected{ + glm::dmat2(0.0, 1 + 64.0 / 255.0, 1.0, 2.0), + glm::dmat2(2.0, 1.0, 1.0, 0.0)}; + checkNormalizedMatN(values, expected, offset, scale); + } + + SECTION("Int16 Mat3 with NoData") { + // clang-format off + std::vector values{ + glm::i16mat3x3( + 1, 2, 3, + -1, -2, -3, + 0, 1, 0), + glm::i16mat3x3( + 1, -1, 0, + 0, 1, 2, + 0, 4, 5), + glm::i16mat3x3( + -1, -1, -1, + 0, 0, 0, + 1, 1, 1)}; + std::optional noData = + glm::i16mat3x3( + -1, -1, -1, + 0, 0, 0, + 1, 1, 1); + // clang-format on + std::vector> expected{ + values[0], + values[1], + std::nullopt}; + checkMatN<3, glm::i16>( + values, + expected, + std::nullopt, + std::nullopt, + noData, + std::nullopt); + }; + + SECTION("Int16 Mat3 with NoData") { + // clang-format off + std::vector values{ + glm::i16mat3x3( + 1, 2, 3, + -1, -2, -3, + 0, 1, 0), + glm::i16mat3x3( + 1, -1, 0, + 0, 1, 2, + 0, 4, 5), + glm::i16mat3x3( + -1, -1, -1, + 0, 0, 0, + 1, 1, 1)}; + std::optional noData = + glm::i16mat3x3( + -1, -1, -1, + 0, 0, 0, + 1, 1, 1); + // clang-format on + std::optional default = glm::i16mat3x3(1); + std::vector> expected{ + values[0], + values[1], + default}; + checkMatN<3, glm::i16>( + values, + expected, + std::nullopt, + std::nullopt, + noData, + default); + }; + + SECTION("Normalized Uint8 Mat2 with all properties") { + std::vector values{ + glm::u8mat2x2(0, 64, 255, 255), + glm::u8mat2x2(0), + glm::u8mat2x2(255, 0, 128, 0)}; + std::optional offset = glm::dmat2(0.0, 1.0, 1.0, 0.0); + std::optional scale = glm::dmat2(2.0, 1.0, 0.0, 2.0); + std::optional noData = glm::u8mat2x2(0); + std::optional defaultValue = glm::dmat2(1.0); + + std::vector> expected{ + glm::dmat2(0.0, 1 + 64.0 / 255.0, 1.0, 2.0), + glm::dmat2(1.0), + glm::dmat2(2.0, 1.0, 1.0, 0.0)}; + checkNormalizedMatN(values, expected, offset, scale, noData, defaultValue); + } + + SECTION("Overrides class property values") { + std::vector values{ + glm::mat2(1.0f), + glm::mat2(2.5f, 1.0f, 1.0f, 2.5f), + glm::mat2(3.0f)}; + std::vector data; + data.resize(values.size() * sizeof(glm::mat2)); + std::memcpy(data.data(), values.data(), data.size()); + + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::MAT2; + classProperty.componentType = ClassProperty::ComponentType::FLOAT32; + classProperty.offset = {0.0f, 0.0f, 0.0f, 0.0f}; + classProperty.scale = {1.0f, 1.0f, 1.0f, 1.0f}; + classProperty.min = {1.0f, 0.0f, 0.0f, 1.0f}; + classProperty.max = {3.0f, 0.0f, 0.0f, 3.0f}; + + PropertyTableProperty propertyTableProperty; + propertyTableProperty.offset = {1.0f, 0.5f, 0.5f, 1.0f}; + propertyTableProperty.scale = {2.0f, 1.0f, 0.0f, 1.0f}; + propertyTableProperty.min = {3.0f, 0.5f, 0.5f, 2.0f}; + propertyTableProperty.max = {7.0f, 1.5f, 0.5f, 4.0f}; + + PropertyTablePropertyView property( + propertyTableProperty, + classProperty, + static_cast(values.size()), + gsl::span(data.data(), data.size())); + + REQUIRE(property.offset()); + REQUIRE(*property.offset() == glm::mat2(1.0f, 0.5f, 0.5f, 1.0f)); + REQUIRE(property.scale()); + REQUIRE(*property.scale() == glm::mat2(2.0f, 1.0f, 0.0f, 1.0f)); + REQUIRE(property.min()); + REQUIRE(*property.min() == glm::mat2(3.0f, 0.5f, 0.5f, 2.0f)); + REQUIRE(property.max()); + REQUIRE(*property.max() == glm::mat2(7.0f, 1.5f, 0.5f, 4.0f)); + + std::vector expected{ + glm::mat2(3.0f, 0.5f, 0.5f, 2.0f), + glm::mat2(6.0f, 1.5f, 0.5f, 3.5f), + glm::mat2(7.0f, 0.5f, 0.5f, 4.0f)}; + for (int64_t i = 0; i < property.size(); ++i) { + REQUIRE(property.getRaw(i) == values[static_cast(i)]); + + std::optional transformedValue = property.get(i); + REQUIRE(transformedValue); + REQUIRE(*transformedValue == expected[static_cast(i)]); + } + } } TEST_CASE("Check boolean PropertyTablePropertyView") { @@ -563,36 +1298,95 @@ TEST_CASE("Check string PropertyTablePropertyView") { ¤tOffset, sizeof(uint32_t)); - PropertyTableProperty propertyTableProperty; - ClassProperty classProperty; - classProperty.type = ClassProperty::Type::STRING; + SECTION("Returns correct values") { + PropertyTableProperty propertyTableProperty; + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::STRING; - PropertyTablePropertyView property( - propertyTableProperty, - classProperty, - static_cast(strings.size()), - gsl::span(buffer.data(), buffer.size()), - gsl::span(), - gsl::span(offsetBuffer.data(), offsetBuffer.size()), - PropertyComponentType::None, - PropertyComponentType::Uint32); + PropertyTablePropertyView property( + propertyTableProperty, + classProperty, + static_cast(strings.size()), + gsl::span(buffer.data(), buffer.size()), + gsl::span(), + gsl::span(offsetBuffer.data(), offsetBuffer.size()), + PropertyComponentType::None, + PropertyComponentType::Uint32); - for (int64_t i = 0; i < property.size(); ++i) { - REQUIRE(property.getRaw(i) == strings[static_cast(i)]); + for (int64_t i = 0; i < property.size(); ++i) { + REQUIRE(property.getRaw(i) == strings[static_cast(i)]); + REQUIRE(property.get(i)); + REQUIRE(*property.get(i) == strings[static_cast(i)]); + } + } + + SECTION("Uses NoData value") { + PropertyTableProperty propertyTableProperty; + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::STRING; + classProperty.noData = "What's going on"; + + PropertyTablePropertyView property( + propertyTableProperty, + classProperty, + static_cast(strings.size()), + gsl::span(buffer.data(), buffer.size()), + gsl::span(), + gsl::span(offsetBuffer.data(), offsetBuffer.size()), + PropertyComponentType::None, + PropertyComponentType::Uint32); + + std::vector> expected{ + strings[0], + std::nullopt, + strings[2]}; + + for (int64_t i = 0; i < property.size(); ++i) { + REQUIRE(property.getRaw(i) == strings[static_cast(i)]); + REQUIRE(property.get(i) == expected[static_cast(i)]); + } + } + + SECTION("Uses NoData and Default value") { + PropertyTableProperty propertyTableProperty; + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::STRING; + classProperty.noData = "What's going on"; + classProperty.defaultProperty = "Hello"; + + PropertyTablePropertyView property( + propertyTableProperty, + classProperty, + static_cast(strings.size()), + gsl::span(buffer.data(), buffer.size()), + gsl::span(), + gsl::span(offsetBuffer.data(), offsetBuffer.size()), + PropertyComponentType::None, + PropertyComponentType::Uint32); + + std::vector> expected{ + strings[0], + "Hello", + strings[2]}; + + for (int64_t i = 0; i < property.size(); ++i) { + REQUIRE(property.getRaw(i) == strings[static_cast(i)]); + REQUIRE(property.get(i) == expected[static_cast(i)]); + } } } TEST_CASE("Check fixed-length scalar array PropertyTablePropertyView") { - SECTION("Fixed-length array of 4 uint8_ts") { + SECTION("Array of 4 uint8_ts") { // clang-format off std::vector data{ 210, 211, 3, 42, 122, 22, 1, 45}; // clang-format on - checkFixedLengthArray(data, 4, static_cast(data.size() / 4)); + checkFixedLengthArray(data, 4); } - SECTION("Fixed-length array of 3 int8_ts") { + SECTION("Array of 3 int8_ts") { // clang-format off std::vector data{ 122, -12, 3, @@ -600,10 +1394,10 @@ TEST_CASE("Check fixed-length scalar array PropertyTablePropertyView") { 5, 6, -22, 5, 6, 1}; // clang-format on - checkFixedLengthArray(data, 3, static_cast(data.size() / 3)); + checkFixedLengthArray(data, 3); } - SECTION("Fixed-length array of 4 int16_ts") { + SECTION("Array of 4 int16_ts") { // clang-format off std::vector data{ -122, 12, 3, 44, @@ -611,10 +1405,10 @@ TEST_CASE("Check fixed-length scalar array PropertyTablePropertyView") { 119, 30, 51, 200, 22000, -500, 6000, 1}; // clang-format on - checkFixedLengthArray(data, 4, static_cast(data.size() / 4)); + checkFixedLengthArray(data, 4); } - SECTION("Fixed-length array of 6 uint32_ts") { + SECTION("Array of 6 uint32_ts") { // clang-format off std::vector data{ 122, 12, 3, 44, 34444, 2222, @@ -622,58 +1416,228 @@ TEST_CASE("Check fixed-length scalar array PropertyTablePropertyView") { 119, 30, 51, 200, 12534, 11, 22000, 500, 6000, 1, 3, 7}; // clang-format on - checkFixedLengthArray(data, 6, static_cast(data.size() / 6)); + checkFixedLengthArray(data, 6); } - SECTION("Fixed-length array of 2 int32_ts") { + SECTION("Array of 2 int32_ts") { // clang-format off - std::vector data{ + std::vector data{ 122, 12, - 3, 44}; + -3, 44}; // clang-format on - checkFixedLengthArray(data, 2, static_cast(data.size() / 2)); + checkFixedLengthArray(data, 2); } - SECTION("Fixed-length array of 4 uint64_ts") { + SECTION("Array of 4 uint64_ts") { // clang-format off std::vector data{ 10022, 120000, 2422, 1111, 3, 440000, 333, 1455}; // clang-format on - checkFixedLengthArray(data, 4, static_cast(data.size() / 4)); + checkFixedLengthArray(data, 4); } - SECTION("Fixed-length array of 4 int64_ts") { + SECTION("Array of 4 int64_ts") { // clang-format off std::vector data{ 10022, -120000, 2422, 1111, 3, 440000, -333, 1455}; // clang-format on - checkFixedLengthArray(data, 4, static_cast(data.size() / 4)); + checkFixedLengthArray(data, 4); } - SECTION("Fixed-length array of 4 floats") { + SECTION("Array of 4 floats") { // clang-format off std::vector data{ 10.022f, -12.43f, 242.2f, 1.111f, 3.333f, 440000.1f, -33.3f, 14.55f}; // clang-format on - checkFixedLengthArray(data, 4, static_cast(data.size() / 4)); + checkFixedLengthArray(data, 4); } - SECTION("Fixed-length array of 4 double") { + SECTION("Array of 4 double") { // clang-format off std::vector data{ 10.022, -12.43, 242.2, 1.111, 3.333, 440000.1, -33.3, 14.55}; // clang-format on - checkFixedLengthArray(data, 4, static_cast(data.size() / 4)); + checkFixedLengthArray(data, 4); + } + + SECTION("Array of 4 uint8_ts") { + // clang-format off + std::vector data{ + 210, 211, 3, 42, + 122, 22, 1, 45}; + // clang-format on + checkFixedLengthArray(data, 4); + } + + SECTION("Array of 4 normalized uint8_ts") { + // clang-format off + std::vector data{ + 255, 64, 0, 255, + 128, 0, 255, 0}; + // clang-format on + std::vector>> expected{ + std::vector{1.0, 64.0 / 255.0, 0.0, 1.0}, + std::vector{128.0 / 255.0, 0.0, 1.0, 0.0}}; + checkNormalizedFixedLengthArray(data, 4, expected); + } + + SECTION("Array of 4 floats with offset / scale") { + // clang-format off + std::vector data{ + 1.0f, 2.0f, 3.0f, 4.0f, + 5.0f, -1.0f, 0.0f, 2.0f + }; + // clang-format on + + std::optional offset = + JsonValue::Array{1.0f, 0.0f, -1.0f, 0.0f}; + std::optional scale = + JsonValue::Array{1.0f, 2.0f, 1.0f, 2.0f}; + + std::vector>> expected{ + std::vector{2.0f, 4.0f, 2.0f, 8.0f}, + std::vector{6.0f, -2.0f, -1.0f, 4.0f}}; + checkFixedLengthArrayWithProperties(data, 4, expected, offset, scale); + } + + SECTION("Array of 2 int32_ts with noData value") { + // clang-format off + std::vector data{ + 122, 12, + -1, -1, + -3, 44}; + // clang-format on + std::optional noData = JsonValue::Array{-1, -1}; + std::vector>> expected{ + std::vector{122, 12}, + std::nullopt, + std::vector{-3, 44}}; + checkFixedLengthArrayWithProperties( + data, + 2, + expected, + std::nullopt, + std::nullopt, + noData, + std::nullopt); + } + + SECTION("Array of 2 int32_ts with noData and default value") { + // clang-format off + std::vector data{ + 122, 12, + -1, -1, + -3, 44}; + // clang-format on + std::optional noData = JsonValue::Array{-1, -1}; + std::optional defaultValue = JsonValue::Array{0, 1}; + std::vector>> expected{ + std::vector{122, 12}, + std::vector{0, 1}, + std::vector{-3, 44}}; + checkFixedLengthArrayWithProperties( + data, + 2, + expected, + std::nullopt, + std::nullopt, + noData, + defaultValue); + } + + SECTION("Array of 3 normalized int8_ts with all properties") { + // clang-format off + std::vector data{ + -128, 0, 64, + -64, 127, -128, + 0, 0, 0}; + // clang-format on + std::optional offset = JsonValue::Array{0, 1, 1}; + std::optional scale = JsonValue::Array{1, -1, 2}; + std::optional noData = JsonValue::Array{0, 0, 0}; + std::optional defaultValue = JsonValue::Array{10, 8, 2}; + + std::vector>> expected{ + std::vector{-1.0, 1.0, 1 + 2 * (64.0 / 127.0)}, + std::vector{-64.0 / 127.0, 0.0, -1.0}, + std::vector{10.0, 8.0, 2.0}, + }; + checkNormalizedFixedLengthArray( + data, + 3, + expected, + offset, + scale, + noData, + defaultValue); + } + + SECTION("Overrides class property values") { + // clang-format off + std::vector data{ + 1.0f, 2.0f, 3.0f, 4.0f, + 0.0f, -1.0f, 1.0f, -2.0f}; + // clang-format on + const int64_t count = 4; + const int64_t instanceCount = 2; + + std::vector buffer; + buffer.resize(data.size() * sizeof(float)); + std::memcpy(buffer.data(), data.data(), buffer.size()); + + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::SCALAR; + classProperty.componentType = ClassProperty::ComponentType::FLOAT32; + + classProperty.array = true; + classProperty.count = count; + classProperty.offset = {0, 0, 0, 0}; + classProperty.scale = {1, 1, 1, 1}; + classProperty.min = {0.0f, -1.0f, 1.0f, -2.0f}; + classProperty.max = {1.0f, 2.0f, 3.0f, 4.0f}; + + PropertyTableProperty propertyTableProperty; + propertyTableProperty.offset = {2, 1, 0, -1}; + propertyTableProperty.scale = {1, 0, 1, -1}; + propertyTableProperty.min = {0.0f, 1.0f, 1.0f, -2.0f}; + propertyTableProperty.max = {1.0f, 1.0f, 3.0f, 4.0f}; + + PropertyTablePropertyView> property( + propertyTableProperty, + classProperty, + instanceCount, + gsl::span(buffer.data(), buffer.size()), + gsl::span(), + gsl::span(), + PropertyComponentType::None, + PropertyComponentType::None); + + REQUIRE(property.arrayCount() == count); + REQUIRE(property.size() == instanceCount); + + std::vector> expected{ + {3.0f, 1.0, 3.0f, -5.0f}, + {2.0f, 1.0f, 1.0f, 1.0f}}; + size_t expectedIdx = 0; + for (int64_t i = 0; i < property.size(); ++i) { + PropertyArrayView rawValues = property.getRaw(i); + auto values = property.get(i); + REQUIRE(values); + for (int64_t j = 0; j < rawValues.size(); ++j) { + REQUIRE(rawValues[j] == data[expectedIdx]); + REQUIRE((*values)[j] == expected[i][j]); + ++expectedIdx; + } + } } } TEST_CASE("Check fixed-length vecN array PropertyTablePropertyView") { - SECTION("Fixed-length array of 4 u8vec2s") { - // clang-format off + SECTION("Array of 4 u8vec2s") { std::vector data{ glm::u8vec2(10, 21), glm::u8vec2(3, 42), @@ -683,12 +1647,10 @@ TEST_CASE("Check fixed-length vecN array PropertyTablePropertyView") { glm::u8vec2(32, 12), glm::u8vec2(8, 19), glm::u8vec2(6, 5)}; - // clang-format on - checkFixedLengthArray(data, 4, static_cast(data.size() / 4)); + checkFixedLengthArray(data, 4); } - SECTION("Fixed-length array of 2 i8vec3s") { - // clang-format off + SECTION("Array of 2 i8vec3s") { std::vector data{ glm::i8vec3(122, -12, 3), glm::i8vec3(44, 11, -2), @@ -696,12 +1658,10 @@ TEST_CASE("Check fixed-length vecN array PropertyTablePropertyView") { glm::i8vec3(5, 6, 1), glm::i8vec3(8, -7, 7), glm::i8vec3(-4, 36, 17)}; - // clang-format on - checkFixedLengthArray(data, 2, static_cast(data.size() / 2)); + checkFixedLengthArray(data, 2); } - SECTION("Fixed-length array of 3 vec4s") { - // clang-format off + SECTION("Array of 3 vec4s") { std::vector data{ glm::vec4(40.2f, -1.2f, 8.8f, 1.0f), glm::vec4(1.4f, 0.11, 34.0f, 0.0f), @@ -709,13 +1669,178 @@ TEST_CASE("Check fixed-length vecN array PropertyTablePropertyView") { glm::vec4(1.0f, 2.0f, 3.0f, 6.0f), glm::vec4(1.08f, -3.71f, 18.0f, -7.0f), glm::vec4(-17.0f, 33.0f, 8.0f, -3.0f)}; + checkFixedLengthArray(data, 3); + } + + SECTION("Array of 2 normalized u8vec2s") { + std::vector data{ + glm::u8vec2(255, 64), + glm::u8vec2(0, 255), + glm::u8vec2(128, 0), + glm::u8vec2(255, 0)}; + std::vector>> expected{ + std::vector{ + glm::dvec2(1.0, 64.0 / 255.0), + glm::dvec2(0.0, 1.0)}, + std::vector{ + glm::dvec2(128.0 / 255.0, 0.0), + glm::dvec2(1.0, 0.0)}}; + checkNormalizedFixedLengthArray(data, 2, expected); + } + + SECTION("Array of 4 floats with offset / scale") { + // clang-format off + std::vector data{ + 1.0f, 2.0f, 3.0f, 4.0f, + 5.0f, -1.0f, 0.0f, 2.0f + }; // clang-format on - checkFixedLengthArray(data, 3, static_cast(data.size() / 3)); + + std::optional offset = + JsonValue::Array{1.0f, 0.0f, -1.0f, 0.0f}; + std::optional scale = + JsonValue::Array{1.0f, 2.0f, 1.0f, 2.0f}; + + std::vector>> expected{ + std::vector{2.0f, 4.0f, 2.0f, 8.0f}, + std::vector{6.0f, -2.0f, -1.0f, 4.0f}}; + checkFixedLengthArrayWithProperties(data, 4, expected, offset, scale); + } + + SECTION("Array of 2 int32_ts with noData value") { + // clang-format off + std::vector data{ + 122, 12, + -1, -1, + -3, 44}; + // clang-format on + std::optional noData = JsonValue::Array{-1, -1}; + std::vector>> expected{ + std::vector{122, 12}, + std::nullopt, + std::vector{-3, 44}}; + checkFixedLengthArrayWithProperties( + data, + 2, + expected, + std::nullopt, + std::nullopt, + noData, + std::nullopt); + } + + SECTION("Array of 2 int32_ts with noData and default value") { + // clang-format off + std::vector data{ + 122, 12, + -1, -1, + -3, 44}; + // clang-format on + std::optional noData = JsonValue::Array{-1, -1}; + std::optional defaultValue = JsonValue::Array{0, 1}; + std::vector>> expected{ + std::vector{122, 12}, + std::vector{0, 1}, + std::vector{-3, 44}}; + checkFixedLengthArrayWithProperties( + data, + 2, + expected, + std::nullopt, + std::nullopt, + noData, + defaultValue); + } + + SECTION("Array of 3 normalized int8_ts with all properties") { + // clang-format off + std::vector data{ + -128, 0, 64, + -64, 127, -128, + 0, 0, 0}; + // clang-format on + std::optional offset = JsonValue::Array{0, 1, 1}; + std::optional scale = JsonValue::Array{1, -1, 2}; + std::optional noData = JsonValue::Array{0, 0, 0}; + std::optional defaultValue = JsonValue::Array{10, 8, 2}; + + std::vector>> expected{ + std::vector{-1.0, 1.0, 1 + 2 * (64.0 / 127.0)}, + std::vector{-64.0 / 127.0, 0.0, -1.0}, + std::vector{10.0, 8.0, 2.0}, + }; + checkNormalizedFixedLengthArray( + data, + 3, + expected, + offset, + scale, + noData, + defaultValue); + } + + SECTION("Overrides class property values") { + // clang-format off + std::vector data{ + 1.0f, 2.0f, 3.0f, 4.0f, + 0.0f, -1.0f, 1.0f, -2.0f}; + // clang-format on + const int64_t count = 4; + const int64_t instanceCount = 2; + + std::vector buffer; + buffer.resize(data.size() * sizeof(float)); + std::memcpy(buffer.data(), data.data(), buffer.size()); + + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::SCALAR; + classProperty.componentType = ClassProperty::ComponentType::FLOAT32; + + classProperty.array = true; + classProperty.count = count; + classProperty.offset = {0, 0, 0, 0}; + classProperty.scale = {1, 1, 1, 1}; + classProperty.min = {0.0f, -1.0f, 1.0f, -2.0f}; + classProperty.max = {1.0f, 2.0f, 3.0f, 4.0f}; + + PropertyTableProperty propertyTableProperty; + propertyTableProperty.offset = {2, 1, 0, -1}; + propertyTableProperty.scale = {1, 0, 1, -1}; + propertyTableProperty.min = {0.0f, 1.0f, 1.0f, -2.0f}; + propertyTableProperty.max = {1.0f, 1.0f, 3.0f, 4.0f}; + + PropertyTablePropertyView> property( + propertyTableProperty, + classProperty, + instanceCount, + gsl::span(buffer.data(), buffer.size()), + gsl::span(), + gsl::span(), + PropertyComponentType::None, + PropertyComponentType::None); + + REQUIRE(property.arrayCount() == count); + REQUIRE(property.size() == instanceCount); + + std::vector> expected{ + {3.0f, 1.0, 3.0f, -5.0f}, + {2.0f, 1.0f, 1.0f, 1.0f}}; + size_t expectedIdx = 0; + for (int64_t i = 0; i < property.size(); ++i) { + PropertyArrayView rawValues = property.getRaw(i); + auto values = property.get(i); + REQUIRE(values); + for (int64_t j = 0; j < rawValues.size(); ++j) { + REQUIRE(rawValues[j] == data[expectedIdx]); + REQUIRE((*values)[j] == expected[i][j]); + ++expectedIdx; + } + } } } TEST_CASE("Check fixed-length matN array PropertyTablePropertyView") { - SECTION("Fixed-length array of 4 i8mat2x2") { + SECTION("Array of 4 i8mat2x2") { // clang-format off std::vector data{ glm::i8mat2x2( @@ -743,10 +1868,10 @@ TEST_CASE("Check fixed-length matN array PropertyTablePropertyView") { 6, 16, 2, 5)}; // clang-format on - checkFixedLengthArray(data, 4, static_cast(data.size() / 4)); + checkFixedLengthArray(data, 4); } - SECTION("Fixed-length array of 2 dmat3s") { + SECTION("Array of 2 dmat3s") { // clang-format off std::vector data{ glm::dmat3( @@ -774,10 +1899,10 @@ TEST_CASE("Check fixed-length matN array PropertyTablePropertyView") { 5.5, 3.09, 0.301, 4.5, 52.4, 1.05)}; // clang-format on - checkFixedLengthArray(data, 2, static_cast(data.size() / 2)); + checkFixedLengthArray(data, 2); } - SECTION("Fixed-length array of 3 u8mat4x4") { + SECTION("Array of 3 u8mat4x4") { // clang-format off std::vector data{ glm::u8mat4x4( @@ -811,7 +1936,7 @@ TEST_CASE("Check fixed-length matN array PropertyTablePropertyView") { 8, 7, 6, 5, 4, 3, 2, 1),}; // clang-format on - checkFixedLengthArray(data, 3, static_cast(data.size() / 3)); + checkFixedLengthArray(data, 3); } } diff --git a/CesiumGltf/test/TestPropertyTypeTraits.cpp b/CesiumGltf/test/TestPropertyTypeTraits.cpp index ff527cb7e..2ed90612d 100644 --- a/CesiumGltf/test/TestPropertyTypeTraits.cpp +++ b/CesiumGltf/test/TestPropertyTypeTraits.cpp @@ -523,20 +523,112 @@ TEST_CASE("TypeToPropertyType") { } } -TEST_CASE("Test CanBeNormalized") { - REQUIRE(CanBeNormalized::value); - REQUIRE(CanBeNormalized::value); - REQUIRE(CanBeNormalized::value); - REQUIRE(CanBeNormalized::value); - REQUIRE(CanBeNormalized::value); - REQUIRE(CanBeNormalized::value); - REQUIRE(CanBeNormalized::value); - REQUIRE(CanBeNormalized::value); - - REQUIRE(!CanBeNormalized::value); - REQUIRE(!CanBeNormalized::value); - REQUIRE(!CanBeNormalized::value); - REQUIRE(!CanBeNormalized::value); +TEST_CASE("CanBeNormalized") { + SECTION("Works for scalars") { + REQUIRE(CanBeNormalized::value); + REQUIRE(CanBeNormalized::value); + REQUIRE(CanBeNormalized::value); + REQUIRE(CanBeNormalized::value); + REQUIRE(CanBeNormalized::value); + REQUIRE(CanBeNormalized::value); + REQUIRE(CanBeNormalized::value); + REQUIRE(CanBeNormalized::value); + + REQUIRE(!CanBeNormalized::value); + REQUIRE(!CanBeNormalized::value); + REQUIRE(!CanBeNormalized::value); + REQUIRE(!CanBeNormalized::value); + } + + SECTION("Works for vecNs") { + REQUIRE(CanBeNormalized::value); + REQUIRE(CanBeNormalized::value); + REQUIRE(CanBeNormalized::value); + + REQUIRE(CanBeNormalized::value); + REQUIRE(CanBeNormalized::value); + REQUIRE(CanBeNormalized::value); + + REQUIRE(CanBeNormalized::value); + REQUIRE(CanBeNormalized::value); + REQUIRE(CanBeNormalized::value); + + REQUIRE(CanBeNormalized::value); + REQUIRE(CanBeNormalized::value); + REQUIRE(CanBeNormalized::value); + + REQUIRE(CanBeNormalized::value); + REQUIRE(CanBeNormalized::value); + REQUIRE(CanBeNormalized::value); + + REQUIRE(CanBeNormalized::value); + REQUIRE(CanBeNormalized::value); + REQUIRE(CanBeNormalized::value); + + REQUIRE(CanBeNormalized::value); + REQUIRE(CanBeNormalized::value); + REQUIRE(CanBeNormalized::value); + + REQUIRE(CanBeNormalized::value); + REQUIRE(CanBeNormalized::value); + REQUIRE(CanBeNormalized::value); + + REQUIRE(!CanBeNormalized::value); + REQUIRE(!CanBeNormalized::value); + REQUIRE(!CanBeNormalized::value); + + REQUIRE(!CanBeNormalized::value); + REQUIRE(!CanBeNormalized::value); + REQUIRE(!CanBeNormalized::value); + } + + SECTION("Works for matN") { + REQUIRE(CanBeNormalized::value); + REQUIRE(CanBeNormalized::value); + REQUIRE(CanBeNormalized::value); + + REQUIRE(CanBeNormalized::value); + REQUIRE(CanBeNormalized::value); + REQUIRE(CanBeNormalized::value); + + REQUIRE(CanBeNormalized::value); + REQUIRE(CanBeNormalized::value); + REQUIRE(CanBeNormalized::value); + + REQUIRE(CanBeNormalized::value); + REQUIRE(CanBeNormalized::value); + REQUIRE(CanBeNormalized::value); + + REQUIRE(CanBeNormalized::value); + REQUIRE(CanBeNormalized::value); + REQUIRE(CanBeNormalized::value); + + REQUIRE(CanBeNormalized::value); + REQUIRE(CanBeNormalized::value); + REQUIRE(CanBeNormalized::value); + + REQUIRE(CanBeNormalized::value); + REQUIRE(CanBeNormalized::value); + REQUIRE(CanBeNormalized::value); + + REQUIRE(CanBeNormalized::value); + REQUIRE(CanBeNormalized::value); + REQUIRE(CanBeNormalized::value); + + REQUIRE(!CanBeNormalized::value); + REQUIRE(!CanBeNormalized::value); + REQUIRE(!CanBeNormalized::value); + + REQUIRE(!CanBeNormalized::value); + REQUIRE(!CanBeNormalized::value); + REQUIRE(!CanBeNormalized::value); + } + + SECTION("Works for arrays") { + REQUIRE(CanBeNormalized>::value); + REQUIRE(CanBeNormalized>::value); + REQUIRE(CanBeNormalized>::value); + } } TEST_CASE("TypeToNormalizedType") { From c0fc8b67959d20ba3a45d3e1257c0a622beb8ae9 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 23 Aug 2023 09:07:35 +1000 Subject: [PATCH 197/421] More tileset metadata to a separate struct. --- .../Cesium3DTilesSelection/TileContent.h | 26 +++---------------- .../src/TilesetJsonLoader.cpp | 8 +++--- .../test/TestTilesetJsonLoader.cpp | 3 ++- .../test/TestTilesetSelectionAlgorithm.cpp | 9 ++++--- 4 files changed, 14 insertions(+), 32 deletions(-) diff --git a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/TileContent.h b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/TileContent.h index 87d112e6b..c7d1c487c 100644 --- a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/TileContent.h +++ b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/TileContent.h @@ -3,9 +3,8 @@ #include "CreditSystem.h" #include "Library.h" #include "RasterOverlayDetails.h" +#include "TilesetMetadata.h" -#include -#include #include #include @@ -52,28 +51,9 @@ struct CESIUM3DTILESSELECTION_API TileEmptyContent {}; */ struct CESIUM3DTILESSELECTION_API TileExternalContent { /** - * @brief An object defining the structure of metadata classes and enums. When - * this is defined, then `schemaUri` shall be undefined. + * @brief The metadata associated with this tileset. */ - std::optional schema; - - /** - * @brief The URI (or IRI) of the external schema file. When this is defined, - * then `schema` shall be undefined. - */ - std::optional schemaUri; - - /** - * @brief An array of groups that tile content may belong to. Each element of - * this array is a metadata entity that describes the group. The tile content - * `group` property is an index into this array. - */ - std::vector groups; - - /** - * @brief A metadata entity that is associated with this tileset. - */ - std::optional metadata; + TilesetMetadata metadata; }; /** diff --git a/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp b/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp index 6113bdda0..ddc563455 100644 --- a/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp +++ b/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp @@ -632,13 +632,13 @@ void parseTilesetMetadata( Cesium3DTilesReader::SchemaReader schemaReader; auto schemaResult = schemaReader.readFromJson(schemaIt->value); if (schemaResult.value) { - externalContent.schema = std::move(*schemaResult.value); + externalContent.metadata.schema = std::move(*schemaResult.value); } } auto schemaUriIt = tilesetJson.FindMember("schemaUri"); if (schemaUriIt != tilesetJson.MemberEnd() && schemaUriIt->value.IsString()) { - externalContent.schemaUri = schemaUriIt->value.GetString(); + externalContent.metadata.schemaUri = schemaUriIt->value.GetString(); } const auto metadataIt = tilesetJson.FindMember("metadata"); @@ -646,7 +646,7 @@ void parseTilesetMetadata( Cesium3DTilesReader::MetadataEntityReader metadataReader; auto metadataResult = metadataReader.readFromJson(metadataIt->value); if (metadataResult.value) { - externalContent.metadata = std::move(*metadataResult.value); + externalContent.metadata.metadata = std::move(*metadataResult.value); } } @@ -655,7 +655,7 @@ void parseTilesetMetadata( Cesium3DTilesReader::GroupMetadataReader groupMetadataReader; auto groupsResult = groupMetadataReader.readArrayFromJson(groupsIt->value); if (groupsResult.value) { - externalContent.groups = std::move(*groupsResult.value); + externalContent.metadata.groups = std::move(*groupsResult.value); } } } diff --git a/Cesium3DTilesSelection/test/TestTilesetJsonLoader.cpp b/Cesium3DTilesSelection/test/TestTilesetJsonLoader.cpp index 8d1d778c4..4559e583d 100644 --- a/Cesium3DTilesSelection/test/TestTilesetJsonLoader.cpp +++ b/Cesium3DTilesSelection/test/TestTilesetJsonLoader.cpp @@ -412,7 +412,8 @@ TEST_CASE("Test creating tileset json loader") { loaderResult.pRootTile->getContent().getExternalContent(); REQUIRE(pExternal); - const std::optional& schema = pExternal->schema; + const TilesetMetadata& metadata = pExternal->metadata; + const std::optional& schema = metadata.schema; REQUIRE(schema); CHECK(schema->id == "foo"); } diff --git a/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp b/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp index 40d4c9288..fd8d635b6 100644 --- a/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp +++ b/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp @@ -1141,7 +1141,8 @@ TEST_CASE("Makes metadata available once root tile is loaded") { TileExternalContent* pExternal = pRoot->getContent().getExternalContent(); REQUIRE(pExternal); - const std::optional& schema = pExternal->schema; + const TilesetMetadata& metadata = pExternal->metadata; + const std::optional& schema = metadata.schema; REQUIRE(schema); CHECK(schema->id == "foo"); } @@ -1209,9 +1210,9 @@ TEST_CASE("Makes metadata available on external tilesets") { REQUIRE(pExternalContent); - REQUIRE(pExternalContent->groups.size() == 2); - CHECK(pExternalContent->groups[0].classProperty == "someClass"); - CHECK(pExternalContent->groups[1].classProperty == "someClass"); + REQUIRE(pExternalContent->metadata.groups.size() == 2); + CHECK(pExternalContent->metadata.groups[0].classProperty == "someClass"); + CHECK(pExternalContent->metadata.groups[1].classProperty == "someClass"); } namespace { From ef2ac5d1dba9993e74c6264868c5b58c5d4f952e Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 23 Aug 2023 09:07:53 +1000 Subject: [PATCH 198/421] Add missing file. --- .../Cesium3DTilesSelection/TilesetMetadata.h | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 Cesium3DTilesSelection/include/Cesium3DTilesSelection/TilesetMetadata.h diff --git a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/TilesetMetadata.h b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/TilesetMetadata.h new file mode 100644 index 000000000..e73a963bf --- /dev/null +++ b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/TilesetMetadata.h @@ -0,0 +1,36 @@ +#pragma once + +#include "Library.h" + +#include +#include + +#include +#include +#include + +struct CESIUM3DTILESSELECTION_API TilesetMetadata { + /** + * @brief An object defining the structure of metadata classes and enums. When + * this is defined, then `schemaUri` shall be undefined. + */ + std::optional schema; + + /** + * @brief The URI (or IRI) of the external schema file. When this is defined, + * then `schema` shall be undefined. + */ + std::optional schemaUri; + + /** + * @brief An array of groups that tile content may belong to. Each element of + * this array is a metadata entity that describes the group. The tile content + * `group` property is an index into this array. + */ + std::vector groups; + + /** + * @brief A metadata entity that is associated with this tileset. + */ + std::optional metadata; +}; From c68207a6a4684229ebed85748b336bc065879482 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 23 Aug 2023 11:30:06 +1000 Subject: [PATCH 199/421] Add MetadataQuery class. --- .../include/Cesium3DTiles/MetadataQuery.h | 74 +++++++++++++++++++ Cesium3DTiles/src/MetadataQuery.cpp | 38 ++++++++++ Cesium3DTiles/test/TestMetadataQuery.cpp | 55 ++++++++++++++ .../include/Cesium3DTilesSelection/Tileset.h | 19 +++++ .../Cesium3DTilesSelection/TilesetMetadata.h | 4 + Cesium3DTilesSelection/src/Tileset.cpp | 17 +++++ CesiumNativeTests/CMakeLists.txt | 1 + 7 files changed, 208 insertions(+) create mode 100644 Cesium3DTiles/include/Cesium3DTiles/MetadataQuery.h create mode 100644 Cesium3DTiles/src/MetadataQuery.cpp create mode 100644 Cesium3DTiles/test/TestMetadataQuery.cpp diff --git a/Cesium3DTiles/include/Cesium3DTiles/MetadataQuery.h b/Cesium3DTiles/include/Cesium3DTiles/MetadataQuery.h new file mode 100644 index 000000000..e4caab50e --- /dev/null +++ b/Cesium3DTiles/include/Cesium3DTiles/MetadataQuery.h @@ -0,0 +1,74 @@ +#pragma once + +#include +#include +#include + +#include +#include + +namespace Cesium3DTiles { + +/** + * @brief Holds the details of a found property in a {@link MetadataEntity}. + * + * Because this structure holds _references_ to the original {@link Schema} and + * {@link MetadataEntity} instances, it will be invalided if either are + * destroyed or modified. Continuing to access this result in that scenario will + * result in undefined behavior. + */ +struct FoundMetadataProperty { + /** + * @brief A reference to the identifier of the class that contains the found + * property within the {@link Schema}. + */ + const std::string& classIdentifier; + + /** + * @brief A reference to the {@link Class} that contains the found property + * within the {@link Schema}. + */ + const Class& classDefinition; + + /** + * @brief A reference to the identifier of the found property within the + * {@link Schema}. + */ + const std::string& propertyIdentifier; + + /** + * @brief A reference to the {@link ClassProperty} describing the found + * property within the {@lnik Schema}. + */ + const ClassProperty& propertyDefinition; + + /** + * @brief A reference to the value of the found property within the + * {@link MetadataEntity}. + */ + const CesiumUtility::JsonValue& propertyValue; +}; + +/** + * @brief Convenience functions for querying {@link MetadataEntity} instances. + */ +class CESIUM3DTILES_API MetadataQuery { +public: + /** + * @brief Gets the first property with a given + * {@link ClassProperty::semantic}. + * + * @param schema The schema to use to look up semantics. + * @param entity The metadata entity to search for a property with the + * semantic. + * @param semantic The semantic to find. + * @return The details of the found property, or `std::nullopt` if a property + * with the given semantic does not exist. + */ + static std::optional findFirstPropertyWithSemantic( + const Schema& schema, + const MetadataEntity& entity, + const std::string& semantic); +}; + +} // namespace Cesium3DTiles diff --git a/Cesium3DTiles/src/MetadataQuery.cpp b/Cesium3DTiles/src/MetadataQuery.cpp new file mode 100644 index 000000000..5705dd845 --- /dev/null +++ b/Cesium3DTiles/src/MetadataQuery.cpp @@ -0,0 +1,38 @@ +#include + +namespace Cesium3DTiles { + +std::optional +MetadataQuery::findFirstPropertyWithSemantic( + const Schema& schema, + const MetadataEntity& entity, + const std::string& semantic) { + auto classIt = schema.classes.find(entity.classProperty); + if (classIt == schema.classes.end()) { + return std::nullopt; + } + + const Cesium3DTiles::Class& klass = classIt->second; + + for (auto it = entity.properties.begin(); it != entity.properties.end(); + ++it) { + const std::pair& property = *it; + auto propertyIt = klass.properties.find(property.first); + if (propertyIt == klass.properties.end()) + continue; + + const ClassProperty& classProperty = propertyIt->second; + if (classProperty.semantic == semantic) { + return FoundMetadataProperty{ + classIt->first, + classIt->second, + it->first, + propertyIt->second, + it->second}; + } + } + + return std::nullopt; +} + +} // namespace Cesium3DTiles diff --git a/Cesium3DTiles/test/TestMetadataQuery.cpp b/Cesium3DTiles/test/TestMetadataQuery.cpp new file mode 100644 index 000000000..6c6b8c839 --- /dev/null +++ b/Cesium3DTiles/test/TestMetadataQuery.cpp @@ -0,0 +1,55 @@ +#include + +#include + +using namespace Cesium3DTiles; +using namespace CesiumUtility; + +TEST_CASE("MetadataQuery") { + SECTION("findFirstPropertyWithSemantic") { + Schema schema{}; + Class& classDefinition = + schema.classes.emplace("someClass", Class()).first->second; + + ClassProperty& classProperty1 = + classDefinition.properties.emplace("someProperty", ClassProperty()) + .first->second; + classProperty1.type = ClassProperty::Type::SCALAR; + classProperty1.componentType = ClassProperty::ComponentType::FLOAT64; + + ClassProperty& classProperty2 = + classDefinition.properties + .emplace("somePropertyWithSemantic", ClassProperty()) + .first->second; + classProperty2.type = ClassProperty::Type::STRING; + classProperty2.semantic = "SOME_SEMANTIC"; + + MetadataEntity withoutSemantic; + withoutSemantic.classProperty = "someClass"; + withoutSemantic.properties.emplace("someProperty", JsonValue(3.0)); + + MetadataEntity withSemantic = withoutSemantic; + withSemantic.properties.emplace( + "somePropertyWithSemantic", + JsonValue("the value")); + + std::optional foundProperty1 = + MetadataQuery::findFirstPropertyWithSemantic( + schema, + withoutSemantic, + "SOME_SEMANTIC"); + CHECK(!foundProperty1); + + std::optional foundProperty2 = + MetadataQuery::findFirstPropertyWithSemantic( + schema, + withSemantic, + "SOME_SEMANTIC"); + REQUIRE(foundProperty2); + CHECK(foundProperty2->classIdentifier == "someClass"); + CHECK(&foundProperty2->classDefinition == &classDefinition); + CHECK(foundProperty2->propertyIdentifier == "somePropertyWithSemantic"); + CHECK(&foundProperty2->propertyDefinition == &classProperty2); + CHECK(foundProperty2->propertyValue.getStringOrDefault("") == "the value"); + } +} diff --git a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/Tileset.h b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/Tileset.h index c0de9c35d..3a1aee0c4 100644 --- a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/Tileset.h +++ b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/Tileset.h @@ -22,6 +22,7 @@ namespace Cesium3DTilesSelection { class TilesetContentManager; +struct TilesetMetadata; /** * @brief A #include +namespace Cesium3DTilesSelection { + struct CESIUM3DTILESSELECTION_API TilesetMetadata { /** * @brief An object defining the structure of metadata classes and enums. When @@ -34,3 +36,5 @@ struct CESIUM3DTILESSELECTION_API TilesetMetadata { */ std::optional metadata; }; + +} // namespace Cesium3DTilesSelection diff --git a/Cesium3DTilesSelection/src/Tileset.cpp b/Cesium3DTilesSelection/src/Tileset.cpp index 4c908201e..95814932e 100644 --- a/Cesium3DTilesSelection/src/Tileset.cpp +++ b/Cesium3DTilesSelection/src/Tileset.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -453,6 +454,22 @@ int64_t Tileset::getTotalDataBytes() const noexcept { return this->_pTilesetContentManager->getTotalDataUsed(); } +const TilesetMetadata* Tileset::findMetadata(const Tile* pTile) const { + if (pTile == nullptr) { + pTile = this->getRootTile(); + } + + while (pTile != nullptr) { + const TileExternalContent* pExternal = + pTile->getContent().getExternalContent(); + if (pExternal) + return &pExternal->metadata; + pTile = pTile->getParent(); + } + + return nullptr; +} + static void markTileNonRendered( TileSelectionState::Result lastResult, Tile& tile, diff --git a/CesiumNativeTests/CMakeLists.txt b/CesiumNativeTests/CMakeLists.txt index e9c65c68f..89604034c 100644 --- a/CesiumNativeTests/CMakeLists.txt +++ b/CesiumNativeTests/CMakeLists.txt @@ -4,6 +4,7 @@ configure_cesium_library(cesium-native-tests) # Add tests here, ensure they define the TEST_SOURCES / TEST_HEADERS # properties. set(cesium_native_targets + Cesium3DTiles Cesium3DTilesReader Cesium3DTilesWriter Cesium3DTilesSelection From 056edba5c71f27bf1d45140283e54715908c9dbc Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 23 Aug 2023 13:41:57 +1000 Subject: [PATCH 200/421] Add a test based on material variants. --- .../test/TestTilesetSelectionAlgorithm.cpp | 98 ++++++++++++++++++ .../test/data/MaterialVariants/parent.b3dm | Bin 0 -> 9680 bytes .../test/data/MaterialVariants/tileset.json | 89 ++++++++++++++++ 3 files changed, 187 insertions(+) create mode 100644 Cesium3DTilesSelection/test/data/MaterialVariants/parent.b3dm create mode 100644 Cesium3DTilesSelection/test/data/MaterialVariants/tileset.json diff --git a/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp b/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp index fd8d635b6..012f9a92f 100644 --- a/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp +++ b/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp @@ -7,6 +7,7 @@ #include "SimplePrepareRendererResource.h" #include "SimpleTaskProcessor.h" +#include #include #include @@ -1215,6 +1216,103 @@ TEST_CASE("Makes metadata available on external tilesets") { CHECK(pExternalContent->metadata.groups[1].classProperty == "someClass"); } +TEST_CASE("Allows access to material variants") { + Cesium3DTilesSelection::registerAllTileContentTypes(); + + std::filesystem::path testDataPath = Cesium3DTilesSelection_TEST_DATA_DIR; + testDataPath = testDataPath / "MaterialVariants"; + std::vector files{"tileset.json", "parent.b3dm"}; + + std::map> + mockCompletedRequests; + for (const auto& file : files) { + std::unique_ptr mockCompletedResponse = + std::make_unique( + static_cast(200), + "doesn't matter", + CesiumAsync::HttpHeaders{}, + readFile(testDataPath / file)); + mockCompletedRequests.insert( + {file, + std::make_shared( + "GET", + file, + CesiumAsync::HttpHeaders{}, + std::move(mockCompletedResponse))}); + } + + std::shared_ptr mockAssetAccessor = + std::make_shared(std::move(mockCompletedRequests)); + TilesetExternals tilesetExternals{ + mockAssetAccessor, + std::make_shared(), + AsyncSystem(std::make_shared()), + nullptr}; + + // create tileset and call updateView() to give it a chance to load + Tileset tileset(tilesetExternals, "tileset.json"); + initializeTileset(tileset); + + const TilesetMetadata* pMetadata = tileset.findMetadata(); + REQUIRE(pMetadata); + REQUIRE(pMetadata->schema); + REQUIRE(pMetadata->metadata); + + std::optional found1 = + Cesium3DTiles::MetadataQuery::findFirstPropertyWithSemantic( + *pMetadata->schema, + *pMetadata->metadata, + "MATERIAL_VARIANTS"); + REQUIRE(found1); + CHECK(found1->classIdentifier == "MaterialVariants"); + CHECK(found1->classDefinition.properties.size() == 1); + CHECK(found1->propertyIdentifier == "material_variants"); + CHECK( + found1->propertyDefinition.description == + "Names of material variants to be expected in the glTF assets"); + REQUIRE(found1->propertyValue.isArray()); + + const JsonValue::Array& variantsJson = found1->propertyValue.getArray(); + std::vector variants(variantsJson.size()); + std::transform( + variantsJson.begin(), + variantsJson.end(), + variants.begin(), + [](const JsonValue& value) { return value.getStringOrDefault(""); }); + REQUIRE(variants.size() == 4); + CHECK(variants[0] == "RGB"); + CHECK(variants[1] == "RRR"); + CHECK(variants[2] == "GGG"); + CHECK(variants[3] == "BBB"); + + std::vector> variantsByGroup; + for (const Cesium3DTiles::GroupMetadata& group : pMetadata->groups) { + std::vector& groupVariants = variantsByGroup.emplace_back(); + + std::optional found2 = + Cesium3DTiles::MetadataQuery::findFirstPropertyWithSemantic( + *pMetadata->schema, + group, + "MATERIAL_VARIANTS"); + REQUIRE(found2); + REQUIRE(found2->propertyValue.isArray()); + + const JsonValue::Array& groupVariantsJson = + found2->propertyValue.getArray(); + groupVariants.reserve(groupVariantsJson.size()); + for (int i = 0; i < groupVariantsJson.size(); ++i) { + groupVariants.emplace_back(groupVariantsJson[i].getStringOrDefault("")); + } + } + + std::vector> expected = { + { "RGB", "RRR" }, + { "GGG", "BBB" } + }; + + CHECK(variantsByGroup == expected); +} + namespace { void runUnconditionallyRefinedTestCase(const TilesetOptions& options) { diff --git a/Cesium3DTilesSelection/test/data/MaterialVariants/parent.b3dm b/Cesium3DTilesSelection/test/data/MaterialVariants/parent.b3dm new file mode 100644 index 0000000000000000000000000000000000000000..8cb958955234e1ecaac3837ecbf0162b8fd47bf8 GIT binary patch literal 9680 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 a{z>^DzQ7@TjxQ;Hj<4`F4pTggZ}1 Date: Wed, 23 Aug 2023 14:18:47 +1000 Subject: [PATCH 201/421] Formatting. --- .../test/TestTilesetSelectionAlgorithm.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp b/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp index 012f9a92f..8627acabc 100644 --- a/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp +++ b/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp @@ -1306,9 +1306,8 @@ TEST_CASE("Allows access to material variants") { } std::vector> expected = { - { "RGB", "RRR" }, - { "GGG", "BBB" } - }; + {"RGB", "RRR"}, + {"GGG", "BBB"}}; CHECK(variantsByGroup == expected); } From d0a3091b12ba9d571c5441f467fd4265171028d5 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 23 Aug 2023 14:28:04 +1000 Subject: [PATCH 202/421] Fix dodgy destructor declaration. --- .../include/CesiumJsonReader/IExtensionJsonHandler.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CesiumJsonReader/include/CesiumJsonReader/IExtensionJsonHandler.h b/CesiumJsonReader/include/CesiumJsonReader/IExtensionJsonHandler.h index 1e193eb6c..6e0fec568 100644 --- a/CesiumJsonReader/include/CesiumJsonReader/IExtensionJsonHandler.h +++ b/CesiumJsonReader/include/CesiumJsonReader/IExtensionJsonHandler.h @@ -11,7 +11,7 @@ namespace CesiumJsonReader { class IExtensionJsonHandler { public: - virtual ~IExtensionJsonHandler() noexcept = 0 {} + virtual ~IExtensionJsonHandler() noexcept = default; virtual void reset( IJsonHandler* pParentHandler, CesiumUtility::ExtensibleObject& o, From ea6a74ef3a97b6d17a6a56df0a87572a7077bf0b Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 23 Aug 2023 14:58:41 +1000 Subject: [PATCH 203/421] Fix another clang warning. --- Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp b/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp index 8627acabc..51138bbea 100644 --- a/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp +++ b/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp @@ -1300,7 +1300,7 @@ TEST_CASE("Allows access to material variants") { const JsonValue::Array& groupVariantsJson = found2->propertyValue.getArray(); groupVariants.reserve(groupVariantsJson.size()); - for (int i = 0; i < groupVariantsJson.size(); ++i) { + for (size_t i = 0; i < groupVariantsJson.size(); ++i) { groupVariants.emplace_back(groupVariantsJson[i].getStringOrDefault("")); } } From 25944b78d418d353bf6582a72e74ecf9e2dfbbe2 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 23 Aug 2023 15:37:02 +1000 Subject: [PATCH 204/421] Read from radidjson::Value. --- CHANGES.md | 12 +++- ...ension3dTilesBoundingVolumeS2JsonHandler.h | 45 +-------------- ...onBufferExtMeshoptCompressionJsonHandler.h | 45 +-------------- ...fferViewExtMeshoptCompressionJsonHandler.h | 45 +-------------- .../src/ExtensionCesiumRTCJsonHandler.h | 45 +-------------- .../src/ExtensionCesiumTileEdgesJsonHandler.h | 45 +-------------- .../ExtensionExtInstanceFeaturesJsonHandler.h | 45 +-------------- .../src/ExtensionExtMeshFeaturesJsonHandler.h | 45 +-------------- ...ExtensionExtMeshGpuInstancingJsonHandler.h | 45 +-------------- ...ensionKhrDracoMeshCompressionJsonHandler.h | 45 +-------------- .../ExtensionKhrMaterialsUnlitJsonHandler.h | 45 +-------------- .../ExtensionKhrTextureBasisuJsonHandler.h | 45 +-------------- .../ExtensionKhrTextureTransformJsonHandler.h | 45 +-------------- ...shPrimitiveExtFeatureMetadataJsonHandler.h | 45 +-------------- ...rimitiveExtStructuralMetadataJsonHandler.h | 45 +-------------- ...PrimitiveKhrMaterialsVariantsJsonHandler.h | 45 +-------------- ...ensionModelExtFeatureMetadataJsonHandler.h | 45 +-------------- ...ionModelExtStructuralMetadataJsonHandler.h | 45 +-------------- ...sionModelKhrMaterialsVariantsJsonHandler.h | 45 +-------------- ...tensionModelMaxarMeshVariantsJsonHandler.h | 45 +-------------- ...xtensionNodeMaxarMeshVariantsJsonHandler.h | 45 +-------------- .../src/ExtensionTextureWebpJsonHandler.h | 45 +-------------- .../CesiumJsonReader/ArrayJsonHandler.h | 8 +++ .../CesiumJsonReader/IExtensionJsonHandler.h | 4 +- .../include/CesiumJsonReader/JsonReader.h | 55 ++++++++++++++++++- .../src/ExtensionsJsonHandler.cpp | 2 +- CesiumJsonReader/src/JsonReader.cpp | 11 ++++ CesiumJsonReader/src/JsonReaderOptions.cpp | 2 + tools/generate-classes/generate.js | 40 +------------- 29 files changed, 109 insertions(+), 970 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index e7c982ab1..0dd6a1868 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,10 +5,13 @@ ##### Breaking Changes :mega: - Renamed `ExtensionReaderContext` to `JsonReaderOptions`, and the `getExtensions` method on various JSON reader classes to `getOptions`. +- `IExtensionJsonHandler` no longer derives from `IJsonHandler`. Instead, it has a new pure virtual method, `getHandler`, that must be implemented to allow clients to obtain the `IJsonHandler`. In almost all implementations, this should simply return `*this`. ##### Additions :tada: - Unknown properties in objects read with a `JsonReader` are now stored in the `unknownProperties` property on `ExtensibleObject` by default. To ignore them, as was done in previous versions, call `setCaptureUnknownProperties` on `JsonReaderOptions`. +- Added `ValueType` type alias to `ArrayJsonHandler`, for consistency with other JSON handlers. +- Added an overload of `JsonReader::readJson` that takes a `rapidjson::Value` instead of a byte buffer. This allows a subtree of a `rapidjson::Document` to be easily and efficiently converted into statically-typed classes via `IJsonHandler`. ### v0.27.0 - 2023-09-01 @@ -56,6 +59,7 @@ - Added support for parsing implicit tilesets that conform to the 3D Tiles 1.1 Spec. ##### Fixes :wrench: + - Fixed various `libjpeg-turbo` build errors, including ones that occurred when building for iOS. ### v0.23.0 - 2023-04-03 @@ -157,7 +161,7 @@ ##### Breaking Changes :mega: - `TileRenderContent::lodTransitionPercentage` now always goes from 0.0 --> 1.0 regardless of if the tile is fading in or out. -- Added a new parameter to `IPrepareRendererResources::prepareInLoadThread`, `rendererOptions`, to allow passing arbitrary data from the renderer. +- Added a new parameter to `IPrepareRendererResources::prepareInLoadThread`, `rendererOptions`, to allow passing arbitrary data from the renderer. ##### Fixes :wrench: @@ -176,7 +180,7 @@ - Removed `TileContentLoadResult`. It has been replaced by `TileContent`. - Removed `TileContentLoader`. It has been replaced by `TilesetContentLoader` and `GltfConverters`. - Removed `ImplicitTraversal`. It has been replaced by `TilesetContentLoader` and `GltfConverters`. -- Removed many methods from the `Cesium3DTilesSelection::Tileset` class: `getUrl()`, `getIonAssetID()`, `getIonAssetToken()`, `notifyTileStartLoading`, `notifyTileDoneLoading()`, `notifyTileUnloading()`, `loadTilesFromJson()`, `requestTileContent()`, `requestAvailabilitySubtree()`, `addContext()`, and `getGltfUpAxis()`. Most of these were already not recommended for use outside of cesium-native. +- Removed many methods from the `Cesium3DTilesSelection::Tileset` class: `getUrl()`, `getIonAssetID()`, `getIonAssetToken()`, `notifyTileStartLoading`, `notifyTileDoneLoading()`, `notifyTileUnloading()`, `loadTilesFromJson()`, `requestTileContent()`, `requestAvailabilitySubtree()`, `addContext()`, and `getGltfUpAxis()`. Most of these were already not recommended for use outside of cesium-native. - Removed many methods from the `Cesium3DTilesSelection::Tile` class: `getTileset()`, `getContext()`, `setContext()`, `getContent()`, `setEmptyContent()`, `getRendererResources()`, `setState()`, `loadContent()`, `processLoadedContent()`, `unloadContent()`, `update()`, and `markPermanentlyFailed()`. Most of these were already not recommended for use outside of cesium-native. ##### Additions :tada: @@ -219,6 +223,7 @@ ### v0.17.0 - 2022-07-01 ##### Fixes :wrench: + - Fixed crash when parsing an empty copyright string in the glTF model. ### v0.16.0 - 2022-06-01 @@ -240,6 +245,7 @@ - Fixed a bug where upsampled quadtree tiles could have siblings with mismatching projections. In addition to the above, this release updates the following third-party libraries used by cesium-native: + - `cpp-httplib` to v0.10.3 ([changes](https://github.com/yhirose/cpp-httplib/compare/c7486ead96dad647b9783941722b5944ac1aaefa...d73395e1dc652465fa9524266cd26ad57365491f)) - `draco` to v1.5.2 ([changes](https://github.com/google/draco/compare/9bf5d2e4833d445acc85eb95da42d715d3711c6f...bd1e8de7dd0596c2cbe5929cbe1f5d2257cd33db)) - `earcut` to v2.2.3 ([changes](https://github.com/mapbox/earcut.hpp/compare/6d18edf0ce046023a7cb55e69c4cd9ba90e2c716...b28acde132cdb8e0ef536a96ca7ada8a651f9169)) @@ -466,7 +472,7 @@ In addition to the above, this release updates the following third-party librari - Fixed a bug that caused 3D Tiles content to fail to load when the status code was zero. This code is used by libcurl for successful read of `file://` URLs, so the bug prevented loading from such URLs in some environments. - Errors and warnings that occur while loading glTF textures are now include in the model load errors and warnings. - Fixes how `generate-classes` deals with reserved C++ keywords. Property names that are C++ keywords should be appended with "Property" as was already done, -but when parsing JSONs the original property name string should be used. + but when parsing JSONs the original property name string should be used. ### v0.8.0 - 2021-10-01 diff --git a/Cesium3DTilesReader/generated/src/Extension3dTilesBoundingVolumeS2JsonHandler.h b/Cesium3DTilesReader/generated/src/Extension3dTilesBoundingVolumeS2JsonHandler.h index dcfee86c0..d8f237521 100644 --- a/Cesium3DTilesReader/generated/src/Extension3dTilesBoundingVolumeS2JsonHandler.h +++ b/Cesium3DTilesReader/generated/src/Extension3dTilesBoundingVolumeS2JsonHandler.h @@ -34,50 +34,7 @@ class Extension3dTilesBoundingVolumeS2JsonHandler CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) override; - virtual IJsonHandler* readNull() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readNull(); - }; - virtual IJsonHandler* readBool(bool b) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readBool(b); - } - virtual IJsonHandler* readInt32(int32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt32(i); - } - virtual IJsonHandler* readUint32(uint32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint32(i); - } - virtual IJsonHandler* readInt64(int64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt64(i); - } - virtual IJsonHandler* readUint64(uint64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint64(i); - } - virtual IJsonHandler* readDouble(double d) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readDouble(d); - } - virtual IJsonHandler* readString(const std::string_view& str) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readString(str); - } - virtual IJsonHandler* readObjectStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectStart(); - } - virtual IJsonHandler* readObjectEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectEnd(); - } - virtual IJsonHandler* readArrayStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayStart(); - } - virtual IJsonHandler* readArrayEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayEnd(); - } - virtual void reportWarning( - const std::string& warning, - std::vector&& context = - std::vector()) override { - CesiumJsonReader::ExtensibleObjectJsonHandler::reportWarning( - warning, - std::move(context)); - } + virtual IJsonHandler& getHandler() override { return *this; } protected: IJsonHandler* readObjectKeyExtension3dTilesBoundingVolumeS2( diff --git a/CesiumGltfReader/generated/src/ExtensionBufferExtMeshoptCompressionJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionBufferExtMeshoptCompressionJsonHandler.h index 7c4871195..5bed9a46e 100644 --- a/CesiumGltfReader/generated/src/ExtensionBufferExtMeshoptCompressionJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionBufferExtMeshoptCompressionJsonHandler.h @@ -32,50 +32,7 @@ class ExtensionBufferExtMeshoptCompressionJsonHandler CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) override; - virtual IJsonHandler* readNull() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readNull(); - }; - virtual IJsonHandler* readBool(bool b) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readBool(b); - } - virtual IJsonHandler* readInt32(int32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt32(i); - } - virtual IJsonHandler* readUint32(uint32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint32(i); - } - virtual IJsonHandler* readInt64(int64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt64(i); - } - virtual IJsonHandler* readUint64(uint64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint64(i); - } - virtual IJsonHandler* readDouble(double d) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readDouble(d); - } - virtual IJsonHandler* readString(const std::string_view& str) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readString(str); - } - virtual IJsonHandler* readObjectStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectStart(); - } - virtual IJsonHandler* readObjectEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectEnd(); - } - virtual IJsonHandler* readArrayStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayStart(); - } - virtual IJsonHandler* readArrayEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayEnd(); - } - virtual void reportWarning( - const std::string& warning, - std::vector&& context = - std::vector()) override { - CesiumJsonReader::ExtensibleObjectJsonHandler::reportWarning( - warning, - std::move(context)); - } + virtual IJsonHandler& getHandler() override { return *this; } protected: IJsonHandler* readObjectKeyExtensionBufferExtMeshoptCompression( diff --git a/CesiumGltfReader/generated/src/ExtensionBufferViewExtMeshoptCompressionJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionBufferViewExtMeshoptCompressionJsonHandler.h index c8783361e..775513949 100644 --- a/CesiumGltfReader/generated/src/ExtensionBufferViewExtMeshoptCompressionJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionBufferViewExtMeshoptCompressionJsonHandler.h @@ -33,50 +33,7 @@ class ExtensionBufferViewExtMeshoptCompressionJsonHandler CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) override; - virtual IJsonHandler* readNull() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readNull(); - }; - virtual IJsonHandler* readBool(bool b) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readBool(b); - } - virtual IJsonHandler* readInt32(int32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt32(i); - } - virtual IJsonHandler* readUint32(uint32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint32(i); - } - virtual IJsonHandler* readInt64(int64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt64(i); - } - virtual IJsonHandler* readUint64(uint64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint64(i); - } - virtual IJsonHandler* readDouble(double d) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readDouble(d); - } - virtual IJsonHandler* readString(const std::string_view& str) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readString(str); - } - virtual IJsonHandler* readObjectStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectStart(); - } - virtual IJsonHandler* readObjectEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectEnd(); - } - virtual IJsonHandler* readArrayStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayStart(); - } - virtual IJsonHandler* readArrayEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayEnd(); - } - virtual void reportWarning( - const std::string& warning, - std::vector&& context = - std::vector()) override { - CesiumJsonReader::ExtensibleObjectJsonHandler::reportWarning( - warning, - std::move(context)); - } + virtual IJsonHandler& getHandler() override { return *this; } protected: IJsonHandler* readObjectKeyExtensionBufferViewExtMeshoptCompression( diff --git a/CesiumGltfReader/generated/src/ExtensionCesiumRTCJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionCesiumRTCJsonHandler.h index 84ffc1482..2626df960 100644 --- a/CesiumGltfReader/generated/src/ExtensionCesiumRTCJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionCesiumRTCJsonHandler.h @@ -32,50 +32,7 @@ class ExtensionCesiumRTCJsonHandler CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) override; - virtual IJsonHandler* readNull() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readNull(); - }; - virtual IJsonHandler* readBool(bool b) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readBool(b); - } - virtual IJsonHandler* readInt32(int32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt32(i); - } - virtual IJsonHandler* readUint32(uint32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint32(i); - } - virtual IJsonHandler* readInt64(int64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt64(i); - } - virtual IJsonHandler* readUint64(uint64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint64(i); - } - virtual IJsonHandler* readDouble(double d) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readDouble(d); - } - virtual IJsonHandler* readString(const std::string_view& str) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readString(str); - } - virtual IJsonHandler* readObjectStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectStart(); - } - virtual IJsonHandler* readObjectEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectEnd(); - } - virtual IJsonHandler* readArrayStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayStart(); - } - virtual IJsonHandler* readArrayEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayEnd(); - } - virtual void reportWarning( - const std::string& warning, - std::vector&& context = - std::vector()) override { - CesiumJsonReader::ExtensibleObjectJsonHandler::reportWarning( - warning, - std::move(context)); - } + virtual IJsonHandler& getHandler() override { return *this; } protected: IJsonHandler* readObjectKeyExtensionCesiumRTC( diff --git a/CesiumGltfReader/generated/src/ExtensionCesiumTileEdgesJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionCesiumTileEdgesJsonHandler.h index d08365fdc..c3588e144 100644 --- a/CesiumGltfReader/generated/src/ExtensionCesiumTileEdgesJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionCesiumTileEdgesJsonHandler.h @@ -32,50 +32,7 @@ class ExtensionCesiumTileEdgesJsonHandler CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) override; - virtual IJsonHandler* readNull() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readNull(); - }; - virtual IJsonHandler* readBool(bool b) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readBool(b); - } - virtual IJsonHandler* readInt32(int32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt32(i); - } - virtual IJsonHandler* readUint32(uint32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint32(i); - } - virtual IJsonHandler* readInt64(int64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt64(i); - } - virtual IJsonHandler* readUint64(uint64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint64(i); - } - virtual IJsonHandler* readDouble(double d) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readDouble(d); - } - virtual IJsonHandler* readString(const std::string_view& str) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readString(str); - } - virtual IJsonHandler* readObjectStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectStart(); - } - virtual IJsonHandler* readObjectEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectEnd(); - } - virtual IJsonHandler* readArrayStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayStart(); - } - virtual IJsonHandler* readArrayEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayEnd(); - } - virtual void reportWarning( - const std::string& warning, - std::vector&& context = - std::vector()) override { - CesiumJsonReader::ExtensibleObjectJsonHandler::reportWarning( - warning, - std::move(context)); - } + virtual IJsonHandler& getHandler() override { return *this; } protected: IJsonHandler* readObjectKeyExtensionCesiumTileEdges( diff --git a/CesiumGltfReader/generated/src/ExtensionExtInstanceFeaturesJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionExtInstanceFeaturesJsonHandler.h index c03c4fada..4d59d63a3 100644 --- a/CesiumGltfReader/generated/src/ExtensionExtInstanceFeaturesJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionExtInstanceFeaturesJsonHandler.h @@ -34,50 +34,7 @@ class ExtensionExtInstanceFeaturesJsonHandler CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) override; - virtual IJsonHandler* readNull() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readNull(); - }; - virtual IJsonHandler* readBool(bool b) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readBool(b); - } - virtual IJsonHandler* readInt32(int32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt32(i); - } - virtual IJsonHandler* readUint32(uint32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint32(i); - } - virtual IJsonHandler* readInt64(int64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt64(i); - } - virtual IJsonHandler* readUint64(uint64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint64(i); - } - virtual IJsonHandler* readDouble(double d) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readDouble(d); - } - virtual IJsonHandler* readString(const std::string_view& str) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readString(str); - } - virtual IJsonHandler* readObjectStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectStart(); - } - virtual IJsonHandler* readObjectEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectEnd(); - } - virtual IJsonHandler* readArrayStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayStart(); - } - virtual IJsonHandler* readArrayEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayEnd(); - } - virtual void reportWarning( - const std::string& warning, - std::vector&& context = - std::vector()) override { - CesiumJsonReader::ExtensibleObjectJsonHandler::reportWarning( - warning, - std::move(context)); - } + virtual IJsonHandler& getHandler() override { return *this; } protected: IJsonHandler* readObjectKeyExtensionExtInstanceFeatures( diff --git a/CesiumGltfReader/generated/src/ExtensionExtMeshFeaturesJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionExtMeshFeaturesJsonHandler.h index e39bf64f5..cc2ede28d 100644 --- a/CesiumGltfReader/generated/src/ExtensionExtMeshFeaturesJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionExtMeshFeaturesJsonHandler.h @@ -34,50 +34,7 @@ class ExtensionExtMeshFeaturesJsonHandler CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) override; - virtual IJsonHandler* readNull() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readNull(); - }; - virtual IJsonHandler* readBool(bool b) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readBool(b); - } - virtual IJsonHandler* readInt32(int32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt32(i); - } - virtual IJsonHandler* readUint32(uint32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint32(i); - } - virtual IJsonHandler* readInt64(int64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt64(i); - } - virtual IJsonHandler* readUint64(uint64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint64(i); - } - virtual IJsonHandler* readDouble(double d) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readDouble(d); - } - virtual IJsonHandler* readString(const std::string_view& str) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readString(str); - } - virtual IJsonHandler* readObjectStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectStart(); - } - virtual IJsonHandler* readObjectEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectEnd(); - } - virtual IJsonHandler* readArrayStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayStart(); - } - virtual IJsonHandler* readArrayEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayEnd(); - } - virtual void reportWarning( - const std::string& warning, - std::vector&& context = - std::vector()) override { - CesiumJsonReader::ExtensibleObjectJsonHandler::reportWarning( - warning, - std::move(context)); - } + virtual IJsonHandler& getHandler() override { return *this; } protected: IJsonHandler* readObjectKeyExtensionExtMeshFeatures( diff --git a/CesiumGltfReader/generated/src/ExtensionExtMeshGpuInstancingJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionExtMeshGpuInstancingJsonHandler.h index 5c0414671..afc99ea04 100644 --- a/CesiumGltfReader/generated/src/ExtensionExtMeshGpuInstancingJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionExtMeshGpuInstancingJsonHandler.h @@ -33,50 +33,7 @@ class ExtensionExtMeshGpuInstancingJsonHandler CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) override; - virtual IJsonHandler* readNull() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readNull(); - }; - virtual IJsonHandler* readBool(bool b) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readBool(b); - } - virtual IJsonHandler* readInt32(int32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt32(i); - } - virtual IJsonHandler* readUint32(uint32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint32(i); - } - virtual IJsonHandler* readInt64(int64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt64(i); - } - virtual IJsonHandler* readUint64(uint64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint64(i); - } - virtual IJsonHandler* readDouble(double d) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readDouble(d); - } - virtual IJsonHandler* readString(const std::string_view& str) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readString(str); - } - virtual IJsonHandler* readObjectStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectStart(); - } - virtual IJsonHandler* readObjectEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectEnd(); - } - virtual IJsonHandler* readArrayStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayStart(); - } - virtual IJsonHandler* readArrayEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayEnd(); - } - virtual void reportWarning( - const std::string& warning, - std::vector&& context = - std::vector()) override { - CesiumJsonReader::ExtensibleObjectJsonHandler::reportWarning( - warning, - std::move(context)); - } + virtual IJsonHandler& getHandler() override { return *this; } protected: IJsonHandler* readObjectKeyExtensionExtMeshGpuInstancing( diff --git a/CesiumGltfReader/generated/src/ExtensionKhrDracoMeshCompressionJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionKhrDracoMeshCompressionJsonHandler.h index e3835e421..a6f3cb67a 100644 --- a/CesiumGltfReader/generated/src/ExtensionKhrDracoMeshCompressionJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionKhrDracoMeshCompressionJsonHandler.h @@ -34,50 +34,7 @@ class ExtensionKhrDracoMeshCompressionJsonHandler CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) override; - virtual IJsonHandler* readNull() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readNull(); - }; - virtual IJsonHandler* readBool(bool b) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readBool(b); - } - virtual IJsonHandler* readInt32(int32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt32(i); - } - virtual IJsonHandler* readUint32(uint32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint32(i); - } - virtual IJsonHandler* readInt64(int64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt64(i); - } - virtual IJsonHandler* readUint64(uint64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint64(i); - } - virtual IJsonHandler* readDouble(double d) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readDouble(d); - } - virtual IJsonHandler* readString(const std::string_view& str) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readString(str); - } - virtual IJsonHandler* readObjectStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectStart(); - } - virtual IJsonHandler* readObjectEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectEnd(); - } - virtual IJsonHandler* readArrayStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayStart(); - } - virtual IJsonHandler* readArrayEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayEnd(); - } - virtual void reportWarning( - const std::string& warning, - std::vector&& context = - std::vector()) override { - CesiumJsonReader::ExtensibleObjectJsonHandler::reportWarning( - warning, - std::move(context)); - } + virtual IJsonHandler& getHandler() override { return *this; } protected: IJsonHandler* readObjectKeyExtensionKhrDracoMeshCompression( diff --git a/CesiumGltfReader/generated/src/ExtensionKhrMaterialsUnlitJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionKhrMaterialsUnlitJsonHandler.h index c143b4169..5125caf45 100644 --- a/CesiumGltfReader/generated/src/ExtensionKhrMaterialsUnlitJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionKhrMaterialsUnlitJsonHandler.h @@ -31,50 +31,7 @@ class ExtensionKhrMaterialsUnlitJsonHandler CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) override; - virtual IJsonHandler* readNull() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readNull(); - }; - virtual IJsonHandler* readBool(bool b) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readBool(b); - } - virtual IJsonHandler* readInt32(int32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt32(i); - } - virtual IJsonHandler* readUint32(uint32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint32(i); - } - virtual IJsonHandler* readInt64(int64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt64(i); - } - virtual IJsonHandler* readUint64(uint64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint64(i); - } - virtual IJsonHandler* readDouble(double d) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readDouble(d); - } - virtual IJsonHandler* readString(const std::string_view& str) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readString(str); - } - virtual IJsonHandler* readObjectStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectStart(); - } - virtual IJsonHandler* readObjectEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectEnd(); - } - virtual IJsonHandler* readArrayStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayStart(); - } - virtual IJsonHandler* readArrayEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayEnd(); - } - virtual void reportWarning( - const std::string& warning, - std::vector&& context = - std::vector()) override { - CesiumJsonReader::ExtensibleObjectJsonHandler::reportWarning( - warning, - std::move(context)); - } + virtual IJsonHandler& getHandler() override { return *this; } protected: IJsonHandler* readObjectKeyExtensionKhrMaterialsUnlit( diff --git a/CesiumGltfReader/generated/src/ExtensionKhrTextureBasisuJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionKhrTextureBasisuJsonHandler.h index fe66ed324..0b3cd768c 100644 --- a/CesiumGltfReader/generated/src/ExtensionKhrTextureBasisuJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionKhrTextureBasisuJsonHandler.h @@ -32,50 +32,7 @@ class ExtensionKhrTextureBasisuJsonHandler CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) override; - virtual IJsonHandler* readNull() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readNull(); - }; - virtual IJsonHandler* readBool(bool b) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readBool(b); - } - virtual IJsonHandler* readInt32(int32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt32(i); - } - virtual IJsonHandler* readUint32(uint32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint32(i); - } - virtual IJsonHandler* readInt64(int64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt64(i); - } - virtual IJsonHandler* readUint64(uint64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint64(i); - } - virtual IJsonHandler* readDouble(double d) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readDouble(d); - } - virtual IJsonHandler* readString(const std::string_view& str) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readString(str); - } - virtual IJsonHandler* readObjectStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectStart(); - } - virtual IJsonHandler* readObjectEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectEnd(); - } - virtual IJsonHandler* readArrayStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayStart(); - } - virtual IJsonHandler* readArrayEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayEnd(); - } - virtual void reportWarning( - const std::string& warning, - std::vector&& context = - std::vector()) override { - CesiumJsonReader::ExtensibleObjectJsonHandler::reportWarning( - warning, - std::move(context)); - } + virtual IJsonHandler& getHandler() override { return *this; } protected: IJsonHandler* readObjectKeyExtensionKhrTextureBasisu( diff --git a/CesiumGltfReader/generated/src/ExtensionKhrTextureTransformJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionKhrTextureTransformJsonHandler.h index 11585ed94..6741affa9 100644 --- a/CesiumGltfReader/generated/src/ExtensionKhrTextureTransformJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionKhrTextureTransformJsonHandler.h @@ -34,50 +34,7 @@ class ExtensionKhrTextureTransformJsonHandler CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) override; - virtual IJsonHandler* readNull() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readNull(); - }; - virtual IJsonHandler* readBool(bool b) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readBool(b); - } - virtual IJsonHandler* readInt32(int32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt32(i); - } - virtual IJsonHandler* readUint32(uint32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint32(i); - } - virtual IJsonHandler* readInt64(int64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt64(i); - } - virtual IJsonHandler* readUint64(uint64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint64(i); - } - virtual IJsonHandler* readDouble(double d) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readDouble(d); - } - virtual IJsonHandler* readString(const std::string_view& str) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readString(str); - } - virtual IJsonHandler* readObjectStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectStart(); - } - virtual IJsonHandler* readObjectEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectEnd(); - } - virtual IJsonHandler* readArrayStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayStart(); - } - virtual IJsonHandler* readArrayEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayEnd(); - } - virtual void reportWarning( - const std::string& warning, - std::vector&& context = - std::vector()) override { - CesiumJsonReader::ExtensibleObjectJsonHandler::reportWarning( - warning, - std::move(context)); - } + virtual IJsonHandler& getHandler() override { return *this; } protected: IJsonHandler* readObjectKeyExtensionKhrTextureTransform( diff --git a/CesiumGltfReader/generated/src/ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler.h index 93ec30a16..b4da79e6e 100644 --- a/CesiumGltfReader/generated/src/ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler.h @@ -36,50 +36,7 @@ class ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) override; - virtual IJsonHandler* readNull() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readNull(); - }; - virtual IJsonHandler* readBool(bool b) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readBool(b); - } - virtual IJsonHandler* readInt32(int32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt32(i); - } - virtual IJsonHandler* readUint32(uint32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint32(i); - } - virtual IJsonHandler* readInt64(int64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt64(i); - } - virtual IJsonHandler* readUint64(uint64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint64(i); - } - virtual IJsonHandler* readDouble(double d) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readDouble(d); - } - virtual IJsonHandler* readString(const std::string_view& str) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readString(str); - } - virtual IJsonHandler* readObjectStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectStart(); - } - virtual IJsonHandler* readObjectEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectEnd(); - } - virtual IJsonHandler* readArrayStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayStart(); - } - virtual IJsonHandler* readArrayEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayEnd(); - } - virtual void reportWarning( - const std::string& warning, - std::vector&& context = - std::vector()) override { - CesiumJsonReader::ExtensibleObjectJsonHandler::reportWarning( - warning, - std::move(context)); - } + virtual IJsonHandler& getHandler() override { return *this; } protected: IJsonHandler* readObjectKeyExtensionMeshPrimitiveExtFeatureMetadata( diff --git a/CesiumGltfReader/generated/src/ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler.h index cc3da651e..e2ab51dbb 100644 --- a/CesiumGltfReader/generated/src/ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler.h @@ -33,50 +33,7 @@ class ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) override; - virtual IJsonHandler* readNull() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readNull(); - }; - virtual IJsonHandler* readBool(bool b) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readBool(b); - } - virtual IJsonHandler* readInt32(int32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt32(i); - } - virtual IJsonHandler* readUint32(uint32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint32(i); - } - virtual IJsonHandler* readInt64(int64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt64(i); - } - virtual IJsonHandler* readUint64(uint64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint64(i); - } - virtual IJsonHandler* readDouble(double d) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readDouble(d); - } - virtual IJsonHandler* readString(const std::string_view& str) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readString(str); - } - virtual IJsonHandler* readObjectStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectStart(); - } - virtual IJsonHandler* readObjectEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectEnd(); - } - virtual IJsonHandler* readArrayStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayStart(); - } - virtual IJsonHandler* readArrayEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayEnd(); - } - virtual void reportWarning( - const std::string& warning, - std::vector&& context = - std::vector()) override { - CesiumJsonReader::ExtensibleObjectJsonHandler::reportWarning( - warning, - std::move(context)); - } + virtual IJsonHandler& getHandler() override { return *this; } protected: IJsonHandler* readObjectKeyExtensionMeshPrimitiveExtStructuralMetadata( diff --git a/CesiumGltfReader/generated/src/ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler.h index 28c93e0e3..2f9ee7e52 100644 --- a/CesiumGltfReader/generated/src/ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler.h @@ -34,50 +34,7 @@ class ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) override; - virtual IJsonHandler* readNull() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readNull(); - }; - virtual IJsonHandler* readBool(bool b) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readBool(b); - } - virtual IJsonHandler* readInt32(int32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt32(i); - } - virtual IJsonHandler* readUint32(uint32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint32(i); - } - virtual IJsonHandler* readInt64(int64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt64(i); - } - virtual IJsonHandler* readUint64(uint64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint64(i); - } - virtual IJsonHandler* readDouble(double d) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readDouble(d); - } - virtual IJsonHandler* readString(const std::string_view& str) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readString(str); - } - virtual IJsonHandler* readObjectStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectStart(); - } - virtual IJsonHandler* readObjectEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectEnd(); - } - virtual IJsonHandler* readArrayStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayStart(); - } - virtual IJsonHandler* readArrayEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayEnd(); - } - virtual void reportWarning( - const std::string& warning, - std::vector&& context = - std::vector()) override { - CesiumJsonReader::ExtensibleObjectJsonHandler::reportWarning( - warning, - std::move(context)); - } + virtual IJsonHandler& getHandler() override { return *this; } protected: IJsonHandler* readObjectKeyExtensionMeshPrimitiveKhrMaterialsVariants( diff --git a/CesiumGltfReader/generated/src/ExtensionModelExtFeatureMetadataJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionModelExtFeatureMetadataJsonHandler.h index 1fc825848..6dc96fa48 100644 --- a/CesiumGltfReader/generated/src/ExtensionModelExtFeatureMetadataJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionModelExtFeatureMetadataJsonHandler.h @@ -38,50 +38,7 @@ class ExtensionModelExtFeatureMetadataJsonHandler CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) override; - virtual IJsonHandler* readNull() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readNull(); - }; - virtual IJsonHandler* readBool(bool b) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readBool(b); - } - virtual IJsonHandler* readInt32(int32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt32(i); - } - virtual IJsonHandler* readUint32(uint32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint32(i); - } - virtual IJsonHandler* readInt64(int64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt64(i); - } - virtual IJsonHandler* readUint64(uint64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint64(i); - } - virtual IJsonHandler* readDouble(double d) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readDouble(d); - } - virtual IJsonHandler* readString(const std::string_view& str) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readString(str); - } - virtual IJsonHandler* readObjectStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectStart(); - } - virtual IJsonHandler* readObjectEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectEnd(); - } - virtual IJsonHandler* readArrayStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayStart(); - } - virtual IJsonHandler* readArrayEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayEnd(); - } - virtual void reportWarning( - const std::string& warning, - std::vector&& context = - std::vector()) override { - CesiumJsonReader::ExtensibleObjectJsonHandler::reportWarning( - warning, - std::move(context)); - } + virtual IJsonHandler& getHandler() override { return *this; } protected: IJsonHandler* readObjectKeyExtensionModelExtFeatureMetadata( diff --git a/CesiumGltfReader/generated/src/ExtensionModelExtStructuralMetadataJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionModelExtStructuralMetadataJsonHandler.h index bce764b8f..0176d6421 100644 --- a/CesiumGltfReader/generated/src/ExtensionModelExtStructuralMetadataJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionModelExtStructuralMetadataJsonHandler.h @@ -38,50 +38,7 @@ class ExtensionModelExtStructuralMetadataJsonHandler CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) override; - virtual IJsonHandler* readNull() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readNull(); - }; - virtual IJsonHandler* readBool(bool b) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readBool(b); - } - virtual IJsonHandler* readInt32(int32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt32(i); - } - virtual IJsonHandler* readUint32(uint32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint32(i); - } - virtual IJsonHandler* readInt64(int64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt64(i); - } - virtual IJsonHandler* readUint64(uint64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint64(i); - } - virtual IJsonHandler* readDouble(double d) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readDouble(d); - } - virtual IJsonHandler* readString(const std::string_view& str) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readString(str); - } - virtual IJsonHandler* readObjectStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectStart(); - } - virtual IJsonHandler* readObjectEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectEnd(); - } - virtual IJsonHandler* readArrayStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayStart(); - } - virtual IJsonHandler* readArrayEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayEnd(); - } - virtual void reportWarning( - const std::string& warning, - std::vector&& context = - std::vector()) override { - CesiumJsonReader::ExtensibleObjectJsonHandler::reportWarning( - warning, - std::move(context)); - } + virtual IJsonHandler& getHandler() override { return *this; } protected: IJsonHandler* readObjectKeyExtensionModelExtStructuralMetadata( diff --git a/CesiumGltfReader/generated/src/ExtensionModelKhrMaterialsVariantsJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionModelKhrMaterialsVariantsJsonHandler.h index 4c967b4b5..e1c5cac88 100644 --- a/CesiumGltfReader/generated/src/ExtensionModelKhrMaterialsVariantsJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionModelKhrMaterialsVariantsJsonHandler.h @@ -34,50 +34,7 @@ class ExtensionModelKhrMaterialsVariantsJsonHandler CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) override; - virtual IJsonHandler* readNull() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readNull(); - }; - virtual IJsonHandler* readBool(bool b) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readBool(b); - } - virtual IJsonHandler* readInt32(int32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt32(i); - } - virtual IJsonHandler* readUint32(uint32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint32(i); - } - virtual IJsonHandler* readInt64(int64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt64(i); - } - virtual IJsonHandler* readUint64(uint64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint64(i); - } - virtual IJsonHandler* readDouble(double d) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readDouble(d); - } - virtual IJsonHandler* readString(const std::string_view& str) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readString(str); - } - virtual IJsonHandler* readObjectStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectStart(); - } - virtual IJsonHandler* readObjectEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectEnd(); - } - virtual IJsonHandler* readArrayStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayStart(); - } - virtual IJsonHandler* readArrayEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayEnd(); - } - virtual void reportWarning( - const std::string& warning, - std::vector&& context = - std::vector()) override { - CesiumJsonReader::ExtensibleObjectJsonHandler::reportWarning( - warning, - std::move(context)); - } + virtual IJsonHandler& getHandler() override { return *this; } protected: IJsonHandler* readObjectKeyExtensionModelKhrMaterialsVariants( diff --git a/CesiumGltfReader/generated/src/ExtensionModelMaxarMeshVariantsJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionModelMaxarMeshVariantsJsonHandler.h index c98b19818..303da2a86 100644 --- a/CesiumGltfReader/generated/src/ExtensionModelMaxarMeshVariantsJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionModelMaxarMeshVariantsJsonHandler.h @@ -35,50 +35,7 @@ class ExtensionModelMaxarMeshVariantsJsonHandler CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) override; - virtual IJsonHandler* readNull() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readNull(); - }; - virtual IJsonHandler* readBool(bool b) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readBool(b); - } - virtual IJsonHandler* readInt32(int32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt32(i); - } - virtual IJsonHandler* readUint32(uint32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint32(i); - } - virtual IJsonHandler* readInt64(int64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt64(i); - } - virtual IJsonHandler* readUint64(uint64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint64(i); - } - virtual IJsonHandler* readDouble(double d) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readDouble(d); - } - virtual IJsonHandler* readString(const std::string_view& str) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readString(str); - } - virtual IJsonHandler* readObjectStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectStart(); - } - virtual IJsonHandler* readObjectEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectEnd(); - } - virtual IJsonHandler* readArrayStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayStart(); - } - virtual IJsonHandler* readArrayEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayEnd(); - } - virtual void reportWarning( - const std::string& warning, - std::vector&& context = - std::vector()) override { - CesiumJsonReader::ExtensibleObjectJsonHandler::reportWarning( - warning, - std::move(context)); - } + virtual IJsonHandler& getHandler() override { return *this; } protected: IJsonHandler* readObjectKeyExtensionModelMaxarMeshVariants( diff --git a/CesiumGltfReader/generated/src/ExtensionNodeMaxarMeshVariantsJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionNodeMaxarMeshVariantsJsonHandler.h index 16bb452ce..9f73a30e8 100644 --- a/CesiumGltfReader/generated/src/ExtensionNodeMaxarMeshVariantsJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionNodeMaxarMeshVariantsJsonHandler.h @@ -34,50 +34,7 @@ class ExtensionNodeMaxarMeshVariantsJsonHandler CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) override; - virtual IJsonHandler* readNull() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readNull(); - }; - virtual IJsonHandler* readBool(bool b) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readBool(b); - } - virtual IJsonHandler* readInt32(int32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt32(i); - } - virtual IJsonHandler* readUint32(uint32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint32(i); - } - virtual IJsonHandler* readInt64(int64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt64(i); - } - virtual IJsonHandler* readUint64(uint64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint64(i); - } - virtual IJsonHandler* readDouble(double d) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readDouble(d); - } - virtual IJsonHandler* readString(const std::string_view& str) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readString(str); - } - virtual IJsonHandler* readObjectStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectStart(); - } - virtual IJsonHandler* readObjectEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectEnd(); - } - virtual IJsonHandler* readArrayStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayStart(); - } - virtual IJsonHandler* readArrayEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayEnd(); - } - virtual void reportWarning( - const std::string& warning, - std::vector&& context = - std::vector()) override { - CesiumJsonReader::ExtensibleObjectJsonHandler::reportWarning( - warning, - std::move(context)); - } + virtual IJsonHandler& getHandler() override { return *this; } protected: IJsonHandler* readObjectKeyExtensionNodeMaxarMeshVariants( diff --git a/CesiumGltfReader/generated/src/ExtensionTextureWebpJsonHandler.h b/CesiumGltfReader/generated/src/ExtensionTextureWebpJsonHandler.h index 04ed382e3..8ffe0739f 100644 --- a/CesiumGltfReader/generated/src/ExtensionTextureWebpJsonHandler.h +++ b/CesiumGltfReader/generated/src/ExtensionTextureWebpJsonHandler.h @@ -32,50 +32,7 @@ class ExtensionTextureWebpJsonHandler CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) override; - virtual IJsonHandler* readNull() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readNull(); - }; - virtual IJsonHandler* readBool(bool b) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readBool(b); - } - virtual IJsonHandler* readInt32(int32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt32(i); - } - virtual IJsonHandler* readUint32(uint32_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint32(i); - } - virtual IJsonHandler* readInt64(int64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readInt64(i); - } - virtual IJsonHandler* readUint64(uint64_t i) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readUint64(i); - } - virtual IJsonHandler* readDouble(double d) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readDouble(d); - } - virtual IJsonHandler* readString(const std::string_view& str) override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readString(str); - } - virtual IJsonHandler* readObjectStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectStart(); - } - virtual IJsonHandler* readObjectEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readObjectEnd(); - } - virtual IJsonHandler* readArrayStart() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayStart(); - } - virtual IJsonHandler* readArrayEnd() override { - return CesiumJsonReader::ExtensibleObjectJsonHandler::readArrayEnd(); - } - virtual void reportWarning( - const std::string& warning, - std::vector&& context = - std::vector()) override { - CesiumJsonReader::ExtensibleObjectJsonHandler::reportWarning( - warning, - std::move(context)); - } + virtual IJsonHandler& getHandler() override { return *this; } protected: IJsonHandler* readObjectKeyExtensionTextureWebp( diff --git a/CesiumJsonReader/include/CesiumJsonReader/ArrayJsonHandler.h b/CesiumJsonReader/include/CesiumJsonReader/ArrayJsonHandler.h index d2902cc0b..60365a7df 100644 --- a/CesiumJsonReader/include/CesiumJsonReader/ArrayJsonHandler.h +++ b/CesiumJsonReader/include/CesiumJsonReader/ArrayJsonHandler.h @@ -15,6 +15,8 @@ namespace CesiumJsonReader { template class CESIUMJSONREADER_API ArrayJsonHandler : public JsonHandler { public: + using ValueType = std::vector; + template ArrayJsonHandler(Ts&&... args) noexcept : JsonHandler(), @@ -129,6 +131,8 @@ template <> class CESIUMJSONREADER_API ArrayJsonHandler : public JsonHandler { public: + using ValueType = std::vector; + ArrayJsonHandler() noexcept : JsonHandler() {} void reset(IJsonHandler* pParent, std::vector* pArray) { @@ -246,6 +250,8 @@ template class CESIUMJSONREADER_API ArrayJsonHandler> : public JsonHandler { public: + using ValueType = std::vector; + ArrayJsonHandler() noexcept : JsonHandler() {} void reset(IJsonHandler* pParent, std::vector* pArray) { @@ -357,6 +363,8 @@ template <> class CESIUMJSONREADER_API ArrayJsonHandler : public JsonHandler { public: + using ValueType = std::vector; + ArrayJsonHandler() noexcept : JsonHandler() {} void reset(IJsonHandler* pParent, std::vector* pArray) { diff --git a/CesiumJsonReader/include/CesiumJsonReader/IExtensionJsonHandler.h b/CesiumJsonReader/include/CesiumJsonReader/IExtensionJsonHandler.h index c640ff1c6..6e0fec568 100644 --- a/CesiumJsonReader/include/CesiumJsonReader/IExtensionJsonHandler.h +++ b/CesiumJsonReader/include/CesiumJsonReader/IExtensionJsonHandler.h @@ -9,12 +9,14 @@ namespace CesiumJsonReader { -class IExtensionJsonHandler : public IJsonHandler { +class IExtensionJsonHandler { public: + virtual ~IExtensionJsonHandler() noexcept = default; virtual void reset( IJsonHandler* pParentHandler, CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) = 0; + virtual IJsonHandler& getHandler() = 0; }; } // namespace CesiumJsonReader diff --git a/CesiumJsonReader/include/CesiumJsonReader/JsonReader.h b/CesiumJsonReader/include/CesiumJsonReader/JsonReader.h index 3680acaa0..0200ace02 100644 --- a/CesiumJsonReader/include/CesiumJsonReader/JsonReader.h +++ b/CesiumJsonReader/include/CesiumJsonReader/JsonReader.h @@ -4,6 +4,7 @@ #include "Library.h" #include +#include #include #include @@ -42,10 +43,16 @@ template struct ReadJsonResult { class CESIUMJSONREADER_API JsonReader { public: /** - * @brief Reads JSON from a byte buffer. + * @brief Reads JSON from a byte buffer into a statically-type class. * * @param data The buffer from which to read JSON. - * @param handler The handler to receive the top-level JSON object. + * @param handler The handler to receive the top-level JSON object. This + * instance must: + * - Implement {@link IJsonHandler}. + * - Contain a `ValueType` type alias indicating the type of the instance to + * be read into. + * - Have a `reset` method taking 1) a parent `IJsonHandler` pointer, and 2) + * and a pointer to a value of type `ValueType`. * @return The result of reading the JSON. */ template @@ -72,6 +79,43 @@ class CESIUMJSONREADER_API JsonReader { return result; } + /** + * @brief Reads JSON from a `rapidjson::Value` into a statically-typed class. + * + * @param data The `rapidjson::Value` from which to read JSON. + * @param handler The handler to receive the top-level JSON object. This + * instance must: + * - Implement {@link IJsonHandler}. + * - Contain a `ValueType` type alias indicating the type of the instance to + * be read into. + * - Have a `reset` method taking 1) a parent `IJsonHandler` pointer, and 2) + * and a pointer to a value of type `ValueType`. + * @return The result of reading the JSON. + */ + template + static ReadJsonResult + readJson(const rapidjson::Value& jsonValue, T& handler) { + ReadJsonResult result; + + result.value.emplace(); + + FinalJsonHandler finalHandler(result.warnings); + handler.reset(&finalHandler, &result.value.value()); + + JsonReader::internalRead( + jsonValue, + handler, + finalHandler, + result.errors, + result.warnings); + + if (!result.errors.empty()) { + result.value.reset(); + } + + return result; + } + private: class FinalJsonHandler : public JsonHandler { public: @@ -92,6 +136,13 @@ class CESIUMJSONREADER_API JsonReader { FinalJsonHandler& finalHandler, std::vector& errors, std::vector& warnings); + + static void internalRead( + const rapidjson::Value& jsonValue, + IJsonHandler& handler, + FinalJsonHandler& finalHandler, + std::vector& errors, + std::vector& warnings); }; } // namespace CesiumJsonReader diff --git a/CesiumJsonReader/src/ExtensionsJsonHandler.cpp b/CesiumJsonReader/src/ExtensionsJsonHandler.cpp index 2b3ef0e28..18535961a 100644 --- a/CesiumJsonReader/src/ExtensionsJsonHandler.cpp +++ b/CesiumJsonReader/src/ExtensionsJsonHandler.cpp @@ -21,7 +21,7 @@ ExtensionsJsonHandler::readObjectKey(const std::string_view& str) { this->_context.createExtensionHandler(str, this->_objectType); if (this->_currentExtensionHandler) { this->_currentExtensionHandler->reset(this, *this->_pObject, str); - return this->_currentExtensionHandler.get(); + return &this->_currentExtensionHandler->getHandler(); } else { return this->ignoreAndContinue(); } diff --git a/CesiumJsonReader/src/JsonReader.cpp b/CesiumJsonReader/src/JsonReader.cpp index 2049d940c..9546eb385 100644 --- a/CesiumJsonReader/src/JsonReader.cpp +++ b/CesiumJsonReader/src/JsonReader.cpp @@ -154,4 +154,15 @@ void JsonReader::FinalJsonHandler::setInputStream( errors.emplace_back(std::move(s)); } } + +void CesiumJsonReader::JsonReader::internalRead( + const rapidjson::Value& jsonValue, + IJsonHandler& handler, + FinalJsonHandler&, + std::vector&, + std::vector&) { + Dispatcher dispatcher{&handler}; + jsonValue.Accept(dispatcher); +} + } // namespace CesiumJsonReader diff --git a/CesiumJsonReader/src/JsonReaderOptions.cpp b/CesiumJsonReader/src/JsonReaderOptions.cpp index ad894ba0c..ead57754d 100644 --- a/CesiumJsonReader/src/JsonReaderOptions.cpp +++ b/CesiumJsonReader/src/JsonReaderOptions.cpp @@ -25,6 +25,8 @@ class AnyExtensionJsonHandler : public JsonObjectJsonHandler, &std::any_cast(value)); } + virtual IJsonHandler& getHandler() override { return *this; } + virtual IJsonHandler* readNull() override { return JsonObjectJsonHandler::readNull(); }; diff --git a/tools/generate-classes/generate.js b/tools/generate-classes/generate.js index cce3b7720..2b4771145 100644 --- a/tools/generate-classes/generate.js +++ b/tools/generate-classes/generate.js @@ -168,45 +168,7 @@ function generate(options, schema, writers) { ${thisConfig.extensionName ? ` virtual void reset(IJsonHandler* pParentHandler, CesiumUtility::ExtensibleObject& o, const std::string_view& extensionName) override; - virtual IJsonHandler* readNull() override { - return ${baseReader}::readNull(); - }; - virtual IJsonHandler* readBool(bool b) override { - return ${baseReader}::readBool(b); - } - virtual IJsonHandler* readInt32(int32_t i) override { - return ${baseReader}::readInt32(i); - } - virtual IJsonHandler* readUint32(uint32_t i) override { - return ${baseReader}::readUint32(i); - } - virtual IJsonHandler* readInt64(int64_t i) override { - return ${baseReader}::readInt64(i); - } - virtual IJsonHandler* readUint64(uint64_t i) override { - return ${baseReader}::readUint64(i); - } - virtual IJsonHandler* readDouble(double d) override { - return ${baseReader}::readDouble(d); - } - virtual IJsonHandler* readString(const std::string_view& str) override { - return ${baseReader}::readString(str); - } - virtual IJsonHandler* readObjectStart() override { - return ${baseReader}::readObjectStart(); - } - virtual IJsonHandler* readObjectEnd() override { - return ${baseReader}::readObjectEnd(); - } - virtual IJsonHandler* readArrayStart() override { - return ${baseReader}::readArrayStart(); - } - virtual IJsonHandler* readArrayEnd() override { - return ${baseReader}::readArrayEnd(); - } - virtual void reportWarning(const std::string& warning, std::vector&& context = std::vector()) override { - ${baseReader}::reportWarning(warning, std::move(context)); - } + virtual IJsonHandler& getHandler() override { return *this; } ` : ""} protected: From b1f2650f555084aaace37f66a72a200720cd2c84 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 23 Aug 2023 18:52:44 +1000 Subject: [PATCH 205/421] Add *Reader classes to CesiumGltfReader and Cesium3DTilesReader. --- CHANGES.md | 1 + .../include/Cesium3DTilesReader/AssetReader.h | 72 + .../Cesium3DTilesReader/AvailabilityReader.h | 72 + .../BoundingVolumeReader.h | 73 + .../Cesium3DTilesReader/BufferReader.h | 72 + .../Cesium3DTilesReader/BufferViewReader.h | 72 + .../Cesium3DTilesReader/ClassPropertyReader.h | 73 + .../include/Cesium3DTilesReader/ClassReader.h | 72 + .../ClassStatisticsReader.h | 73 + .../Cesium3DTilesReader/ContentReader.h | 72 + .../include/Cesium3DTilesReader/EnumReader.h | 72 + .../Cesium3DTilesReader/EnumValueReader.h | 72 + .../Extension3dTilesBoundingVolumeS2Reader.h | 78 + .../Cesium3DTilesReader/GroupMetadataReader.h | 73 + .../ImplicitTilingReader.h | 73 + .../MetadataEntityReader.h | 73 + .../Cesium3DTilesReader/PropertiesReader.h | 72 + .../PropertyStatisticsReader.h | 74 + .../PropertyTablePropertyReader.h | 74 + .../Cesium3DTilesReader/PropertyTableReader.h | 73 + .../Cesium3DTilesReader/SchemaReader.h | 72 + .../Cesium3DTilesReader/StatisticsReader.h | 72 + .../Cesium3DTilesReader/SubtreeReader.h | 72 + .../Cesium3DTilesReader/SubtreesReader.h | 72 + .../include/Cesium3DTilesReader/TileReader.h | 72 + .../Cesium3DTilesReader/TilesetReader.h | 72 + .../generated/src/GeneratedJsonHandlers.cpp | 899 +++++ .../Cesium3DTilesReader/SchemaReader.h | 72 - .../Cesium3DTilesReader/SubtreeReader.h | 73 - .../Cesium3DTilesReader/TilesetReader.h | 72 - Cesium3DTilesReader/src/SchemaReader.cpp | 48 - Cesium3DTilesReader/src/SubtreeReader.cpp | 48 - Cesium3DTilesReader/src/TilesetReader.cpp | 48 - .../test/TestTilesetReader.cpp | 52 +- .../test/TestTilesetWriter.cpp | 11 +- .../include/CesiumGltfReader/AccessorReader.h | 72 + .../AccessorSparseIndicesReader.h | 74 + .../CesiumGltfReader/AccessorSparseReader.h | 73 + .../AccessorSparseValuesReader.h | 74 + .../CesiumGltfReader/AnimationChannelReader.h | 73 + .../AnimationChannelTargetReader.h | 74 + .../CesiumGltfReader/AnimationReader.h | 72 + .../CesiumGltfReader/AnimationSamplerReader.h | 73 + .../include/CesiumGltfReader/AssetReader.h | 72 + .../include/CesiumGltfReader/BufferReader.h | 72 + .../CesiumGltfReader/BufferViewReader.h | 72 + .../CameraOrthographicReader.h | 73 + .../CameraPerspectiveReader.h | 73 + .../include/CesiumGltfReader/CameraReader.h | 72 + .../CesiumGltfReader/ClassPropertyReader.h | 73 + .../include/CesiumGltfReader/ClassReader.h | 72 + .../CesiumGltfReader/ClassStatisticsReader.h | 73 + .../include/CesiumGltfReader/EnumReader.h | 72 + .../CesiumGltfReader/EnumValueReader.h | 72 + ...tensionBufferExtMeshoptCompressionReader.h | 78 + ...ionBufferViewExtMeshoptCompressionReader.h | 78 + .../ExtensionCesiumRTCReader.h | 73 + .../ExtensionCesiumTileEdgesReader.h | 75 + ...ensionExtInstanceFeaturesFeatureIdReader.h | 78 + .../ExtensionExtInstanceFeaturesReader.h | 76 + .../ExtensionExtMeshFeaturesFeatureIdReader.h | 78 + ...ionExtMeshFeaturesFeatureIdTextureReader.h | 78 + .../ExtensionExtMeshFeaturesReader.h | 75 + .../ExtensionExtMeshGpuInstancingReader.h | 76 + ...ExtStructuralMetadataClassPropertyReader.h | 78 + ...xtensionExtStructuralMetadataClassReader.h | 78 + ...ExtensionExtStructuralMetadataEnumReader.h | 78 + ...sionExtStructuralMetadataEnumValueReader.h | 78 + ...lMetadataPropertyAttributePropertyReader.h | 81 + ...tructuralMetadataPropertyAttributeReader.h | 79 + ...turalMetadataPropertyTablePropertyReader.h | 81 + ...ExtStructuralMetadataPropertyTableReader.h | 78 + ...ralMetadataPropertyTexturePropertyReader.h | 81 + ...tStructuralMetadataPropertyTextureReader.h | 78 + ...tensionExtStructuralMetadataSchemaReader.h | 78 + .../ExtensionKhrDracoMeshCompressionReader.h | 76 + .../ExtensionKhrMaterialsUnlitReader.h | 75 + .../ExtensionKhrTextureBasisuReader.h | 75 + .../ExtensionKhrTextureTransformReader.h | 76 + ...ionMeshPrimitiveExtFeatureMetadataReader.h | 78 + ...MeshPrimitiveExtStructuralMetadataReader.h | 78 + ...eKhrMaterialsVariantsMappingsValueReader.h | 81 + ...nMeshPrimitiveKhrMaterialsVariantsReader.h | 78 + .../ExtensionModelExtFeatureMetadataReader.h | 76 + ...xtensionModelExtStructuralMetadataReader.h | 78 + ...ExtensionModelKhrMaterialsVariantsReader.h | 78 + ...sionModelKhrMaterialsVariantsValueReader.h | 78 + .../ExtensionModelMaxarMeshVariantsReader.h | 76 + ...tensionModelMaxarMeshVariantsValueReader.h | 78 + ...NodeMaxarMeshVariantsMappingsValueReader.h | 78 + .../ExtensionNodeMaxarMeshVariantsReader.h | 76 + .../ExtensionTextureWebpReader.h | 74 + .../FeatureIDAttributeReader.h | 73 + .../CesiumGltfReader/FeatureIDTextureReader.h | 73 + .../CesiumGltfReader/FeatureIDsReader.h | 72 + .../FeatureTablePropertyReader.h | 74 + .../CesiumGltfReader/FeatureTableReader.h | 72 + .../CesiumGltfReader/FeatureTextureReader.h | 73 + .../include/CesiumGltfReader/ImageReader.h | 72 + .../MaterialNormalTextureInfoReader.h | 75 + .../MaterialOcclusionTextureInfoReader.h | 76 + .../MaterialPBRMetallicRoughnessReader.h | 76 + .../include/CesiumGltfReader/MaterialReader.h | 72 + .../CesiumGltfReader/MeshPrimitiveReader.h | 73 + .../include/CesiumGltfReader/MeshReader.h | 72 + .../include/CesiumGltfReader/ModelReader.h | 72 + .../include/CesiumGltfReader/NodeReader.h | 72 + .../PropertyStatisticsReader.h | 73 + .../include/CesiumGltfReader/SamplerReader.h | 72 + .../include/CesiumGltfReader/SceneReader.h | 72 + .../include/CesiumGltfReader/SchemaReader.h | 72 + .../include/CesiumGltfReader/SkinReader.h | 72 + .../CesiumGltfReader/StatisticsReader.h | 72 + .../CesiumGltfReader/TextureAccessorReader.h | 73 + .../CesiumGltfReader/TextureInfoReader.h | 72 + .../include/CesiumGltfReader/TextureReader.h | 72 + .../generated/src/GeneratedJsonHandlers.cpp | 3300 ++++++++++++++++- tools/generate-classes/NameFormatters.js | 4 +- tools/generate-classes/generate.js | 177 +- tools/generate-classes/resolveProperty.js | 8 +- 120 files changed, 12271 insertions(+), 429 deletions(-) create mode 100644 Cesium3DTilesReader/generated/include/Cesium3DTilesReader/AssetReader.h create mode 100644 Cesium3DTilesReader/generated/include/Cesium3DTilesReader/AvailabilityReader.h create mode 100644 Cesium3DTilesReader/generated/include/Cesium3DTilesReader/BoundingVolumeReader.h create mode 100644 Cesium3DTilesReader/generated/include/Cesium3DTilesReader/BufferReader.h create mode 100644 Cesium3DTilesReader/generated/include/Cesium3DTilesReader/BufferViewReader.h create mode 100644 Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ClassPropertyReader.h create mode 100644 Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ClassReader.h create mode 100644 Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ClassStatisticsReader.h create mode 100644 Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ContentReader.h create mode 100644 Cesium3DTilesReader/generated/include/Cesium3DTilesReader/EnumReader.h create mode 100644 Cesium3DTilesReader/generated/include/Cesium3DTilesReader/EnumValueReader.h create mode 100644 Cesium3DTilesReader/generated/include/Cesium3DTilesReader/Extension3dTilesBoundingVolumeS2Reader.h create mode 100644 Cesium3DTilesReader/generated/include/Cesium3DTilesReader/GroupMetadataReader.h create mode 100644 Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ImplicitTilingReader.h create mode 100644 Cesium3DTilesReader/generated/include/Cesium3DTilesReader/MetadataEntityReader.h create mode 100644 Cesium3DTilesReader/generated/include/Cesium3DTilesReader/PropertiesReader.h create mode 100644 Cesium3DTilesReader/generated/include/Cesium3DTilesReader/PropertyStatisticsReader.h create mode 100644 Cesium3DTilesReader/generated/include/Cesium3DTilesReader/PropertyTablePropertyReader.h create mode 100644 Cesium3DTilesReader/generated/include/Cesium3DTilesReader/PropertyTableReader.h create mode 100644 Cesium3DTilesReader/generated/include/Cesium3DTilesReader/SchemaReader.h create mode 100644 Cesium3DTilesReader/generated/include/Cesium3DTilesReader/StatisticsReader.h create mode 100644 Cesium3DTilesReader/generated/include/Cesium3DTilesReader/SubtreeReader.h create mode 100644 Cesium3DTilesReader/generated/include/Cesium3DTilesReader/SubtreesReader.h create mode 100644 Cesium3DTilesReader/generated/include/Cesium3DTilesReader/TileReader.h create mode 100644 Cesium3DTilesReader/generated/include/Cesium3DTilesReader/TilesetReader.h delete mode 100644 Cesium3DTilesReader/include/Cesium3DTilesReader/SchemaReader.h delete mode 100644 Cesium3DTilesReader/include/Cesium3DTilesReader/SubtreeReader.h delete mode 100644 Cesium3DTilesReader/include/Cesium3DTilesReader/TilesetReader.h delete mode 100644 Cesium3DTilesReader/src/SchemaReader.cpp delete mode 100644 Cesium3DTilesReader/src/SubtreeReader.cpp delete mode 100644 Cesium3DTilesReader/src/TilesetReader.cpp create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/AccessorReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/AccessorSparseIndicesReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/AccessorSparseReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/AccessorSparseValuesReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/AnimationChannelReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/AnimationChannelTargetReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/AnimationReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/AnimationSamplerReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/AssetReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/BufferReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/BufferViewReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/CameraOrthographicReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/CameraPerspectiveReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/CameraReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ClassPropertyReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ClassReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ClassStatisticsReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/EnumReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/EnumValueReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionBufferExtMeshoptCompressionReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionBufferViewExtMeshoptCompressionReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionCesiumRTCReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionCesiumTileEdgesReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtInstanceFeaturesFeatureIdReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtInstanceFeaturesReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtMeshFeaturesFeatureIdReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtMeshFeaturesFeatureIdTextureReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtMeshFeaturesReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtMeshGpuInstancingReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataClassPropertyReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataClassReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataEnumReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataEnumValueReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyAttributePropertyReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyAttributeReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyTablePropertyReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyTableReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyTexturePropertyReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyTextureReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataSchemaReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionKhrDracoMeshCompressionReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionKhrMaterialsUnlitReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionKhrTextureBasisuReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionKhrTextureTransformReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionMeshPrimitiveExtFeatureMetadataReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionMeshPrimitiveExtStructuralMetadataReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionMeshPrimitiveKhrMaterialsVariantsReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelExtFeatureMetadataReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelExtStructuralMetadataReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelKhrMaterialsVariantsReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelKhrMaterialsVariantsValueReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelMaxarMeshVariantsReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelMaxarMeshVariantsValueReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionNodeMaxarMeshVariantsMappingsValueReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionNodeMaxarMeshVariantsReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionTextureWebpReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/FeatureIDAttributeReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/FeatureIDTextureReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/FeatureIDsReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/FeatureTablePropertyReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/FeatureTableReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/FeatureTextureReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ImageReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/MaterialNormalTextureInfoReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/MaterialOcclusionTextureInfoReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/MaterialPBRMetallicRoughnessReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/MaterialReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/MeshPrimitiveReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/MeshReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/ModelReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/NodeReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/PropertyStatisticsReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/SamplerReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/SceneReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/SchemaReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/SkinReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/StatisticsReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/TextureAccessorReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/TextureInfoReader.h create mode 100644 CesiumGltfReader/generated/include/CesiumGltfReader/TextureReader.h diff --git a/CHANGES.md b/CHANGES.md index 0dd6a1868..eda1b8801 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -12,6 +12,7 @@ - Unknown properties in objects read with a `JsonReader` are now stored in the `unknownProperties` property on `ExtensibleObject` by default. To ignore them, as was done in previous versions, call `setCaptureUnknownProperties` on `JsonReaderOptions`. - Added `ValueType` type alias to `ArrayJsonHandler`, for consistency with other JSON handlers. - Added an overload of `JsonReader::readJson` that takes a `rapidjson::Value` instead of a byte buffer. This allows a subtree of a `rapidjson::Document` to be easily and efficiently converted into statically-typed classes via `IJsonHandler`. +- Added `*Reader` classes to `CesiumGltfReader` and `Cesium3DTilesReader` to allow each of the classes to be individually read from JSON. ### v0.27.0 - 2023-09-01 diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/AssetReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/AssetReader.h new file mode 100644 index 000000000..9abdcd5d5 --- /dev/null +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/AssetReader.h @@ -0,0 +1,72 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace Cesium3DTiles { +struct Asset; +} + +namespace Cesium3DTilesReader { + +/** + * @brief Reads {@link Asset} instances from JSON. + */ +class CESIUM3DTILESREADER_API AssetReader { +public: + /** + * @brief Constructs a new instance. + */ + AssetReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Asset from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Asset from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Asset from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace Cesium3DTilesReader \ No newline at end of file diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/AvailabilityReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/AvailabilityReader.h new file mode 100644 index 000000000..e3cd4845f --- /dev/null +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/AvailabilityReader.h @@ -0,0 +1,72 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace Cesium3DTiles { +struct Availability; +} + +namespace Cesium3DTilesReader { + +/** + * @brief Reads {@link Availability} instances from JSON. + */ +class CESIUM3DTILESREADER_API AvailabilityReader { +public: + /** + * @brief Constructs a new instance. + */ + AvailabilityReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Availability from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Availability from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Availability from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace Cesium3DTilesReader \ No newline at end of file diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/BoundingVolumeReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/BoundingVolumeReader.h new file mode 100644 index 000000000..7de46c70e --- /dev/null +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/BoundingVolumeReader.h @@ -0,0 +1,73 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace Cesium3DTiles { +struct BoundingVolume; +} + +namespace Cesium3DTilesReader { + +/** + * @brief Reads {@link BoundingVolume} instances from JSON. + */ +class CESIUM3DTILESREADER_API BoundingVolumeReader { +public: + /** + * @brief Constructs a new instance. + */ + BoundingVolumeReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of BoundingVolume from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of BoundingVolume from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of BoundingVolume from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace Cesium3DTilesReader \ No newline at end of file diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/BufferReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/BufferReader.h new file mode 100644 index 000000000..7a735cca7 --- /dev/null +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/BufferReader.h @@ -0,0 +1,72 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace Cesium3DTiles { +struct Buffer; +} + +namespace Cesium3DTilesReader { + +/** + * @brief Reads {@link Buffer} instances from JSON. + */ +class CESIUM3DTILESREADER_API BufferReader { +public: + /** + * @brief Constructs a new instance. + */ + BufferReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Buffer from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Buffer from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Buffer from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace Cesium3DTilesReader \ No newline at end of file diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/BufferViewReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/BufferViewReader.h new file mode 100644 index 000000000..3813e1a97 --- /dev/null +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/BufferViewReader.h @@ -0,0 +1,72 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace Cesium3DTiles { +struct BufferView; +} + +namespace Cesium3DTilesReader { + +/** + * @brief Reads {@link BufferView} instances from JSON. + */ +class CESIUM3DTILESREADER_API BufferViewReader { +public: + /** + * @brief Constructs a new instance. + */ + BufferViewReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of BufferView from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of BufferView from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of BufferView from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace Cesium3DTilesReader \ No newline at end of file diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ClassPropertyReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ClassPropertyReader.h new file mode 100644 index 000000000..20d68bfcf --- /dev/null +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ClassPropertyReader.h @@ -0,0 +1,73 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace Cesium3DTiles { +struct ClassProperty; +} + +namespace Cesium3DTilesReader { + +/** + * @brief Reads {@link ClassProperty} instances from JSON. + */ +class CESIUM3DTILESREADER_API ClassPropertyReader { +public: + /** + * @brief Constructs a new instance. + */ + ClassPropertyReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ClassProperty from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ClassProperty from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of ClassProperty from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace Cesium3DTilesReader \ No newline at end of file diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ClassReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ClassReader.h new file mode 100644 index 000000000..c3020b056 --- /dev/null +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ClassReader.h @@ -0,0 +1,72 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace Cesium3DTiles { +struct Class; +} + +namespace Cesium3DTilesReader { + +/** + * @brief Reads {@link Class} instances from JSON. + */ +class CESIUM3DTILESREADER_API ClassReader { +public: + /** + * @brief Constructs a new instance. + */ + ClassReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Class from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Class from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Class from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace Cesium3DTilesReader \ No newline at end of file diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ClassStatisticsReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ClassStatisticsReader.h new file mode 100644 index 000000000..dd11e3a32 --- /dev/null +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ClassStatisticsReader.h @@ -0,0 +1,73 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace Cesium3DTiles { +struct ClassStatistics; +} + +namespace Cesium3DTilesReader { + +/** + * @brief Reads {@link ClassStatistics} instances from JSON. + */ +class CESIUM3DTILESREADER_API ClassStatisticsReader { +public: + /** + * @brief Constructs a new instance. + */ + ClassStatisticsReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ClassStatistics from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ClassStatistics from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of ClassStatistics from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace Cesium3DTilesReader \ No newline at end of file diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ContentReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ContentReader.h new file mode 100644 index 000000000..deddd1921 --- /dev/null +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ContentReader.h @@ -0,0 +1,72 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace Cesium3DTiles { +struct Content; +} + +namespace Cesium3DTilesReader { + +/** + * @brief Reads {@link Content} instances from JSON. + */ +class CESIUM3DTILESREADER_API ContentReader { +public: + /** + * @brief Constructs a new instance. + */ + ContentReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Content from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Content from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Content from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace Cesium3DTilesReader \ No newline at end of file diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/EnumReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/EnumReader.h new file mode 100644 index 000000000..7ea040745 --- /dev/null +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/EnumReader.h @@ -0,0 +1,72 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace Cesium3DTiles { +struct Enum; +} + +namespace Cesium3DTilesReader { + +/** + * @brief Reads {@link Enum} instances from JSON. + */ +class CESIUM3DTILESREADER_API EnumReader { +public: + /** + * @brief Constructs a new instance. + */ + EnumReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Enum from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Enum from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Enum from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace Cesium3DTilesReader \ No newline at end of file diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/EnumValueReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/EnumValueReader.h new file mode 100644 index 000000000..96ef662d0 --- /dev/null +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/EnumValueReader.h @@ -0,0 +1,72 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace Cesium3DTiles { +struct EnumValue; +} + +namespace Cesium3DTilesReader { + +/** + * @brief Reads {@link EnumValue} instances from JSON. + */ +class CESIUM3DTILESREADER_API EnumValueReader { +public: + /** + * @brief Constructs a new instance. + */ + EnumValueReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of EnumValue from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of EnumValue from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of EnumValue from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace Cesium3DTilesReader \ No newline at end of file diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/Extension3dTilesBoundingVolumeS2Reader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/Extension3dTilesBoundingVolumeS2Reader.h new file mode 100644 index 000000000..e5dcb3560 --- /dev/null +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/Extension3dTilesBoundingVolumeS2Reader.h @@ -0,0 +1,78 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace Cesium3DTiles { +struct Extension3dTilesBoundingVolumeS2; +} + +namespace Cesium3DTilesReader { + +/** + * @brief Reads {@link Extension3dTilesBoundingVolumeS2} instances from JSON. + */ +class CESIUM3DTILESREADER_API Extension3dTilesBoundingVolumeS2Reader { +public: + /** + * @brief Constructs a new instance. + */ + Extension3dTilesBoundingVolumeS2Reader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Extension3dTilesBoundingVolumeS2 from a byte + * buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + Cesium3DTiles::Extension3dTilesBoundingVolumeS2> + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Extension3dTilesBoundingVolumeS2 from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + Cesium3DTiles::Extension3dTilesBoundingVolumeS2> + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Extension3dTilesBoundingVolumeS2 from + * a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace Cesium3DTilesReader \ No newline at end of file diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/GroupMetadataReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/GroupMetadataReader.h new file mode 100644 index 000000000..60163eb62 --- /dev/null +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/GroupMetadataReader.h @@ -0,0 +1,73 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace Cesium3DTiles { +struct GroupMetadata; +} + +namespace Cesium3DTilesReader { + +/** + * @brief Reads {@link GroupMetadata} instances from JSON. + */ +class CESIUM3DTILESREADER_API GroupMetadataReader { +public: + /** + * @brief Constructs a new instance. + */ + GroupMetadataReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of GroupMetadata from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of GroupMetadata from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of GroupMetadata from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace Cesium3DTilesReader \ No newline at end of file diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ImplicitTilingReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ImplicitTilingReader.h new file mode 100644 index 000000000..416c6442e --- /dev/null +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/ImplicitTilingReader.h @@ -0,0 +1,73 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace Cesium3DTiles { +struct ImplicitTiling; +} + +namespace Cesium3DTilesReader { + +/** + * @brief Reads {@link ImplicitTiling} instances from JSON. + */ +class CESIUM3DTILESREADER_API ImplicitTilingReader { +public: + /** + * @brief Constructs a new instance. + */ + ImplicitTilingReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ImplicitTiling from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ImplicitTiling from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of ImplicitTiling from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace Cesium3DTilesReader \ No newline at end of file diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/MetadataEntityReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/MetadataEntityReader.h new file mode 100644 index 000000000..d203c2795 --- /dev/null +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/MetadataEntityReader.h @@ -0,0 +1,73 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace Cesium3DTiles { +struct MetadataEntity; +} + +namespace Cesium3DTilesReader { + +/** + * @brief Reads {@link MetadataEntity} instances from JSON. + */ +class CESIUM3DTILESREADER_API MetadataEntityReader { +public: + /** + * @brief Constructs a new instance. + */ + MetadataEntityReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of MetadataEntity from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of MetadataEntity from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of MetadataEntity from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace Cesium3DTilesReader \ No newline at end of file diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/PropertiesReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/PropertiesReader.h new file mode 100644 index 000000000..7e60dadc3 --- /dev/null +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/PropertiesReader.h @@ -0,0 +1,72 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace Cesium3DTiles { +struct Properties; +} + +namespace Cesium3DTilesReader { + +/** + * @brief Reads {@link Properties} instances from JSON. + */ +class CESIUM3DTILESREADER_API PropertiesReader { +public: + /** + * @brief Constructs a new instance. + */ + PropertiesReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Properties from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Properties from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Properties from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace Cesium3DTilesReader \ No newline at end of file diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/PropertyStatisticsReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/PropertyStatisticsReader.h new file mode 100644 index 000000000..dd1fc3737 --- /dev/null +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/PropertyStatisticsReader.h @@ -0,0 +1,74 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace Cesium3DTiles { +struct PropertyStatistics; +} + +namespace Cesium3DTilesReader { + +/** + * @brief Reads {@link PropertyStatistics} instances from JSON. + */ +class CESIUM3DTILESREADER_API PropertyStatisticsReader { +public: + /** + * @brief Constructs a new instance. + */ + PropertyStatisticsReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of PropertyStatistics from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of PropertyStatistics from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of PropertyStatistics from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace Cesium3DTilesReader \ No newline at end of file diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/PropertyTablePropertyReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/PropertyTablePropertyReader.h new file mode 100644 index 000000000..c67ed85d6 --- /dev/null +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/PropertyTablePropertyReader.h @@ -0,0 +1,74 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace Cesium3DTiles { +struct PropertyTableProperty; +} + +namespace Cesium3DTilesReader { + +/** + * @brief Reads {@link PropertyTableProperty} instances from JSON. + */ +class CESIUM3DTILESREADER_API PropertyTablePropertyReader { +public: + /** + * @brief Constructs a new instance. + */ + PropertyTablePropertyReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of PropertyTableProperty from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of PropertyTableProperty from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of PropertyTableProperty from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace Cesium3DTilesReader \ No newline at end of file diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/PropertyTableReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/PropertyTableReader.h new file mode 100644 index 000000000..13ee8ab76 --- /dev/null +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/PropertyTableReader.h @@ -0,0 +1,73 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace Cesium3DTiles { +struct PropertyTable; +} + +namespace Cesium3DTilesReader { + +/** + * @brief Reads {@link PropertyTable} instances from JSON. + */ +class CESIUM3DTILESREADER_API PropertyTableReader { +public: + /** + * @brief Constructs a new instance. + */ + PropertyTableReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of PropertyTable from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of PropertyTable from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of PropertyTable from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace Cesium3DTilesReader \ No newline at end of file diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/SchemaReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/SchemaReader.h new file mode 100644 index 000000000..b99ae26c2 --- /dev/null +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/SchemaReader.h @@ -0,0 +1,72 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace Cesium3DTiles { +struct Schema; +} + +namespace Cesium3DTilesReader { + +/** + * @brief Reads {@link Schema} instances from JSON. + */ +class CESIUM3DTILESREADER_API SchemaReader { +public: + /** + * @brief Constructs a new instance. + */ + SchemaReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Schema from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Schema from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Schema from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace Cesium3DTilesReader \ No newline at end of file diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/StatisticsReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/StatisticsReader.h new file mode 100644 index 000000000..808b52f03 --- /dev/null +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/StatisticsReader.h @@ -0,0 +1,72 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace Cesium3DTiles { +struct Statistics; +} + +namespace Cesium3DTilesReader { + +/** + * @brief Reads {@link Statistics} instances from JSON. + */ +class CESIUM3DTILESREADER_API StatisticsReader { +public: + /** + * @brief Constructs a new instance. + */ + StatisticsReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Statistics from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Statistics from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Statistics from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace Cesium3DTilesReader \ No newline at end of file diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/SubtreeReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/SubtreeReader.h new file mode 100644 index 000000000..4d3d5c808 --- /dev/null +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/SubtreeReader.h @@ -0,0 +1,72 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace Cesium3DTiles { +struct Subtree; +} + +namespace Cesium3DTilesReader { + +/** + * @brief Reads {@link Subtree} instances from JSON. + */ +class CESIUM3DTILESREADER_API SubtreeReader { +public: + /** + * @brief Constructs a new instance. + */ + SubtreeReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Subtree from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Subtree from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Subtree from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace Cesium3DTilesReader \ No newline at end of file diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/SubtreesReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/SubtreesReader.h new file mode 100644 index 000000000..21a2bda57 --- /dev/null +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/SubtreesReader.h @@ -0,0 +1,72 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace Cesium3DTiles { +struct Subtrees; +} + +namespace Cesium3DTilesReader { + +/** + * @brief Reads {@link Subtrees} instances from JSON. + */ +class CESIUM3DTILESREADER_API SubtreesReader { +public: + /** + * @brief Constructs a new instance. + */ + SubtreesReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Subtrees from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Subtrees from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Subtrees from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace Cesium3DTilesReader \ No newline at end of file diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/TileReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/TileReader.h new file mode 100644 index 000000000..96b083a27 --- /dev/null +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/TileReader.h @@ -0,0 +1,72 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace Cesium3DTiles { +struct Tile; +} + +namespace Cesium3DTilesReader { + +/** + * @brief Reads {@link Tile} instances from JSON. + */ +class CESIUM3DTILESREADER_API TileReader { +public: + /** + * @brief Constructs a new instance. + */ + TileReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Tile from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Tile from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Tile from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace Cesium3DTilesReader \ No newline at end of file diff --git a/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/TilesetReader.h b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/TilesetReader.h new file mode 100644 index 000000000..aa066c793 --- /dev/null +++ b/Cesium3DTilesReader/generated/include/Cesium3DTilesReader/TilesetReader.h @@ -0,0 +1,72 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace Cesium3DTiles { +struct Tileset; +} + +namespace Cesium3DTilesReader { + +/** + * @brief Reads {@link Tileset} instances from JSON. + */ +class CESIUM3DTILESREADER_API TilesetReader { +public: + /** + * @brief Constructs a new instance. + */ + TilesetReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Tileset from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Tileset from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Tileset from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace Cesium3DTilesReader \ No newline at end of file diff --git a/Cesium3DTilesReader/generated/src/GeneratedJsonHandlers.cpp b/Cesium3DTilesReader/generated/src/GeneratedJsonHandlers.cpp index 9fec23d8c..f4c59b3f5 100644 --- a/Cesium3DTilesReader/generated/src/GeneratedJsonHandlers.cpp +++ b/Cesium3DTilesReader/generated/src/GeneratedJsonHandlers.cpp @@ -1,8 +1,12 @@ // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "Extension3dTilesBoundingVolumeS2JsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -65,12 +69,58 @@ CesiumJsonReader::IJsonHandler* Extension3dTilesBoundingVolumeS2JsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +Extension3dTilesBoundingVolumeS2Reader:: + Extension3dTilesBoundingVolumeS2Reader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +Extension3dTilesBoundingVolumeS2Reader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +Extension3dTilesBoundingVolumeS2Reader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult< + Cesium3DTiles::Extension3dTilesBoundingVolumeS2> +Extension3dTilesBoundingVolumeS2Reader::readFromJson( + const gsl::span& data) const { + Extension3dTilesBoundingVolumeS2JsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult< + Cesium3DTiles::Extension3dTilesBoundingVolumeS2> +Extension3dTilesBoundingVolumeS2Reader::readFromJson( + const rapidjson::Value& value) const { + Extension3dTilesBoundingVolumeS2JsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +Extension3dTilesBoundingVolumeS2Reader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + Cesium3DTiles::Extension3dTilesBoundingVolumeS2, + Extension3dTilesBoundingVolumeS2JsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace Cesium3DTilesReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "StatisticsJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -110,12 +160,47 @@ CesiumJsonReader::IJsonHandler* StatisticsJsonHandler::readObjectKeyStatistics( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +StatisticsReader::StatisticsReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& StatisticsReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +StatisticsReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +StatisticsReader::readFromJson(const gsl::span& data) const { + StatisticsJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +StatisticsReader::readFromJson(const rapidjson::Value& value) const { + StatisticsJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +StatisticsReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader:: + ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace Cesium3DTilesReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ClassStatisticsJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -159,12 +244,51 @@ ClassStatisticsJsonHandler::readObjectKeyClassStatistics( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ClassStatisticsReader::ClassStatisticsReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& ClassStatisticsReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ClassStatisticsReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +ClassStatisticsReader::readFromJson( + const gsl::span& data) const { + ClassStatisticsJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +ClassStatisticsReader::readFromJson(const rapidjson::Value& value) const { + ClassStatisticsJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +ClassStatisticsReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + Cesium3DTiles::ClassStatistics, + ClassStatisticsJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace Cesium3DTilesReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "PropertyStatisticsJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -229,12 +353,52 @@ PropertyStatisticsJsonHandler::readObjectKeyPropertyStatistics( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +PropertyStatisticsReader::PropertyStatisticsReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& PropertyStatisticsReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +PropertyStatisticsReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +PropertyStatisticsReader::readFromJson( + const gsl::span& data) const { + PropertyStatisticsJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +PropertyStatisticsReader::readFromJson(const rapidjson::Value& value) const { + PropertyStatisticsJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +PropertyStatisticsReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + Cesium3DTiles::PropertyStatistics, + PropertyStatisticsJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace Cesium3DTilesReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "SchemaJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -289,12 +453,45 @@ CesiumJsonReader::IJsonHandler* SchemaJsonHandler::readObjectKeySchema( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +SchemaReader::SchemaReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& SchemaReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& SchemaReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +SchemaReader::readFromJson(const gsl::span& data) const { + SchemaJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +SchemaReader::readFromJson(const rapidjson::Value& value) const { + SchemaJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +SchemaReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace Cesium3DTilesReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "EnumJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -343,12 +540,45 @@ CesiumJsonReader::IJsonHandler* EnumJsonHandler::readObjectKeyEnum( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +EnumReader::EnumReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& EnumReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& EnumReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +EnumReader::readFromJson(const gsl::span& data) const { + EnumJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +EnumReader::readFromJson(const rapidjson::Value& value) const { + EnumJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +EnumReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace Cesium3DTilesReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "EnumValueJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -394,12 +624,46 @@ CesiumJsonReader::IJsonHandler* EnumValueJsonHandler::readObjectKeyEnumValue( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +EnumValueReader::EnumValueReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& EnumValueReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& EnumValueReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +EnumValueReader::readFromJson(const gsl::span& data) const { + EnumValueJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +EnumValueReader::readFromJson(const rapidjson::Value& value) const { + EnumValueJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +EnumValueReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader:: + ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace Cesium3DTilesReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ClassJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -445,12 +709,45 @@ CesiumJsonReader::IJsonHandler* ClassJsonHandler::readObjectKeyClass( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ClassReader::ClassReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& ClassReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& ClassReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +ClassReader::readFromJson(const gsl::span& data) const { + ClassJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +ClassReader::readFromJson(const rapidjson::Value& value) const { + ClassJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +ClassReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace Cesium3DTilesReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ClassPropertyJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -536,12 +833,50 @@ ClassPropertyJsonHandler::readObjectKeyClassProperty( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ClassPropertyReader::ClassPropertyReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& ClassPropertyReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ClassPropertyReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +ClassPropertyReader::readFromJson( + const gsl::span& data) const { + ClassPropertyJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +ClassPropertyReader::readFromJson(const rapidjson::Value& value) const { + ClassPropertyJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +ClassPropertyReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader:: + ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace Cesium3DTilesReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "SubtreeJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -620,12 +955,45 @@ CesiumJsonReader::IJsonHandler* SubtreeJsonHandler::readObjectKeySubtree( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +SubtreeReader::SubtreeReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& SubtreeReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& SubtreeReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +SubtreeReader::readFromJson(const gsl::span& data) const { + SubtreeJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +SubtreeReader::readFromJson(const rapidjson::Value& value) const { + SubtreeJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +SubtreeReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace Cesium3DTilesReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "MetadataEntityJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -669,12 +1037,50 @@ MetadataEntityJsonHandler::readObjectKeyMetadataEntity( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +MetadataEntityReader::MetadataEntityReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& MetadataEntityReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +MetadataEntityReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +MetadataEntityReader::readFromJson( + const gsl::span& data) const { + MetadataEntityJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +MetadataEntityReader::readFromJson(const rapidjson::Value& value) const { + MetadataEntityJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +MetadataEntityReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader:: + ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace Cesium3DTilesReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "AvailabilityJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -721,12 +1127,47 @@ AvailabilityJsonHandler::readObjectKeyAvailability( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +AvailabilityReader::AvailabilityReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& AvailabilityReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +AvailabilityReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +AvailabilityReader::readFromJson(const gsl::span& data) const { + AvailabilityJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +AvailabilityReader::readFromJson(const rapidjson::Value& value) const { + AvailabilityJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +AvailabilityReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader:: + ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace Cesium3DTilesReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "PropertyTableJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -776,12 +1217,50 @@ PropertyTableJsonHandler::readObjectKeyPropertyTable( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +PropertyTableReader::PropertyTableReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& PropertyTableReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +PropertyTableReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +PropertyTableReader::readFromJson( + const gsl::span& data) const { + PropertyTableJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +PropertyTableReader::readFromJson(const rapidjson::Value& value) const { + PropertyTableJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +PropertyTableReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader:: + ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace Cesium3DTilesReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "PropertyTablePropertyJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -852,12 +1331,53 @@ PropertyTablePropertyJsonHandler::readObjectKeyPropertyTableProperty( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +PropertyTablePropertyReader::PropertyTablePropertyReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& PropertyTablePropertyReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +PropertyTablePropertyReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +PropertyTablePropertyReader::readFromJson( + const gsl::span& data) const { + PropertyTablePropertyJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +PropertyTablePropertyReader::readFromJson(const rapidjson::Value& value) const { + PropertyTablePropertyJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +PropertyTablePropertyReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + Cesium3DTiles::PropertyTableProperty, + PropertyTablePropertyJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace Cesium3DTilesReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "BufferViewJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -906,12 +1426,47 @@ CesiumJsonReader::IJsonHandler* BufferViewJsonHandler::readObjectKeyBufferView( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +BufferViewReader::BufferViewReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& BufferViewReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +BufferViewReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +BufferViewReader::readFromJson(const gsl::span& data) const { + BufferViewJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +BufferViewReader::readFromJson(const rapidjson::Value& value) const { + BufferViewJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +BufferViewReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader:: + ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace Cesium3DTilesReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "BufferJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -957,12 +1512,45 @@ CesiumJsonReader::IJsonHandler* BufferJsonHandler::readObjectKeyBuffer( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +BufferReader::BufferReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& BufferReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& BufferReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +BufferReader::readFromJson(const gsl::span& data) const { + BufferJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +BufferReader::readFromJson(const rapidjson::Value& value) const { + BufferJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +BufferReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace Cesium3DTilesReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "TilesetJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -1035,12 +1623,45 @@ CesiumJsonReader::IJsonHandler* TilesetJsonHandler::readObjectKeyTileset( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +TilesetReader::TilesetReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& TilesetReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& TilesetReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +TilesetReader::readFromJson(const gsl::span& data) const { + TilesetJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +TilesetReader::readFromJson(const rapidjson::Value& value) const { + TilesetJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +TilesetReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace Cesium3DTilesReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "TileJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -1110,12 +1731,45 @@ CesiumJsonReader::IJsonHandler* TileJsonHandler::readObjectKeyTile( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +TileReader::TileReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& TileReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& TileReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +TileReader::readFromJson(const gsl::span& data) const { + TileJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +TileReader::readFromJson(const rapidjson::Value& value) const { + TileJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +TileReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace Cesium3DTilesReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ImplicitTilingJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -1171,12 +1825,50 @@ ImplicitTilingJsonHandler::readObjectKeyImplicitTiling( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ImplicitTilingReader::ImplicitTilingReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& ImplicitTilingReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ImplicitTilingReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +ImplicitTilingReader::readFromJson( + const gsl::span& data) const { + ImplicitTilingJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +ImplicitTilingReader::readFromJson(const rapidjson::Value& value) const { + ImplicitTilingJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +ImplicitTilingReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader:: + ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace Cesium3DTilesReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "SubtreesJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -1215,12 +1907,46 @@ CesiumJsonReader::IJsonHandler* SubtreesJsonHandler::readObjectKeySubtrees( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +SubtreesReader::SubtreesReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& SubtreesReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& SubtreesReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +SubtreesReader::readFromJson(const gsl::span& data) const { + SubtreesJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +SubtreesReader::readFromJson(const rapidjson::Value& value) const { + SubtreesJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +SubtreesReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader:: + ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace Cesium3DTilesReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ContentJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -1269,12 +1995,45 @@ CesiumJsonReader::IJsonHandler* ContentJsonHandler::readObjectKeyContent( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ContentReader::ContentReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& ContentReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& ContentReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +ContentReader::readFromJson(const gsl::span& data) const { + ContentJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +ContentReader::readFromJson(const rapidjson::Value& value) const { + ContentJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +ContentReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace Cesium3DTilesReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "BoundingVolumeJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -1321,12 +2080,50 @@ BoundingVolumeJsonHandler::readObjectKeyBoundingVolume( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +BoundingVolumeReader::BoundingVolumeReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& BoundingVolumeReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +BoundingVolumeReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +BoundingVolumeReader::readFromJson( + const gsl::span& data) const { + BoundingVolumeJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +BoundingVolumeReader::readFromJson(const rapidjson::Value& value) const { + BoundingVolumeJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +BoundingVolumeReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader:: + ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace Cesium3DTilesReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "GroupMetadataJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -1365,12 +2162,50 @@ GroupMetadataJsonHandler::readObjectKeyGroupMetadata( return this->readObjectKeyMetadataEntity(objectType, str, *this->_pObject); } +GroupMetadataReader::GroupMetadataReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& GroupMetadataReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +GroupMetadataReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +GroupMetadataReader::readFromJson( + const gsl::span& data) const { + GroupMetadataJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +GroupMetadataReader::readFromJson(const rapidjson::Value& value) const { + GroupMetadataJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +GroupMetadataReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader:: + ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace Cesium3DTilesReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "PropertiesJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -1413,12 +2248,47 @@ CesiumJsonReader::IJsonHandler* PropertiesJsonHandler::readObjectKeyProperties( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +PropertiesReader::PropertiesReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& PropertiesReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +PropertiesReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +PropertiesReader::readFromJson(const gsl::span& data) const { + PropertiesJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +PropertiesReader::readFromJson(const rapidjson::Value& value) const { + PropertiesJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +PropertiesReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader:: + ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace Cesium3DTilesReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "AssetJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -1461,4 +2331,33 @@ CesiumJsonReader::IJsonHandler* AssetJsonHandler::readObjectKeyAsset( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +AssetReader::AssetReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& AssetReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& AssetReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +AssetReader::readFromJson(const gsl::span& data) const { + AssetJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +AssetReader::readFromJson(const rapidjson::Value& value) const { + AssetJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +AssetReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace Cesium3DTilesReader diff --git a/Cesium3DTilesReader/include/Cesium3DTilesReader/SchemaReader.h b/Cesium3DTilesReader/include/Cesium3DTilesReader/SchemaReader.h deleted file mode 100644 index 1dd433aa1..000000000 --- a/Cesium3DTilesReader/include/Cesium3DTilesReader/SchemaReader.h +++ /dev/null @@ -1,72 +0,0 @@ -#pragma once - -#include "Cesium3DTilesReader/Library.h" - -#include -#include - -#include - -#include -#include -#include -#include -#include -#include - -namespace Cesium3DTilesReader { - -/** - * @brief The result of reading a schema with - * {@link SchemaReader::readSchema}. - */ -struct CESIUM3DTILESREADER_API SchemaReaderResult { - /** - * @brief The read schema, or std::nullopt if the schema could not be read. - */ - std::optional schema; - - /** - * @brief Errors, if any, that occurred during the load process. - */ - std::vector errors; - - /** - * @brief Warnings, if any, that occurred during the load process. - */ - std::vector warnings; -}; - -/** - * @brief Reads schemas. - */ -class CESIUM3DTILESREADER_API SchemaReader { -public: - /** - * @brief Constructs a new instance. - */ - SchemaReader(); - - /** - * @brief Gets the options controlling how the JSON is read. - */ - CesiumJsonReader::JsonReaderOptions& getOptions(); - - /** - * @brief Gets the options controlling how the JSON is read. - */ - const CesiumJsonReader::JsonReaderOptions& getOptions() const; - - /** - * @brief Reads a schema. - * - * @param data The buffer from which to read the schema. - * @return The result of reading the schame. - */ - SchemaReaderResult readSchema(const gsl::span& data) const; - -private: - CesiumJsonReader::JsonReaderOptions _context; -}; - -} // namespace Cesium3DTilesReader diff --git a/Cesium3DTilesReader/include/Cesium3DTilesReader/SubtreeReader.h b/Cesium3DTilesReader/include/Cesium3DTilesReader/SubtreeReader.h deleted file mode 100644 index 05f3c35bd..000000000 --- a/Cesium3DTilesReader/include/Cesium3DTilesReader/SubtreeReader.h +++ /dev/null @@ -1,73 +0,0 @@ -#pragma once - -#include "Cesium3DTilesReader/Library.h" - -#include -#include - -#include - -#include -#include -#include -#include -#include -#include - -namespace Cesium3DTilesReader { - -/** - * @brief The result of reading a subtree with - * {@link SubtreeReader::readSubtree}. - */ -struct CESIUM3DTILESREADER_API SubtreeReaderResult { - /** - * @brief The read subtree, or std::nullopt if the subtree could not be read. - */ - std::optional subtree; - - /** - * @brief Errors, if any, that occurred during the load process. - */ - std::vector errors; - - /** - * @brief Warnings, if any, that occurred during the load process. - */ - std::vector warnings; -}; - -/** - * @brief Reads subtrees. - */ -class CESIUM3DTILESREADER_API SubtreeReader { -public: - /** - * @brief Constructs a new instance. - */ - SubtreeReader(); - - /** - * @brief Gets the options controlling how the JSON is read. - */ - CesiumJsonReader::JsonReaderOptions& getOptions(); - - /** - * @brief Gets the options controlling how the JSON is read. - */ - const CesiumJsonReader::JsonReaderOptions& getOptions() const; - - /** - * @brief Reads a subtree. - * - * @param data The buffer from which to read the subtree. - * @param options Options for how to read the subtree. - * @return The result of reading the subtree. - */ - SubtreeReaderResult readSubtree(const gsl::span& data) const; - -private: - CesiumJsonReader::JsonReaderOptions _context; -}; - -} // namespace Cesium3DTilesReader diff --git a/Cesium3DTilesReader/include/Cesium3DTilesReader/TilesetReader.h b/Cesium3DTilesReader/include/Cesium3DTilesReader/TilesetReader.h deleted file mode 100644 index ba2741cd7..000000000 --- a/Cesium3DTilesReader/include/Cesium3DTilesReader/TilesetReader.h +++ /dev/null @@ -1,72 +0,0 @@ -#pragma once - -#include "Cesium3DTilesReader/Library.h" - -#include -#include - -#include - -#include -#include -#include -#include -#include -#include - -namespace Cesium3DTilesReader { - -/** - * @brief The result of reading a tileset with - * {@link TilesetReader::readTileset}. - */ -struct CESIUM3DTILESREADER_API TilesetReaderResult { - /** - * @brief The read tileset, or std::nullopt if the tileset could not be read. - */ - std::optional tileset; - - /** - * @brief Errors, if any, that occurred during the load process. - */ - std::vector errors; - - /** - * @brief Warnings, if any, that occurred during the load process. - */ - std::vector warnings; -}; - -/** - * @brief Reads tilesets. - */ -class CESIUM3DTILESREADER_API TilesetReader { -public: - /** - * @brief Constructs a new instance. - */ - TilesetReader(); - - /** - * @brief Gets the options controlling how the JSON is read. - */ - CesiumJsonReader::JsonReaderOptions& getOptions(); - - /** - * @brief Gets the options controlling how the JSON is read. - */ - const CesiumJsonReader::JsonReaderOptions& getOptions() const; - - /** - * @brief Reads a tileset. - * - * @param data The buffer from which to read the tileset. - * @return The result of reading the tileset. - */ - TilesetReaderResult readTileset(const gsl::span& data) const; - -private: - CesiumJsonReader::JsonReaderOptions _context; -}; - -} // namespace Cesium3DTilesReader diff --git a/Cesium3DTilesReader/src/SchemaReader.cpp b/Cesium3DTilesReader/src/SchemaReader.cpp deleted file mode 100644 index 6d76f9f95..000000000 --- a/Cesium3DTilesReader/src/SchemaReader.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include "Cesium3DTilesReader/SchemaReader.h" - -#include "SchemaJsonHandler.h" -#include "registerExtensions.h" - -#include -#include - -namespace Cesium3DTilesReader { - -namespace { - -SchemaReaderResult readSchemaJson( - const CesiumJsonReader::JsonReaderOptions& context, - const gsl::span& data) { - - CESIUM_TRACE("Cesium3DTilesReader::SchemaReader::readSchemaJson"); - - SchemaJsonHandler schemaHandler(context); - CesiumJsonReader::ReadJsonResult jsonResult = - CesiumJsonReader::JsonReader::readJson(data, schemaHandler); - - return SchemaReaderResult{ - std::move(jsonResult.value), - std::move(jsonResult.errors), - std::move(jsonResult.warnings)}; -} - -} // namespace - -SchemaReader::SchemaReader() { registerExtensions(this->_context); } - -CesiumJsonReader::JsonReaderOptions& SchemaReader::getOptions() { - return this->_context; -} - -const CesiumJsonReader::JsonReaderOptions& SchemaReader::getOptions() const { - return this->_context; -} - -SchemaReaderResult -SchemaReader::readSchema(const gsl::span& data) const { - const CesiumJsonReader::JsonReaderOptions& context = this->getOptions(); - SchemaReaderResult result = readSchemaJson(context, data); - - return result; -} -} // namespace Cesium3DTilesReader diff --git a/Cesium3DTilesReader/src/SubtreeReader.cpp b/Cesium3DTilesReader/src/SubtreeReader.cpp deleted file mode 100644 index c121abf22..000000000 --- a/Cesium3DTilesReader/src/SubtreeReader.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include "Cesium3DTilesReader/SubtreeReader.h" - -#include "SubtreeJsonHandler.h" -#include "registerExtensions.h" - -#include -#include - -namespace Cesium3DTilesReader { - -namespace { - -SubtreeReaderResult readSubtreeJson( - const CesiumJsonReader::JsonReaderOptions& context, - const gsl::span& data) { - - CESIUM_TRACE("Cesium3DTilesReader::SubtreeReader::readSubtreeJson"); - - SubtreeJsonHandler subtreeHandler(context); - CesiumJsonReader::ReadJsonResult jsonResult = - CesiumJsonReader::JsonReader::readJson(data, subtreeHandler); - - return SubtreeReaderResult{ - std::move(jsonResult.value), - std::move(jsonResult.errors), - std::move(jsonResult.warnings)}; -} - -} // namespace - -SubtreeReader::SubtreeReader() { registerExtensions(this->_context); } - -CesiumJsonReader::JsonReaderOptions& SubtreeReader::getOptions() { - return this->_context; -} - -const CesiumJsonReader::JsonReaderOptions& SubtreeReader::getOptions() const { - return this->_context; -} - -SubtreeReaderResult -SubtreeReader::readSubtree(const gsl::span& data) const { - const CesiumJsonReader::JsonReaderOptions& context = this->getOptions(); - SubtreeReaderResult result = readSubtreeJson(context, data); - - return result; -} -} // namespace Cesium3DTilesReader diff --git a/Cesium3DTilesReader/src/TilesetReader.cpp b/Cesium3DTilesReader/src/TilesetReader.cpp deleted file mode 100644 index ba6ab2942..000000000 --- a/Cesium3DTilesReader/src/TilesetReader.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include "Cesium3DTilesReader/TilesetReader.h" - -#include "TilesetJsonHandler.h" -#include "registerExtensions.h" - -#include -#include - -namespace Cesium3DTilesReader { - -namespace { - -TilesetReaderResult readTilesetJson( - const CesiumJsonReader::JsonReaderOptions& context, - const gsl::span& data) { - - CESIUM_TRACE("Cesium3DTilesReader::TilesetReader::readTilesetJson"); - - TilesetJsonHandler tilesetHandler(context); - CesiumJsonReader::ReadJsonResult jsonResult = - CesiumJsonReader::JsonReader::readJson(data, tilesetHandler); - - return TilesetReaderResult{ - std::move(jsonResult.value), - std::move(jsonResult.errors), - std::move(jsonResult.warnings)}; -} - -} // namespace - -TilesetReader::TilesetReader() { registerExtensions(this->_context); } - -CesiumJsonReader::JsonReaderOptions& TilesetReader::getOptions() { - return this->_context; -} - -const CesiumJsonReader::JsonReaderOptions& TilesetReader::getOptions() const { - return this->_context; -} - -TilesetReaderResult -TilesetReader::readTileset(const gsl::span& data) const { - const CesiumJsonReader::JsonReaderOptions& context = this->getOptions(); - TilesetReaderResult result = readTilesetJson(context, data); - - return result; -} -} // namespace Cesium3DTilesReader diff --git a/Cesium3DTilesReader/test/TestTilesetReader.cpp b/Cesium3DTilesReader/test/TestTilesetReader.cpp index 83a9c7acf..aca84cdd1 100644 --- a/Cesium3DTilesReader/test/TestTilesetReader.cpp +++ b/Cesium3DTilesReader/test/TestTilesetReader.cpp @@ -1,6 +1,5 @@ -#include "Cesium3DTilesReader/TilesetReader.h" - #include +#include #include #include @@ -34,10 +33,10 @@ TEST_CASE("Reads tileset JSON") { tilesetFile /= "tileset.json"; std::vector data = readFile(tilesetFile); Cesium3DTilesReader::TilesetReader reader; - Cesium3DTilesReader::TilesetReaderResult result = reader.readTileset(data); - REQUIRE(result.tileset); + auto result = reader.readFromJson(data); + REQUIRE(result.value); - const Cesium3DTiles::Tileset& tileset = result.tileset.value(); + const Cesium3DTiles::Tileset& tileset = result.value.value(); REQUIRE(tileset.asset.version == "1.0"); REQUIRE(tileset.geometricError == 494.50961650991815); @@ -146,13 +145,13 @@ TEST_CASE("Reads extras") { )"; Cesium3DTilesReader::TilesetReader reader; - Cesium3DTilesReader::TilesetReaderResult result = reader.readTileset( + auto result = reader.readFromJson( gsl::span(reinterpret_cast(s.c_str()), s.size())); REQUIRE(result.errors.empty()); REQUIRE(result.warnings.empty()); - REQUIRE(result.tileset.has_value()); + REQUIRE(result.value.has_value()); - Cesium3DTiles::Tileset& tileset = result.tileset.value(); + Cesium3DTiles::Tileset& tileset = result.value.value(); auto ait = tileset.extras.find("A"); REQUIRE(ait != tileset.extras.end()); @@ -224,13 +223,13 @@ TEST_CASE("Reads 3DTILES_content_gltf") { )"; Cesium3DTilesReader::TilesetReader reader; - Cesium3DTilesReader::TilesetReaderResult result = reader.readTileset( + auto result = reader.readFromJson( gsl::span(reinterpret_cast(s.c_str()), s.size())); REQUIRE(result.errors.empty()); REQUIRE(result.warnings.empty()); - REQUIRE(result.tileset.has_value()); + REQUIRE(result.value.has_value()); - Cesium3DTiles::Tileset& tileset = result.tileset.value(); + Cesium3DTiles::Tileset& tileset = result.value.value(); CHECK(tileset.asset.version == "1.0"); const std::vector tilesetExtensionUsed{ @@ -274,17 +273,15 @@ TEST_CASE("Reads custom extension") { )"; Cesium3DTilesReader::TilesetReader reader; - Cesium3DTilesReader::TilesetReaderResult withCustomExt = reader.readTileset( + auto withCustomExt = reader.readFromJson( gsl::span(reinterpret_cast(s.c_str()), s.size())); REQUIRE(withCustomExt.errors.empty()); - REQUIRE(withCustomExt.tileset.has_value()); + REQUIRE(withCustomExt.value.has_value()); - REQUIRE(withCustomExt.tileset->extensions.size() == 2); + REQUIRE(withCustomExt.value->extensions.size() == 2); - CesiumUtility::JsonValue* pA = - withCustomExt.tileset->getGenericExtension("A"); - CesiumUtility::JsonValue* pB = - withCustomExt.tileset->getGenericExtension("B"); + CesiumUtility::JsonValue* pA = withCustomExt.value->getGenericExtension("A"); + CesiumUtility::JsonValue* pB = withCustomExt.value->getGenericExtension("B"); REQUIRE(pA != nullptr); REQUIRE(pB != nullptr); @@ -303,11 +300,10 @@ TEST_CASE("Reads custom extension") { "B", CesiumJsonReader::ExtensionState::Disabled); - Cesium3DTilesReader::TilesetReaderResult withoutCustomExt = - reader.readTileset( - gsl::span(reinterpret_cast(s.c_str()), s.size())); + auto withoutCustomExt = reader.readFromJson( + gsl::span(reinterpret_cast(s.c_str()), s.size())); - auto& zeroExtensions = withoutCustomExt.tileset->extensions; + auto& zeroExtensions = withoutCustomExt.value->extensions; REQUIRE(zeroExtensions.empty()); } @@ -318,13 +314,13 @@ TEST_CASE("Reads tileset JSON with unknown properties") { tilesetFile /= "tileset-with-unsupported-properties.json"; std::vector data = readFile(tilesetFile); Cesium3DTilesReader::TilesetReader reader; - Cesium3DTilesReader::TilesetReaderResult result = reader.readTileset(data); + auto result = reader.readFromJson(data); CHECK(result.errors.empty()); CHECK(result.warnings.empty()); - REQUIRE(result.tileset); + REQUIRE(result.value); const CesiumUtility::JsonValue::Object& unknownProperties = - result.tileset->asset.unknownProperties; + result.value->asset.unknownProperties; auto itString = unknownProperties.find("someString"); REQUIRE(itString != unknownProperties.end()); @@ -383,12 +379,12 @@ TEST_CASE("Reads tileset JSON with unknown properties and ignores them when " std::vector data = readFile(tilesetFile); Cesium3DTilesReader::TilesetReader reader; reader.getOptions().setCaptureUnknownProperties(false); - Cesium3DTilesReader::TilesetReaderResult result = reader.readTileset(data); + auto result = reader.readFromJson(data); CHECK(result.errors.empty()); CHECK(result.warnings.empty()); - REQUIRE(result.tileset); + REQUIRE(result.value); const CesiumUtility::JsonValue::Object& unknownProperties = - result.tileset->asset.unknownProperties; + result.value->asset.unknownProperties; CHECK(unknownProperties.empty()); } diff --git a/Cesium3DTilesWriter/test/TestTilesetWriter.cpp b/Cesium3DTilesWriter/test/TestTilesetWriter.cpp index f2213d3e8..5aa913f6f 100644 --- a/Cesium3DTilesWriter/test/TestTilesetWriter.cpp +++ b/Cesium3DTilesWriter/test/TestTilesetWriter.cpp @@ -11,15 +11,14 @@ namespace { void check(const std::string& input, const std::string& expectedOutput) { Cesium3DTilesReader::TilesetReader reader; - Cesium3DTilesReader::TilesetReaderResult readResult = - reader.readTileset(gsl::span( - reinterpret_cast(input.c_str()), - input.size())); + auto readResult = reader.readFromJson(gsl::span( + reinterpret_cast(input.c_str()), + input.size())); REQUIRE(readResult.errors.empty()); REQUIRE(readResult.warnings.empty()); - REQUIRE(readResult.tileset.has_value()); + REQUIRE(readResult.value.has_value()); - Cesium3DTiles::Tileset& tileset = readResult.tileset.value(); + Cesium3DTiles::Tileset& tileset = readResult.value.value(); Cesium3DTilesWriter::TilesetWriter writer; Cesium3DTilesWriter::TilesetWriterResult writeResult = diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/AccessorReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/AccessorReader.h new file mode 100644 index 000000000..804f1d620 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/AccessorReader.h @@ -0,0 +1,72 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct Accessor; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link Accessor} instances from JSON. + */ +class CESIUMGLTFREADER_API AccessorReader { +public: + /** + * @brief Constructs a new instance. + */ + AccessorReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Accessor from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Accessor from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Accessor from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/AccessorSparseIndicesReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/AccessorSparseIndicesReader.h new file mode 100644 index 000000000..64381e2a3 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/AccessorSparseIndicesReader.h @@ -0,0 +1,74 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct AccessorSparseIndices; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link AccessorSparseIndices} instances from JSON. + */ +class CESIUMGLTFREADER_API AccessorSparseIndicesReader { +public: + /** + * @brief Constructs a new instance. + */ + AccessorSparseIndicesReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of AccessorSparseIndices from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of AccessorSparseIndices from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of AccessorSparseIndices from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/AccessorSparseReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/AccessorSparseReader.h new file mode 100644 index 000000000..d52c8261c --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/AccessorSparseReader.h @@ -0,0 +1,73 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct AccessorSparse; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link AccessorSparse} instances from JSON. + */ +class CESIUMGLTFREADER_API AccessorSparseReader { +public: + /** + * @brief Constructs a new instance. + */ + AccessorSparseReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of AccessorSparse from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of AccessorSparse from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of AccessorSparse from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/AccessorSparseValuesReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/AccessorSparseValuesReader.h new file mode 100644 index 000000000..59f524297 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/AccessorSparseValuesReader.h @@ -0,0 +1,74 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct AccessorSparseValues; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link AccessorSparseValues} instances from JSON. + */ +class CESIUMGLTFREADER_API AccessorSparseValuesReader { +public: + /** + * @brief Constructs a new instance. + */ + AccessorSparseValuesReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of AccessorSparseValues from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of AccessorSparseValues from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of AccessorSparseValues from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/AnimationChannelReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/AnimationChannelReader.h new file mode 100644 index 000000000..f1bc4e980 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/AnimationChannelReader.h @@ -0,0 +1,73 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct AnimationChannel; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link AnimationChannel} instances from JSON. + */ +class CESIUMGLTFREADER_API AnimationChannelReader { +public: + /** + * @brief Constructs a new instance. + */ + AnimationChannelReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of AnimationChannel from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of AnimationChannel from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of AnimationChannel from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/AnimationChannelTargetReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/AnimationChannelTargetReader.h new file mode 100644 index 000000000..23b1ea78b --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/AnimationChannelTargetReader.h @@ -0,0 +1,74 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct AnimationChannelTarget; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link AnimationChannelTarget} instances from JSON. + */ +class CESIUMGLTFREADER_API AnimationChannelTargetReader { +public: + /** + * @brief Constructs a new instance. + */ + AnimationChannelTargetReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of AnimationChannelTarget from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of AnimationChannelTarget from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of AnimationChannelTarget from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/AnimationReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/AnimationReader.h new file mode 100644 index 000000000..db5c3c9c0 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/AnimationReader.h @@ -0,0 +1,72 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct Animation; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link Animation} instances from JSON. + */ +class CESIUMGLTFREADER_API AnimationReader { +public: + /** + * @brief Constructs a new instance. + */ + AnimationReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Animation from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Animation from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Animation from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/AnimationSamplerReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/AnimationSamplerReader.h new file mode 100644 index 000000000..0ebf280a6 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/AnimationSamplerReader.h @@ -0,0 +1,73 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct AnimationSampler; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link AnimationSampler} instances from JSON. + */ +class CESIUMGLTFREADER_API AnimationSamplerReader { +public: + /** + * @brief Constructs a new instance. + */ + AnimationSamplerReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of AnimationSampler from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of AnimationSampler from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of AnimationSampler from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/AssetReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/AssetReader.h new file mode 100644 index 000000000..9df7561a8 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/AssetReader.h @@ -0,0 +1,72 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct Asset; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link Asset} instances from JSON. + */ +class CESIUMGLTFREADER_API AssetReader { +public: + /** + * @brief Constructs a new instance. + */ + AssetReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Asset from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Asset from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Asset from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/BufferReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/BufferReader.h new file mode 100644 index 000000000..eb4998d01 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/BufferReader.h @@ -0,0 +1,72 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct Buffer; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link Buffer} instances from JSON. + */ +class CESIUMGLTFREADER_API BufferReader { +public: + /** + * @brief Constructs a new instance. + */ + BufferReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Buffer from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Buffer from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Buffer from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/BufferViewReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/BufferViewReader.h new file mode 100644 index 000000000..af00165cb --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/BufferViewReader.h @@ -0,0 +1,72 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct BufferView; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link BufferView} instances from JSON. + */ +class CESIUMGLTFREADER_API BufferViewReader { +public: + /** + * @brief Constructs a new instance. + */ + BufferViewReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of BufferView from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of BufferView from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of BufferView from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/CameraOrthographicReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/CameraOrthographicReader.h new file mode 100644 index 000000000..9c8527379 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/CameraOrthographicReader.h @@ -0,0 +1,73 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct CameraOrthographic; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link CameraOrthographic} instances from JSON. + */ +class CESIUMGLTFREADER_API CameraOrthographicReader { +public: + /** + * @brief Constructs a new instance. + */ + CameraOrthographicReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of CameraOrthographic from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of CameraOrthographic from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of CameraOrthographic from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/CameraPerspectiveReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/CameraPerspectiveReader.h new file mode 100644 index 000000000..0c17d7113 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/CameraPerspectiveReader.h @@ -0,0 +1,73 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct CameraPerspective; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link CameraPerspective} instances from JSON. + */ +class CESIUMGLTFREADER_API CameraPerspectiveReader { +public: + /** + * @brief Constructs a new instance. + */ + CameraPerspectiveReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of CameraPerspective from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of CameraPerspective from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of CameraPerspective from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/CameraReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/CameraReader.h new file mode 100644 index 000000000..39a2facd2 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/CameraReader.h @@ -0,0 +1,72 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct Camera; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link Camera} instances from JSON. + */ +class CESIUMGLTFREADER_API CameraReader { +public: + /** + * @brief Constructs a new instance. + */ + CameraReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Camera from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Camera from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Camera from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ClassPropertyReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ClassPropertyReader.h new file mode 100644 index 000000000..162c5ff96 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ClassPropertyReader.h @@ -0,0 +1,73 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ClassProperty; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link ClassProperty} instances from JSON. + */ +class CESIUMGLTFREADER_API ClassPropertyReader { +public: + /** + * @brief Constructs a new instance. + */ + ClassPropertyReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ClassProperty from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ClassProperty from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of ClassProperty from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ClassReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ClassReader.h new file mode 100644 index 000000000..852ce802a --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ClassReader.h @@ -0,0 +1,72 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct Class; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link Class} instances from JSON. + */ +class CESIUMGLTFREADER_API ClassReader { +public: + /** + * @brief Constructs a new instance. + */ + ClassReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Class from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Class from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Class from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ClassStatisticsReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ClassStatisticsReader.h new file mode 100644 index 000000000..746b16067 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ClassStatisticsReader.h @@ -0,0 +1,73 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ClassStatistics; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link ClassStatistics} instances from JSON. + */ +class CESIUMGLTFREADER_API ClassStatisticsReader { +public: + /** + * @brief Constructs a new instance. + */ + ClassStatisticsReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ClassStatistics from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ClassStatistics from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of ClassStatistics from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/EnumReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/EnumReader.h new file mode 100644 index 000000000..388273a98 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/EnumReader.h @@ -0,0 +1,72 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct Enum; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link Enum} instances from JSON. + */ +class CESIUMGLTFREADER_API EnumReader { +public: + /** + * @brief Constructs a new instance. + */ + EnumReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Enum from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Enum from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Enum from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/EnumValueReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/EnumValueReader.h new file mode 100644 index 000000000..149c8cf47 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/EnumValueReader.h @@ -0,0 +1,72 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct EnumValue; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link EnumValue} instances from JSON. + */ +class CESIUMGLTFREADER_API EnumValueReader { +public: + /** + * @brief Constructs a new instance. + */ + EnumValueReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of EnumValue from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of EnumValue from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of EnumValue from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionBufferExtMeshoptCompressionReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionBufferExtMeshoptCompressionReader.h new file mode 100644 index 000000000..32d905978 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionBufferExtMeshoptCompressionReader.h @@ -0,0 +1,78 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionBufferExtMeshoptCompression; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link ExtensionBufferExtMeshoptCompression} instances from JSON. + */ +class CESIUMGLTFREADER_API ExtensionBufferExtMeshoptCompressionReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionBufferExtMeshoptCompressionReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionBufferExtMeshoptCompression from a + * byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionBufferExtMeshoptCompression> + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionBufferExtMeshoptCompression from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionBufferExtMeshoptCompression> + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of ExtensionBufferExtMeshoptCompression + * from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionBufferViewExtMeshoptCompressionReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionBufferViewExtMeshoptCompressionReader.h new file mode 100644 index 000000000..da7ff77c8 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionBufferViewExtMeshoptCompressionReader.h @@ -0,0 +1,78 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionBufferViewExtMeshoptCompression; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link ExtensionBufferViewExtMeshoptCompression} instances from JSON. + */ +class CESIUMGLTFREADER_API ExtensionBufferViewExtMeshoptCompressionReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionBufferViewExtMeshoptCompressionReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionBufferViewExtMeshoptCompression from a + * byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionBufferViewExtMeshoptCompression> + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionBufferViewExtMeshoptCompression from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionBufferViewExtMeshoptCompression> + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of + * ExtensionBufferViewExtMeshoptCompression from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionCesiumRTCReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionCesiumRTCReader.h new file mode 100644 index 000000000..f524c7906 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionCesiumRTCReader.h @@ -0,0 +1,73 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionCesiumRTC; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link ExtensionCesiumRTC} instances from JSON. + */ +class CESIUMGLTFREADER_API ExtensionCesiumRTCReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionCesiumRTCReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionCesiumRTC from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionCesiumRTC from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of ExtensionCesiumRTC from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionCesiumTileEdgesReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionCesiumTileEdgesReader.h new file mode 100644 index 000000000..b9df268d2 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionCesiumTileEdgesReader.h @@ -0,0 +1,75 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionCesiumTileEdges; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link ExtensionCesiumTileEdges} instances from JSON. + */ +class CESIUMGLTFREADER_API ExtensionCesiumTileEdgesReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionCesiumTileEdgesReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionCesiumTileEdges from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionCesiumTileEdges from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of ExtensionCesiumTileEdges from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtInstanceFeaturesFeatureIdReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtInstanceFeaturesFeatureIdReader.h new file mode 100644 index 000000000..a3fa2919b --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtInstanceFeaturesFeatureIdReader.h @@ -0,0 +1,78 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionExtInstanceFeaturesFeatureId; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link ExtensionExtInstanceFeaturesFeatureId} instances from JSON. + */ +class CESIUMGLTFREADER_API ExtensionExtInstanceFeaturesFeatureIdReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionExtInstanceFeaturesFeatureIdReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionExtInstanceFeaturesFeatureId from a + * byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtInstanceFeaturesFeatureId> + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionExtInstanceFeaturesFeatureId from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtInstanceFeaturesFeatureId> + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of ExtensionExtInstanceFeaturesFeatureId + * from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtInstanceFeaturesReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtInstanceFeaturesReader.h new file mode 100644 index 000000000..88cae65fa --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtInstanceFeaturesReader.h @@ -0,0 +1,76 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionExtInstanceFeatures; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link ExtensionExtInstanceFeatures} instances from JSON. + */ +class CESIUMGLTFREADER_API ExtensionExtInstanceFeaturesReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionExtInstanceFeaturesReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionExtInstanceFeatures from a byte + * buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionExtInstanceFeatures from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of ExtensionExtInstanceFeatures from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtMeshFeaturesFeatureIdReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtMeshFeaturesFeatureIdReader.h new file mode 100644 index 000000000..e3f51fd79 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtMeshFeaturesFeatureIdReader.h @@ -0,0 +1,78 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionExtMeshFeaturesFeatureId; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link ExtensionExtMeshFeaturesFeatureId} instances from JSON. + */ +class CESIUMGLTFREADER_API ExtensionExtMeshFeaturesFeatureIdReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionExtMeshFeaturesFeatureIdReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionExtMeshFeaturesFeatureId from a byte + * buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtMeshFeaturesFeatureId> + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionExtMeshFeaturesFeatureId from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtMeshFeaturesFeatureId> + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of ExtensionExtMeshFeaturesFeatureId + * from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtMeshFeaturesFeatureIdTextureReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtMeshFeaturesFeatureIdTextureReader.h new file mode 100644 index 000000000..36b324965 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtMeshFeaturesFeatureIdTextureReader.h @@ -0,0 +1,78 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionExtMeshFeaturesFeatureIdTexture; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link ExtensionExtMeshFeaturesFeatureIdTexture} instances from JSON. + */ +class CESIUMGLTFREADER_API ExtensionExtMeshFeaturesFeatureIdTextureReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionExtMeshFeaturesFeatureIdTextureReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionExtMeshFeaturesFeatureIdTexture from a + * byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtMeshFeaturesFeatureIdTexture> + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionExtMeshFeaturesFeatureIdTexture from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtMeshFeaturesFeatureIdTexture> + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of + * ExtensionExtMeshFeaturesFeatureIdTexture from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtMeshFeaturesReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtMeshFeaturesReader.h new file mode 100644 index 000000000..8d369d631 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtMeshFeaturesReader.h @@ -0,0 +1,75 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionExtMeshFeatures; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link ExtensionExtMeshFeatures} instances from JSON. + */ +class CESIUMGLTFREADER_API ExtensionExtMeshFeaturesReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionExtMeshFeaturesReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionExtMeshFeatures from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionExtMeshFeatures from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of ExtensionExtMeshFeatures from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtMeshGpuInstancingReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtMeshGpuInstancingReader.h new file mode 100644 index 000000000..507301ac9 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtMeshGpuInstancingReader.h @@ -0,0 +1,76 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionExtMeshGpuInstancing; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link ExtensionExtMeshGpuInstancing} instances from JSON. + */ +class CESIUMGLTFREADER_API ExtensionExtMeshGpuInstancingReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionExtMeshGpuInstancingReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionExtMeshGpuInstancing from a byte + * buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionExtMeshGpuInstancing from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of ExtensionExtMeshGpuInstancing from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataClassPropertyReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataClassPropertyReader.h new file mode 100644 index 000000000..f1ba1e071 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataClassPropertyReader.h @@ -0,0 +1,78 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionExtStructuralMetadataClassProperty; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link ExtensionExtStructuralMetadataClassProperty} instances from JSON. + */ +class CESIUMGLTFREADER_API ExtensionExtStructuralMetadataClassPropertyReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionExtStructuralMetadataClassPropertyReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionExtStructuralMetadataClassProperty + * from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataClassProperty> + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionExtStructuralMetadataClassProperty + * from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataClassProperty> + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of + * ExtensionExtStructuralMetadataClassProperty from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataClassReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataClassReader.h new file mode 100644 index 000000000..cbdac90cd --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataClassReader.h @@ -0,0 +1,78 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionExtStructuralMetadataClass; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link ExtensionExtStructuralMetadataClass} instances from JSON. + */ +class CESIUMGLTFREADER_API ExtensionExtStructuralMetadataClassReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionExtStructuralMetadataClassReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionExtStructuralMetadataClass from a byte + * buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataClass> + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionExtStructuralMetadataClass from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataClass> + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of ExtensionExtStructuralMetadataClass + * from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataEnumReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataEnumReader.h new file mode 100644 index 000000000..e64155cf9 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataEnumReader.h @@ -0,0 +1,78 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionExtStructuralMetadataEnum; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link ExtensionExtStructuralMetadataEnum} instances from JSON. + */ +class CESIUMGLTFREADER_API ExtensionExtStructuralMetadataEnumReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionExtStructuralMetadataEnumReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionExtStructuralMetadataEnum from a byte + * buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataEnum> + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionExtStructuralMetadataEnum from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataEnum> + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of ExtensionExtStructuralMetadataEnum + * from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataEnumValueReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataEnumValueReader.h new file mode 100644 index 000000000..4b960a26c --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataEnumValueReader.h @@ -0,0 +1,78 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionExtStructuralMetadataEnumValue; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link ExtensionExtStructuralMetadataEnumValue} instances from JSON. + */ +class CESIUMGLTFREADER_API ExtensionExtStructuralMetadataEnumValueReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionExtStructuralMetadataEnumValueReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionExtStructuralMetadataEnumValue from a + * byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataEnumValue> + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionExtStructuralMetadataEnumValue from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataEnumValue> + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of + * ExtensionExtStructuralMetadataEnumValue from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyAttributePropertyReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyAttributePropertyReader.h new file mode 100644 index 000000000..bcbc21953 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyAttributePropertyReader.h @@ -0,0 +1,81 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionExtStructuralMetadataPropertyAttributeProperty; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link ExtensionExtStructuralMetadataPropertyAttributeProperty} instances from JSON. + */ +class CESIUMGLTFREADER_API + ExtensionExtStructuralMetadataPropertyAttributePropertyReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionExtStructuralMetadataPropertyAttributePropertyReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of + * ExtensionExtStructuralMetadataPropertyAttributeProperty from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataPropertyAttributeProperty> + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of + * ExtensionExtStructuralMetadataPropertyAttributeProperty from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataPropertyAttributeProperty> + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of + * ExtensionExtStructuralMetadataPropertyAttributeProperty from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyAttributeReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyAttributeReader.h new file mode 100644 index 000000000..ae043c249 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyAttributeReader.h @@ -0,0 +1,79 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionExtStructuralMetadataPropertyAttribute; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link ExtensionExtStructuralMetadataPropertyAttribute} instances from JSON. + */ +class CESIUMGLTFREADER_API + ExtensionExtStructuralMetadataPropertyAttributeReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionExtStructuralMetadataPropertyAttributeReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionExtStructuralMetadataPropertyAttribute + * from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataPropertyAttribute> + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionExtStructuralMetadataPropertyAttribute + * from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataPropertyAttribute> + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of + * ExtensionExtStructuralMetadataPropertyAttribute from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyTablePropertyReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyTablePropertyReader.h new file mode 100644 index 000000000..7a9d09c39 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyTablePropertyReader.h @@ -0,0 +1,81 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionExtStructuralMetadataPropertyTableProperty; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link ExtensionExtStructuralMetadataPropertyTableProperty} instances from JSON. + */ +class CESIUMGLTFREADER_API + ExtensionExtStructuralMetadataPropertyTablePropertyReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionExtStructuralMetadataPropertyTablePropertyReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of + * ExtensionExtStructuralMetadataPropertyTableProperty from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataPropertyTableProperty> + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of + * ExtensionExtStructuralMetadataPropertyTableProperty from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataPropertyTableProperty> + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of + * ExtensionExtStructuralMetadataPropertyTableProperty from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyTableReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyTableReader.h new file mode 100644 index 000000000..7c0407433 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyTableReader.h @@ -0,0 +1,78 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionExtStructuralMetadataPropertyTable; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link ExtensionExtStructuralMetadataPropertyTable} instances from JSON. + */ +class CESIUMGLTFREADER_API ExtensionExtStructuralMetadataPropertyTableReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionExtStructuralMetadataPropertyTableReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionExtStructuralMetadataPropertyTable + * from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataPropertyTable> + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionExtStructuralMetadataPropertyTable + * from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataPropertyTable> + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of + * ExtensionExtStructuralMetadataPropertyTable from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyTexturePropertyReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyTexturePropertyReader.h new file mode 100644 index 000000000..33d2db6d5 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyTexturePropertyReader.h @@ -0,0 +1,81 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionExtStructuralMetadataPropertyTextureProperty; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link ExtensionExtStructuralMetadataPropertyTextureProperty} instances from JSON. + */ +class CESIUMGLTFREADER_API + ExtensionExtStructuralMetadataPropertyTexturePropertyReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionExtStructuralMetadataPropertyTexturePropertyReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of + * ExtensionExtStructuralMetadataPropertyTextureProperty from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataPropertyTextureProperty> + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of + * ExtensionExtStructuralMetadataPropertyTextureProperty from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataPropertyTextureProperty> + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of + * ExtensionExtStructuralMetadataPropertyTextureProperty from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyTextureReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyTextureReader.h new file mode 100644 index 000000000..24e512639 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataPropertyTextureReader.h @@ -0,0 +1,78 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionExtStructuralMetadataPropertyTexture; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link ExtensionExtStructuralMetadataPropertyTexture} instances from JSON. + */ +class CESIUMGLTFREADER_API ExtensionExtStructuralMetadataPropertyTextureReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionExtStructuralMetadataPropertyTextureReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionExtStructuralMetadataPropertyTexture + * from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataPropertyTexture> + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionExtStructuralMetadataPropertyTexture + * from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataPropertyTexture> + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of + * ExtensionExtStructuralMetadataPropertyTexture from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataSchemaReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataSchemaReader.h new file mode 100644 index 000000000..1ee96e3d8 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionExtStructuralMetadataSchemaReader.h @@ -0,0 +1,78 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionExtStructuralMetadataSchema; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link ExtensionExtStructuralMetadataSchema} instances from JSON. + */ +class CESIUMGLTFREADER_API ExtensionExtStructuralMetadataSchemaReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionExtStructuralMetadataSchemaReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionExtStructuralMetadataSchema from a + * byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataSchema> + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionExtStructuralMetadataSchema from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataSchema> + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of ExtensionExtStructuralMetadataSchema + * from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionKhrDracoMeshCompressionReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionKhrDracoMeshCompressionReader.h new file mode 100644 index 000000000..51ac2ee70 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionKhrDracoMeshCompressionReader.h @@ -0,0 +1,76 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionKhrDracoMeshCompression; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link ExtensionKhrDracoMeshCompression} instances from JSON. + */ +class CESIUMGLTFREADER_API ExtensionKhrDracoMeshCompressionReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionKhrDracoMeshCompressionReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionKhrDracoMeshCompression from a byte + * buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionKhrDracoMeshCompression from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of ExtensionKhrDracoMeshCompression from + * a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionKhrMaterialsUnlitReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionKhrMaterialsUnlitReader.h new file mode 100644 index 000000000..43307034d --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionKhrMaterialsUnlitReader.h @@ -0,0 +1,75 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionKhrMaterialsUnlit; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link ExtensionKhrMaterialsUnlit} instances from JSON. + */ +class CESIUMGLTFREADER_API ExtensionKhrMaterialsUnlitReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionKhrMaterialsUnlitReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionKhrMaterialsUnlit from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionKhrMaterialsUnlit from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of ExtensionKhrMaterialsUnlit from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionKhrTextureBasisuReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionKhrTextureBasisuReader.h new file mode 100644 index 000000000..dcf213fe8 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionKhrTextureBasisuReader.h @@ -0,0 +1,75 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionKhrTextureBasisu; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link ExtensionKhrTextureBasisu} instances from JSON. + */ +class CESIUMGLTFREADER_API ExtensionKhrTextureBasisuReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionKhrTextureBasisuReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionKhrTextureBasisu from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionKhrTextureBasisu from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of ExtensionKhrTextureBasisu from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionKhrTextureTransformReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionKhrTextureTransformReader.h new file mode 100644 index 000000000..7559901b5 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionKhrTextureTransformReader.h @@ -0,0 +1,76 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionKhrTextureTransform; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link ExtensionKhrTextureTransform} instances from JSON. + */ +class CESIUMGLTFREADER_API ExtensionKhrTextureTransformReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionKhrTextureTransformReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionKhrTextureTransform from a byte + * buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionKhrTextureTransform from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of ExtensionKhrTextureTransform from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionMeshPrimitiveExtFeatureMetadataReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionMeshPrimitiveExtFeatureMetadataReader.h new file mode 100644 index 000000000..03d8de9ab --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionMeshPrimitiveExtFeatureMetadataReader.h @@ -0,0 +1,78 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionMeshPrimitiveExtFeatureMetadata; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link ExtensionMeshPrimitiveExtFeatureMetadata} instances from JSON. + */ +class CESIUMGLTFREADER_API ExtensionMeshPrimitiveExtFeatureMetadataReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionMeshPrimitiveExtFeatureMetadataReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionMeshPrimitiveExtFeatureMetadata from a + * byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionMeshPrimitiveExtFeatureMetadata> + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionMeshPrimitiveExtFeatureMetadata from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionMeshPrimitiveExtFeatureMetadata> + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of + * ExtensionMeshPrimitiveExtFeatureMetadata from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionMeshPrimitiveExtStructuralMetadataReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionMeshPrimitiveExtStructuralMetadataReader.h new file mode 100644 index 000000000..09c94d295 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionMeshPrimitiveExtStructuralMetadataReader.h @@ -0,0 +1,78 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionMeshPrimitiveExtStructuralMetadata; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link ExtensionMeshPrimitiveExtStructuralMetadata} instances from JSON. + */ +class CESIUMGLTFREADER_API ExtensionMeshPrimitiveExtStructuralMetadataReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionMeshPrimitiveExtStructuralMetadataReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionMeshPrimitiveExtStructuralMetadata + * from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionMeshPrimitiveExtStructuralMetadata> + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionMeshPrimitiveExtStructuralMetadata + * from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionMeshPrimitiveExtStructuralMetadata> + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of + * ExtensionMeshPrimitiveExtStructuralMetadata from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueReader.h new file mode 100644 index 000000000..321da8d86 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueReader.h @@ -0,0 +1,81 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValue; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValue} instances from JSON. + */ +class CESIUMGLTFREADER_API + ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of + * ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValue from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValue> + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of + * ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValue from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValue> + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of + * ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValue from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionMeshPrimitiveKhrMaterialsVariantsReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionMeshPrimitiveKhrMaterialsVariantsReader.h new file mode 100644 index 000000000..4bdfa1a06 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionMeshPrimitiveKhrMaterialsVariantsReader.h @@ -0,0 +1,78 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionMeshPrimitiveKhrMaterialsVariants; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link ExtensionMeshPrimitiveKhrMaterialsVariants} instances from JSON. + */ +class CESIUMGLTFREADER_API ExtensionMeshPrimitiveKhrMaterialsVariantsReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionMeshPrimitiveKhrMaterialsVariantsReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionMeshPrimitiveKhrMaterialsVariants from + * a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionMeshPrimitiveKhrMaterialsVariants> + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionMeshPrimitiveKhrMaterialsVariants from + * a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionMeshPrimitiveKhrMaterialsVariants> + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of + * ExtensionMeshPrimitiveKhrMaterialsVariants from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelExtFeatureMetadataReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelExtFeatureMetadataReader.h new file mode 100644 index 000000000..1ffdfe190 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelExtFeatureMetadataReader.h @@ -0,0 +1,76 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionModelExtFeatureMetadata; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link ExtensionModelExtFeatureMetadata} instances from JSON. + */ +class CESIUMGLTFREADER_API ExtensionModelExtFeatureMetadataReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionModelExtFeatureMetadataReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionModelExtFeatureMetadata from a byte + * buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionModelExtFeatureMetadata from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of ExtensionModelExtFeatureMetadata from + * a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelExtStructuralMetadataReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelExtStructuralMetadataReader.h new file mode 100644 index 000000000..b3aa0e002 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelExtStructuralMetadataReader.h @@ -0,0 +1,78 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionModelExtStructuralMetadata; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link ExtensionModelExtStructuralMetadata} instances from JSON. + */ +class CESIUMGLTFREADER_API ExtensionModelExtStructuralMetadataReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionModelExtStructuralMetadataReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionModelExtStructuralMetadata from a byte + * buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionModelExtStructuralMetadata> + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionModelExtStructuralMetadata from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionModelExtStructuralMetadata> + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of ExtensionModelExtStructuralMetadata + * from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelKhrMaterialsVariantsReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelKhrMaterialsVariantsReader.h new file mode 100644 index 000000000..72320b8db --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelKhrMaterialsVariantsReader.h @@ -0,0 +1,78 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionModelKhrMaterialsVariants; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link ExtensionModelKhrMaterialsVariants} instances from JSON. + */ +class CESIUMGLTFREADER_API ExtensionModelKhrMaterialsVariantsReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionModelKhrMaterialsVariantsReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionModelKhrMaterialsVariants from a byte + * buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionModelKhrMaterialsVariants> + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionModelKhrMaterialsVariants from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionModelKhrMaterialsVariants> + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of ExtensionModelKhrMaterialsVariants + * from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelKhrMaterialsVariantsValueReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelKhrMaterialsVariantsValueReader.h new file mode 100644 index 000000000..6f6e83962 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelKhrMaterialsVariantsValueReader.h @@ -0,0 +1,78 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionModelKhrMaterialsVariantsValue; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link ExtensionModelKhrMaterialsVariantsValue} instances from JSON. + */ +class CESIUMGLTFREADER_API ExtensionModelKhrMaterialsVariantsValueReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionModelKhrMaterialsVariantsValueReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionModelKhrMaterialsVariantsValue from a + * byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionModelKhrMaterialsVariantsValue> + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionModelKhrMaterialsVariantsValue from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionModelKhrMaterialsVariantsValue> + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of + * ExtensionModelKhrMaterialsVariantsValue from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelMaxarMeshVariantsReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelMaxarMeshVariantsReader.h new file mode 100644 index 000000000..89b5af2da --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelMaxarMeshVariantsReader.h @@ -0,0 +1,76 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionModelMaxarMeshVariants; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link ExtensionModelMaxarMeshVariants} instances from JSON. + */ +class CESIUMGLTFREADER_API ExtensionModelMaxarMeshVariantsReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionModelMaxarMeshVariantsReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionModelMaxarMeshVariants from a byte + * buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionModelMaxarMeshVariants from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of ExtensionModelMaxarMeshVariants from + * a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelMaxarMeshVariantsValueReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelMaxarMeshVariantsValueReader.h new file mode 100644 index 000000000..d89c5206c --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionModelMaxarMeshVariantsValueReader.h @@ -0,0 +1,78 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionModelMaxarMeshVariantsValue; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link ExtensionModelMaxarMeshVariantsValue} instances from JSON. + */ +class CESIUMGLTFREADER_API ExtensionModelMaxarMeshVariantsValueReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionModelMaxarMeshVariantsValueReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionModelMaxarMeshVariantsValue from a + * byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionModelMaxarMeshVariantsValue> + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionModelMaxarMeshVariantsValue from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionModelMaxarMeshVariantsValue> + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of ExtensionModelMaxarMeshVariantsValue + * from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionNodeMaxarMeshVariantsMappingsValueReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionNodeMaxarMeshVariantsMappingsValueReader.h new file mode 100644 index 000000000..071bd32dd --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionNodeMaxarMeshVariantsMappingsValueReader.h @@ -0,0 +1,78 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionNodeMaxarMeshVariantsMappingsValue; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link ExtensionNodeMaxarMeshVariantsMappingsValue} instances from JSON. + */ +class CESIUMGLTFREADER_API ExtensionNodeMaxarMeshVariantsMappingsValueReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionNodeMaxarMeshVariantsMappingsValueReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionNodeMaxarMeshVariantsMappingsValue + * from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionNodeMaxarMeshVariantsMappingsValue> + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionNodeMaxarMeshVariantsMappingsValue + * from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionNodeMaxarMeshVariantsMappingsValue> + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of + * ExtensionNodeMaxarMeshVariantsMappingsValue from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionNodeMaxarMeshVariantsReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionNodeMaxarMeshVariantsReader.h new file mode 100644 index 000000000..28558d042 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionNodeMaxarMeshVariantsReader.h @@ -0,0 +1,76 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionNodeMaxarMeshVariants; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link ExtensionNodeMaxarMeshVariants} instances from JSON. + */ +class CESIUMGLTFREADER_API ExtensionNodeMaxarMeshVariantsReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionNodeMaxarMeshVariantsReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionNodeMaxarMeshVariants from a byte + * buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionNodeMaxarMeshVariants from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of ExtensionNodeMaxarMeshVariants from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionTextureWebpReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionTextureWebpReader.h new file mode 100644 index 000000000..a97af321a --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionTextureWebpReader.h @@ -0,0 +1,74 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct ExtensionTextureWebp; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link ExtensionTextureWebp} instances from JSON. + */ +class CESIUMGLTFREADER_API ExtensionTextureWebpReader { +public: + /** + * @brief Constructs a new instance. + */ + ExtensionTextureWebpReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ExtensionTextureWebp from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ExtensionTextureWebp from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of ExtensionTextureWebp from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureIDAttributeReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureIDAttributeReader.h new file mode 100644 index 000000000..bc31b3743 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureIDAttributeReader.h @@ -0,0 +1,73 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct FeatureIDAttribute; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link FeatureIDAttribute} instances from JSON. + */ +class CESIUMGLTFREADER_API FeatureIDAttributeReader { +public: + /** + * @brief Constructs a new instance. + */ + FeatureIDAttributeReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of FeatureIDAttribute from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of FeatureIDAttribute from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of FeatureIDAttribute from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureIDTextureReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureIDTextureReader.h new file mode 100644 index 000000000..b8fda0b54 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureIDTextureReader.h @@ -0,0 +1,73 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct FeatureIDTexture; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link FeatureIDTexture} instances from JSON. + */ +class CESIUMGLTFREADER_API FeatureIDTextureReader { +public: + /** + * @brief Constructs a new instance. + */ + FeatureIDTextureReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of FeatureIDTexture from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of FeatureIDTexture from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of FeatureIDTexture from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureIDsReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureIDsReader.h new file mode 100644 index 000000000..caf4779ff --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureIDsReader.h @@ -0,0 +1,72 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct FeatureIDs; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link FeatureIDs} instances from JSON. + */ +class CESIUMGLTFREADER_API FeatureIDsReader { +public: + /** + * @brief Constructs a new instance. + */ + FeatureIDsReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of FeatureIDs from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of FeatureIDs from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of FeatureIDs from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureTablePropertyReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureTablePropertyReader.h new file mode 100644 index 000000000..4183569ff --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureTablePropertyReader.h @@ -0,0 +1,74 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct FeatureTableProperty; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link FeatureTableProperty} instances from JSON. + */ +class CESIUMGLTFREADER_API FeatureTablePropertyReader { +public: + /** + * @brief Constructs a new instance. + */ + FeatureTablePropertyReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of FeatureTableProperty from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of FeatureTableProperty from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of FeatureTableProperty from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureTableReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureTableReader.h new file mode 100644 index 000000000..764a98583 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureTableReader.h @@ -0,0 +1,72 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct FeatureTable; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link FeatureTable} instances from JSON. + */ +class CESIUMGLTFREADER_API FeatureTableReader { +public: + /** + * @brief Constructs a new instance. + */ + FeatureTableReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of FeatureTable from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of FeatureTable from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of FeatureTable from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureTextureReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureTextureReader.h new file mode 100644 index 000000000..1504abef4 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureTextureReader.h @@ -0,0 +1,73 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct FeatureTexture; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link FeatureTexture} instances from JSON. + */ +class CESIUMGLTFREADER_API FeatureTextureReader { +public: + /** + * @brief Constructs a new instance. + */ + FeatureTextureReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of FeatureTexture from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of FeatureTexture from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of FeatureTexture from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ImageReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ImageReader.h new file mode 100644 index 000000000..8dc8a4fcc --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ImageReader.h @@ -0,0 +1,72 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct Image; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link Image} instances from JSON. + */ +class CESIUMGLTFREADER_API ImageReader { +public: + /** + * @brief Constructs a new instance. + */ + ImageReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Image from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Image from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Image from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/MaterialNormalTextureInfoReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/MaterialNormalTextureInfoReader.h new file mode 100644 index 000000000..a51503dcc --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/MaterialNormalTextureInfoReader.h @@ -0,0 +1,75 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct MaterialNormalTextureInfo; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link MaterialNormalTextureInfo} instances from JSON. + */ +class CESIUMGLTFREADER_API MaterialNormalTextureInfoReader { +public: + /** + * @brief Constructs a new instance. + */ + MaterialNormalTextureInfoReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of MaterialNormalTextureInfo from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of MaterialNormalTextureInfo from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of MaterialNormalTextureInfo from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/MaterialOcclusionTextureInfoReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/MaterialOcclusionTextureInfoReader.h new file mode 100644 index 000000000..a39dd089c --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/MaterialOcclusionTextureInfoReader.h @@ -0,0 +1,76 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct MaterialOcclusionTextureInfo; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link MaterialOcclusionTextureInfo} instances from JSON. + */ +class CESIUMGLTFREADER_API MaterialOcclusionTextureInfoReader { +public: + /** + * @brief Constructs a new instance. + */ + MaterialOcclusionTextureInfoReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of MaterialOcclusionTextureInfo from a byte + * buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of MaterialOcclusionTextureInfo from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of MaterialOcclusionTextureInfo from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/MaterialPBRMetallicRoughnessReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/MaterialPBRMetallicRoughnessReader.h new file mode 100644 index 000000000..7eda8c603 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/MaterialPBRMetallicRoughnessReader.h @@ -0,0 +1,76 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct MaterialPBRMetallicRoughness; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link MaterialPBRMetallicRoughness} instances from JSON. + */ +class CESIUMGLTFREADER_API MaterialPBRMetallicRoughnessReader { +public: + /** + * @brief Constructs a new instance. + */ + MaterialPBRMetallicRoughnessReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of MaterialPBRMetallicRoughness from a byte + * buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of MaterialPBRMetallicRoughness from a + * rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of MaterialPBRMetallicRoughness from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult< + std::vector> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/MaterialReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/MaterialReader.h new file mode 100644 index 000000000..133cc0a53 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/MaterialReader.h @@ -0,0 +1,72 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct Material; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link Material} instances from JSON. + */ +class CESIUMGLTFREADER_API MaterialReader { +public: + /** + * @brief Constructs a new instance. + */ + MaterialReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Material from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Material from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Material from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/MeshPrimitiveReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/MeshPrimitiveReader.h new file mode 100644 index 000000000..db5c35b6b --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/MeshPrimitiveReader.h @@ -0,0 +1,73 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct MeshPrimitive; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link MeshPrimitive} instances from JSON. + */ +class CESIUMGLTFREADER_API MeshPrimitiveReader { +public: + /** + * @brief Constructs a new instance. + */ + MeshPrimitiveReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of MeshPrimitive from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of MeshPrimitive from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of MeshPrimitive from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/MeshReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/MeshReader.h new file mode 100644 index 000000000..05587f7ae --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/MeshReader.h @@ -0,0 +1,72 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct Mesh; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link Mesh} instances from JSON. + */ +class CESIUMGLTFREADER_API MeshReader { +public: + /** + * @brief Constructs a new instance. + */ + MeshReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Mesh from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Mesh from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Mesh from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/ModelReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/ModelReader.h new file mode 100644 index 000000000..637a21481 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/ModelReader.h @@ -0,0 +1,72 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct Model; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link Model} instances from JSON. + */ +class CESIUMGLTFREADER_API ModelReader { +public: + /** + * @brief Constructs a new instance. + */ + ModelReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Model from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Model from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Model from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/NodeReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/NodeReader.h new file mode 100644 index 000000000..c2c55438d --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/NodeReader.h @@ -0,0 +1,72 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct Node; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link Node} instances from JSON. + */ +class CESIUMGLTFREADER_API NodeReader { +public: + /** + * @brief Constructs a new instance. + */ + NodeReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Node from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Node from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Node from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/PropertyStatisticsReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/PropertyStatisticsReader.h new file mode 100644 index 000000000..2e9374d15 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/PropertyStatisticsReader.h @@ -0,0 +1,73 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct PropertyStatistics; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link PropertyStatistics} instances from JSON. + */ +class CESIUMGLTFREADER_API PropertyStatisticsReader { +public: + /** + * @brief Constructs a new instance. + */ + PropertyStatisticsReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of PropertyStatistics from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of PropertyStatistics from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of PropertyStatistics from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/SamplerReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/SamplerReader.h new file mode 100644 index 000000000..99f6e8815 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/SamplerReader.h @@ -0,0 +1,72 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct Sampler; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link Sampler} instances from JSON. + */ +class CESIUMGLTFREADER_API SamplerReader { +public: + /** + * @brief Constructs a new instance. + */ + SamplerReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Sampler from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Sampler from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Sampler from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/SceneReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/SceneReader.h new file mode 100644 index 000000000..162d5e201 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/SceneReader.h @@ -0,0 +1,72 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct Scene; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link Scene} instances from JSON. + */ +class CESIUMGLTFREADER_API SceneReader { +public: + /** + * @brief Constructs a new instance. + */ + SceneReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Scene from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Scene from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Scene from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/SchemaReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/SchemaReader.h new file mode 100644 index 000000000..3cda72932 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/SchemaReader.h @@ -0,0 +1,72 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct Schema; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link Schema} instances from JSON. + */ +class CESIUMGLTFREADER_API SchemaReader { +public: + /** + * @brief Constructs a new instance. + */ + SchemaReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Schema from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Schema from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Schema from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/SkinReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/SkinReader.h new file mode 100644 index 000000000..1402f486a --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/SkinReader.h @@ -0,0 +1,72 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct Skin; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link Skin} instances from JSON. + */ +class CESIUMGLTFREADER_API SkinReader { +public: + /** + * @brief Constructs a new instance. + */ + SkinReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Skin from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Skin from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Skin from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/StatisticsReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/StatisticsReader.h new file mode 100644 index 000000000..e8f42bf53 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/StatisticsReader.h @@ -0,0 +1,72 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct Statistics; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link Statistics} instances from JSON. + */ +class CESIUMGLTFREADER_API StatisticsReader { +public: + /** + * @brief Constructs a new instance. + */ + StatisticsReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Statistics from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Statistics from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Statistics from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/TextureAccessorReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/TextureAccessorReader.h new file mode 100644 index 000000000..ced1e7ef9 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/TextureAccessorReader.h @@ -0,0 +1,73 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct TextureAccessor; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link TextureAccessor} instances from JSON. + */ +class CESIUMGLTFREADER_API TextureAccessorReader { +public: + /** + * @brief Constructs a new instance. + */ + TextureAccessorReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of TextureAccessor from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of TextureAccessor from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of TextureAccessor from a + * rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/TextureInfoReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/TextureInfoReader.h new file mode 100644 index 000000000..313d48dc8 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/TextureInfoReader.h @@ -0,0 +1,72 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct TextureInfo; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link TextureInfo} instances from JSON. + */ +class CESIUMGLTFREADER_API TextureInfoReader { +public: + /** + * @brief Constructs a new instance. + */ + TextureInfoReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of TextureInfo from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of TextureInfo from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of TextureInfo from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/TextureReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/TextureReader.h new file mode 100644 index 000000000..b1832c762 --- /dev/null +++ b/CesiumGltfReader/generated/include/CesiumGltfReader/TextureReader.h @@ -0,0 +1,72 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CesiumGltf { +struct Texture; +} + +namespace CesiumGltfReader { + +/** + * @brief Reads {@link Texture} instances from JSON. + */ +class CESIUMGLTFREADER_API TextureReader { +public: + /** + * @brief Constructs a new instance. + */ + TextureReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of Texture from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of Texture from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult + readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of Texture from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> + readArrayFromJson(const rapidjson::Value& value) const; + +private: + CesiumJsonReader::JsonReaderOptions _options; +}; + +} // namespace CesiumGltfReader \ No newline at end of file diff --git a/CesiumGltfReader/generated/src/GeneratedJsonHandlers.cpp b/CesiumGltfReader/generated/src/GeneratedJsonHandlers.cpp index 660088e7a..832291018 100644 --- a/CesiumGltfReader/generated/src/GeneratedJsonHandlers.cpp +++ b/CesiumGltfReader/generated/src/GeneratedJsonHandlers.cpp @@ -1,8 +1,12 @@ // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionCesiumRTCJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -54,12 +58,52 @@ ExtensionCesiumRTCJsonHandler::readObjectKeyExtensionCesiumRTC( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionCesiumRTCReader::ExtensionCesiumRTCReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& ExtensionCesiumRTCReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionCesiumRTCReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +ExtensionCesiumRTCReader::readFromJson( + const gsl::span& data) const { + ExtensionCesiumRTCJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +ExtensionCesiumRTCReader::readFromJson(const rapidjson::Value& value) const { + ExtensionCesiumRTCJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +ExtensionCesiumRTCReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionCesiumRTC, + ExtensionCesiumRTCJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionCesiumTileEdgesJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -123,12 +167,55 @@ ExtensionCesiumTileEdgesJsonHandler::readObjectKeyExtensionCesiumTileEdges( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionCesiumTileEdgesReader::ExtensionCesiumTileEdgesReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionCesiumTileEdgesReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionCesiumTileEdgesReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +ExtensionCesiumTileEdgesReader::readFromJson( + const gsl::span& data) const { + ExtensionCesiumTileEdgesJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +ExtensionCesiumTileEdgesReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionCesiumTileEdgesJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionCesiumTileEdgesReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionCesiumTileEdges, + ExtensionCesiumTileEdgesJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionModelExtFeatureMetadataJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -200,12 +287,56 @@ CesiumJsonReader::IJsonHandler* ExtensionModelExtFeatureMetadataJsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionModelExtFeatureMetadataReader:: + ExtensionModelExtFeatureMetadataReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionModelExtFeatureMetadataReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionModelExtFeatureMetadataReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +ExtensionModelExtFeatureMetadataReader::readFromJson( + const gsl::span& data) const { + ExtensionModelExtFeatureMetadataJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +ExtensionModelExtFeatureMetadataReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionModelExtFeatureMetadataJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionModelExtFeatureMetadataReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionModelExtFeatureMetadata, + ExtensionModelExtFeatureMetadataJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -280,12 +411,58 @@ ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionMeshPrimitiveExtFeatureMetadataReader:: + ExtensionMeshPrimitiveExtFeatureMetadataReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionMeshPrimitiveExtFeatureMetadataReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionMeshPrimitiveExtFeatureMetadataReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionMeshPrimitiveExtFeatureMetadata> +ExtensionMeshPrimitiveExtFeatureMetadataReader::readFromJson( + const gsl::span& data) const { + ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionMeshPrimitiveExtFeatureMetadata> +ExtensionMeshPrimitiveExtFeatureMetadataReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionMeshPrimitiveExtFeatureMetadataReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionMeshPrimitiveExtFeatureMetadata, + ExtensionMeshPrimitiveExtFeatureMetadataJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionExtInstanceFeaturesJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -341,12 +518,55 @@ CesiumJsonReader::IJsonHandler* ExtensionExtInstanceFeaturesJsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionExtInstanceFeaturesReader::ExtensionExtInstanceFeaturesReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionExtInstanceFeaturesReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionExtInstanceFeaturesReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +ExtensionExtInstanceFeaturesReader::readFromJson( + const gsl::span& data) const { + ExtensionExtInstanceFeaturesJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +ExtensionExtInstanceFeaturesReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionExtInstanceFeaturesJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionExtInstanceFeaturesReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionExtInstanceFeatures, + ExtensionExtInstanceFeaturesJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionExtMeshFeaturesJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -401,12 +621,55 @@ ExtensionExtMeshFeaturesJsonHandler::readObjectKeyExtensionExtMeshFeatures( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionExtMeshFeaturesReader::ExtensionExtMeshFeaturesReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionExtMeshFeaturesReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionExtMeshFeaturesReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +ExtensionExtMeshFeaturesReader::readFromJson( + const gsl::span& data) const { + ExtensionExtMeshFeaturesJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +ExtensionExtMeshFeaturesReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionExtMeshFeaturesJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionExtMeshFeaturesReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionExtMeshFeatures, + ExtensionExtMeshFeaturesJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionExtMeshGpuInstancingJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -461,12 +724,55 @@ CesiumJsonReader::IJsonHandler* ExtensionExtMeshGpuInstancingJsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionExtMeshGpuInstancingReader::ExtensionExtMeshGpuInstancingReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionExtMeshGpuInstancingReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionExtMeshGpuInstancingReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +ExtensionExtMeshGpuInstancingReader::readFromJson( + const gsl::span& data) const { + ExtensionExtMeshGpuInstancingJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +ExtensionExtMeshGpuInstancingReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionExtMeshGpuInstancingJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionExtMeshGpuInstancingReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionExtMeshGpuInstancing, + ExtensionExtMeshGpuInstancingJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionBufferExtMeshoptCompressionJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -523,12 +829,58 @@ ExtensionBufferExtMeshoptCompressionJsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionBufferExtMeshoptCompressionReader:: + ExtensionBufferExtMeshoptCompressionReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionBufferExtMeshoptCompressionReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionBufferExtMeshoptCompressionReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionBufferExtMeshoptCompression> +ExtensionBufferExtMeshoptCompressionReader::readFromJson( + const gsl::span& data) const { + ExtensionBufferExtMeshoptCompressionJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionBufferExtMeshoptCompression> +ExtensionBufferExtMeshoptCompressionReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionBufferExtMeshoptCompressionJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionBufferExtMeshoptCompressionReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionBufferExtMeshoptCompression, + ExtensionBufferExtMeshoptCompressionJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionBufferViewExtMeshoptCompressionJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -606,12 +958,58 @@ ExtensionBufferViewExtMeshoptCompressionJsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionBufferViewExtMeshoptCompressionReader:: + ExtensionBufferViewExtMeshoptCompressionReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionBufferViewExtMeshoptCompressionReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionBufferViewExtMeshoptCompressionReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionBufferViewExtMeshoptCompression> +ExtensionBufferViewExtMeshoptCompressionReader::readFromJson( + const gsl::span& data) const { + ExtensionBufferViewExtMeshoptCompressionJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionBufferViewExtMeshoptCompression> +ExtensionBufferViewExtMeshoptCompressionReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionBufferViewExtMeshoptCompressionJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionBufferViewExtMeshoptCompressionReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionBufferViewExtMeshoptCompression, + ExtensionBufferViewExtMeshoptCompressionJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionModelExtStructuralMetadataJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -686,12 +1084,58 @@ CesiumJsonReader::IJsonHandler* ExtensionModelExtStructuralMetadataJsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionModelExtStructuralMetadataReader:: + ExtensionModelExtStructuralMetadataReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionModelExtStructuralMetadataReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionModelExtStructuralMetadataReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionModelExtStructuralMetadata> +ExtensionModelExtStructuralMetadataReader::readFromJson( + const gsl::span& data) const { + ExtensionModelExtStructuralMetadataJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionModelExtStructuralMetadata> +ExtensionModelExtStructuralMetadataReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionModelExtStructuralMetadataJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionModelExtStructuralMetadataReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionModelExtStructuralMetadata, + ExtensionModelExtStructuralMetadataJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -760,12 +1204,60 @@ ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionMeshPrimitiveExtStructuralMetadataReader:: + ExtensionMeshPrimitiveExtStructuralMetadataReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionMeshPrimitiveExtStructuralMetadataReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionMeshPrimitiveExtStructuralMetadataReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionMeshPrimitiveExtStructuralMetadata> +ExtensionMeshPrimitiveExtStructuralMetadataReader::readFromJson( + const gsl::span& data) const { + ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler handler( + this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionMeshPrimitiveExtStructuralMetadata> +ExtensionMeshPrimitiveExtStructuralMetadataReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler handler( + this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionMeshPrimitiveExtStructuralMetadataReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionMeshPrimitiveExtStructuralMetadata, + ExtensionMeshPrimitiveExtStructuralMetadataJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionKhrDracoMeshCompressionJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -825,12 +1317,56 @@ CesiumJsonReader::IJsonHandler* ExtensionKhrDracoMeshCompressionJsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionKhrDracoMeshCompressionReader:: + ExtensionKhrDracoMeshCompressionReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionKhrDracoMeshCompressionReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionKhrDracoMeshCompressionReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +ExtensionKhrDracoMeshCompressionReader::readFromJson( + const gsl::span& data) const { + ExtensionKhrDracoMeshCompressionJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +ExtensionKhrDracoMeshCompressionReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionKhrDracoMeshCompressionJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionKhrDracoMeshCompressionReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionKhrDracoMeshCompression, + ExtensionKhrDracoMeshCompressionJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionKhrMaterialsUnlitJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -883,12 +1419,55 @@ ExtensionKhrMaterialsUnlitJsonHandler::readObjectKeyExtensionKhrMaterialsUnlit( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionKhrMaterialsUnlitReader::ExtensionKhrMaterialsUnlitReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionKhrMaterialsUnlitReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionKhrMaterialsUnlitReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +ExtensionKhrMaterialsUnlitReader::readFromJson( + const gsl::span& data) const { + ExtensionKhrMaterialsUnlitJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +ExtensionKhrMaterialsUnlitReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionKhrMaterialsUnlitJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionKhrMaterialsUnlitReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionKhrMaterialsUnlit, + ExtensionKhrMaterialsUnlitJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionModelKhrMaterialsVariantsJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -945,12 +1524,56 @@ CesiumJsonReader::IJsonHandler* ExtensionModelKhrMaterialsVariantsJsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionModelKhrMaterialsVariantsReader:: + ExtensionModelKhrMaterialsVariantsReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionModelKhrMaterialsVariantsReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionModelKhrMaterialsVariantsReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +ExtensionModelKhrMaterialsVariantsReader::readFromJson( + const gsl::span& data) const { + ExtensionModelKhrMaterialsVariantsJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +ExtensionModelKhrMaterialsVariantsReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionModelKhrMaterialsVariantsJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionModelKhrMaterialsVariantsReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionModelKhrMaterialsVariants, + ExtensionModelKhrMaterialsVariantsJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -1010,12 +1633,58 @@ ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionMeshPrimitiveKhrMaterialsVariantsReader:: + ExtensionMeshPrimitiveKhrMaterialsVariantsReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionMeshPrimitiveKhrMaterialsVariantsReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionMeshPrimitiveKhrMaterialsVariantsReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionMeshPrimitiveKhrMaterialsVariants> +ExtensionMeshPrimitiveKhrMaterialsVariantsReader::readFromJson( + const gsl::span& data) const { + ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionMeshPrimitiveKhrMaterialsVariants> +ExtensionMeshPrimitiveKhrMaterialsVariantsReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionMeshPrimitiveKhrMaterialsVariantsReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionMeshPrimitiveKhrMaterialsVariants, + ExtensionMeshPrimitiveKhrMaterialsVariantsJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionKhrTextureBasisuJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -1069,12 +1738,55 @@ ExtensionKhrTextureBasisuJsonHandler::readObjectKeyExtensionKhrTextureBasisu( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionKhrTextureBasisuReader::ExtensionKhrTextureBasisuReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionKhrTextureBasisuReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionKhrTextureBasisuReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +ExtensionKhrTextureBasisuReader::readFromJson( + const gsl::span& data) const { + ExtensionKhrTextureBasisuJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +ExtensionKhrTextureBasisuReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionKhrTextureBasisuJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionKhrTextureBasisuReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionKhrTextureBasisu, + ExtensionKhrTextureBasisuJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionModelMaxarMeshVariantsJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -1133,12 +1845,55 @@ CesiumJsonReader::IJsonHandler* ExtensionModelMaxarMeshVariantsJsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionModelMaxarMeshVariantsReader::ExtensionModelMaxarMeshVariantsReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionModelMaxarMeshVariantsReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionModelMaxarMeshVariantsReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +ExtensionModelMaxarMeshVariantsReader::readFromJson( + const gsl::span& data) const { + ExtensionModelMaxarMeshVariantsJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +ExtensionModelMaxarMeshVariantsReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionModelMaxarMeshVariantsJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionModelMaxarMeshVariantsReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionModelMaxarMeshVariants, + ExtensionModelMaxarMeshVariantsJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionNodeMaxarMeshVariantsJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -1194,12 +1949,55 @@ CesiumJsonReader::IJsonHandler* ExtensionNodeMaxarMeshVariantsJsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionNodeMaxarMeshVariantsReader::ExtensionNodeMaxarMeshVariantsReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionNodeMaxarMeshVariantsReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionNodeMaxarMeshVariantsReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +ExtensionNodeMaxarMeshVariantsReader::readFromJson( + const gsl::span& data) const { + ExtensionNodeMaxarMeshVariantsJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +ExtensionNodeMaxarMeshVariantsReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionNodeMaxarMeshVariantsJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionNodeMaxarMeshVariantsReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionNodeMaxarMeshVariants, + ExtensionNodeMaxarMeshVariantsJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionKhrTextureTransformJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -1264,12 +2062,55 @@ CesiumJsonReader::IJsonHandler* ExtensionKhrTextureTransformJsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionKhrTextureTransformReader::ExtensionKhrTextureTransformReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionKhrTextureTransformReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionKhrTextureTransformReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +ExtensionKhrTextureTransformReader::readFromJson( + const gsl::span& data) const { + ExtensionKhrTextureTransformJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +ExtensionKhrTextureTransformReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionKhrTextureTransformJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionKhrTextureTransformReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionKhrTextureTransform, + ExtensionKhrTextureTransformJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionTextureWebpJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -1321,12 +2162,52 @@ ExtensionTextureWebpJsonHandler::readObjectKeyExtensionTextureWebp( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionTextureWebpReader::ExtensionTextureWebpReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& ExtensionTextureWebpReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionTextureWebpReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +ExtensionTextureWebpReader::readFromJson( + const gsl::span& data) const { + ExtensionTextureWebpJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +ExtensionTextureWebpReader::readFromJson(const rapidjson::Value& value) const { + ExtensionTextureWebpJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +ExtensionTextureWebpReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionTextureWebp, + ExtensionTextureWebpJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionNodeMaxarMeshVariantsMappingsValueJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -1376,12 +2257,60 @@ ExtensionNodeMaxarMeshVariantsMappingsValueJsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionNodeMaxarMeshVariantsMappingsValueReader:: + ExtensionNodeMaxarMeshVariantsMappingsValueReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionNodeMaxarMeshVariantsMappingsValueReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionNodeMaxarMeshVariantsMappingsValueReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionNodeMaxarMeshVariantsMappingsValue> +ExtensionNodeMaxarMeshVariantsMappingsValueReader::readFromJson( + const gsl::span& data) const { + ExtensionNodeMaxarMeshVariantsMappingsValueJsonHandler handler( + this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionNodeMaxarMeshVariantsMappingsValue> +ExtensionNodeMaxarMeshVariantsMappingsValueReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionNodeMaxarMeshVariantsMappingsValueJsonHandler handler( + this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionNodeMaxarMeshVariantsMappingsValueReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionNodeMaxarMeshVariantsMappingsValue, + ExtensionNodeMaxarMeshVariantsMappingsValueJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionModelMaxarMeshVariantsValueJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -1424,12 +2353,58 @@ ExtensionModelMaxarMeshVariantsValueJsonHandler:: return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } -} // namespace CesiumGltfReader -// This file was generated by generate-classes. -// DO NOT EDIT THIS FILE! -#include "ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueJsonHandler.h" +ExtensionModelMaxarMeshVariantsValueReader:: + ExtensionModelMaxarMeshVariantsValueReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionModelMaxarMeshVariantsValueReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionModelMaxarMeshVariantsValueReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionModelMaxarMeshVariantsValue> +ExtensionModelMaxarMeshVariantsValueReader::readFromJson( + const gsl::span& data) const { + ExtensionModelMaxarMeshVariantsValueJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionModelMaxarMeshVariantsValue> +ExtensionModelMaxarMeshVariantsValueReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionModelMaxarMeshVariantsValueJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionModelMaxarMeshVariantsValueReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionModelMaxarMeshVariantsValue, + ExtensionModelMaxarMeshVariantsValueJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +} // namespace CesiumGltfReader +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#include "ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -1483,12 +2458,61 @@ ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueJsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueReader:: + ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueReader::getOptions() + const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValue> +ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueReader::readFromJson( + const gsl::span& data) const { + ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueJsonHandler handler( + this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValue> +ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueJsonHandler handler( + this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueReader:: + readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValue, + ExtensionMeshPrimitiveKhrMaterialsVariantsMappingsValueJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionModelKhrMaterialsVariantsValueJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -1531,12 +2555,58 @@ ExtensionModelKhrMaterialsVariantsValueJsonHandler:: return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } +ExtensionModelKhrMaterialsVariantsValueReader:: + ExtensionModelKhrMaterialsVariantsValueReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionModelKhrMaterialsVariantsValueReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionModelKhrMaterialsVariantsValueReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionModelKhrMaterialsVariantsValue> +ExtensionModelKhrMaterialsVariantsValueReader::readFromJson( + const gsl::span& data) const { + ExtensionModelKhrMaterialsVariantsValueJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionModelKhrMaterialsVariantsValue> +ExtensionModelKhrMaterialsVariantsValueReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionModelKhrMaterialsVariantsValueJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionModelKhrMaterialsVariantsValueReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionModelKhrMaterialsVariantsValue, + ExtensionModelKhrMaterialsVariantsValueJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionExtStructuralMetadataPropertyAttributeJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -1586,12 +2656,60 @@ ExtensionExtStructuralMetadataPropertyAttributeJsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionExtStructuralMetadataPropertyAttributeReader:: + ExtensionExtStructuralMetadataPropertyAttributeReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionExtStructuralMetadataPropertyAttributeReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionExtStructuralMetadataPropertyAttributeReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataPropertyAttribute> +ExtensionExtStructuralMetadataPropertyAttributeReader::readFromJson( + const gsl::span& data) const { + ExtensionExtStructuralMetadataPropertyAttributeJsonHandler handler( + this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataPropertyAttribute> +ExtensionExtStructuralMetadataPropertyAttributeReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionExtStructuralMetadataPropertyAttributeJsonHandler handler( + this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionExtStructuralMetadataPropertyAttributeReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionExtStructuralMetadataPropertyAttribute, + ExtensionExtStructuralMetadataPropertyAttributeJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionExtStructuralMetadataPropertyAttributePropertyJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -1651,12 +2769,61 @@ ExtensionExtStructuralMetadataPropertyAttributePropertyJsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionExtStructuralMetadataPropertyAttributePropertyReader:: + ExtensionExtStructuralMetadataPropertyAttributePropertyReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionExtStructuralMetadataPropertyAttributePropertyReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionExtStructuralMetadataPropertyAttributePropertyReader::getOptions() + const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataPropertyAttributeProperty> +ExtensionExtStructuralMetadataPropertyAttributePropertyReader::readFromJson( + const gsl::span& data) const { + ExtensionExtStructuralMetadataPropertyAttributePropertyJsonHandler handler( + this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataPropertyAttributeProperty> +ExtensionExtStructuralMetadataPropertyAttributePropertyReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionExtStructuralMetadataPropertyAttributePropertyJsonHandler handler( + this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +ExtensionExtStructuralMetadataPropertyAttributePropertyReader:: + readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionExtStructuralMetadataPropertyAttributeProperty, + ExtensionExtStructuralMetadataPropertyAttributePropertyJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionExtStructuralMetadataPropertyTextureJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -1706,12 +2873,60 @@ ExtensionExtStructuralMetadataPropertyTextureJsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionExtStructuralMetadataPropertyTextureReader:: + ExtensionExtStructuralMetadataPropertyTextureReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionExtStructuralMetadataPropertyTextureReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionExtStructuralMetadataPropertyTextureReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataPropertyTexture> +ExtensionExtStructuralMetadataPropertyTextureReader::readFromJson( + const gsl::span& data) const { + ExtensionExtStructuralMetadataPropertyTextureJsonHandler handler( + this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataPropertyTexture> +ExtensionExtStructuralMetadataPropertyTextureReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionExtStructuralMetadataPropertyTextureJsonHandler handler( + this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionExtStructuralMetadataPropertyTextureReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionExtStructuralMetadataPropertyTexture, + ExtensionExtStructuralMetadataPropertyTextureJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionExtStructuralMetadataPropertyTexturePropertyJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -1770,12 +2985,61 @@ ExtensionExtStructuralMetadataPropertyTexturePropertyJsonHandler:: return this->readObjectKeyTextureInfo(objectType, str, *this->_pObject); } +ExtensionExtStructuralMetadataPropertyTexturePropertyReader:: + ExtensionExtStructuralMetadataPropertyTexturePropertyReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionExtStructuralMetadataPropertyTexturePropertyReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionExtStructuralMetadataPropertyTexturePropertyReader::getOptions() + const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataPropertyTextureProperty> +ExtensionExtStructuralMetadataPropertyTexturePropertyReader::readFromJson( + const gsl::span& data) const { + ExtensionExtStructuralMetadataPropertyTexturePropertyJsonHandler handler( + this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataPropertyTextureProperty> +ExtensionExtStructuralMetadataPropertyTexturePropertyReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionExtStructuralMetadataPropertyTexturePropertyJsonHandler handler( + this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +ExtensionExtStructuralMetadataPropertyTexturePropertyReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionExtStructuralMetadataPropertyTextureProperty, + ExtensionExtStructuralMetadataPropertyTexturePropertyJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "TextureInfoJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -1819,12 +3083,47 @@ TextureInfoJsonHandler::readObjectKeyTextureInfo( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +TextureInfoReader::TextureInfoReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& TextureInfoReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +TextureInfoReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +TextureInfoReader::readFromJson(const gsl::span& data) const { + TextureInfoJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +TextureInfoReader::readFromJson(const rapidjson::Value& value) const { + TextureInfoJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +TextureInfoReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader:: + ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionExtStructuralMetadataPropertyTableJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -1877,12 +3176,60 @@ ExtensionExtStructuralMetadataPropertyTableJsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionExtStructuralMetadataPropertyTableReader:: + ExtensionExtStructuralMetadataPropertyTableReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionExtStructuralMetadataPropertyTableReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionExtStructuralMetadataPropertyTableReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataPropertyTable> +ExtensionExtStructuralMetadataPropertyTableReader::readFromJson( + const gsl::span& data) const { + ExtensionExtStructuralMetadataPropertyTableJsonHandler handler( + this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataPropertyTable> +ExtensionExtStructuralMetadataPropertyTableReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionExtStructuralMetadataPropertyTableJsonHandler handler( + this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionExtStructuralMetadataPropertyTableReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionExtStructuralMetadataPropertyTable, + ExtensionExtStructuralMetadataPropertyTableJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionExtStructuralMetadataPropertyTablePropertyJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -1956,12 +3303,60 @@ ExtensionExtStructuralMetadataPropertyTablePropertyJsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionExtStructuralMetadataPropertyTablePropertyReader:: + ExtensionExtStructuralMetadataPropertyTablePropertyReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionExtStructuralMetadataPropertyTablePropertyReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionExtStructuralMetadataPropertyTablePropertyReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataPropertyTableProperty> +ExtensionExtStructuralMetadataPropertyTablePropertyReader::readFromJson( + const gsl::span& data) const { + ExtensionExtStructuralMetadataPropertyTablePropertyJsonHandler handler( + this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataPropertyTableProperty> +ExtensionExtStructuralMetadataPropertyTablePropertyReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionExtStructuralMetadataPropertyTablePropertyJsonHandler handler( + this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +ExtensionExtStructuralMetadataPropertyTablePropertyReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionExtStructuralMetadataPropertyTableProperty, + ExtensionExtStructuralMetadataPropertyTablePropertyJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionExtStructuralMetadataSchemaJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -2020,12 +3415,58 @@ ExtensionExtStructuralMetadataSchemaJsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionExtStructuralMetadataSchemaReader:: + ExtensionExtStructuralMetadataSchemaReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionExtStructuralMetadataSchemaReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionExtStructuralMetadataSchemaReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataSchema> +ExtensionExtStructuralMetadataSchemaReader::readFromJson( + const gsl::span& data) const { + ExtensionExtStructuralMetadataSchemaJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataSchema> +ExtensionExtStructuralMetadataSchemaReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionExtStructuralMetadataSchemaJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionExtStructuralMetadataSchemaReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionExtStructuralMetadataSchema, + ExtensionExtStructuralMetadataSchemaJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionExtStructuralMetadataEnumJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -2077,12 +3518,56 @@ CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataEnumJsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionExtStructuralMetadataEnumReader:: + ExtensionExtStructuralMetadataEnumReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionExtStructuralMetadataEnumReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionExtStructuralMetadataEnumReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +ExtensionExtStructuralMetadataEnumReader::readFromJson( + const gsl::span& data) const { + ExtensionExtStructuralMetadataEnumJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +ExtensionExtStructuralMetadataEnumReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionExtStructuralMetadataEnumJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionExtStructuralMetadataEnumReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionExtStructuralMetadataEnum, + ExtensionExtStructuralMetadataEnumJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionExtStructuralMetadataEnumValueJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -2132,12 +3617,58 @@ ExtensionExtStructuralMetadataEnumValueJsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionExtStructuralMetadataEnumValueReader:: + ExtensionExtStructuralMetadataEnumValueReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionExtStructuralMetadataEnumValueReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionExtStructuralMetadataEnumValueReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataEnumValue> +ExtensionExtStructuralMetadataEnumValueReader::readFromJson( + const gsl::span& data) const { + ExtensionExtStructuralMetadataEnumValueJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataEnumValue> +ExtensionExtStructuralMetadataEnumValueReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionExtStructuralMetadataEnumValueJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionExtStructuralMetadataEnumValueReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionExtStructuralMetadataEnumValue, + ExtensionExtStructuralMetadataEnumValueJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionExtStructuralMetadataClassJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -2186,12 +3717,58 @@ CesiumJsonReader::IJsonHandler* ExtensionExtStructuralMetadataClassJsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionExtStructuralMetadataClassReader:: + ExtensionExtStructuralMetadataClassReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionExtStructuralMetadataClassReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionExtStructuralMetadataClassReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataClass> +ExtensionExtStructuralMetadataClassReader::readFromJson( + const gsl::span& data) const { + ExtensionExtStructuralMetadataClassJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataClass> +ExtensionExtStructuralMetadataClassReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionExtStructuralMetadataClassJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionExtStructuralMetadataClassReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionExtStructuralMetadataClass, + ExtensionExtStructuralMetadataClassJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionExtStructuralMetadataClassPropertyJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -2280,12 +3857,60 @@ ExtensionExtStructuralMetadataClassPropertyJsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionExtStructuralMetadataClassPropertyReader:: + ExtensionExtStructuralMetadataClassPropertyReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionExtStructuralMetadataClassPropertyReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionExtStructuralMetadataClassPropertyReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataClassProperty> +ExtensionExtStructuralMetadataClassPropertyReader::readFromJson( + const gsl::span& data) const { + ExtensionExtStructuralMetadataClassPropertyJsonHandler handler( + this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtStructuralMetadataClassProperty> +ExtensionExtStructuralMetadataClassPropertyReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionExtStructuralMetadataClassPropertyJsonHandler handler( + this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionExtStructuralMetadataClassPropertyReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionExtStructuralMetadataClassProperty, + ExtensionExtStructuralMetadataClassPropertyJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionExtMeshFeaturesFeatureIdJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -2343,12 +3968,56 @@ CesiumJsonReader::IJsonHandler* ExtensionExtMeshFeaturesFeatureIdJsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionExtMeshFeaturesFeatureIdReader:: + ExtensionExtMeshFeaturesFeatureIdReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionExtMeshFeaturesFeatureIdReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionExtMeshFeaturesFeatureIdReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +ExtensionExtMeshFeaturesFeatureIdReader::readFromJson( + const gsl::span& data) const { + ExtensionExtMeshFeaturesFeatureIdJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +ExtensionExtMeshFeaturesFeatureIdReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionExtMeshFeaturesFeatureIdJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionExtMeshFeaturesFeatureIdReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionExtMeshFeaturesFeatureId, + ExtensionExtMeshFeaturesFeatureIdJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionExtMeshFeaturesFeatureIdTextureJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -2391,12 +4060,58 @@ ExtensionExtMeshFeaturesFeatureIdTextureJsonHandler:: return this->readObjectKeyTextureInfo(objectType, str, *this->_pObject); } +ExtensionExtMeshFeaturesFeatureIdTextureReader:: + ExtensionExtMeshFeaturesFeatureIdTextureReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionExtMeshFeaturesFeatureIdTextureReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionExtMeshFeaturesFeatureIdTextureReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtMeshFeaturesFeatureIdTexture> +ExtensionExtMeshFeaturesFeatureIdTextureReader::readFromJson( + const gsl::span& data) const { + ExtensionExtMeshFeaturesFeatureIdTextureJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtMeshFeaturesFeatureIdTexture> +ExtensionExtMeshFeaturesFeatureIdTextureReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionExtMeshFeaturesFeatureIdTextureJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionExtMeshFeaturesFeatureIdTextureReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionExtMeshFeaturesFeatureIdTexture, + ExtensionExtMeshFeaturesFeatureIdTextureJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ExtensionExtInstanceFeaturesFeatureIdJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -2452,12 +4167,58 @@ ExtensionExtInstanceFeaturesFeatureIdJsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ExtensionExtInstanceFeaturesFeatureIdReader:: + ExtensionExtInstanceFeaturesFeatureIdReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +ExtensionExtInstanceFeaturesFeatureIdReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ExtensionExtInstanceFeaturesFeatureIdReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtInstanceFeaturesFeatureId> +ExtensionExtInstanceFeaturesFeatureIdReader::readFromJson( + const gsl::span& data) const { + ExtensionExtInstanceFeaturesFeatureIdJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult< + CesiumGltf::ExtensionExtInstanceFeaturesFeatureId> +ExtensionExtInstanceFeaturesFeatureIdReader::readFromJson( + const rapidjson::Value& value) const { + ExtensionExtInstanceFeaturesFeatureIdJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +ExtensionExtInstanceFeaturesFeatureIdReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::ExtensionExtInstanceFeaturesFeatureId, + ExtensionExtInstanceFeaturesFeatureIdJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "FeatureIDTextureJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -2501,12 +4262,51 @@ FeatureIDTextureJsonHandler::readObjectKeyFeatureIDTexture( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +FeatureIDTextureReader::FeatureIDTextureReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& FeatureIDTextureReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +FeatureIDTextureReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +FeatureIDTextureReader::readFromJson( + const gsl::span& data) const { + FeatureIDTextureJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +FeatureIDTextureReader::readFromJson(const rapidjson::Value& value) const { + FeatureIDTextureJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +FeatureIDTextureReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::FeatureIDTexture, + FeatureIDTextureJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "TextureAccessorJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -2550,12 +4350,50 @@ TextureAccessorJsonHandler::readObjectKeyTextureAccessor( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +TextureAccessorReader::TextureAccessorReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& TextureAccessorReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +TextureAccessorReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +TextureAccessorReader::readFromJson( + const gsl::span& data) const { + TextureAccessorJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +TextureAccessorReader::readFromJson(const rapidjson::Value& value) const { + TextureAccessorJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +TextureAccessorReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader:: + ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "FeatureIDAttributeJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -2599,12 +4437,52 @@ FeatureIDAttributeJsonHandler::readObjectKeyFeatureIDAttribute( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +FeatureIDAttributeReader::FeatureIDAttributeReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& FeatureIDAttributeReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +FeatureIDAttributeReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +FeatureIDAttributeReader::readFromJson( + const gsl::span& data) const { + FeatureIDAttributeJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +FeatureIDAttributeReader::readFromJson(const rapidjson::Value& value) const { + FeatureIDAttributeJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +FeatureIDAttributeReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::FeatureIDAttribute, + FeatureIDAttributeJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "FeatureIDsJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -2650,12 +4528,47 @@ CesiumJsonReader::IJsonHandler* FeatureIDsJsonHandler::readObjectKeyFeatureIDs( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } -} // namespace CesiumGltfReader +FeatureIDsReader::FeatureIDsReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& FeatureIDsReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +FeatureIDsReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +FeatureIDsReader::readFromJson(const gsl::span& data) const { + FeatureIDsJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +FeatureIDsReader::readFromJson(const rapidjson::Value& value) const { + FeatureIDsJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +FeatureIDsReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader:: + ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +} // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "FeatureTextureJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -2699,12 +4612,50 @@ FeatureTextureJsonHandler::readObjectKeyFeatureTexture( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +FeatureTextureReader::FeatureTextureReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& FeatureTextureReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +FeatureTextureReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +FeatureTextureReader::readFromJson( + const gsl::span& data) const { + FeatureTextureJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +FeatureTextureReader::readFromJson(const rapidjson::Value& value) const { + FeatureTextureJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +FeatureTextureReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader:: + ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "FeatureTableJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -2751,12 +4702,47 @@ FeatureTableJsonHandler::readObjectKeyFeatureTable( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +FeatureTableReader::FeatureTableReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& FeatureTableReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +FeatureTableReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +FeatureTableReader::readFromJson(const gsl::span& data) const { + FeatureTableJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +FeatureTableReader::readFromJson(const rapidjson::Value& value) const { + FeatureTableJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +FeatureTableReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader:: + ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "FeatureTablePropertyJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -2812,12 +4798,52 @@ FeatureTablePropertyJsonHandler::readObjectKeyFeatureTableProperty( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +FeatureTablePropertyReader::FeatureTablePropertyReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& FeatureTablePropertyReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +FeatureTablePropertyReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +FeatureTablePropertyReader::readFromJson( + const gsl::span& data) const { + FeatureTablePropertyJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +FeatureTablePropertyReader::readFromJson(const rapidjson::Value& value) const { + FeatureTablePropertyJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +FeatureTablePropertyReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::FeatureTableProperty, + FeatureTablePropertyJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "StatisticsJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -2857,12 +4883,47 @@ CesiumJsonReader::IJsonHandler* StatisticsJsonHandler::readObjectKeyStatistics( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +StatisticsReader::StatisticsReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& StatisticsReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +StatisticsReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +StatisticsReader::readFromJson(const gsl::span& data) const { + StatisticsJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +StatisticsReader::readFromJson(const rapidjson::Value& value) const { + StatisticsJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +StatisticsReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader:: + ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ClassStatisticsJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -2906,12 +4967,50 @@ ClassStatisticsJsonHandler::readObjectKeyClassStatistics( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ClassStatisticsReader::ClassStatisticsReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& ClassStatisticsReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ClassStatisticsReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +ClassStatisticsReader::readFromJson( + const gsl::span& data) const { + ClassStatisticsJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +ClassStatisticsReader::readFromJson(const rapidjson::Value& value) const { + ClassStatisticsJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +ClassStatisticsReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader:: + ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "PropertyStatisticsJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -2976,12 +5075,52 @@ PropertyStatisticsJsonHandler::readObjectKeyPropertyStatistics( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +PropertyStatisticsReader::PropertyStatisticsReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& PropertyStatisticsReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +PropertyStatisticsReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +PropertyStatisticsReader::readFromJson( + const gsl::span& data) const { + PropertyStatisticsJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +PropertyStatisticsReader::readFromJson(const rapidjson::Value& value) const { + PropertyStatisticsJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +PropertyStatisticsReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::PropertyStatistics, + PropertyStatisticsJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "SchemaJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -3033,12 +5172,45 @@ CesiumJsonReader::IJsonHandler* SchemaJsonHandler::readObjectKeySchema( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +SchemaReader::SchemaReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& SchemaReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& SchemaReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +SchemaReader::readFromJson(const gsl::span& data) const { + SchemaJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +SchemaReader::readFromJson(const rapidjson::Value& value) const { + SchemaJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +SchemaReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "EnumJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -3087,12 +5259,45 @@ CesiumJsonReader::IJsonHandler* EnumJsonHandler::readObjectKeyEnum( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +EnumReader::EnumReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& EnumReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& EnumReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +EnumReader::readFromJson(const gsl::span& data) const { + EnumJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +EnumReader::readFromJson(const rapidjson::Value& value) const { + EnumJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +EnumReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler handler( + this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "EnumValueJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -3138,12 +5343,46 @@ CesiumJsonReader::IJsonHandler* EnumValueJsonHandler::readObjectKeyEnumValue( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +EnumValueReader::EnumValueReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& EnumValueReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& EnumValueReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +EnumValueReader::readFromJson(const gsl::span& data) const { + EnumValueJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +EnumValueReader::readFromJson(const rapidjson::Value& value) const { + EnumValueJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +EnumValueReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader:: + ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ClassJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -3189,12 +5428,45 @@ CesiumJsonReader::IJsonHandler* ClassJsonHandler::readObjectKeyClass( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ClassReader::ClassReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& ClassReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& ClassReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +ClassReader::readFromJson(const gsl::span& data) const { + ClassJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +ClassReader::readFromJson(const rapidjson::Value& value) const { + ClassJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +ClassReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ClassPropertyJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -3268,12 +5540,50 @@ ClassPropertyJsonHandler::readObjectKeyClassProperty( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ClassPropertyReader::ClassPropertyReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& ClassPropertyReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +ClassPropertyReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +ClassPropertyReader::readFromJson( + const gsl::span& data) const { + ClassPropertyJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +ClassPropertyReader::readFromJson(const rapidjson::Value& value) const { + ClassPropertyJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +ClassPropertyReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader:: + ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ModelJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -3364,12 +5674,45 @@ CesiumJsonReader::IJsonHandler* ModelJsonHandler::readObjectKeyModel( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +ModelReader::ModelReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& ModelReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& ModelReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +ModelReader::readFromJson(const gsl::span& data) const { + ModelJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +ModelReader::readFromJson(const rapidjson::Value& value) const { + ModelJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +ModelReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "TextureJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -3412,12 +5755,45 @@ CesiumJsonReader::IJsonHandler* TextureJsonHandler::readObjectKeyTexture( return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } +TextureReader::TextureReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& TextureReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& TextureReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +TextureReader::readFromJson(const gsl::span& data) const { + TextureJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +TextureReader::readFromJson(const rapidjson::Value& value) const { + TextureJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +TextureReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "SkinJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -3466,12 +5842,45 @@ CesiumJsonReader::IJsonHandler* SkinJsonHandler::readObjectKeySkin( return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } +SkinReader::SkinReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& SkinReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& SkinReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +SkinReader::readFromJson(const gsl::span& data) const { + SkinJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +SkinReader::readFromJson(const rapidjson::Value& value) const { + SkinJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +SkinReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler handler( + this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "SceneJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -3510,12 +5919,45 @@ CesiumJsonReader::IJsonHandler* SceneJsonHandler::readObjectKeyScene( return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } +SceneReader::SceneReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& SceneReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& SceneReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +SceneReader::readFromJson(const gsl::span& data) const { + SceneJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +SceneReader::readFromJson(const rapidjson::Value& value) const { + SceneJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +SceneReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "SamplerJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -3564,12 +6006,45 @@ CesiumJsonReader::IJsonHandler* SamplerJsonHandler::readObjectKeySampler( return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } +SamplerReader::SamplerReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& SamplerReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& SamplerReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +SamplerReader::readFromJson(const gsl::span& data) const { + SamplerJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +SamplerReader::readFromJson(const rapidjson::Value& value) const { + SamplerJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +SamplerReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "NodeJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -3633,12 +6108,45 @@ CesiumJsonReader::IJsonHandler* NodeJsonHandler::readObjectKeyNode( return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } +NodeReader::NodeReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& NodeReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& NodeReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +NodeReader::readFromJson(const gsl::span& data) const { + NodeJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +NodeReader::readFromJson(const rapidjson::Value& value) const { + NodeJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +NodeReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler handler( + this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "MeshJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -3681,12 +6189,45 @@ CesiumJsonReader::IJsonHandler* MeshJsonHandler::readObjectKeyMesh( return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } +MeshReader::MeshReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& MeshReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& MeshReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +MeshReader::readFromJson(const gsl::span& data) const { + MeshJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +MeshReader::readFromJson(const rapidjson::Value& value) const { + MeshJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +MeshReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler handler( + this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "MeshPrimitiveJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -3739,12 +6280,50 @@ MeshPrimitiveJsonHandler::readObjectKeyMeshPrimitive( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +MeshPrimitiveReader::MeshPrimitiveReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& MeshPrimitiveReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +MeshPrimitiveReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +MeshPrimitiveReader::readFromJson( + const gsl::span& data) const { + MeshPrimitiveJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +MeshPrimitiveReader::readFromJson(const rapidjson::Value& value) const { + MeshPrimitiveJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +MeshPrimitiveReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader:: + ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "MaterialJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -3814,12 +6393,45 @@ CesiumJsonReader::IJsonHandler* MaterialJsonHandler::readObjectKeyMaterial( return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } +MaterialReader::MaterialReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& MaterialReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& MaterialReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +MaterialReader::readFromJson(const gsl::span& data) const { + MaterialJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +MaterialReader::readFromJson(const rapidjson::Value& value) const { + MaterialJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +MaterialReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "MaterialOcclusionTextureInfoJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -3861,12 +6473,55 @@ CesiumJsonReader::IJsonHandler* MaterialOcclusionTextureInfoJsonHandler:: return this->readObjectKeyTextureInfo(objectType, str, *this->_pObject); } +MaterialOcclusionTextureInfoReader::MaterialOcclusionTextureInfoReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +MaterialOcclusionTextureInfoReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +MaterialOcclusionTextureInfoReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +MaterialOcclusionTextureInfoReader::readFromJson( + const gsl::span& data) const { + MaterialOcclusionTextureInfoJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +MaterialOcclusionTextureInfoReader::readFromJson( + const rapidjson::Value& value) const { + MaterialOcclusionTextureInfoJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +MaterialOcclusionTextureInfoReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::MaterialOcclusionTextureInfo, + MaterialOcclusionTextureInfoJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "MaterialNormalTextureInfoJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -3907,12 +6562,55 @@ MaterialNormalTextureInfoJsonHandler::readObjectKeyMaterialNormalTextureInfo( return this->readObjectKeyTextureInfo(objectType, str, *this->_pObject); } +MaterialNormalTextureInfoReader::MaterialNormalTextureInfoReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +MaterialNormalTextureInfoReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +MaterialNormalTextureInfoReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +MaterialNormalTextureInfoReader::readFromJson( + const gsl::span& data) const { + MaterialNormalTextureInfoJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +MaterialNormalTextureInfoReader::readFromJson( + const rapidjson::Value& value) const { + MaterialNormalTextureInfoJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +MaterialNormalTextureInfoReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::MaterialNormalTextureInfo, + MaterialNormalTextureInfoJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "MaterialPBRMetallicRoughnessJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -3979,12 +6677,55 @@ CesiumJsonReader::IJsonHandler* MaterialPBRMetallicRoughnessJsonHandler:: return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +MaterialPBRMetallicRoughnessReader::MaterialPBRMetallicRoughnessReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +MaterialPBRMetallicRoughnessReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +MaterialPBRMetallicRoughnessReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +MaterialPBRMetallicRoughnessReader::readFromJson( + const gsl::span& data) const { + MaterialPBRMetallicRoughnessJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +MaterialPBRMetallicRoughnessReader::readFromJson( + const rapidjson::Value& value) const { + MaterialPBRMetallicRoughnessJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +MaterialPBRMetallicRoughnessReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::MaterialPBRMetallicRoughness, + MaterialPBRMetallicRoughnessJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "ImageJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -4030,12 +6771,45 @@ CesiumJsonReader::IJsonHandler* ImageJsonHandler::readObjectKeyImage( return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } +ImageReader::ImageReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& ImageReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& ImageReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +ImageReader::readFromJson(const gsl::span& data) const { + ImageJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +ImageReader::readFromJson(const rapidjson::Value& value) const { + ImageJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +ImageReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "CameraJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -4081,12 +6855,45 @@ CesiumJsonReader::IJsonHandler* CameraJsonHandler::readObjectKeyCamera( return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } +CameraReader::CameraReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& CameraReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& CameraReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +CameraReader::readFromJson(const gsl::span& data) const { + CameraJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +CameraReader::readFromJson(const rapidjson::Value& value) const { + CameraJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +CameraReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "CameraPerspectiveJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -4136,12 +6943,52 @@ CameraPerspectiveJsonHandler::readObjectKeyCameraPerspective( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +CameraPerspectiveReader::CameraPerspectiveReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& CameraPerspectiveReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +CameraPerspectiveReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +CameraPerspectiveReader::readFromJson( + const gsl::span& data) const { + CameraPerspectiveJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +CameraPerspectiveReader::readFromJson(const rapidjson::Value& value) const { + CameraPerspectiveJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +CameraPerspectiveReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::CameraPerspective, + CameraPerspectiveJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "CameraOrthographicJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -4191,12 +7038,52 @@ CameraOrthographicJsonHandler::readObjectKeyCameraOrthographic( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +CameraOrthographicReader::CameraOrthographicReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& CameraOrthographicReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +CameraOrthographicReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +CameraOrthographicReader::readFromJson( + const gsl::span& data) const { + CameraOrthographicJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +CameraOrthographicReader::readFromJson(const rapidjson::Value& value) const { + CameraOrthographicJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +CameraOrthographicReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::CameraOrthographic, + CameraOrthographicJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "BufferViewJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -4248,12 +7135,47 @@ CesiumJsonReader::IJsonHandler* BufferViewJsonHandler::readObjectKeyBufferView( return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } +BufferViewReader::BufferViewReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& BufferViewReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +BufferViewReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +BufferViewReader::readFromJson(const gsl::span& data) const { + BufferViewJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +BufferViewReader::readFromJson(const rapidjson::Value& value) const { + BufferViewJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +BufferViewReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader:: + ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "BufferJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -4296,12 +7218,45 @@ CesiumJsonReader::IJsonHandler* BufferJsonHandler::readObjectKeyBuffer( return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } +BufferReader::BufferReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& BufferReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& BufferReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +BufferReader::readFromJson(const gsl::span& data) const { + BufferJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +BufferReader::readFromJson(const rapidjson::Value& value) const { + BufferJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +BufferReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "AssetJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -4350,12 +7305,45 @@ CesiumJsonReader::IJsonHandler* AssetJsonHandler::readObjectKeyAsset( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +AssetReader::AssetReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& AssetReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& AssetReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +AssetReader::readFromJson(const gsl::span& data) const { + AssetJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +AssetReader::readFromJson(const rapidjson::Value& value) const { + AssetJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +AssetReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "AnimationJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -4398,12 +7386,46 @@ CesiumJsonReader::IJsonHandler* AnimationJsonHandler::readObjectKeyAnimation( return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } +AnimationReader::AnimationReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& AnimationReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& AnimationReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +AnimationReader::readFromJson(const gsl::span& data) const { + AnimationJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +AnimationReader::readFromJson(const rapidjson::Value& value) const { + AnimationJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +AnimationReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader:: + ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "AnimationSamplerJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -4450,12 +7472,51 @@ AnimationSamplerJsonHandler::readObjectKeyAnimationSampler( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +AnimationSamplerReader::AnimationSamplerReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& AnimationSamplerReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +AnimationSamplerReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +AnimationSamplerReader::readFromJson( + const gsl::span& data) const { + AnimationSamplerJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +AnimationSamplerReader::readFromJson(const rapidjson::Value& value) const { + AnimationSamplerJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +AnimationSamplerReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::AnimationSampler, + AnimationSamplerJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "AnimationChannelJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -4499,12 +7560,51 @@ AnimationChannelJsonHandler::readObjectKeyAnimationChannel( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +AnimationChannelReader::AnimationChannelReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& AnimationChannelReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +AnimationChannelReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +AnimationChannelReader::readFromJson( + const gsl::span& data) const { + AnimationChannelJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +AnimationChannelReader::readFromJson(const rapidjson::Value& value) const { + AnimationChannelJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +AnimationChannelReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::AnimationChannel, + AnimationChannelJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "AnimationChannelTargetJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -4548,12 +7648,55 @@ AnimationChannelTargetJsonHandler::readObjectKeyAnimationChannelTarget( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +AnimationChannelTargetReader::AnimationChannelTargetReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& +AnimationChannelTargetReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +AnimationChannelTargetReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +AnimationChannelTargetReader::readFromJson( + const gsl::span& data) const { + AnimationChannelTargetJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +AnimationChannelTargetReader::readFromJson( + const rapidjson::Value& value) const { + AnimationChannelTargetJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult< + std::vector> +AnimationChannelTargetReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::AnimationChannelTarget, + AnimationChannelTargetJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "AccessorJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -4617,12 +7760,45 @@ CesiumJsonReader::IJsonHandler* AccessorJsonHandler::readObjectKeyAccessor( return this->readObjectKeyNamedObject(objectType, str, *this->_pObject); } +AccessorReader::AccessorReader() { registerExtensions(this->_options); } + +CesiumJsonReader::JsonReaderOptions& AccessorReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& AccessorReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +AccessorReader::readFromJson(const gsl::span& data) const { + AccessorJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +AccessorReader::readFromJson(const rapidjson::Value& value) const { + AccessorJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +AccessorReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "AccessorSparseJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -4669,12 +7845,50 @@ AccessorSparseJsonHandler::readObjectKeyAccessorSparse( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +AccessorSparseReader::AccessorSparseReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& AccessorSparseReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +AccessorSparseReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +AccessorSparseReader::readFromJson( + const gsl::span& data) const { + AccessorSparseJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +AccessorSparseReader::readFromJson(const rapidjson::Value& value) const { + AccessorSparseJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +AccessorSparseReader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader:: + ArrayJsonHandler + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "AccessorSparseValuesJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -4718,12 +7932,52 @@ AccessorSparseValuesJsonHandler::readObjectKeyAccessorSparseValues( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +AccessorSparseValuesReader::AccessorSparseValuesReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& AccessorSparseValuesReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +AccessorSparseValuesReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +AccessorSparseValuesReader::readFromJson( + const gsl::span& data) const { + AccessorSparseValuesJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +AccessorSparseValuesReader::readFromJson(const rapidjson::Value& value) const { + AccessorSparseValuesJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +AccessorSparseValuesReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::AccessorSparseValues, + AccessorSparseValuesJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "AccessorSparseIndicesJsonHandler.h" +#include "registerExtensions.h" #include +#include +#include +#include #include #include @@ -4770,4 +8024,40 @@ AccessorSparseIndicesJsonHandler::readObjectKeyAccessorSparseIndices( return this->readObjectKeyExtensibleObject(objectType, str, *this->_pObject); } +AccessorSparseIndicesReader::AccessorSparseIndicesReader() { + registerExtensions(this->_options); +} + +CesiumJsonReader::JsonReaderOptions& AccessorSparseIndicesReader::getOptions() { + return this->_options; +} + +const CesiumJsonReader::JsonReaderOptions& +AccessorSparseIndicesReader::getOptions() const { + return this->_options; +} + +CesiumJsonReader::ReadJsonResult +AccessorSparseIndicesReader::readFromJson( + const gsl::span& data) const { + AccessorSparseIndicesJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); +} + +CesiumJsonReader::ReadJsonResult +AccessorSparseIndicesReader::readFromJson(const rapidjson::Value& value) const { + AccessorSparseIndicesJsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + +CesiumJsonReader::ReadJsonResult> +AccessorSparseIndicesReader::readArrayFromJson( + const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler< + CesiumGltf::AccessorSparseIndices, + AccessorSparseIndicesJsonHandler> + handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); +} + } // namespace CesiumGltfReader diff --git a/tools/generate-classes/NameFormatters.js b/tools/generate-classes/NameFormatters.js index 5cafe07d5..4a2a49516 100644 --- a/tools/generate-classes/NameFormatters.js +++ b/tools/generate-classes/NameFormatters.js @@ -34,7 +34,7 @@ const NameFormatters = { } }, - getReaderIncludeFromName: function getReaderIncludeFromName( + getJsonHandlerIncludeFromName: function getJsonHandlerIncludeFromName( name, readerNamespace ) { @@ -54,7 +54,7 @@ const NameFormatters = { } }, - getReaderName: function getReaderName(name, readerNamespace) { + getJsonHandlerName: function getJsonHandlerName(name, readerNamespace) { const pieces = name.match(qualifiedTypeNameRegex); if (pieces && pieces.groups && pieces.groups.namespace) { const namespace = NameFormatters.getReaderNamespace( diff --git a/tools/generate-classes/generate.js b/tools/generate-classes/generate.js index 2b4771145..4cd3dd03d 100644 --- a/tools/generate-classes/generate.js +++ b/tools/generate-classes/generate.js @@ -122,39 +122,42 @@ function generate(options, schema, writers) { ); fs.writeFileSync(headerOutputPath, unindent(header), "utf-8"); - let readerHeaders = lodash.uniq([ - NameFormatters.getReaderIncludeFromName(base, readerNamespace), + let jsonHandlerHeaders = lodash.uniq([ + NameFormatters.getJsonHandlerIncludeFromName(base, readerNamespace), `<${namespace}/${name}.h>`, ...lodash.flatten(properties.map((property) => property.readerHeaders)), ]); // Prevent header from including itself for recursive types like Tile - readerHeaders = readerHeaders.filter((readerHeader) => { + jsonHandlerHeaders = jsonHandlerHeaders.filter((readerHeader) => { return readerHeader !== `"${name}JsonHandler.h"`; }); - readerHeaders.sort(); + jsonHandlerHeaders.sort(); const readerLocalTypes = lodash.uniq( lodash.flatten(properties.map((property) => property.readerLocalTypes)) ); - const baseReader = NameFormatters.getReaderName(base, readerNamespace); + const baseJsonHandler = NameFormatters.getJsonHandlerName( + base, + readerNamespace + ); // prettier-ignore - const readerHeader = ` + const jsonHandlerHeader = ` // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #pragma once - ${readerHeaders.map((header) => `#include ${header}`).join("\n")} + ${jsonHandlerHeaders.map((header) => `#include ${header}`).join("\n")} namespace CesiumJsonReader { class JsonReaderOptions; } namespace ${readerNamespace} { - class ${name}JsonHandler : public ${baseReader}${thisConfig.extensionName ? `, public CesiumJsonReader::IExtensionJsonHandler` : ""} { + class ${name}JsonHandler : public ${baseJsonHandler}${thisConfig.extensionName ? `, public CesiumJsonReader::IExtensionJsonHandler` : ""} { public: using ValueType = ${namespace}::${name}; @@ -187,25 +190,116 @@ function generate(options, schema, writers) { } `; - const readerHeaderOutputDir = path.join(readerOutputDir, "generated", "src"); + const jsonHandlerHeaderOutputDir = path.join( + readerOutputDir, + "generated", + "src" + ); + fs.mkdirSync(jsonHandlerHeaderOutputDir, { recursive: true }); + + const jsonHandlerHeaderOutputPath = path.join( + jsonHandlerHeaderOutputDir, + name + "JsonHandler.h" + ); + fs.writeFileSync( + jsonHandlerHeaderOutputPath, + unindent(jsonHandlerHeader), + "utf-8" + ); + + const readerHeader = ` + // This file was generated by generate-classes. + // DO NOT EDIT THIS FILE! + #pragma once + + #include <${readerNamespace}/Library.h> + #include + #include + #include <${namespace}/${name}.h> + #include + #include + #include + + namespace ${namespace} { + struct ${name}; + } + + namespace ${readerNamespace} { + + /** + * @brief Reads {@link ${name}} instances from JSON. + */ + class ${readerNamespace.toUpperCase()}_API ${name}Reader { + public: + /** + * @brief Constructs a new instance. + */ + ${name}Reader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Reads an instance of ${name} from a byte buffer. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult<${namespace}::${name}> readFromJson(const gsl::span& data) const; + + /** + * @brief Reads an instance of ${name} from a rapidJson::Value. + * + * @param data The buffer from which to read the instance. + * @return The result of reading the instance. + */ + CesiumJsonReader::ReadJsonResult<${namespace}::${name}> readFromJson(const rapidjson::Value& value) const; + + /** + * @brief Reads an array of instances of ${name} from a rapidJson::Value. + * + * @param data The buffer from which to read the array of instances. + * @return The result of reading the array of instances. + */ + CesiumJsonReader::ReadJsonResult> readArrayFromJson(const rapidjson::Value& value) const; + + private: + CesiumJsonReader::JsonReaderOptions _options; + }; + + } // namespace ${readerNamespace}`; + + const readerHeaderOutputDir = path.join( + readerOutputDir, + "generated", + "include", + readerNamespace + ); fs.mkdirSync(readerHeaderOutputDir, { recursive: true }); const readerHeaderOutputPath = path.join( readerHeaderOutputDir, - name + "JsonHandler.h" + name + "Reader.h" ); fs.writeFileSync(readerHeaderOutputPath, unindent(readerHeader), "utf-8"); - const readerLocalTypesImpl = lodash.uniq( + const jsonHandlerLocalTypesImpl = lodash.uniq( lodash.flatten(properties.map((property) => property.readerLocalTypesImpl)) ); - const readerHeadersImpl = lodash.uniq([ + const jsonHandlerHeadersImpl = lodash.uniq([ ...lodash.flatten(properties.map((property) => property.readerHeadersImpl)), ]); - readerHeadersImpl.sort(); + jsonHandlerHeadersImpl.sort(); - function generateReaderOptionsInitializerList(properties, varName) { + function generateJsonHandlerOptionsInitializerList(properties, varName) { const initializerList = properties .filter((p) => p.readerType.toLowerCase().indexOf("jsonhandler") != -1) .map( @@ -218,22 +312,26 @@ function generate(options, schema, writers) { return initializerList == "" ? "" : ", " + initializerList; } // prettier-ignore - const readerImpl = ` + const jsonHandlerImpl = ` // This file was generated by generate-classes. // DO NOT EDIT THIS FILE! #include "${name}JsonHandler.h" #include <${namespace}/${name}.h> - ${readerHeadersImpl.map((header) => `#include ${header}`).join("\n")} + ${jsonHandlerHeadersImpl.map((header) => `#include ${header}`).join("\n")} + #include <${readerNamespace}/${name}Reader.h> + #include + #include + #include "registerExtensions.h" #include #include namespace ${readerNamespace} { - ${name}JsonHandler::${name}JsonHandler(const CesiumJsonReader::JsonReaderOptions& options) noexcept : ${baseReader}(options)${generateReaderOptionsInitializerList(properties, 'options')} {} + ${name}JsonHandler::${name}JsonHandler(const CesiumJsonReader::JsonReaderOptions& options) noexcept : ${baseJsonHandler}(options)${generateJsonHandlerOptionsInitializerList(properties, 'options')} {} void ${name}JsonHandler::reset(CesiumJsonReader::IJsonHandler* pParentHandler, ${namespace}::${name}* pObject) { - ${baseReader}::reset(pParentHandler, pObject); + ${baseJsonHandler}::reset(pParentHandler, pObject); this->_pObject = pObject; } @@ -267,7 +365,32 @@ function generate(options, schema, writers) { return this->readObjectKey${NameFormatters.removeNamespace(base)}(objectType, str, *this->_pObject); } - ${indent(readerLocalTypesImpl.join("\n\n"), 8)} + ${name}Reader::${name}Reader() { registerExtensions(this->_options); } + + CesiumJsonReader::JsonReaderOptions& ${name}Reader::getOptions() { + return this->_options; + } + + const CesiumJsonReader::JsonReaderOptions& ${name}Reader::getOptions() const { + return this->_options; + } + + CesiumJsonReader::ReadJsonResult<${namespace}::${name}> ${name}Reader::readFromJson(const gsl::span& data) const { + ${name}JsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(data, handler); + } + + CesiumJsonReader::ReadJsonResult<${namespace}::${name}> ${name}Reader::readFromJson(const rapidjson::Value& value) const { + ${name}JsonHandler handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); + } + + CesiumJsonReader::ReadJsonResult> ${name}Reader::readArrayFromJson(const rapidjson::Value& value) const { + CesiumJsonReader::ArrayJsonHandler<${namespace}::${name}, ${name}JsonHandler> handler(this->_options); + return CesiumJsonReader::JsonReader::readJson(value, handler); + } + + ${indent(jsonHandlerLocalTypesImpl.join("\n\n"), 8)} } // namespace ${readerNamespace} `; @@ -389,16 +512,24 @@ function generate(options, schema, writers) { if (options.oneHandlerFile) { const readerSourceOutputPath = path.join( - readerHeaderOutputDir, + jsonHandlerHeaderOutputDir, "GeneratedJsonHandlers.cpp" ); - fs.appendFileSync(readerSourceOutputPath, unindent(readerImpl), "utf-8"); + fs.appendFileSync( + readerSourceOutputPath, + unindent(jsonHandlerImpl), + "utf-8" + ); } else { const readerSourceOutputPath = path.join( - readerHeaderOutputDir, + jsonHandlerHeaderOutputDir, name + "JsonHandler.cpp" ); - fs.writeFileSync(readerSourceOutputPath, unindent(readerImpl), "utf-8"); + fs.writeFileSync( + readerSourceOutputPath, + unindent(jsonHandlerImpl), + "utf-8" + ); } const schemas = lodash.flatten( diff --git a/tools/generate-classes/resolveProperty.js b/tools/generate-classes/resolveProperty.js index f39f817e2..66e70f776 100644 --- a/tools/generate-classes/resolveProperty.js +++ b/tools/generate-classes/resolveProperty.js @@ -115,9 +115,9 @@ function resolveProperty( NameFormatters.getIncludeFromName(type, namespace), ...(makeOptional ? [""] : []), ], - readerType: NameFormatters.getReaderName(type, readerNamespace), + readerType: NameFormatters.getJsonHandlerName(type, readerNamespace), readerHeaders: [ - NameFormatters.getReaderIncludeFromName(type, readerNamespace), + NameFormatters.getJsonHandlerIncludeFromName(type, readerNamespace), ], schemas: [schema], }; @@ -198,9 +198,9 @@ function resolveProperty( NameFormatters.getIncludeFromName(type, namespace), ...(makeOptional ? [""] : []), ], - readerType: NameFormatters.getReaderName(type, readerNamespace), + readerType: NameFormatters.getJsonHandlerName(type, readerNamespace), readerHeaders: [ - NameFormatters.getReaderIncludeFromName(type, readerNamespace), + NameFormatters.getJsonHandlerIncludeFromName(type, readerNamespace), ], schemas: [itemSchema], }; From b3d13bcbb0402f40c9c83ea7f10fa357ee245a77 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 23 Aug 2023 19:02:19 +1000 Subject: [PATCH 206/421] Update CHANGES.md. --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index eda1b8801..3ecc5b4f1 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,6 +6,7 @@ - Renamed `ExtensionReaderContext` to `JsonReaderOptions`, and the `getExtensions` method on various JSON reader classes to `getOptions`. - `IExtensionJsonHandler` no longer derives from `IJsonHandler`. Instead, it has a new pure virtual method, `getHandler`, that must be implemented to allow clients to obtain the `IJsonHandler`. In almost all implementations, this should simply return `*this`. +- In `SubtreeReader`, `SchemaReader`, and `TilesetReader`, the `readSubtree`, `readSchema`, and `readTileset` methods (respectively) have been renamed to `readFromJson` and return a templated `ReadJsonResult` instead of a bespoke result class. ##### Additions :tada: From d24e5f9dfdc21c660ded96b492c8e638c9538459 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 23 Aug 2023 20:58:14 +1000 Subject: [PATCH 207/421] Update CHANGES.md, remove unnecessary change. --- CHANGES.md | 6 ++++++ .../include/Cesium3DTilesSelection/TilesetContentLoader.h | 1 - 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 3ecc5b4f1..40100dbb4 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -7,6 +7,8 @@ - Renamed `ExtensionReaderContext` to `JsonReaderOptions`, and the `getExtensions` method on various JSON reader classes to `getOptions`. - `IExtensionJsonHandler` no longer derives from `IJsonHandler`. Instead, it has a new pure virtual method, `getHandler`, that must be implemented to allow clients to obtain the `IJsonHandler`. In almost all implementations, this should simply return `*this`. - In `SubtreeReader`, `SchemaReader`, and `TilesetReader`, the `readSubtree`, `readSchema`, and `readTileset` methods (respectively) have been renamed to `readFromJson` and return a templated `ReadJsonResult` instead of a bespoke result class. +- `TileExternalContent` is now heap allocated and stored in `TileContent` with a `std::unique_ptr`. +- The root `Tile` of a `Cesium3DTilesSelection::Tileset` now represents the tileset.json itself, and the `root` tile specified in the tileset.json is its only child. This makes the shape of the tile tree consistent between a standard top-level tileset and an external tileset embedded elsewhere in the tree. In both cases, the "tile" that represents the tileset.json itself has content of type `TileExternalContent`. ##### Additions :tada: @@ -14,6 +16,10 @@ - Added `ValueType` type alias to `ArrayJsonHandler`, for consistency with other JSON handlers. - Added an overload of `JsonReader::readJson` that takes a `rapidjson::Value` instead of a byte buffer. This allows a subtree of a `rapidjson::Document` to be easily and efficiently converted into statically-typed classes via `IJsonHandler`. - Added `*Reader` classes to `CesiumGltfReader` and `Cesium3DTilesReader` to allow each of the classes to be individually read from JSON. +- Added `getExternalContent` method to the `TileContent` class. +- `TileExternalContent` now holds the metadata (`schema`, `schemaUri`, `metadata`, and `groups`) stored in the tileset.json. +- Added `findMetadata` method to `Cesium3DTilesSelection::Tileset`. It returns a `TilesetMetadata` instance representing the metadata associated with a tileset.json. +- Added `MetadataQuery` class to make it easier to find properties with specific semantics in `TilesetMetadata`. ### v0.27.0 - 2023-09-01 diff --git a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/TilesetContentLoader.h b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/TilesetContentLoader.h index ad10786e9..5b767456b 100644 --- a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/TilesetContentLoader.h +++ b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/TilesetContentLoader.h @@ -7,7 +7,6 @@ #include "TileLoadResult.h" #include "TilesetOptions.h" -#include #include #include #include From bc9dd9e6e5f2cecd609ce62dc426963a5712ea2b Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Wed, 23 Aug 2023 09:58:02 -0400 Subject: [PATCH 208/421] Add static casts for integer optionals --- ...gradeBatchTableToExtStructuralMetadata.cpp | 3 +- .../CesiumGltf/PropertyTablePropertyView.h | 4 +- .../CesiumGltf/PropertyTransformations.h | 55 +++++++------------ .../test/TestPropertyTablePropertyView.cpp | 8 +-- 4 files changed, 29 insertions(+), 41 deletions(-) diff --git a/Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp b/Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp index 8a8a7509f..3efdfec28 100644 --- a/Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp +++ b/Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp @@ -55,7 +55,8 @@ static void checkNonArrayProperty( } else if constexpr ( std::is_same_v || std::is_same_v) { - REQUIRE(propertyView.getRaw(i) == Approx(expected[static_cast(i)])); + REQUIRE( + propertyView.getRaw(i) == Approx(expected[static_cast(i)])); } else { REQUIRE( static_cast(propertyView.getRaw(i)) == diff --git a/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h index c63f6e661..2830305b9 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h @@ -603,7 +603,7 @@ class PropertyTablePropertyView constexpr glm::length_t N = ElementType::length(); using T = typename ElementType::value_type; using NormalizedT = typename NormalizedType::value_type; - return transformVecN( + return transformValue>( normalize(value), this->offset(), this->scale()); @@ -613,7 +613,7 @@ class PropertyTablePropertyView constexpr glm::length_t N = ElementType::length(); using T = typename ElementType::value_type; using NormalizedT = typename NormalizedType::value_type; - return transformMatN( + return transformValue>( normalize(value), this->offset(), this->scale()); diff --git a/CesiumGltf/include/CesiumGltf/PropertyTransformations.h b/CesiumGltf/include/CesiumGltf/PropertyTransformations.h index 9716520a9..4f4b9ce3d 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTransformations.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTransformations.h @@ -78,40 +78,6 @@ T transformValue( return result; } -template -glm::vec transformVecN( - const glm::vec& value, - const std::optional>& offset, - const std::optional>& scale) { - glm::vec result = value; - if (scale) { - result = applyScale>(result, *scale); - } - - if (offset) { - result += *offset; - } - - return result; -} - -template -glm::mat transformMatN( - const glm::mat& value, - const std::optional>& offset, - const std::optional>& scale) { - glm::mat result = value; - if (scale) { - result = applyScale>(result, *scale); - } - - if (offset) { - result += *offset; - } - - return result; -} - template PropertyArrayView transformArray( const PropertyArrayView& value, @@ -177,4 +143,25 @@ PropertyArrayView> transformNormalizedVecNArray( return PropertyArrayView>(std::move(result)); } + +template +PropertyArrayView> transformNormalizedMatNArray( + const PropertyArrayView>& value, + const std::optional>>& offset, + const std::optional>>& scale) { + std::vector> result(static_cast(value.size())); + for (int64_t i = 0; i < value.size(); i++) { + result[i] = normalize(value[i]); + + if (scale) { + result[i] = applyScale>(result[i], (*scale)[i]); + } + + if (offset) { + result[i] = result[i] + (*offset)[i]; + } + } + + return PropertyArrayView>(std::move(result)); +} } // namespace CesiumGltf diff --git a/CesiumGltf/test/TestPropertyTablePropertyView.cpp b/CesiumGltf/test/TestPropertyTablePropertyView.cpp index 8094f00c8..332251ecc 100644 --- a/CesiumGltf/test/TestPropertyTablePropertyView.cpp +++ b/CesiumGltf/test/TestPropertyTablePropertyView.cpp @@ -704,7 +704,7 @@ TEST_CASE("Check scalar PropertyTablePropertyView") { SECTION("Int16 with NoData") { std::vector values{-1, 3, 7, -1}; - std::optional noData = -1; + std::optional noData = static_cast(-1); std::vector> expected{ std::nullopt, 3, @@ -721,8 +721,8 @@ TEST_CASE("Check scalar PropertyTablePropertyView") { SECTION("Int16 with NoData and Default") { std::vector values{-1, 3, 7, -1}; - std::optional noData = -1; - std::optional defaultValue = 0; + std::optional noData = static_cast(-1); + std::optional defaultValue = static_cast(0); std::vector> expected{0, 3, 7, 0}; checkScalar( values, @@ -737,7 +737,7 @@ TEST_CASE("Check scalar PropertyTablePropertyView") { std::vector values{0, 64, 128, 255}; std::optional offset = 1.0; std::optional scale = 2.0; - std::optional noData = 0; + std::optional noData = static_cast(0); std::optional defaultValue = 10.0; std::vector> expected{ 10.0, From 4f37e7a2172dc1b437dfb6a6fe9ce4642dcec331 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Wed, 23 Aug 2023 10:32:28 -0400 Subject: [PATCH 209/421] Try to fix CI errors --- .../test/TestPropertyTablePropertyView.cpp | 354 +++++++----------- 1 file changed, 138 insertions(+), 216 deletions(-) diff --git a/CesiumGltf/test/TestPropertyTablePropertyView.cpp b/CesiumGltf/test/TestPropertyTablePropertyView.cpp index 332251ecc..94af0a123 100644 --- a/CesiumGltf/test/TestPropertyTablePropertyView.cpp +++ b/CesiumGltf/test/TestPropertyTablePropertyView.cpp @@ -12,30 +12,6 @@ using namespace CesiumGltf; using namespace CesiumUtility; -#pragma region Utility - -template -JsonValue::Array getArrayFromVecN(glm::vec vecN) { - JsonValue::Array values(N); - for (glm::length_t i = 0; i < N; i++) { - values[i] = vecN[i]; - } - - return values; -} - -template -JsonValue::Array getArrayFromMatN(glm::mat matN) { - JsonValue::Array values(N * N); - for (glm::length_t i = 0; i < N; i++) { - for (glm::length_t j = 0; j < N; j++) { - values[N * i + j] = matN[i][j]; - } - } - - return values; -} - template static void checkNumeric(const std::vector& expected) { std::vector data; data.resize(expected.size() * sizeof(T)); @@ -71,10 +47,10 @@ template static void checkScalar( const std::vector& values, const std::vector>& expected, - const std::optional offset = std::nullopt, - const std::optional scale = std::nullopt, - const std::optional noData = std::nullopt, - const std::optional defaultValue = std::nullopt) { + const std::optional offset = std::nullopt, + const std::optional scale = std::nullopt, + const std::optional noData = std::nullopt, + const std::optional defaultValue = std::nullopt) { std::vector data; data.resize(values.size() * sizeof(T)); std::memcpy(data.data(), values.data(), data.size()); @@ -88,21 +64,10 @@ static void checkScalar( classProperty.componentType = convertPropertyComponentTypeToString(componentType); - if (offset) { - classProperty.offset = *offset; - } - - if (scale) { - classProperty.scale = *scale; - } - - if (noData) { - classProperty.noData = *noData; - } - - if (defaultValue) { - classProperty.defaultProperty = *defaultValue; - } + classProperty.offset = offset; + classProperty.scale = scale; + classProperty.noData = noData; + classProperty.defaultProperty = defaultValue; PropertyTablePropertyView property( propertyTableProperty, @@ -127,10 +92,10 @@ template static void checkVecN( const std::vector>& values, const std::vector>>& expected, - const std::optional> offset = std::nullopt, - const std::optional> scale = std::nullopt, - const std::optional> noData = std::nullopt, - const std::optional> defaultValue = std::nullopt) { + const std::optional offset = std::nullopt, + const std::optional scale = std::nullopt, + const std::optional noData = std::nullopt, + const std::optional defaultValue = std::nullopt) { std::vector data; data.resize(values.size() * sizeof(glm::vec)); std::memcpy(data.data(), values.data(), data.size()); @@ -144,21 +109,10 @@ static void checkVecN( classProperty.componentType = convertPropertyComponentTypeToString(componentType); - if (offset) { - classProperty.offset = getArrayFromVecN(*offset); - } - - if (scale) { - classProperty.scale = getArrayFromVecN(*scale); - } - - if (noData) { - classProperty.noData = getArrayFromVecN(*noData); - } - - if (defaultValue) { - classProperty.defaultProperty = getArrayFromVecN(*defaultValue); - } + classProperty.offset = offset; + classProperty.scale = scale; + classProperty.noData = noData; + classProperty.defaultProperty = defaultValue; PropertyTablePropertyView> property( propertyTableProperty, @@ -178,10 +132,10 @@ template static void checkMatN( const std::vector>& values, const std::vector>>& expected, - const std::optional> offset = std::nullopt, - const std::optional> scale = std::nullopt, - const std::optional> noData = std::nullopt, - const std::optional> defaultValue = std::nullopt) { + const std::optional offset = std::nullopt, + const std::optional scale = std::nullopt, + const std::optional noData = std::nullopt, + const std::optional defaultValue = std::nullopt) { std::vector data; data.resize(values.size() * sizeof(glm::mat)); std::memcpy(data.data(), values.data(), data.size()); @@ -195,21 +149,10 @@ static void checkMatN( classProperty.componentType = convertPropertyComponentTypeToString(componentType); - if (offset) { - classProperty.offset = getArrayFromMatN(*offset); - } - - if (scale) { - classProperty.scale = getArrayFromMatN(*scale); - } - - if (noData) { - classProperty.noData = getArrayFromMatN(*noData); - } - - if (defaultValue) { - classProperty.defaultProperty = getArrayFromMatN(*defaultValue); - } + classProperty.offset = offset; + classProperty.scale = scale; + classProperty.noData = noData; + classProperty.defaultProperty = defaultValue; PropertyTablePropertyView> property( propertyTableProperty, @@ -229,10 +172,10 @@ template static void checkNormalizedScalar( const std::vector& values, const std::vector>& expected, - const std::optional offset = std::nullopt, - const std::optional scale = std::nullopt, - const std::optional noData = std::nullopt, - const std::optional defaultValue = std::nullopt) { + const std::optional offset = std::nullopt, + const std::optional scale = std::nullopt, + const std::optional noData = std::nullopt, + const std::optional defaultValue = std::nullopt) { std::vector data; data.resize(values.size() * sizeof(T)); std::memcpy(data.data(), values.data(), data.size()); @@ -248,21 +191,10 @@ static void checkNormalizedScalar( classProperty.normalized = true; - if (offset) { - classProperty.offset = *offset; - } - - if (scale) { - classProperty.scale = *scale; - } - - if (noData) { - classProperty.noData = *noData; - } - - if (defaultValue) { - classProperty.defaultProperty = *defaultValue; - } + classProperty.offset = offset; + classProperty.scale = scale; + classProperty.noData = noData; + classProperty.defaultProperty = defaultValue; PropertyTablePropertyView property( propertyTableProperty, @@ -282,10 +214,10 @@ template static void checkNormalizedVecN( const std::vector>& values, const std::vector>>& expected, - const std::optional> offset = std::nullopt, - const std::optional> scale = std::nullopt, - const std::optional> noData = std::nullopt, - const std::optional> defaultValue = std::nullopt) { + const std::optional offset = std::nullopt, + const std::optional scale = std::nullopt, + const std::optional noData = std::nullopt, + const std::optional defaultValue = std::nullopt) { std::vector data; data.resize(values.size() * sizeof(glm::vec)); std::memcpy(data.data(), values.data(), data.size()); @@ -301,21 +233,10 @@ static void checkNormalizedVecN( classProperty.normalized = true; - if (offset) { - classProperty.offset = getArrayFromVecN(*offset); - } - - if (scale) { - classProperty.scale = getArrayFromVecN(*scale); - } - - if (noData) { - classProperty.noData = getArrayFromVecN(*noData); - } - - if (defaultValue) { - classProperty.defaultProperty = getArrayFromVecN(*defaultValue); - } + classProperty.offset = offset; + classProperty.scale = scale; + classProperty.noData = noData; + classProperty.defaultProperty = defaultValue; PropertyTablePropertyView, true> property( propertyTableProperty, @@ -335,10 +256,10 @@ template static void checkNormalizedMatN( const std::vector>& values, const std::vector>>& expected, - const std::optional> offset = std::nullopt, - const std::optional> scale = std::nullopt, - const std::optional> noData = std::nullopt, - const std::optional> defaultValue = std::nullopt) { + const std::optional offset = std::nullopt, + const std::optional scale = std::nullopt, + const std::optional noData = std::nullopt, + const std::optional defaultValue = std::nullopt) { std::vector data; data.resize(values.size() * sizeof(glm::mat)); std::memcpy(data.data(), values.data(), data.size()); @@ -354,21 +275,10 @@ static void checkNormalizedMatN( classProperty.normalized = true; - if (offset) { - classProperty.offset = getArrayFromMatN(*offset); - } - - if (scale) { - classProperty.scale = getArrayFromMatN(*scale); - } - - if (noData) { - classProperty.noData = getArrayFromMatN(*noData); - } - - if (defaultValue) { - classProperty.defaultProperty = getArrayFromMatN(*defaultValue); - } + classProperty.offset = offset; + classProperty.scale = scale; + classProperty.noData = noData; + classProperty.defaultProperty = defaultValue; PropertyTablePropertyView, true> property( propertyTableProperty, @@ -635,8 +545,6 @@ static void checkNormalizedFixedLengthArray( } } -#pragma endregion - TEST_CASE("Check scalar PropertyTablePropertyView") { SECTION("Uint8") { std::vector data{12, 33, 56, 67}; @@ -684,16 +592,16 @@ TEST_CASE("Check scalar PropertyTablePropertyView") { SECTION("Float with Offset / Scale") { std::vector values{12.5f, -12.5f, -5.0f, 6.75f}; - std::optional offset = 1.0f; - std::optional scale = 2.0f; + std::optional offset = 1.0f; + std::optional scale = 2.0f; std::vector> expected{26.0f, -24.0f, -9.0f, 14.5f}; checkScalar(values, expected, offset, scale); } SECTION("Normalized Uint8 with Offset and Scale") { std::vector values{0, 64, 128, 255}; - std::optional offset = 1.0; - std::optional scale = 2.0; + std::optional offset = 1.0; + std::optional scale = 2.0; std::vector> expected{ 1.0, 1 + 2 * (64.0 / 255.0), @@ -704,7 +612,7 @@ TEST_CASE("Check scalar PropertyTablePropertyView") { SECTION("Int16 with NoData") { std::vector values{-1, 3, 7, -1}; - std::optional noData = static_cast(-1); + std::optional noData = -1; std::vector> expected{ std::nullopt, 3, @@ -721,8 +629,8 @@ TEST_CASE("Check scalar PropertyTablePropertyView") { SECTION("Int16 with NoData and Default") { std::vector values{-1, 3, 7, -1}; - std::optional noData = static_cast(-1); - std::optional defaultValue = static_cast(0); + std::optional noData = -1; + std::optional defaultValue = 0; std::vector> expected{0, 3, 7, 0}; checkScalar( values, @@ -735,10 +643,10 @@ TEST_CASE("Check scalar PropertyTablePropertyView") { SECTION("Normalized Uint8 with all properties") { std::vector values{0, 64, 128, 255}; - std::optional offset = 1.0; - std::optional scale = 2.0; - std::optional noData = static_cast(0); - std::optional defaultValue = 10.0; + std::optional offset = 1.0; + std::optional scale = 2.0; + std::optional noData = 0; + std::optional defaultValue = 10.0; std::vector> expected{ 10.0, 1 + 2 * (64.0 / 255.0), @@ -858,8 +766,8 @@ TEST_CASE("Check vecN PropertyTablePropertyView") { glm::vec3(6.5f, 2.0f, 4.0f), glm::vec3(8.0f, -3.0f, 1.0f), }; - std::optional offset = glm::vec3(1.0f, 2.0, 3.0f); - std::optional scale = glm::vec3(2.0f, 1.0f, 2.0f); + std::optional offset = JsonValue::Array{1.0f, 2.0f, 3.0f}; + std::optional scale = JsonValue::Array{2.0f, 1.0f, 2.0f}; std::vector> expected{ glm::vec3(1.0f, 0.5f, -7.0f), glm::vec3(14.0f, 4.0f, 11.0f), @@ -873,8 +781,8 @@ TEST_CASE("Check vecN PropertyTablePropertyView") { glm::u8vec2(0, 64), glm::u8vec2(128, 255), glm::u8vec2(255, 0)}; - std::optional offset = glm::dvec2(0.0, 1.0); - std::optional scale = glm::dvec2(2.0, 1.0); + std::optional offset = JsonValue::Array{0.0, 1.0}; + std::optional scale = JsonValue::Array{2.0, 1.0}; std::vector> expected{ glm::dvec2(0.0, 1 + (64.0 / 255.0)), glm::dvec2(2 * (128.0 / 255.0), 2.0), @@ -887,7 +795,7 @@ TEST_CASE("Check vecN PropertyTablePropertyView") { glm::i16vec2(-1, 3), glm::i16vec2(-1, -1), glm::i16vec2(7, 0)}; - std::optional noData = glm::i16vec2(-1, -1); + std::optional noData = JsonValue::Array{-1, -1}; std::vector> expected{ glm::i16vec2(-1, 3), std::nullopt, @@ -906,8 +814,8 @@ TEST_CASE("Check vecN PropertyTablePropertyView") { glm::i16vec2(-1, 3), glm::i16vec2(-1, -1), glm::i16vec2(7, 0)}; - std::optional noData = glm::i16vec2(-1, -1); - std::optional defaultValue = glm::i16vec2(0, 1); + std::optional noData = JsonValue::Array{-1, -1}; + std::optional defaultValue = JsonValue::Array{0, 1}; std::vector> expected{ glm::i16vec2(-1, 3), glm::i16vec2(0, 1), @@ -927,10 +835,10 @@ TEST_CASE("Check vecN PropertyTablePropertyView") { glm::u8vec2(128, 255), glm::u8vec2(255, 0), glm::u8vec2(0, 0)}; - std::optional offset = glm::dvec2(0.0, 1.0); - std::optional scale = glm::dvec2(2.0, 1.0); - std::optional noData = glm::u8vec2(0, 0); - std::optional defaultValue = glm::dvec2(5.0, 15.0); + std::optional offset = JsonValue::Array{0.0, 1.0}; + std::optional scale = JsonValue::Array{2.0, 1.0}; + std::optional noData = JsonValue::Array{0, 0}; + std::optional defaultValue = JsonValue::Array{5.0, 15.0}; std::vector> expected{ glm::dvec2(0.0, 1 + (64.0 / 255.0)), glm::dvec2(2 * (128.0 / 255.0), 2.0), @@ -1079,8 +987,10 @@ TEST_CASE("Check matN PropertyTablePropertyView") { glm::mat2(6.5f, 2.0f, -2.0f, 0.0f), glm::mat2(8.0f, -1.0f, -3.0f, 1.0f), }; - std::optional offset = glm::mat2(1.0f, 2.0, 3.0f, 1.0f); - std::optional scale = glm::mat2(2.0f, 0.0f, 0.0f, 2.0f); + std::optional offset = + JsonValue::Array{1.0f, 2.0f, 3.0f, 1.0f}; + std::optional scale = + JsonValue::Array{2.0f, 0.0f, 0.0f, 2.0f}; std::vector> expected{ glm::mat2(3.0f, 2.0f, 3.0f, 5.0f), glm::mat2(14.0f, 2.0f, 3.0f, 1.0f), @@ -1093,8 +1003,10 @@ TEST_CASE("Check matN PropertyTablePropertyView") { std::vector values{ glm::u8mat2x2(0, 64, 255, 255), glm::u8mat2x2(255, 0, 128, 0)}; - std::optional offset = glm::dmat2(0.0, 1.0, 1.0, 0.0); - std::optional scale = glm::dmat2(2.0, 1.0, 0.0, 2.0); + std::optional offset = + JsonValue::Array{0.0, 1.0, 1.0, 0.0}; + std::optional scale = + JsonValue::Array{2.0, 1.0, 0.0, 2.0}; std::vector> expected{ glm::dmat2(0.0, 1 + 64.0 / 255.0, 1.0, 2.0), glm::dmat2(2.0, 1.0, 1.0, 0.0)}; @@ -1116,11 +1028,11 @@ TEST_CASE("Check matN PropertyTablePropertyView") { -1, -1, -1, 0, 0, 0, 1, 1, 1)}; - std::optional noData = - glm::i16mat3x3( + std::optional noData = + JsonValue::Array{ -1, -1, -1, 0, 0, 0, - 1, 1, 1); + 1, 1, 1}; // clang-format on std::vector> expected{ values[0], @@ -1150,24 +1062,26 @@ TEST_CASE("Check matN PropertyTablePropertyView") { -1, -1, -1, 0, 0, 0, 1, 1, 1)}; - std::optional noData = - glm::i16mat3x3( - -1, -1, -1, - 0, 0, 0, - 1, 1, 1); + std::optional noData = JsonValue::Array{ + -1, -1, -1, + 0, 0, 0, + 1, 1, 1}; + std::optional defaultValue = JsonValue::Array{ + 1, 0, 0, + 0, 1, 0, + 0, 0, 1}; // clang-format on - std::optional default = glm::i16mat3x3(1); std::vector> expected{ values[0], values[1], - default}; + glm::i16mat3x3(1)}; checkMatN<3, glm::i16>( values, expected, std::nullopt, std::nullopt, noData, - default); + defaultValue); }; SECTION("Normalized Uint8 Mat2 with all properties") { @@ -1175,10 +1089,13 @@ TEST_CASE("Check matN PropertyTablePropertyView") { glm::u8mat2x2(0, 64, 255, 255), glm::u8mat2x2(0), glm::u8mat2x2(255, 0, 128, 0)}; - std::optional offset = glm::dmat2(0.0, 1.0, 1.0, 0.0); - std::optional scale = glm::dmat2(2.0, 1.0, 0.0, 2.0); - std::optional noData = glm::u8mat2x2(0); - std::optional defaultValue = glm::dmat2(1.0); + std::optional offset = + JsonValue::Array{0.0, 1.0, 1.0, 0.0}; + std::optional scale = + JsonValue::Array{2.0, 1.0, 0.0, 2.0}; + std::optional noData = JsonValue::Array{0, 0, 0, 0}; + std::optional defaultValue = + JsonValue::Array{1.0, 0.0, 0.0, 1.0}; std::vector> expected{ glm::dmat2(0.0, 1 + 64.0 / 255.0, 1.0, 2.0), @@ -1629,7 +1546,9 @@ TEST_CASE("Check fixed-length scalar array PropertyTablePropertyView") { REQUIRE(values); for (int64_t j = 0; j < rawValues.size(); ++j) { REQUIRE(rawValues[j] == data[expectedIdx]); - REQUIRE((*values)[j] == expected[i][j]); + REQUIRE( + (*values)[j] == + expected[static_cast(i)][static_cast(j)]); ++expectedIdx; } } @@ -1688,38 +1607,38 @@ TEST_CASE("Check fixed-length vecN array PropertyTablePropertyView") { checkNormalizedFixedLengthArray(data, 2, expected); } - SECTION("Array of 4 floats with offset / scale") { + SECTION("Array of 2 vec2s with offset / scale") { // clang-format off - std::vector data{ - 1.0f, 2.0f, 3.0f, 4.0f, - 5.0f, -1.0f, 0.0f, 2.0f + std::vector data{ + glm::vec2(1.0f, 2.0f), glm::vec2(3.0f, 4.0f), + glm::vec2(5.0f, -1.0f), glm::vec2(0.0f, 2.0f) }; // clang-format on std::optional offset = - JsonValue::Array{1.0f, 0.0f, -1.0f, 0.0f}; + JsonValue::Array{{1.0f, 0.0f}, {-1.0f, 0.0f}}; std::optional scale = - JsonValue::Array{1.0f, 2.0f, 1.0f, 2.0f}; + JsonValue::Array{{1.0f, 2.0f}, {1.0f, 2.0f}}; - std::vector>> expected{ - std::vector{2.0f, 4.0f, 2.0f, 8.0f}, - std::vector{6.0f, -2.0f, -1.0f, 4.0f}}; - checkFixedLengthArrayWithProperties(data, 4, expected, offset, scale); + std::vector>> expected{ + std::vector{glm::vec2(2.0f, 4.0f), glm::vec2(2.0f, 8.0f)}, + std::vector{glm::vec2(6.0f, -2.0f), glm::vec2(-1.0f, 4.0f)}}; + checkFixedLengthArrayWithProperties(data, 2, expected, offset, scale); } - SECTION("Array of 2 int32_ts with noData value") { + SECTION("Array of 2 ivec2 with noData value") { // clang-format off - std::vector data{ - 122, 12, - -1, -1, - -3, 44}; + std::vector data{ + glm::ivec2(122, 12), glm::ivec2(-1, -1), + glm::ivec2( -3, 44), glm::ivec2(0, 7), + glm::ivec2(-1, -1), glm::ivec2(0, 0)}; // clang-format on - std::optional noData = JsonValue::Array{-1, -1}; - std::vector>> expected{ - std::vector{122, 12}, - std::nullopt, - std::vector{-3, 44}}; - checkFixedLengthArrayWithProperties( + std::optional noData = JsonValue::Array{{-1, -1}, {0, 0}}; + std::vector>> expected{ + std::vector{glm::ivec2(122, 12), glm::ivec2(-1, -1)}, + std::vector{glm::ivec2(-3, 44), glm::ivec2(0, 7)}, + std::nullopt}; + checkFixedLengthArrayWithProperties( data, 2, expected, @@ -1729,20 +1648,21 @@ TEST_CASE("Check fixed-length vecN array PropertyTablePropertyView") { std::nullopt); } - SECTION("Array of 2 int32_ts with noData and default value") { + SECTION("Array of 2 ivec2 with noData and default value") { // clang-format off - std::vector data{ - 122, 12, - -1, -1, - -3, 44}; + std::vector data{ + glm::ivec2(122, 12), glm::ivec2(-1, -1), + glm::ivec2( -3, 44), glm::ivec2(0, 7), + glm::ivec2(-1, -1), glm::ivec2(0, 0)}; // clang-format on - std::optional noData = JsonValue::Array{-1, -1}; - std::optional defaultValue = JsonValue::Array{0, 1}; - std::vector>> expected{ - std::vector{122, 12}, - std::vector{0, 1}, - std::vector{-3, 44}}; - checkFixedLengthArrayWithProperties( + std::optional noData = JsonValue::Array{{-1, -1}, {0, 0}}; + std::optional defaultValue = + JsonValue::Array{{1, 1}, {1, 2}}; + std::vector>> expected{ + std::vector{glm::ivec2(122, 12), glm::ivec2(-1, -1)}, + std::vector{glm::ivec2(-3, 44), glm::ivec2(0, 7)}, + std::vector{glm::ivec2(1, 1), glm::ivec2(1, 2)}}; + checkFixedLengthArrayWithProperties( data, 2, expected, @@ -1832,7 +1752,9 @@ TEST_CASE("Check fixed-length vecN array PropertyTablePropertyView") { REQUIRE(values); for (int64_t j = 0; j < rawValues.size(); ++j) { REQUIRE(rawValues[j] == data[expectedIdx]); - REQUIRE((*values)[j] == expected[i][j]); + REQUIRE( + (*values)[j] == + expected[static_cast(i)][static_cast(j)]); ++expectedIdx; } } From fc190004b614528e267ef7a9cf08c9c18c27e300 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Wed, 23 Aug 2023 11:17:12 -0400 Subject: [PATCH 210/421] Reinitialize const ints outside of class to avoid errors --- CesiumGltf/src/PropertyTablePropertyView.cpp | 42 +++++++ CesiumGltf/src/PropertyView.cpp | 18 +++ .../test/TestPropertyTablePropertyView.cpp | 106 ++++++++++++------ 3 files changed, 133 insertions(+), 33 deletions(-) create mode 100644 CesiumGltf/src/PropertyTablePropertyView.cpp create mode 100644 CesiumGltf/src/PropertyView.cpp diff --git a/CesiumGltf/src/PropertyTablePropertyView.cpp b/CesiumGltf/src/PropertyTablePropertyView.cpp new file mode 100644 index 000000000..48f162915 --- /dev/null +++ b/CesiumGltf/src/PropertyTablePropertyView.cpp @@ -0,0 +1,42 @@ +#include "CesiumGltf/PropertyTablePropertyView.h" + +using namespace CesiumGltf; + +// Re-initialize consts here to avoid "undefined reference" errors with GCC / +// Clang. +const PropertyViewStatusType + PropertyTablePropertyViewStatus::ErrorInvalidPropertyTable; +const PropertyViewStatusType + PropertyTablePropertyViewStatus::ErrorInvalidValueBufferView; +const PropertyViewStatusType + PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetBufferView; +const PropertyViewStatusType + PropertyTablePropertyViewStatus::ErrorInvalidStringOffsetBufferView; +const PropertyViewStatusType + PropertyTablePropertyViewStatus::ErrorInvalidValueBuffer; +const PropertyViewStatusType + PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetBuffer; +const PropertyViewStatusType + PropertyTablePropertyViewStatus::ErrorInvalidStringOffsetBuffer; +const PropertyViewStatusType + PropertyTablePropertyViewStatus::ErrorBufferViewOutOfBounds; +const PropertyViewStatusType + PropertyTablePropertyViewStatus::ErrorBufferViewSizeNotDivisibleByTypeSize; +const PropertyViewStatusType PropertyTablePropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount; +const PropertyViewStatusType + PropertyTablePropertyViewStatus::ErrorArrayCountAndOffsetBufferCoexist; +const PropertyViewStatusType + PropertyTablePropertyViewStatus::ErrorArrayCountAndOffsetBufferDontExist; +const PropertyViewStatusType + PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType; +const PropertyViewStatusType + PropertyTablePropertyViewStatus::ErrorInvalidStringOffsetType; +const PropertyViewStatusType + PropertyTablePropertyViewStatus::ErrorArrayOffsetsNotSorted; +const PropertyViewStatusType + PropertyTablePropertyViewStatus::ErrorStringOffsetsNotSorted; +const PropertyViewStatusType + PropertyTablePropertyViewStatus::ErrorArrayOffsetOutOfBounds; +const PropertyViewStatusType + PropertyTablePropertyViewStatus::ErrorStringOffsetOutOfBounds; diff --git a/CesiumGltf/src/PropertyView.cpp b/CesiumGltf/src/PropertyView.cpp new file mode 100644 index 000000000..030469450 --- /dev/null +++ b/CesiumGltf/src/PropertyView.cpp @@ -0,0 +1,18 @@ +#include "CesiumGltf/PropertyView.h" + +using namespace CesiumGltf; + +// Re-initialize consts here to avoid "undefined reference" errors with GCC / +// Clang. +const PropertyViewStatusType PropertyViewStatus::Valid; +const PropertyViewStatusType PropertyViewStatus::ErrorNonexistentProperty; +const PropertyViewStatusType PropertyViewStatus::ErrorTypeMismatch; +const PropertyViewStatusType PropertyViewStatus::ErrorComponentTypeMismatch; +const PropertyViewStatusType PropertyViewStatus::ErrorArrayTypeMismatch; +const PropertyViewStatusType PropertyViewStatus::ErrorInvalidNormalization; +const PropertyViewStatusType PropertyViewStatus::ErrorInvalidOffset; +const PropertyViewStatusType PropertyViewStatus::ErrorInvalidScale; +const PropertyViewStatusType PropertyViewStatus::ErrorInvalidMax; +const PropertyViewStatusType PropertyViewStatus::ErrorInvalidMin; +const PropertyViewStatusType PropertyViewStatus::ErrorInvalidNoDataValue; +const PropertyViewStatusType PropertyViewStatus::ErrorInvalidDefaultValue; diff --git a/CesiumGltf/test/TestPropertyTablePropertyView.cpp b/CesiumGltf/test/TestPropertyTablePropertyView.cpp index 94af0a123..7009dc0f1 100644 --- a/CesiumGltf/test/TestPropertyTablePropertyView.cpp +++ b/CesiumGltf/test/TestPropertyTablePropertyView.cpp @@ -12,6 +12,15 @@ using namespace CesiumGltf; using namespace CesiumUtility; +template +static void +checkArrayEqual(PropertyArrayView arrayView, std::vector expected) { + REQUIRE(arrayView.size() == static_cast(expected.size())); + for (int64_t i = 0; i < arrayView.size(); i++) { + REQUIRE(arrayView[i] == expected[static_cast(i)]); + } +} + template static void checkNumeric(const std::vector& expected) { std::vector data; data.resize(expected.size() * sizeof(T)); @@ -1536,6 +1545,18 @@ TEST_CASE("Check fixed-length scalar array PropertyTablePropertyView") { REQUIRE(property.arrayCount() == count); REQUIRE(property.size() == instanceCount); + REQUIRE(property.offset()); + checkArrayEqual(*property.offset(), {2, 1, 0, -1}); + + REQUIRE(property.scale()); + checkArrayEqual(*property.scale(), {1, 0, 1, -1}); + + REQUIRE(property.min()); + checkArrayEqual(*property.min(), {0.0f, 1.0f, 1.0f, -2.0f}); + + REQUIRE(property.max()); + checkArrayEqual(*property.max(), {1.0f, 1.0f, 3.0f, 4.0f}); + std::vector> expected{ {3.0f, 1.0, 3.0f, -5.0f}, {2.0f, 1.0f, 1.0f, 1.0f}}; @@ -1672,26 +1693,29 @@ TEST_CASE("Check fixed-length vecN array PropertyTablePropertyView") { defaultValue); } - SECTION("Array of 3 normalized int8_ts with all properties") { + SECTION("Array of 2 normalized i8vec2 with all properties") { // clang-format off - std::vector data{ - -128, 0, 64, - -64, 127, -128, - 0, 0, 0}; + std::vector data{ + glm::i8vec2(-128, 0), glm::i8vec2(64, -64), + glm::i8vec2(127, -128), glm::i8vec2(0, 0), + glm::i8vec2(0), glm::i8vec2(0)}; // clang-format on - std::optional offset = JsonValue::Array{0, 1, 1}; - std::optional scale = JsonValue::Array{1, -1, 2}; - std::optional noData = JsonValue::Array{0, 0, 0}; - std::optional defaultValue = JsonValue::Array{10, 8, 2}; + std::optional offset = JsonValue::Array{{0, 1}, {1, 2}}; + std::optional scale = JsonValue::Array{{1, -1}, {2, 1}}; + std::optional noData = JsonValue::Array{{0, 0}, {0, 0}}; + std::optional defaultValue = + JsonValue::Array{{10, 2}, {4, 8}}; - std::vector>> expected{ - std::vector{-1.0, 1.0, 1 + 2 * (64.0 / 127.0)}, - std::vector{-64.0 / 127.0, 0.0, -1.0}, - std::vector{10.0, 8.0, 2.0}, + std::vector>> expected{ + std::vector{ + glm::dvec2(-1.0, 1.0), + glm::dvec2(1 + 2 * (64.0 / 127.0), 2 - (64.0 / 127.0))}, + std::vector{glm::dvec2(1, 2), glm::dvec2(1, 2)}, + std::vector{glm::dvec2(10, 2), glm::dvec2(4, 8)}, }; checkNormalizedFixedLengthArray( data, - 3, + 2, expected, offset, scale, @@ -1701,35 +1725,35 @@ TEST_CASE("Check fixed-length vecN array PropertyTablePropertyView") { SECTION("Overrides class property values") { // clang-format off - std::vector data{ - 1.0f, 2.0f, 3.0f, 4.0f, - 0.0f, -1.0f, 1.0f, -2.0f}; + std::vector data{ + glm::vec2(1.0f, 2.0f), glm::vec2(3.0f, 4.0f), + glm::vec2(0.0f, -1.0f), glm::vec2(1.0f, -2.0f)}; // clang-format on - const int64_t count = 4; + const int64_t count = 2; const int64_t instanceCount = 2; std::vector buffer; - buffer.resize(data.size() * sizeof(float)); + buffer.resize(data.size() * sizeof(glm::vec2)); std::memcpy(buffer.data(), data.data(), buffer.size()); ClassProperty classProperty; - classProperty.type = ClassProperty::Type::SCALAR; + classProperty.type = ClassProperty::Type::VEC2; classProperty.componentType = ClassProperty::ComponentType::FLOAT32; classProperty.array = true; classProperty.count = count; - classProperty.offset = {0, 0, 0, 0}; - classProperty.scale = {1, 1, 1, 1}; - classProperty.min = {0.0f, -1.0f, 1.0f, -2.0f}; - classProperty.max = {1.0f, 2.0f, 3.0f, 4.0f}; + classProperty.offset = JsonValue::Array{{0, 0}, {0, 0}}; + classProperty.scale = JsonValue::Array{{1, 1}, {1, 1}}; + classProperty.min = JsonValue::Array{{0.0f, -1.0f}, {1.0f, -2.0f}}; + classProperty.max = JsonValue::Array{{1.0f, 2.0f}, {3.0f, 4.0f}}; PropertyTableProperty propertyTableProperty; - propertyTableProperty.offset = {2, 1, 0, -1}; - propertyTableProperty.scale = {1, 0, 1, -1}; - propertyTableProperty.min = {0.0f, 1.0f, 1.0f, -2.0f}; - propertyTableProperty.max = {1.0f, 1.0f, 3.0f, 4.0f}; + propertyTableProperty.offset = JsonValue::Array{{2, 1}, {0, -1}}; + propertyTableProperty.scale = JsonValue::Array{{1, 0}, {1, -1}}; + propertyTableProperty.min = JsonValue::Array{{0.0f, 1.0f}, {1.0f, -2.0f}}; + propertyTableProperty.max = JsonValue::Array{{1.0f, 1.0f}, {3.0f, 4.0f}}; - PropertyTablePropertyView> property( + PropertyTablePropertyView> property( propertyTableProperty, classProperty, instanceCount, @@ -1742,12 +1766,28 @@ TEST_CASE("Check fixed-length vecN array PropertyTablePropertyView") { REQUIRE(property.arrayCount() == count); REQUIRE(property.size() == instanceCount); - std::vector> expected{ - {3.0f, 1.0, 3.0f, -5.0f}, - {2.0f, 1.0f, 1.0f, 1.0f}}; + REQUIRE(property.offset()); + checkArrayEqual(*property.offset(), {glm::vec2{2, 1}, glm::vec2{0, -1}}); + + REQUIRE(property.scale()); + checkArrayEqual(*property.scale(), {glm::vec2{1, 0}, glm::vec2{1, -1}}); + + REQUIRE(property.min()); + checkArrayEqual( + *property.min(), + {glm::vec2{0.0f, 1.0f}, glm::vec2{1.0f, -2.0f}}); + + REQUIRE(property.max()); + checkArrayEqual( + *property.max(), + {glm::vec2(1.0f, 1.0f), glm::vec2(3.0f, 4.0f)}); + + std::vector> expected{ + {glm::vec2(3.0f, 1.0), glm::vec2(3.0f, -5.0f)}, + {glm::vec2(2.0f, 1.0f), glm::vec2(1.0f, 1.0f)}}; size_t expectedIdx = 0; for (int64_t i = 0; i < property.size(); ++i) { - PropertyArrayView rawValues = property.getRaw(i); + PropertyArrayView rawValues = property.getRaw(i); auto values = property.get(i); REQUIRE(values); for (int64_t j = 0; j < rawValues.size(); ++j) { From 12d1bbcaaa5b0a6035c7d1038b4fa22429b2f2f9 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Wed, 23 Aug 2023 14:55:48 -0400 Subject: [PATCH 211/421] Finish PropertyTablePropertyView tests --- .../include/CesiumGltf/PropertyArrayView.h | 12 + .../CesiumGltf/PropertyTablePropertyView.h | 9 + .../CesiumGltf/PropertyTransformations.h | 19 +- CesiumGltf/include/CesiumGltf/PropertyView.h | 2 +- .../test/TestPropertyTablePropertyView.cpp | 1647 +++++++++++++++-- 5 files changed, 1523 insertions(+), 166 deletions(-) diff --git a/CesiumGltf/include/CesiumGltf/PropertyArrayView.h b/CesiumGltf/include/CesiumGltf/PropertyArrayView.h index 9be1867cf..80d8bbc61 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyArrayView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyArrayView.h @@ -57,6 +57,10 @@ template class PropertyArrayView { } bool operator==(const PropertyArrayView& other) const noexcept { + if (this->size() != other.size()) { + return false; + } + for (int64_t i = 0; i < size(); i++) { if ((*this)[i] != other[i]) { return false; @@ -104,6 +108,10 @@ template <> class PropertyArrayView { int64_t size() const noexcept { return _size; } bool operator==(const PropertyArrayView& other) const noexcept { + if (this->size() != other.size()) { + return false; + } + for (int64_t i = 0; i < size(); i++) { if ((*this)[i] != other[i]) { return false; @@ -164,6 +172,10 @@ template <> class PropertyArrayView { bool operator==(const PropertyArrayView& other) const noexcept { + if (this->size() != other.size()) { + return false; + } + for (int64_t i = 0; i < size(); i++) { if ((*this)[i] != other[i]) { return false; diff --git a/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h index 2830305b9..a071ee15e 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h @@ -636,6 +636,15 @@ class PropertyTablePropertyView this->offset(), this->scale()); } + + if constexpr (IsMetadataMatN::value) { + constexpr glm::length_t N = ArrayElementType::length(); + using T = typename ArrayElementType::value_type; + return transformNormalizedMatNArray( + value, + this->offset(), + this->scale()); + } } } diff --git a/CesiumGltf/include/CesiumGltf/PropertyTransformations.h b/CesiumGltf/include/CesiumGltf/PropertyTransformations.h index 4f4b9ce3d..440e39454 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTransformations.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTransformations.h @@ -111,7 +111,7 @@ PropertyArrayView transformNormalizedArray( result[i] = normalize(value[i]); if (scale) { - result[i] = applyScale(result[i], (*scale)[i]); + result[i] = result[i] * (*scale)[i]; } if (offset) { @@ -132,7 +132,7 @@ PropertyArrayView> transformNormalizedVecNArray( result[i] = normalize(value[i]); if (scale) { - result[i] = applyScale>(result[i], (*scale)[i]); + result[i] = result[i] * (*scale)[i]; } if (offset) { @@ -143,18 +143,17 @@ PropertyArrayView> transformNormalizedVecNArray( return PropertyArrayView>(std::move(result)); } - template -PropertyArrayView> transformNormalizedMatNArray( - const PropertyArrayView>& value, - const std::optional>>& offset, - const std::optional>>& scale) { - std::vector> result(static_cast(value.size())); +PropertyArrayView> transformNormalizedMatNArray( + const PropertyArrayView>& value, + const std::optional>>& offset, + const std::optional>>& scale) { + std::vector> result(static_cast(value.size())); for (int64_t i = 0; i < value.size(); i++) { result[i] = normalize(value[i]); if (scale) { - result[i] = applyScale>(result[i], (*scale)[i]); + result[i] = applyScale>(result[i], (*scale)[i]); } if (offset) { @@ -162,6 +161,6 @@ PropertyArrayView> transformNormalizedMatNArray( } } - return PropertyArrayView>(std::move(result)); + return PropertyArrayView>(std::move(result)); } } // namespace CesiumGltf diff --git a/CesiumGltf/include/CesiumGltf/PropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyView.h index 469f8392d..4beb9aebf 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyView.h @@ -2269,7 +2269,7 @@ template <> class PropertyView> { */ std::optional> defaultValue() const noexcept { - if (_noDataSize > 0) { + if (_defaultValueSize > 0) { return PropertyArrayView( gsl::span( _defaultValue.data(), diff --git a/CesiumGltf/test/TestPropertyTablePropertyView.cpp b/CesiumGltf/test/TestPropertyTablePropertyView.cpp index 7009dc0f1..016fd5bf5 100644 --- a/CesiumGltf/test/TestPropertyTablePropertyView.cpp +++ b/CesiumGltf/test/TestPropertyTablePropertyView.cpp @@ -306,7 +306,7 @@ static void checkNormalizedMatN( template static void checkVariableLengthArray( const std::vector& data, - const std::vector offsets, + const std::vector& offsets, PropertyComponentType offsetType, int64_t instanceCount) { // copy data to buffer @@ -359,6 +359,163 @@ static void checkVariableLengthArray( REQUIRE(expectedIdx == data.size()); } +template +static void checkVariableLengthArrayWithProperties( + const std::vector& data, + const std::vector& offsets, + PropertyComponentType offsetType, + int64_t instanceCount, + const std::vector>>& expected, + const std::optional noData = std::nullopt, + const std::optional defaultValue = std::nullopt) { + // copy data to buffer + std::vector buffer; + buffer.resize(data.size() * sizeof(DataType)); + std::memcpy(buffer.data(), data.data(), data.size() * sizeof(DataType)); + + // copy offset to buffer + std::vector offsetBuffer; + offsetBuffer.resize(offsets.size() * sizeof(OffsetType)); + std::memcpy( + offsetBuffer.data(), + offsets.data(), + offsets.size() * sizeof(OffsetType)); + + PropertyTableProperty propertyTableProperty; + ClassProperty classProperty; + classProperty.type = + convertPropertyTypeToString(TypeToPropertyType::value); + + PropertyComponentType componentType = TypeToPropertyType::component; + if (componentType != PropertyComponentType::None) { + classProperty.componentType = + convertPropertyComponentTypeToString(componentType); + } + + classProperty.array = true; + classProperty.noData = noData; + classProperty.defaultProperty = defaultValue; + + PropertyTablePropertyView> property( + propertyTableProperty, + classProperty, + instanceCount, + gsl::span(buffer.data(), buffer.size()), + gsl::span(offsetBuffer.data(), offsetBuffer.size()), + gsl::span(), + offsetType, + PropertyComponentType::None); + + REQUIRE(property.arrayCount() == 0); + + // Check raw values first + size_t expectedIdx = 0; + for (int64_t i = 0; i < property.size(); ++i) { + PropertyArrayView values = property.getRaw(i); + for (int64_t j = 0; j < values.size(); ++j) { + REQUIRE(values[j] == data[expectedIdx]); + ++expectedIdx; + } + } + + REQUIRE(expectedIdx == data.size()); + + // Check values with properties applied + for (int64_t i = 0; i < property.size(); ++i) { + std::optional> maybeValues = property.get(i); + if (!maybeValues) { + REQUIRE(!expected[static_cast(i)]); + continue; + } + + auto values = *maybeValues; + auto expectedValues = *expected[static_cast(i)]; + + for (int64_t j = 0; j < values.size(); ++j) { + REQUIRE(values[j] == expectedValues[static_cast(j)]); + } + } +} + +template < + typename DataType, + typename OffsetType, + typename NormalizedType = typename TypeToNormalizedType::type> +static void checkNormalizedVariableLengthArray( + const std::vector& data, + const std::vector offsets, + PropertyComponentType offsetType, + int64_t instanceCount, + const std::vector>>& expected, + const std::optional noData = std::nullopt, + const std::optional defaultValue = std::nullopt) { + // copy data to buffer + std::vector buffer; + buffer.resize(data.size() * sizeof(DataType)); + std::memcpy(buffer.data(), data.data(), data.size() * sizeof(DataType)); + + // copy offset to buffer + std::vector offsetBuffer; + offsetBuffer.resize(offsets.size() * sizeof(OffsetType)); + std::memcpy( + offsetBuffer.data(), + offsets.data(), + offsets.size() * sizeof(OffsetType)); + + PropertyTableProperty propertyTableProperty; + ClassProperty classProperty; + classProperty.type = + convertPropertyTypeToString(TypeToPropertyType::value); + + PropertyComponentType componentType = TypeToPropertyType::component; + classProperty.componentType = + convertPropertyComponentTypeToString(componentType); + + classProperty.array = true; + classProperty.normalized = true; + classProperty.noData = noData; + classProperty.defaultProperty = defaultValue; + + PropertyTablePropertyView, true> property( + propertyTableProperty, + classProperty, + instanceCount, + gsl::span(buffer.data(), buffer.size()), + gsl::span(offsetBuffer.data(), offsetBuffer.size()), + offsetType); + + REQUIRE(property.arrayCount() == 0); + + // Check raw values first + size_t expectedIdx = 0; + for (int64_t i = 0; i < property.size(); ++i) { + PropertyArrayView values = property.getRaw(i); + for (int64_t j = 0; j < values.size(); ++j) { + REQUIRE(values[j] == data[expectedIdx]); + ++expectedIdx; + } + } + + REQUIRE(expectedIdx == data.size()); + + // Check values with properties applied + for (int64_t i = 0; i < property.size(); ++i) { + std::optional> maybeValues = + property.get(i); + if (!maybeValues) { + REQUIRE(!expected[static_cast(i)]); + continue; + } + + auto values = *maybeValues; + auto expectedValues = *expected[static_cast(i)]; + + for (int64_t j = 0; j < values.size(); ++j) { + REQUIRE(values[j] == expectedValues[static_cast(j)]); + } + } +} + template static void checkFixedLengthArray( const std::vector& data, @@ -468,16 +625,16 @@ static void checkFixedLengthArrayWithProperties( // Check values with properties applied for (int64_t i = 0; i < property.size(); ++i) { std::optional> maybeValues = property.get(i); - std::optional> expectedValues = - expected[static_cast(i)]; - REQUIRE(maybeValues.has_value() == expectedValues.has_value()); if (!maybeValues) { + REQUIRE(!expected[static_cast(i)]); continue; } auto values = *maybeValues; + auto expectedValues = *expected[static_cast(i)]; + for (int64_t j = 0; j < values.size(); ++j) { - REQUIRE(values[j] == (*expectedValues)[static_cast(j)]); + REQUIRE(values[j] == expectedValues[static_cast(j)]); } } } @@ -543,6 +700,7 @@ static void checkNormalizedFixedLengthArray( std::optional> maybeValues = property.get(i); if (!maybeValues) { REQUIRE(!expected[static_cast(i)]); + continue; } auto values = *maybeValues; @@ -970,55 +1128,103 @@ TEST_CASE("Check matN PropertyTablePropertyView") { } SECTION("Normalized Uint8 Mat2") { + // clang-format off std::vector values{ - glm::u8mat2x2(0, 64, 255, 255), - glm::u8mat2x2(255, 0, 128, 0)}; + glm::u8mat2x2( + 0, 64, + 255, 255), + glm::u8mat2x2( + 255, 0, + 128, 0)}; std::vector> expected{ - glm::dmat2(0.0, 64.0 / 255.0, 1.0, 1.0), - glm::dmat2(1.0, 0.0, 128.0 / 255.0, 0.0)}; + glm::dmat2( + 0.0, 64.0 / 255.0, + 1.0, 1.0), + glm::dmat2( + 1.0, 0.0, + 128.0 / 255.0, 0.0)}; + // clang-format on checkNormalizedMatN(values, expected); } SECTION("Normalized Int16 Mat2") { + // clang-format off std::vector values{ - glm::i16mat2x2(-32768, 0, 16384, 32767), - glm::i16mat2x2(0, 32767, 32767, -32768)}; + glm::i16mat2x2( + -32768, 0, + 16384, 32767), + glm::i16mat2x2( + 0, 32767, + 32767, -32768)}; std::vector> expected{ - glm::dmat2(-1.0, 0.0, 16384.0 / 32767.0, 1.0), - glm::dmat2(0.0, 1.0, 1.0, -1.0), + glm::dmat2( + -1.0, 0.0, + 16384.0 / 32767.0, 1.0), + glm::dmat2( + 0.0, 1.0, + 1.0, -1.0), }; + // clang-format on checkNormalizedMatN(values, expected); } SECTION("Float Mat2 with Offset / Scale") { + // clang-format off std::vector values{ - glm::mat2(1.0f, 3.0f, 4.0f, 2.0f), - glm::mat2(6.5f, 2.0f, -2.0f, 0.0f), - glm::mat2(8.0f, -1.0f, -3.0f, 1.0f), + glm::mat2( + 1.0f, 3.0f, + 4.0f, 2.0f), + glm::mat2( + 6.5f, 2.0f, + -2.0f, 0.0f), + glm::mat2( + 8.0f, -1.0f, + -3.0f, 1.0f), }; - std::optional offset = - JsonValue::Array{1.0f, 2.0f, 3.0f, 1.0f}; - std::optional scale = - JsonValue::Array{2.0f, 0.0f, 0.0f, 2.0f}; + std::optional offset = JsonValue::Array{ + 1.0f, 2.0f, + 3.0f, 1.0f}; + std::optional scale = JsonValue::Array{ + 2.0f, 0.0f, + 0.0f, 2.0f}; std::vector> expected{ - glm::mat2(3.0f, 2.0f, 3.0f, 5.0f), - glm::mat2(14.0f, 2.0f, 3.0f, 1.0f), - glm::mat2(17.0f, 2.0f, 3.0f, 3.0f), + glm::mat2( + 3.0f, 2.0f, + 3.0f, 5.0f), + glm::mat2( + 14.0f, 2.0f, + 3.0f, 1.0f), + glm::mat2( + 17.0f, 2.0f, + 3.0f, 3.0f), }; + // clang-format on checkMatN(values, expected, offset, scale); } SECTION("Normalized Uint8 Mat2 with Offset and Scale") { + // clang-format off std::vector values{ - glm::u8mat2x2(0, 64, 255, 255), - glm::u8mat2x2(255, 0, 128, 0)}; - std::optional offset = - JsonValue::Array{0.0, 1.0, 1.0, 0.0}; - std::optional scale = - JsonValue::Array{2.0, 1.0, 0.0, 2.0}; + glm::u8mat2x2( + 0, 64, + 255, 255), + glm::u8mat2x2( + 255, 0, + 128, 0)}; + std::optional offset = JsonValue::Array{ + 0.0, 1.0, + 1.0, 0.0}; + std::optional scale = JsonValue::Array{ + 2.0, 1.0, + 0.0, 2.0}; std::vector> expected{ - glm::dmat2(0.0, 1 + 64.0 / 255.0, 1.0, 2.0), - glm::dmat2(2.0, 1.0, 1.0, 0.0)}; + glm::dmat2( + 0.0, 1 + 64.0 / 255.0, + 1.0, 2.0), + glm::dmat2( + 2.0, 1.0, + 1.0, 0.0)}; + // clang-format on checkNormalizedMatN(values, expected, offset, scale); } @@ -1094,30 +1300,47 @@ TEST_CASE("Check matN PropertyTablePropertyView") { }; SECTION("Normalized Uint8 Mat2 with all properties") { + // clang-format off std::vector values{ - glm::u8mat2x2(0, 64, 255, 255), + glm::u8mat2x2( + 0, 64, + 255, 255), glm::u8mat2x2(0), - glm::u8mat2x2(255, 0, 128, 0)}; - std::optional offset = - JsonValue::Array{0.0, 1.0, 1.0, 0.0}; - std::optional scale = - JsonValue::Array{2.0, 1.0, 0.0, 2.0}; - std::optional noData = JsonValue::Array{0, 0, 0, 0}; - std::optional defaultValue = - JsonValue::Array{1.0, 0.0, 0.0, 1.0}; + glm::u8mat2x2( + 255, 0, + 128, 0)}; + std::optional offset = JsonValue::Array{ + 0.0, 1.0, + 1.0, 0.0}; + std::optional scale = JsonValue::Array{ + 2.0, 1.0, + 0.0, 2.0}; + std::optional noData = JsonValue::Array{ + 0, 0, + 0, 0}; + std::optional defaultValue = JsonValue::Array{ + 1.0, 0.0, + 0.0, 1.0}; std::vector> expected{ - glm::dmat2(0.0, 1 + 64.0 / 255.0, 1.0, 2.0), + glm::dmat2( + 0.0, 1 + 64.0 / 255.0, + 1.0, 2.0), glm::dmat2(1.0), - glm::dmat2(2.0, 1.0, 1.0, 0.0)}; + glm::dmat2( + 2.0, 1.0, + 1.0, 0.0)}; + // clang-format on checkNormalizedMatN(values, expected, offset, scale, noData, defaultValue); } SECTION("Overrides class property values") { + // clang-fomrat off std::vector values{ glm::mat2(1.0f), glm::mat2(2.5f, 1.0f, 1.0f, 2.5f), glm::mat2(3.0f)}; + // clang-format on std::vector data; data.resize(values.size() * sizeof(glm::mat2)); std::memcpy(data.data(), values.data(), data.size()); @@ -1125,16 +1348,20 @@ TEST_CASE("Check matN PropertyTablePropertyView") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::MAT2; classProperty.componentType = ClassProperty::ComponentType::FLOAT32; + // clang-fomrat off classProperty.offset = {0.0f, 0.0f, 0.0f, 0.0f}; classProperty.scale = {1.0f, 1.0f, 1.0f, 1.0f}; classProperty.min = {1.0f, 0.0f, 0.0f, 1.0f}; classProperty.max = {3.0f, 0.0f, 0.0f, 3.0f}; + // clang-fomrat on PropertyTableProperty propertyTableProperty; + // clang-fomrat off propertyTableProperty.offset = {1.0f, 0.5f, 0.5f, 1.0f}; propertyTableProperty.scale = {2.0f, 1.0f, 0.0f, 1.0f}; propertyTableProperty.min = {3.0f, 0.5f, 0.5f, 2.0f}; propertyTableProperty.max = {7.0f, 1.5f, 0.5f, 4.0f}; + // clang-fomrat on PropertyTablePropertyView property( propertyTableProperty, @@ -1142,19 +1369,35 @@ TEST_CASE("Check matN PropertyTablePropertyView") { static_cast(values.size()), gsl::span(data.data(), data.size())); + // clang-format off REQUIRE(property.offset()); - REQUIRE(*property.offset() == glm::mat2(1.0f, 0.5f, 0.5f, 1.0f)); + REQUIRE(*property.offset() == glm::mat2( + 1.0f, 0.5f, + 0.5f, 1.0f)); REQUIRE(property.scale()); - REQUIRE(*property.scale() == glm::mat2(2.0f, 1.0f, 0.0f, 1.0f)); + REQUIRE(*property.scale() == glm::mat2( + 2.0f, 1.0f, + 0.0f, 1.0f)); REQUIRE(property.min()); - REQUIRE(*property.min() == glm::mat2(3.0f, 0.5f, 0.5f, 2.0f)); + REQUIRE(*property.min() == glm::mat2( + 3.0f, 0.5f, + 0.5f, 2.0f)); REQUIRE(property.max()); - REQUIRE(*property.max() == glm::mat2(7.0f, 1.5f, 0.5f, 4.0f)); + REQUIRE(*property.max() == glm::mat2( + 7.0f, 1.5f, + 0.5f, 4.0f)); std::vector expected{ - glm::mat2(3.0f, 0.5f, 0.5f, 2.0f), - glm::mat2(6.0f, 1.5f, 0.5f, 3.5f), - glm::mat2(7.0f, 0.5f, 0.5f, 4.0f)}; + glm::mat2( + 3.0f, 0.5f, + 0.5f, 2.0f), + glm::mat2( + 6.0f, 1.5f, + 0.5f, 3.5f), + glm::mat2( + 7.0f, 0.5f, + 0.5f, 4.0f)}; + // clang-format on for (int64_t i = 0; i < property.size(); ++i) { REQUIRE(property.getRaw(i) == values[static_cast(i)]); @@ -1783,7 +2026,7 @@ TEST_CASE("Check fixed-length vecN array PropertyTablePropertyView") { {glm::vec2(1.0f, 1.0f), glm::vec2(3.0f, 4.0f)}); std::vector> expected{ - {glm::vec2(3.0f, 1.0), glm::vec2(3.0f, -5.0f)}, + {glm::vec2(3.0f, 1.0f), glm::vec2(3.0f, -5.0f)}, {glm::vec2(2.0f, 1.0f), glm::vec2(1.0f, 1.0f)}}; size_t expectedIdx = 0; for (int64_t i = 0; i < property.size(); ++i) { @@ -1900,81 +2143,609 @@ TEST_CASE("Check fixed-length matN array PropertyTablePropertyView") { // clang-format on checkFixedLengthArray(data, 3); } -} -TEST_CASE("Check variable-length scalar array PropertyTablePropertyView") { - SECTION("Variable-length array of uint8_t") { + SECTION("Array of 2 normalized u8mat2x2s") { // clang-format off - std::vector data{ - 3, 2, - 0, 45, 2, 1, 4, - 1, 3, 2, - 1, 3, 4, 1 - }; - std::vector offsets{ - 0, 2, 7, 10, 14 - }; + std::vector data{ + glm::u8mat2x2( + 255, 64, + 0, 255), + glm::u8mat2x2( + 0, 255, + 64, 128), + glm::u8mat2x2( + 128, 0, + 0, 255), + glm::u8mat2x2( + 255, 32, + 255, 0)}; + + std::vector>> expected{ + std::vector{ + glm::dmat2( + 1.0, 64.0 / 255.0, + 0.0, 1.0), + glm::dmat2( + 0.0, 1.0, + 64.0 / 255.0, 128.0 / 255.0), + }, + std::vector{ + glm::dmat2( + 128.0 / 255.0, 0.0, + 0.0, 1.0), + glm::dmat2( + 1.0, 32.0 / 255.0, + 1.0, 0.0), + }}; // clang-format on - checkVariableLengthArray(data, offsets, PropertyComponentType::Uint32, 4); + checkNormalizedFixedLengthArray(data, 2, expected); } - SECTION("Variable-length array of int32_t") { + SECTION("Array of 2 mat2s with offset / scale") { // clang-format off - std::vector data{ - 3, 200, - 0, 450, 200, 1, 4, - 1, 3, 2, - 1, 3, 4, 1 - }; - std::vector offsets{ - 0, 2 * sizeof(int32_t), 7 * sizeof(int32_t), 10 * sizeof(int32_t), 14 - * sizeof(int32_t) - }; - // clang-format on + std::vector data{ + glm::mat2( + 1.0f, 2.0f, + 3.0f, 4.0f), + glm::mat2( + 5.0f, -1.0f, + 0.0f, 2.0f), + glm::mat2( + -1.0f, -1.0f, + 0.0f, -2.0f), + glm::mat2( + 0.0f, -2.0f, + 4.0f, 3.0f)}; - checkVariableLengthArray(data, offsets, PropertyComponentType::Uint32, 4); + std::optional offset = + JsonValue::Array{ + {1.0f, 0.0f, + 2.0f, 3.0f}, + {-1.0f, 0.0f, + 0.0f, 2.0f}}; + std::optional scale = + JsonValue::Array{ + {1.0f, 2.0f, + 1.0f, 0.0f}, + {1.0f, -1.0f, + -1.0f, 2.0f}}; + + std::vector>> expected{ + std::vector{ + glm::mat2( + 2.0f, 4.0f, + 5.0f, 3.0f), + glm::mat2( + 4.0f, 1.0f, + 0.0f, 6.0f)}, + std::vector{ + glm::mat2( + 0.0f, -2.0f, + 2.0f, 3.0f), + glm::mat2( + -1.0f, 2.0f, + -4.0f, 8.0f)}}; + // clang-format on + checkFixedLengthArrayWithProperties(data, 2, expected, offset, scale); } - SECTION("Variable-length array of double") { + SECTION("Array of 2 imat2x2 with noData value") { // clang-format off - std::vector data{ - 3.333, 200.2, - 0.1122, 4.50, 2.30, 1.22, 4.444, - 1.4, 3.3, 2.2, - 1.11, 3.2, 4.111, 1.44 - }; - std::vector offsets{ - 0, 2 * sizeof(double), 7 * sizeof(double), 10 * sizeof(double), 14 * - sizeof(double) - }; + std::vector data{ + glm::imat2x2( + 122, 12, + 4, 6), + glm::imat2x2(-1), + glm::imat2x2( + -3, 44, + 7, 8), + glm::imat2x2( + 0, 7, + -1, 0), + glm::imat2x2(-1), + glm::imat2x2(0)}; + std::optional noData = JsonValue::Array{ + {-1, 0, + 0, -1}, + {0, 0, + 0, 0}}; + std::vector>> expected{ + std::vector{ + glm::imat2x2( + 122, 12, + 4, 6), + glm::imat2x2(-1)}, + std::vector{ + glm::imat2x2( + -3, 44, + 7, 8), + glm::imat2x2( + 0, 7, + -1, 0)}, + std::nullopt}; // clang-format on - - checkVariableLengthArray(data, offsets, PropertyComponentType::Uint32, 4); + checkFixedLengthArrayWithProperties( + data, + 2, + expected, + std::nullopt, + std::nullopt, + noData, + std::nullopt); } -} -TEST_CASE("Check variable-length vecN array PropertyTablePropertyView") { - SECTION("Variable-length array of ivec2") { + SECTION("Array of 2 imat2x2 with noData and default value") { // clang-format off - std::vector data{ - glm::ivec2(3, -2), glm::ivec2(20, 3), - glm::ivec2(0, 45), glm::ivec2(-10, 2), glm::ivec2(4, 4), glm::ivec2(1, -1), - glm::ivec2(3, 1), glm::ivec2(3, 2), glm::ivec2(0, -5), - glm::ivec2(-9, 10), glm::ivec2(8, -2) + std::vector data{ + glm::imat2x2( + 122, 12, + 4, 6), + glm::imat2x2(-1), + glm::imat2x2( + -3, 44, + 7, 8), + glm::imat2x2( + 0, 7, + -1, 0), + glm::imat2x2(-1), + glm::imat2x2(0)}; + std::optional noData = JsonValue::Array{ + {-1, 0, + 0, -1}, + {0, 0, + 0, 0}}; + std::optional defaultValue = JsonValue::Array{ + {2, 0, + 0, 2}, + {1, 0, + 0, 1} }; + std::vector>> expected{ + std::vector{ + glm::imat2x2( + 122, 12, + 4, 6), + glm::imat2x2(-1)}, + std::vector{ + glm::imat2x2( + -3, 44, + 7, 8), + glm::imat2x2( + 0, 7, + -1, 0)}, + std::vector{ + glm::imat2x2(2), + glm::imat2x2(1) + }}; // clang-format on - std::vector offsets{ - 0, - 2 * sizeof(glm::ivec2), - 6 * sizeof(glm::ivec2), + checkFixedLengthArrayWithProperties( + data, + 2, + expected, + std::nullopt, + std::nullopt, + noData, + defaultValue); + } + + SECTION("Array of 2 normalized i8mat2x2 with all properties") { + // clang-format off + std::vector data{ + glm::i8mat2x2(-128), + glm::i8mat2x2( + 64, -64, + 0, 255), + glm::i8mat2x2( + 127, -128, + -128, 0), + glm::i8mat2x2(0), + glm::i8mat2x2(0), + glm::i8mat2x2( + -128, -128, + -128, -128)}; + std::optional offset = JsonValue::Array{ + {0, 1, + 2, 3}, + {1, 2, + 0, -2}}; + std::optional scale = JsonValue::Array{ + {1, -1, + 0, 1}, + {2, 1, + -1, 0}}; + std::optional noData = JsonValue::Array{ + {0, 0, + 0, 0}, + {-128, -128, + -128, -128}}; + std::optional defaultValue = JsonValue::Array{ + {1, 0, + 0, 1}, + {2, 0, + 0, 2}}; + + std::vector>> expected{ + std::vector{ + glm::dmat2( + -1.0, 1.0, + 2.0, 2.0), + glm::dmat2( + 1 + 2 * (64.0 / 127.0), 2 - (64.0 / 127.0), + 0, -2)}, + std::vector{ + glm::dmat2( + 1.0, 2.0, + 2.0, 3.0), + glm::dmat2( + 1.0, 2.0, + 0.0, -2.0)}, + std::vector{ + glm::dmat2(1), + glm::dmat2(2)}, + }; + // clang-format on + checkNormalizedFixedLengthArray( + data, + 2, + expected, + offset, + scale, + noData, + defaultValue); + } + + SECTION("Overrides class property values") { + // clang-format off + std::vector data{ + glm::mat2(1.0f), + glm::mat2(2.0f), + glm::mat2(3.0f), + glm::mat2(4.0f)}; + // clang-format on + const int64_t count = 2; + const int64_t instanceCount = 2; + + std::vector buffer; + buffer.resize(data.size() * sizeof(glm::mat2)); + std::memcpy(buffer.data(), data.data(), buffer.size()); + + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::MAT2; + classProperty.componentType = ClassProperty::ComponentType::FLOAT32; + + classProperty.array = true; + classProperty.count = count; + // clang-format off + classProperty.offset = JsonValue::Array{ + {0, 0, + 0, 0}, + {0, 0, + 0, 0}}; + classProperty.scale = JsonValue::Array{ + {1, 1, + 1, 1}, + {1, 1, + 1, 1}}; + classProperty.min = JsonValue::Array{ + {1.0f, 0.0f, + 0.0f, 1.0f}, + {2.0f, 0.0f, + 0.0f, 2.0f}}; + classProperty.max = JsonValue::Array{ + {3.0f, 0.0f, + 0.0f, 3.0f}, + {4.0f, 0.0f, + 0.0f, 4.0f}}; + // clang-format on + + PropertyTableProperty propertyTableProperty; + // clang-format off + propertyTableProperty.offset = JsonValue::Array{ + {2, 1, + -1, -2}, + {0, -1, + 4, 0}}; + propertyTableProperty.scale = JsonValue::Array{ + {1, 0, + 1, 2}, + {1, -1, + 3, 2}}; + propertyTableProperty.min = JsonValue::Array{ + {2.0f, 1.0f, + -1.0f, 0.0f}, + {2.0f, -1.0f, + -4.0f, 4.0f}}; + propertyTableProperty.max = JsonValue::Array{ + {5.0f, 1.0f, + -1.0f, 4.0f}, + {4.0f, -1.0f, + 4.0f, 8.0f}}; + // clang-format on + + PropertyTablePropertyView> property( + propertyTableProperty, + classProperty, + instanceCount, + gsl::span(buffer.data(), buffer.size()), + gsl::span(), + gsl::span(), + PropertyComponentType::None, + PropertyComponentType::None); + + REQUIRE(property.arrayCount() == count); + REQUIRE(property.size() == instanceCount); + + // clang-format off + REQUIRE(property.offset()); + checkArrayEqual(*property.offset(), { + glm::mat2( + 2, 1, + -1, -2), + glm::mat2( + 0, -1, + 4, 0)}); + + REQUIRE(property.scale()); + checkArrayEqual(*property.scale(), { + glm::mat2( + 1, 0, + 1, 2), + glm::mat2( + 1, -1, + 3, 2)}); + + REQUIRE(property.min()); + checkArrayEqual(*property.min(), { + glm::mat2{ + 2.0f, 1.0f, + -1.0f, 0.0f}, + glm::mat2{ + 2.0f, -1.0f, + -4.0f, 4.0f}}); + + REQUIRE(property.max()); + checkArrayEqual( *property.max(), { + glm::mat2( + 5.0f, 1.0f, + -1.0f, 4.0f), + glm::mat2( + 4.0f, -1.0f, + 4.0f, 8.0f)}); + + std::vector> expected{ + {glm::mat2( + 3.0f, 1.0f, + -1.0f, 0.0f), + glm::mat2( + 2.0f, -1.0f, + 4.0f, 4.0f)}, + {glm::mat2( + 5.0f, 1.0f, + -1.0f, 4.0f), + glm::mat2( + 4.0f, -1.0f, + 4.0f, 8.0f)}}; + // clang-format on + size_t expectedIdx = 0; + for (int64_t i = 0; i < property.size(); ++i) { + PropertyArrayView rawValues = property.getRaw(i); + auto values = property.get(i); + REQUIRE(values); + for (int64_t j = 0; j < rawValues.size(); ++j) { + REQUIRE(rawValues[j] == data[expectedIdx]); + REQUIRE( + (*values)[j] == + expected[static_cast(i)][static_cast(j)]); + ++expectedIdx; + } + } + } +} + +TEST_CASE("Check variable-length scalar array PropertyTablePropertyView") { + SECTION("Array of uint8_t") { + // clang-format off + std::vector data{ + 3, 2, + 0, 45, 2, 1, 4, + 1, 3, 2, + 1, 3, 4, 1 + }; + std::vector offsets{ + 0, 2, 7, 10, 14 + }; + // clang-format on + + checkVariableLengthArray(data, offsets, PropertyComponentType::Uint32, 4); + } + + SECTION("Array of int32_t") { + // clang-format off + std::vector data{ + 3, 200, + 0, 450, 200, 1, 4, + 1, 3, 2, + 1, 3, 4, 1 + }; + std::vector offsets{ + 0, 2 * sizeof(int32_t), 7 * sizeof(int32_t), 10 * sizeof(int32_t), 14 + * sizeof(int32_t) + }; + // clang-format on + + checkVariableLengthArray(data, offsets, PropertyComponentType::Uint32, 4); + } + + SECTION("Array of double") { + // clang-format off + std::vector data{ + 3.333, 200.2, + 0.1122, 4.50, 2.30, 1.22, 4.444, + 1.4, 3.3, 2.2, + 1.11, 3.2, 4.111, 1.44 + }; + std::vector offsets{ + 0, 2 * sizeof(double), 7 * sizeof(double), 10 * sizeof(double), 14 * + sizeof(double) + }; + // clang-format on + + checkVariableLengthArray(data, offsets, PropertyComponentType::Uint32, 4); + } + + SECTION("Array of normalized uint8_t") { + // clang-format off + std::vector data{ + 255, 0, + 0, 255, 128, + 64, + }; + std::vector offsets{ + 0, 2, 5, 6 + }; + // clang-format on + + std::vector>> expected{ + std::vector{1.0, 0.0}, + std::vector{0.0, 1.0, 128.0 / 255.0}, + std::vector{64.0 / 255.0}, + }; + checkNormalizedVariableLengthArray( + data, + offsets, + PropertyComponentType::Uint32, + 3, + expected); + } + + SECTION("Array of int32_t with NoData") { + // clang-format off + std::vector data{ + 3, 200, + 0, 450, 200, 1, 4, + 1, 3, 2, + 0, + 1, 3, 4, 1 + }; + // clang-format on + std::vector offsets{ + 0, + 2 * sizeof(int32_t), + 7 * sizeof(int32_t), + 10 * sizeof(int32_t), + 11 * sizeof(int32_t), + 15 * sizeof(int32_t)}; + + std::optional noData = JsonValue::Array{0}; + + std::vector>> expected{ + std::vector{3, 200}, + std::vector{0, 450, 200, 1, 4}, + std::vector{1, 3, 2}, + std::nullopt, + std::vector{1, 3, 4, 1}}; + checkVariableLengthArrayWithProperties( + data, + offsets, + PropertyComponentType::Uint32, + 5, + expected, + noData); + } + + SECTION("Array of int32_t with NoData and DefaultValue") { + // clang-format off + std::vector data{ + 3, 200, + 0, 450, 200, 1, 4, + 1, 3, 2, + 0, + 1, 3, 4, 1 + }; + // clang-format on + std::vector offsets{ + 0, + 2 * sizeof(int32_t), + 7 * sizeof(int32_t), + 10 * sizeof(int32_t), + 11 * sizeof(int32_t), + 15 * sizeof(int32_t)}; + + std::optional noData = JsonValue::Array{0}; + std::optional defaultValue = JsonValue::Array{1}; + + std::vector>> expected{ + std::vector{3, 200}, + std::vector{0, 450, 200, 1, 4}, + std::vector{1, 3, 2}, + std::vector{1}, + std::vector{1, 3, 4, 1}}; + checkVariableLengthArrayWithProperties( + data, + offsets, + PropertyComponentType::Uint32, + 5, + expected, + noData, + defaultValue); + } + + SECTION("Array of normalized uint8_t with NoData and DefaultValue") { + // clang-format off + std::vector data{ + 255, 0, + 0, 255, 128, + 64, + 255, 255 + }; + std::vector offsets{ + 0, 2, 5, 6, 8 + }; + // clang-format on + + std::optional noData = JsonValue::Array{255, 255}; + std::optional defaultValue = JsonValue::Array{-1.0}; + + std::vector>> expected{ + std::vector{1.0, 0.0}, + std::vector{0.0, 1.0, 128.0 / 255.0}, + std::vector{64.0 / 255}, + std::vector{-1.0}, + }; + + checkNormalizedVariableLengthArray( + data, + offsets, + PropertyComponentType::Uint32, + 4, + expected, + noData, + defaultValue); + } +} + +TEST_CASE("Check variable-length vecN array PropertyTablePropertyView") { + SECTION("Array of ivec2") { + // clang-format off + std::vector data{ + glm::ivec2(3, -2), glm::ivec2(20, 3), + glm::ivec2(0, 45), glm::ivec2(-10, 2), glm::ivec2(4, 4), glm::ivec2(1, -1), + glm::ivec2(3, 1), glm::ivec2(3, 2), glm::ivec2(0, -5), + glm::ivec2(-9, 10), glm::ivec2(8, -2) + }; + // clang-format on + std::vector offsets{ + 0, + 2 * sizeof(glm::ivec2), + 6 * sizeof(glm::ivec2), 9 * sizeof(glm::ivec2), 11 * sizeof(glm::ivec2)}; checkVariableLengthArray(data, offsets, PropertyComponentType::Uint32, 4); } - SECTION("Variable-length array of dvec3") { + SECTION("Array of dvec3") { // clang-format off std::vector data{ glm::dvec3(-0.02, 2.0, 1.0), glm::dvec3(9.92, 9.0, -8.0), @@ -1995,7 +2766,7 @@ TEST_CASE("Check variable-length vecN array PropertyTablePropertyView") { checkVariableLengthArray(data, offsets, PropertyComponentType::Uint32, 5); } - SECTION("Variable-length array of u8vec4") { + SECTION("Array of u8vec4") { // clang-format off std::vector data{ glm::u8vec4(1, 2, 3, 4), glm::u8vec4(5, 6, 7, 8), @@ -2016,10 +2787,162 @@ TEST_CASE("Check variable-length vecN array PropertyTablePropertyView") { checkVariableLengthArray(data, offsets, PropertyComponentType::Uint32, 5); } + + SECTION("Array of normalized u8vec2") { + // clang-format off + std::vector data{ + glm::u8vec2(255, 0), glm::u8vec2(0, 64), + glm::u8vec2(0, 0), + glm::u8vec2(128, 255), glm::u8vec2(255, 255), glm::u8vec2(32, 64) + }; + // clang-format on + std::vector offsets{ + 0 * sizeof(glm::u8vec2), + 2 * sizeof(glm::u8vec2), + 3 * sizeof(glm::u8vec2), + 6 * sizeof(glm::u8vec2)}; + + std::vector>> expected{ + std::vector{ + glm::dvec2(1.0, 0.0), + glm::dvec2(0.0, 64.0 / 255.0)}, + std::vector{glm::dvec2(0.0, 0.0)}, + std::vector{ + glm::dvec2(128.0 / 255.0, 1.0), + glm::dvec2(1.0, 1.0), + glm::dvec2(32.0 / 255.0, 64.0 / 255.0)}, + }; + checkNormalizedVariableLengthArray( + data, + offsets, + PropertyComponentType::Uint32, + 3, + expected); + } + + SECTION("Array of ivec3 with NoData") { + // clang-format off + std::vector data{ + glm::ivec3(3, 200, 1), glm::ivec3(2, 4, 6), + glm::ivec3(-1, 0, -450), + glm::ivec3(200, 1, 4), glm::ivec3(1, 3, 2), glm::ivec3(0), + glm::ivec3(-1), + glm::ivec3(1, 3, 4), glm::ivec3(1, 0, 1) + }; + // clang-format on + std::vector offsets{ + 0, + 2 * sizeof(glm::ivec3), + 3 * sizeof(glm::ivec3), + 6 * sizeof(glm::ivec3), + 7 * sizeof(glm::ivec3), + 9 * sizeof(glm::ivec3)}; + + std::optional noData = JsonValue::Array{}; + noData->push_back(JsonValue::Array{-1, -1, -1}); + + std::vector>> expected{ + std::vector{glm::ivec3(3, 200, 1), glm::ivec3(2, 4, 6)}, + std::vector{glm::ivec3(-1, 0, -450)}, + std::vector{ + glm::ivec3(200, 1, 4), + glm::ivec3(1, 3, 2), + glm::ivec3(0)}, + std::nullopt, + std::vector{glm::ivec3(1, 3, 4), glm::ivec3(1, 0, 1)}}; + checkVariableLengthArrayWithProperties( + data, + offsets, + PropertyComponentType::Uint32, + 5, + expected, + noData); + } + + SECTION("Array of ivec3 with NoData and DefaultValue") { + // clang-format off + std::vector data{ + glm::ivec3(3, 200, 1), glm::ivec3(2, 4, 6), + glm::ivec3(-1, 0, -450), + glm::ivec3(200, 1, 4), glm::ivec3(1, 3, 2), glm::ivec3(0), + glm::ivec3(-1), + glm::ivec3(1, 3, 4), glm::ivec3(1, 0, 1) + }; + // clang-format on + std::vector offsets{ + 0, + 2 * sizeof(glm::ivec3), + 3 * sizeof(glm::ivec3), + 6 * sizeof(glm::ivec3), + 7 * sizeof(glm::ivec3), + 9 * sizeof(glm::ivec3)}; + + std::optional noData = JsonValue::Array{}; + noData->push_back(JsonValue::Array{-1, -1, -1}); + std::optional defaultValue = JsonValue::Array{}; + defaultValue->push_back(JsonValue::Array{0, 0, 0}); + + std::vector>> expected{ + std::vector{glm::ivec3(3, 200, 1), glm::ivec3(2, 4, 6)}, + std::vector{glm::ivec3(-1, 0, -450)}, + std::vector{ + glm::ivec3(200, 1, 4), + glm::ivec3(1, 3, 2), + glm::ivec3(0)}, + std::vector{glm::ivec3(0)}, + std::vector{glm::ivec3(1, 3, 4), glm::ivec3(1, 0, 1)}}; + checkVariableLengthArrayWithProperties( + data, + offsets, + PropertyComponentType::Uint32, + 5, + expected, + noData, + defaultValue); + } + + SECTION("Array of normalized u8vec2 with NoData and DefaultValue") { + // clang-format off + std::vector data{ + glm::u8vec2(255, 0), glm::u8vec2(0, 64), + glm::u8vec2(0, 0), + glm::u8vec2(128, 255), glm::u8vec2(255, 255), glm::u8vec2(32, 64) + }; + // clang-format on + std::vector offsets{ + 0 * sizeof(glm::u8vec2), + 2 * sizeof(glm::u8vec2), + 3 * sizeof(glm::u8vec2), + 6 * sizeof(glm::u8vec2)}; + + std::optional noData = JsonValue::Array{}; + noData->push_back(JsonValue::Array{0, 0}); + std::optional defaultValue = JsonValue::Array{}; + defaultValue->push_back(JsonValue::Array{-1.0, -1.0}); + + std::vector>> expected{ + std::vector{ + glm::dvec2(1.0, 0.0), + glm::dvec2(0.0, 64.0 / 255.0)}, + std::vector{glm::dvec2(-1.0)}, + std::vector{ + glm::dvec2(128.0 / 255.0, 1.0), + glm::dvec2(1.0, 1.0), + glm::dvec2(32.0 / 255.0, 64.0 / 255.0)}, + }; + checkNormalizedVariableLengthArray( + data, + offsets, + PropertyComponentType::Uint32, + 3, + expected, + noData, + defaultValue); + } } TEST_CASE("Check variable-length matN array PropertyTablePropertyView") { - SECTION("Variable-length array of dmat2") { + SECTION("Array of dmat2") { // clang-format off std::vector data0{ glm::dmat2( @@ -2063,7 +2986,7 @@ TEST_CASE("Check variable-length matN array PropertyTablePropertyView") { checkVariableLengthArray(data, offsets, PropertyComponentType::Uint32, 3); } - SECTION("Variable-length array of i16mat3x3") { + SECTION("Array of i16mat3x3") { // clang-format off std::vector data0{ glm::i16mat3x3( @@ -2112,7 +3035,7 @@ TEST_CASE("Check variable-length matN array PropertyTablePropertyView") { checkVariableLengthArray(data, offsets, PropertyComponentType::Uint32, 3); } - SECTION("Variable-length array of u8mat4x4") { + SECTION("Array of u8mat4x4") { // clang-format off std::vector data0{ glm::u8mat4x4( @@ -2166,6 +3089,172 @@ TEST_CASE("Check variable-length matN array PropertyTablePropertyView") { checkVariableLengthArray(data, offsets, PropertyComponentType::Uint32, 3); } + + SECTION("Array of normalized u8mat2x2") { + // clang-format off + std::vector data{ + glm::u8mat2x2(255), glm::u8mat2x2(64), + glm::u8mat2x2(0), + glm::u8mat2x2(128), glm::u8mat2x2(255), glm::u8mat2x2(32) + }; + // clang-format on + std::vector offsets{ + 0 * sizeof(glm::u8mat2x2), + 2 * sizeof(glm::u8mat2x2), + 3 * sizeof(glm::u8mat2x2), + 6 * sizeof(glm::u8mat2x2)}; + + std::vector>> expected{ + std::vector{glm::dmat2(1.0), glm::dmat2(64.0 / 255.0)}, + std::vector{glm::dmat2(0.0)}, + std::vector{ + glm::dmat2(128.0 / 255.0), + glm::dmat2(1.0), + glm::dmat2(32.0 / 255.0)}, + }; + checkNormalizedVariableLengthArray( + data, + offsets, + PropertyComponentType::Uint32, + 3, + expected); + } + + SECTION("Array of imat3x3 with NoData") { + // clang-format off + std::vector data{ + glm::imat3x3(3), glm::imat3x3(2), + glm::imat3x3(-1), + glm::imat3x3(200), glm::imat3x3(1), glm::imat3x3(0), + glm::imat3x3(-1), + glm::imat3x3(1), glm::imat3x3(24) + }; + // clang-format on + std::vector offsets{ + 0, + 2 * sizeof(glm::imat3x3), + 3 * sizeof(glm::imat3x3), + 6 * sizeof(glm::imat3x3), + 7 * sizeof(glm::imat3x3), + 9 * sizeof(glm::imat3x3)}; + + // clang-format off + std::optional noData = JsonValue::Array{}; + noData->push_back(JsonValue::Array{ + -1, 0, 0, + 0, -1, 0, + 0, 0, -1}); + // clang-format on + + std::vector>> expected{ + std::vector{glm::imat3x3(3), glm::imat3x3(2)}, + std::nullopt, + std::vector{ + glm::imat3x3(200), + glm::imat3x3(1), + glm::imat3x3(0)}, + std::nullopt, + std::vector{glm::imat3x3(1), glm::imat3x3(24)}}; + checkVariableLengthArrayWithProperties( + data, + offsets, + PropertyComponentType::Uint32, + 5, + expected, + noData); + } + + SECTION("Array of imat3x3 with NoData and DefaultValue") { + // clang-format off + std::vector data{ + glm::imat3x3(3), glm::imat3x3(2), + glm::imat3x3(-1), + glm::imat3x3(200), glm::imat3x3(1), glm::imat3x3(0), + glm::imat3x3(-1), + glm::imat3x3(1), glm::imat3x3(24) + }; + // clang-format on + std::vector offsets{ + 0, + 2 * sizeof(glm::imat3x3), + 3 * sizeof(glm::imat3x3), + 6 * sizeof(glm::imat3x3), + 7 * sizeof(glm::imat3x3), + 9 * sizeof(glm::imat3x3)}; + + // clang-format off + std::optional noData = JsonValue::Array{}; + noData->push_back(JsonValue::Array{ + -1, 0, 0, + 0, -1, 0, + 0, 0, -1}); + std::optional defaultValue = JsonValue::Array{}; + defaultValue->push_back(JsonValue::Array{ + 99, 0, 0, + 0, 99, 0, + 0, 0, 99}); + // clang-format on + + std::vector>> expected{ + std::vector{glm::imat3x3(3), glm::imat3x3(2)}, + std::vector{glm::imat3x3(99)}, + std::vector{ + glm::imat3x3(200), + glm::imat3x3(1), + glm::imat3x3(0)}, + std::vector{glm::imat3x3(99)}, + std::vector{glm::imat3x3(1), glm::imat3x3(24)}}; + checkVariableLengthArrayWithProperties( + data, + offsets, + PropertyComponentType::Uint32, + 5, + expected, + noData, + defaultValue); + } + + SECTION("Array of normalized u8mat2x2 with NoData and DefaultValue") { + // clang-format off + std::vector data{ + glm::u8mat2x2(255), glm::u8mat2x2(64), + glm::u8mat2x2(0), + glm::u8mat2x2(128), glm::u8mat2x2(255), glm::u8mat2x2(32) + }; + // clang-format on + std::vector offsets{ + 0 * sizeof(glm::u8mat2x2), + 2 * sizeof(glm::u8mat2x2), + 3 * sizeof(glm::u8mat2x2), + 6 * sizeof(glm::u8mat2x2)}; + + // clang-format off + std::optional noData = JsonValue::Array{}; + noData->push_back(JsonValue::Array{ + 0, 0, + 0, 0}); + std::optional defaultValue = JsonValue::Array{}; + defaultValue->push_back(JsonValue::Array{ + -1.0, 0.0, + 0.0, -1.0}); + // clang-format on + std::vector>> expected{ + std::vector{glm::dmat2(1.0), glm::dmat2(64.0 / 255.0)}, + std::vector{glm::dmat2(-1.0)}, + std::vector{ + glm::dmat2(128.0 / 255.0), + glm::dmat2(1.0), + glm::dmat2(32.0 / 255.0)}, + }; + checkNormalizedVariableLengthArray( + data, + offsets, + PropertyComponentType::Uint32, + 3, + expected, + noData, + defaultValue); + } } TEST_CASE("Check fixed-length array of string") { @@ -2213,35 +3302,155 @@ TEST_CASE("Check fixed-length array of string") { ¤tStringOffset, sizeof(uint32_t)); - PropertyTableProperty propertyTableProperty; - ClassProperty classProperty; - classProperty.type = ClassProperty::Type::STRING; - classProperty.array = true; - classProperty.count = 3; + SECTION("Returns correct values") { + PropertyTableProperty propertyTableProperty; + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::STRING; + classProperty.array = true; + classProperty.count = 3; - PropertyTablePropertyView> property( - propertyTableProperty, - classProperty, - static_cast(stringCount / 3), - gsl::span(buffer.data(), buffer.size()), - gsl::span(), - gsl::span(stringOffsets.data(), stringOffsets.size()), - PropertyComponentType::None, - PropertyComponentType::Uint32); + PropertyTablePropertyView> property( + propertyTableProperty, + classProperty, + static_cast(stringCount / 3), + gsl::span(buffer.data(), buffer.size()), + gsl::span(), + gsl::span(stringOffsets.data(), stringOffsets.size()), + PropertyComponentType::None, + PropertyComponentType::Uint32); - REQUIRE(property.arrayCount() == classProperty.count); + REQUIRE(property.arrayCount() == classProperty.count); - size_t expectedIdx = 0; - for (int64_t i = 0; i < property.size(); ++i) { - PropertyArrayView values = property.getRaw(i); - for (int64_t j = 0; j < values.size(); ++j) { - std::string_view v = values[j]; - REQUIRE(v == strings[expectedIdx]); - ++expectedIdx; + size_t expectedIdx = 0; + for (int64_t i = 0; i < property.size(); ++i) { + PropertyArrayView values = property.getRaw(i); + auto maybeValues = property.get(i); + REQUIRE(maybeValues); + + for (int64_t j = 0; j < values.size(); ++j) { + REQUIRE(values[j] == strings[expectedIdx]); + REQUIRE((*maybeValues)[j] == strings[expectedIdx]); + ++expectedIdx; + } } + + REQUIRE(expectedIdx == stringCount); } - REQUIRE(expectedIdx == stringCount); + SECTION("Uses NoData value") { + PropertyTableProperty propertyTableProperty; + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::STRING; + classProperty.array = true; + classProperty.count = 3; + classProperty.noData = {"Test 1", "Test 2", "Test 3"}; + + PropertyTablePropertyView> property( + propertyTableProperty, + classProperty, + static_cast(stringCount / 3), + gsl::span(buffer.data(), buffer.size()), + gsl::span(), + gsl::span(stringOffsets.data(), stringOffsets.size()), + PropertyComponentType::None, + PropertyComponentType::Uint32); + + REQUIRE(property.arrayCount() == classProperty.count); + + std::vector>> expected{ + std::nullopt, + std::vector{"Test 4", "Test 5", "Test 6"}, + std::vector{ + "This is a fine test", + "What's going on", + "Good morning"}}; + + // Check raw values first + size_t expectedIdx = 0; + for (int64_t i = 0; i < property.size(); ++i) { + PropertyArrayView values = property.getRaw(i); + for (int64_t j = 0; j < values.size(); ++j) { + std::string_view v = values[j]; + REQUIRE(v == strings[expectedIdx]); + ++expectedIdx; + } + } + + REQUIRE(expectedIdx == stringCount); + + // Check values with properties + for (int64_t i = 0; i < property.size(); ++i) { + auto maybeValues = property.get(i); + if (!maybeValues) { + REQUIRE(!expected[static_cast(i)]); + continue; + } + + auto values = *maybeValues; + auto expectedValues = *expected[static_cast(i)]; + for (int64_t j = 0; j < values.size(); ++j) { + REQUIRE(values[j] == expectedValues[j]); + } + } + } + + SECTION("Uses NoData and DefaultValue") { + PropertyTableProperty propertyTableProperty; + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::STRING; + classProperty.array = true; + classProperty.count = 3; + classProperty.noData = {"Test 1", "Test 2", "Test 3"}; + classProperty.defaultProperty = {"Default 1", "Default 2", "Default 3"}; + + PropertyTablePropertyView> property( + propertyTableProperty, + classProperty, + static_cast(stringCount / 3), + gsl::span(buffer.data(), buffer.size()), + gsl::span(), + gsl::span(stringOffsets.data(), stringOffsets.size()), + PropertyComponentType::None, + PropertyComponentType::Uint32); + + REQUIRE(property.arrayCount() == classProperty.count); + + std::vector>> expected{ + std::vector{"Default 1", "Default 2", "Default 3"}, + std::vector{"Test 4", "Test 5", "Test 6"}, + std::vector{ + "This is a fine test", + "What's going on", + "Good morning"}}; + + // Check raw values first + size_t expectedIdx = 0; + for (int64_t i = 0; i < property.size(); ++i) { + PropertyArrayView values = property.getRaw(i); + for (int64_t j = 0; j < values.size(); ++j) { + std::string_view v = values[j]; + REQUIRE(v == strings[expectedIdx]); + ++expectedIdx; + } + } + + REQUIRE(expectedIdx == stringCount); + + // Check values with properties + for (int64_t i = 0; i < property.size(); ++i) { + auto maybeValues = property.get(i); + if (!maybeValues) { + REQUIRE(!expected[static_cast(i)]); + continue; + } + + auto values = *maybeValues; + auto expectedValues = *expected[static_cast(i)]; + for (int64_t j = 0; j < values.size(); ++j) { + REQUIRE(values[j] == expectedValues[j]); + } + } + } } TEST_CASE("Check variable-length string array PropertyTablePropertyView") { @@ -2250,13 +3459,15 @@ TEST_CASE("Check variable-length string array PropertyTablePropertyView") { 0, 4 * sizeof(uint32_t), 7 * sizeof(uint32_t), - 11 * sizeof(uint32_t) + 8 * sizeof(uint32_t), + 12 * sizeof(uint32_t) }; std::vector strings{ "Test 1", "Test 2", "Test 3", "Test 4", "Test 5", "Test 6", "Test 7", - "test 8", "Test 9", "Test 10", "Test 11" + "Null", + "Test 8", "Test 9", "Test 10", "Test 11" }; // clang-format on @@ -2292,36 +3503,162 @@ TEST_CASE("Check variable-length string array PropertyTablePropertyView") { ¤tOffset, sizeof(uint32_t)); - PropertyTableProperty propertyTableProperty; - ClassProperty classProperty; - classProperty.type = ClassProperty::Type::STRING; - classProperty.array = true; + SECTION("Returns correct values") { + PropertyTableProperty propertyTableProperty; + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::STRING; + classProperty.array = true; - PropertyTablePropertyView> property( - propertyTableProperty, - classProperty, - 3, - gsl::span(buffer.data(), buffer.size()), - gsl::span( - reinterpret_cast(arrayOffsets.data()), - arrayOffsets.size() * sizeof(uint32_t)), - gsl::span(stringOffsets.data(), stringOffsets.size()), - PropertyComponentType::Uint32, - PropertyComponentType::Uint32); + PropertyTablePropertyView> property( + propertyTableProperty, + classProperty, + 4, + gsl::span(buffer.data(), buffer.size()), + gsl::span( + reinterpret_cast(arrayOffsets.data()), + arrayOffsets.size() * sizeof(uint32_t)), + gsl::span(stringOffsets.data(), stringOffsets.size()), + PropertyComponentType::Uint32, + PropertyComponentType::Uint32); - REQUIRE(property.arrayCount() == 0); + REQUIRE(property.arrayCount() == 0); - size_t expectedIdx = 0; - for (int64_t i = 0; i < property.size(); ++i) { - PropertyArrayView values = property.getRaw(i); - for (int64_t j = 0; j < values.size(); ++j) { - std::string_view v = values[j]; - REQUIRE(v == strings[expectedIdx]); - ++expectedIdx; + size_t expectedIdx = 0; + for (int64_t i = 0; i < property.size(); ++i) { + PropertyArrayView values = property.getRaw(i); + auto maybeValues = property.get(i); + REQUIRE(maybeValues); + + for (int64_t j = 0; j < values.size(); ++j) { + REQUIRE(values[j] == strings[expectedIdx]); + REQUIRE((*maybeValues)[j] == strings[expectedIdx]); + ++expectedIdx; + } + } + + REQUIRE(expectedIdx == stringCount); + } + + SECTION("Uses NoData Value") { + PropertyTableProperty propertyTableProperty; + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::STRING; + classProperty.array = true; + classProperty.noData = JsonValue::Array{"Null"}; + + PropertyTablePropertyView> property( + propertyTableProperty, + classProperty, + 4, + gsl::span(buffer.data(), buffer.size()), + gsl::span( + reinterpret_cast(arrayOffsets.data()), + arrayOffsets.size() * sizeof(uint32_t)), + gsl::span(stringOffsets.data(), stringOffsets.size()), + PropertyComponentType::Uint32, + PropertyComponentType::Uint32); + + REQUIRE(property.arrayCount() == 0); + + std::vector>> expected{ + std::vector{"Test 1", "Test 2", "Test 3", "Test 4"}, + std::vector{"Test 5", "Test 6", "Test 7"}, + std::nullopt, + std::vector{ + "Test 8", + "Test 9", + "Test 10", + "Test 11"}}; + + // Check raw values first + size_t expectedIdx = 0; + for (int64_t i = 0; i < property.size(); ++i) { + PropertyArrayView values = property.getRaw(i); + for (int64_t j = 0; j < values.size(); ++j) { + std::string_view v = values[j]; + REQUIRE(v == strings[expectedIdx]); + ++expectedIdx; + } + } + + REQUIRE(expectedIdx == stringCount); + + // Check values with properties + for (int64_t i = 0; i < property.size(); ++i) { + auto maybeValues = property.get(i); + if (!maybeValues) { + REQUIRE(!expected[static_cast(i)]); + continue; + } + + auto values = *maybeValues; + auto expectedValues = *expected[static_cast(i)]; + for (int64_t j = 0; j < values.size(); ++j) { + REQUIRE(values[j] == expectedValues[j]); + } } } - REQUIRE(expectedIdx == stringCount); + SECTION("Uses NoData and DefaultValue") { + PropertyTableProperty propertyTableProperty; + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::STRING; + classProperty.array = true; + classProperty.noData = JsonValue::Array{"Null"}; + classProperty.defaultProperty = JsonValue::Array{"Default"}; + + PropertyTablePropertyView> property( + propertyTableProperty, + classProperty, + 4, + gsl::span(buffer.data(), buffer.size()), + gsl::span( + reinterpret_cast(arrayOffsets.data()), + arrayOffsets.size() * sizeof(uint32_t)), + gsl::span(stringOffsets.data(), stringOffsets.size()), + PropertyComponentType::Uint32, + PropertyComponentType::Uint32); + + REQUIRE(property.arrayCount() == 0); + + std::vector>> expected{ + std::vector{"Test 1", "Test 2", "Test 3", "Test 4"}, + std::vector{"Test 5", "Test 6", "Test 7"}, + std::vector{"Default"}, + std::vector{ + "Test 8", + "Test 9", + "Test 10", + "Test 11"}}; + + // Check raw values first + size_t expectedIdx = 0; + for (int64_t i = 0; i < property.size(); ++i) { + PropertyArrayView values = property.getRaw(i); + for (int64_t j = 0; j < values.size(); ++j) { + std::string_view v = values[j]; + REQUIRE(v == strings[expectedIdx]); + ++expectedIdx; + } + } + + REQUIRE(expectedIdx == stringCount); + + // Check values with properties + for (int64_t i = 0; i < property.size(); ++i) { + auto maybeValues = property.get(i); + if (!maybeValues) { + REQUIRE(!expected[static_cast(i)]); + continue; + } + + auto values = *maybeValues; + auto expectedValues = *expected[static_cast(i)]; + for (int64_t j = 0; j < values.size(); ++j) { + REQUIRE(values[j] == expectedValues[j]); + } + } + } } TEST_CASE("Check fixed-length boolean array PropertyTablePropertyView") { From fe26c94446139a13b44fce9cca022a0f933ba6e5 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Thu, 24 Aug 2023 07:07:09 +1000 Subject: [PATCH 212/421] Remove unnecessary forward declaration. --- Cesium3DTilesSelection/src/TilesetContentManager.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Cesium3DTilesSelection/src/TilesetContentManager.h b/Cesium3DTilesSelection/src/TilesetContentManager.h index 6b7d6b7c0..355fd82a2 100644 --- a/Cesium3DTilesSelection/src/TilesetContentManager.h +++ b/Cesium3DTilesSelection/src/TilesetContentManager.h @@ -16,10 +16,6 @@ #include -namespace Cesium3DTiles { -struct Schema; -} - namespace Cesium3DTilesSelection { class TilesetContentManager From e8525be4fe0fde547a8f33c7bfb95cbbea3b3986 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Thu, 24 Aug 2023 15:35:53 +1000 Subject: [PATCH 213/421] Update doc. --- .../LocalHorizontalCoordinateSystem.h | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/CesiumGeospatial/include/CesiumGeospatial/LocalHorizontalCoordinateSystem.h b/CesiumGeospatial/include/CesiumGeospatial/LocalHorizontalCoordinateSystem.h index 8b6f378cd..1d849568f 100644 --- a/CesiumGeospatial/include/CesiumGeospatial/LocalHorizontalCoordinateSystem.h +++ b/CesiumGeospatial/include/CesiumGeospatial/LocalHorizontalCoordinateSystem.h @@ -77,6 +77,11 @@ class CESIUMGEOSPATIAL_API LocalHorizontalCoordinateSystem { * the Earth-Centered, Earth-Fixed frame. This is an advanced constructor and * should be avoided in most cases. * + * This constructor can be used to save/restore the state of an instance. It + * can also be used to create unusual coordinate systems that can't be created + * by the other constructors, such as coordinate systems where the axes aren't + * aligned with compass directions. + * * @param localToEcef The transformation matrix from this coordinate system to * the ECEF frame. */ @@ -87,10 +92,17 @@ class CESIUMGEOSPATIAL_API LocalHorizontalCoordinateSystem { * between the local frame and the Earth-Centered, Earth-Fixed frame. This is * an advanced constructor and should be avoided in most cases. * + * This constructor can be used to save/restore the state of an instance. It + * can also be used to create unusual coordinate systems that can't be created + * by the other constructors, such as coordinate systems where the axes aren't + * aligned with compass directions. + * * @param localToEcef The transformation matrix from this coordinate system to - * the ECEF frame. This must be the inverse of `ecefToLocal`. + * the ECEF frame. This must be the inverse of `ecefToLocal` or behavior is + * undefined, but this is not enforced. * @param ecefToLocal The transformation matrix from the ECEF frame to this - * coordinate system. This must be the inverse of `localToEcef`. + * coordinate system. This must be the inverse of `localToEcef` or behavior is + * undefined, but this is not enforced. */ LocalHorizontalCoordinateSystem( const glm::dmat4& localToEcef, From 4194e31a10e8fbbd25b6a93cc2bd975b4aa7f65c Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Thu, 24 Aug 2023 15:57:22 +1000 Subject: [PATCH 214/421] sizeof(uint32_t) instead of 4. Tweak test comment. --- CesiumIonClient/src/fillWithRandomBytes.cpp | 2 +- CesiumIonClient/test/TestFillWithRandomBytes.cpp | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CesiumIonClient/src/fillWithRandomBytes.cpp b/CesiumIonClient/src/fillWithRandomBytes.cpp index 51a69115a..7d489b8c5 100644 --- a/CesiumIonClient/src/fillWithRandomBytes.cpp +++ b/CesiumIonClient/src/fillWithRandomBytes.cpp @@ -41,7 +41,7 @@ void fillWithRandomBytes(const gsl::span& buffer) { } if (i < buffer.size()) { - assert(buffer.size() - i < 4); + assert(buffer.size() - i < sizeof(uint32_t)); std::uint32_t extra; if (rand_s(&extra) != 0) { diff --git a/CesiumIonClient/test/TestFillWithRandomBytes.cpp b/CesiumIonClient/test/TestFillWithRandomBytes.cpp index e3e8fe46a..930e9cf63 100644 --- a/CesiumIonClient/test/TestFillWithRandomBytes.cpp +++ b/CesiumIonClient/test/TestFillWithRandomBytes.cpp @@ -20,7 +20,9 @@ TEST_CASE("fillWithRandomBytes") { // In the unlikely event the value is zero, generate some new random // values. Repeat this up to 10 times. The chances that the random value // at a particular position is zero ten times in a row is vanishingly - // small. + // small. We don't care if previous positions get a zero on regeneration, + // we're just trying to test for the possibility of an off-by-one error + // making a particular position _always_ zero. for (int k = 0; buffer[j] == 0 && k < 10; ++k) fillWithRandomBytes(bufferSpan); CHECK(buffer[j] != 0); From 8253f1004b917350e746235654f00d9618047a88 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Thu, 24 Aug 2023 16:20:10 +1000 Subject: [PATCH 215/421] Add doc about code generation to the main readme. --- README.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/README.md b/README.md index 9d0fb9edc..7c74cc90f 100644 --- a/README.md +++ b/README.md @@ -84,3 +84,20 @@ Or, you can easily build it in Visual Studio Code with the `CMake Tools` extensi * Install [Doxygen](https://www.doxygen.nl/). * Run: `cmake --build build --target cesium-native-docs` * Open `build/doc/html/index.html` + +#### Regenerate glTF and 3D Tiles classes + +Much of the code in `CesiumGltf`, `Cesium3DTiles`, `CesiumGltfReader`, and `Cesium3DTilesReader` is generated from the standards' JSON Schema specifications. To regenerate the code: + +* Make sure you have a relatively recent version of Node.js installed. +* Install dependencies by running: + +``` +npm install +cd tools/generate-classes +npm install +cd .. +``` + +* From the repo root directory, run `npm run generate-gltf` or `npm run generate-3d-tiles`. +* On Windows, the line endings of the generated files will be different than those checked into the repo. Just `git add` them and git will fix the line endings (no need to commit). From 3b40023c411fb3dce1daed1f1753b68403b8184a Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Thu, 24 Aug 2023 16:21:26 +1000 Subject: [PATCH 216/421] Fix install script. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7c74cc90f..28c45d195 100644 --- a/README.md +++ b/README.md @@ -96,7 +96,7 @@ Much of the code in `CesiumGltf`, `Cesium3DTiles`, `CesiumGltfReader`, and `Cesi npm install cd tools/generate-classes npm install -cd .. +cd ../.. ``` * From the repo root directory, run `npm run generate-gltf` or `npm run generate-3d-tiles`. From 9a1abb295d9d13a36d205f3320b9a3d0311270dd Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Thu, 24 Aug 2023 19:13:40 +1000 Subject: [PATCH 217/421] Add missing dll export of FoundMetadataProperty. --- Cesium3DTiles/include/Cesium3DTiles/MetadataQuery.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cesium3DTiles/include/Cesium3DTiles/MetadataQuery.h b/Cesium3DTiles/include/Cesium3DTiles/MetadataQuery.h index e4caab50e..c0f5b3213 100644 --- a/Cesium3DTiles/include/Cesium3DTiles/MetadataQuery.h +++ b/Cesium3DTiles/include/Cesium3DTiles/MetadataQuery.h @@ -17,7 +17,7 @@ namespace Cesium3DTiles { * destroyed or modified. Continuing to access this result in that scenario will * result in undefined behavior. */ -struct FoundMetadataProperty { +struct CESIUM3DTILES_API FoundMetadataProperty { /** * @brief A reference to the identifier of the class that contains the found * property within the {@link Schema}. From 53bc30391a082b1d83b54dbb31e8f6428ad2ed60 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Thu, 24 Aug 2023 10:55:43 -0400 Subject: [PATCH 218/421] Integrate normalization with PropertyTableView --- CHANGES.md | 2 + .../CesiumGltf/PropertyTablePropertyView.h | 20 +- .../PropertyTablePropertyViewType.h | 285 +++++++++ .../include/CesiumGltf/PropertyTableView.h | 548 +++++++++++------- .../CesiumGltf/PropertyTexturePropertyView.h | 70 +-- .../include/CesiumGltf/PropertyTextureView.h | 11 +- CesiumGltf/include/CesiumGltf/PropertyView.h | 2 +- CesiumGltf/src/PropertyTableView.cpp | 84 +++ CesiumGltf/src/PropertyTextureView.cpp | 8 +- .../test/TestPropertyTablePropertyView.cpp | 8 +- CesiumGltf/test/TestPropertyTableView.cpp | 109 ++-- 11 files changed, 802 insertions(+), 345 deletions(-) create mode 100644 CesiumGltf/include/CesiumGltf/PropertyTablePropertyViewType.h diff --git a/CHANGES.md b/CHANGES.md index 91672f6d1..178e949bf 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -12,6 +12,7 @@ - Replaced `FeatureIDTextureView` with `FeatureIdTextureView`, which views a `FeatureIdTexture` in `EXT_mesh_features`. Feature ID textures from `EXT_feature_metadata` are no longer supported. - Renamed `FeatureIDTextureViewStatus` to `FeatureIdTextureViewStatus` for consistency. - Replaced `MetadataFeatureTableView` with `PropertyTableView`, which views a `PropertyTable` in `EXT_structural_metadata`. +- Replaced `MetadataPropertyView` with `PropertyTablePropertyView`, which is a templated view of a `PropertyTableProperty` in `EXT_structural_metadata`. This takes two template parameters: a typename `T` , and a `bool` indicating whether or not the values are normalized. - Renamed `MetadataArrayView` to `PropertyArrayView`. - Replaced `FeatureTextureView` with `PropertyTextureView`, which views a `PropertyTexture` in `EXT_structural_metadata`. - Renamed `FeatureTextureViewStatus` to `PropertyTextureViewStatus`. @@ -24,6 +25,7 @@ - Added `PropertyTableViewStatus` to indicate whether a `PropertyTableView` is valid. - Added `PropertyComponentType` to reflect the values of `componentType` in a `ClassProperty` from `EXT_structural_metadata`. +- Added `PropertyView`, which acts as a base class for all metadata property views. This takes two template parameters: a typename `T` , and a `bool` indicating whether or not the values are normalized. ### v0.25.0 - 2023-06-01 diff --git a/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h index a071ee15e..4c1c42cae 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h @@ -148,6 +148,10 @@ int64_t getOffsetTypeSize(PropertyComponentType offsetType) noexcept { } } // namespace +/** + * @brief A view on the data of the {@link PropertyTableProperty} that is created + * by a {@link PropertyTableView}. + */ template class PropertyTablePropertyView; @@ -166,14 +170,14 @@ class PropertyTablePropertyView; * one of the aforementioned types. */ template -class PropertyTablePropertyView - : public PropertyView { +class PropertyTablePropertyView + : public PropertyView { public: /** * @brief Constructs an invalid instance for a non-existent property. */ PropertyTablePropertyView() - : PropertyView(), + : PropertyView(), _values{}, _size{0}, _arrayOffsets{}, @@ -189,7 +193,7 @@ class PropertyTablePropertyView * @param status The value of {@link PropertyTablePropertyViewStatus} indicating the error with the property. */ PropertyTablePropertyView(PropertyViewStatusType status) - : PropertyView(status), + : PropertyView(status), _values{}, _size{0}, _arrayOffsets{}, @@ -204,7 +208,8 @@ class PropertyTablePropertyView } /** - * @brief Construct an instance pointing to non-array data specified by a {@link PropertyTableProperty}. + * @brief Construct an instance pointing to data specified by a {@link PropertyTableProperty}. + * Used for non-array or fixed-length array data. * * @param property The {@link PropertyTableProperty} * @param classProperty The {@link ClassProperty} this property conforms to. @@ -230,7 +235,6 @@ class PropertyTablePropertyView /** * @brief Construct an instance pointing to the data specified by a {@link PropertyTableProperty}. * - * * @param property The {@link PropertyTableProperty} * @param classProperty The {@link ClassProperty} this property conforms to. * @param size The number of elements in the property table specified by {@link PropertyTable::count} @@ -516,7 +520,8 @@ class PropertyTablePropertyView } /** - * @brief Construct an instance pointing to non-array data specified by a {@link PropertyTableProperty}. + * @brief Construct an instance pointing to data specified by a {@link PropertyTableProperty}. + * Used for non-array or fixed-length array data. * * @param property The {@link PropertyTableProperty} * @param classProperty The {@link ClassProperty} this property conforms to. @@ -562,6 +567,7 @@ class PropertyTablePropertyView _arrayOffsetType{arrayOffsetType}, _arrayOffsetTypeSize{getOffsetTypeSize(arrayOffsetType)} {} + /** * @brief Get the value of an element of the {@link PropertyTable}, * with normalization and other value transforms applied. In other words, the diff --git a/CesiumGltf/include/CesiumGltf/PropertyTablePropertyViewType.h b/CesiumGltf/include/CesiumGltf/PropertyTablePropertyViewType.h new file mode 100644 index 000000000..e879c1973 --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/PropertyTablePropertyViewType.h @@ -0,0 +1,285 @@ +#pragma once + +#include "CesiumGltf/PropertyTablePropertyView.h" + +#include + +#include +#include +#include +#include +#include + +namespace CesiumGltf { + +#define View PropertyTablePropertyView + +/** + * @brief An alias for all of the potential types of + * {@link PropertyTablePropertyView} possible, with and without normalization. + * Can be useful for applications that want to implement abstract + * representations of PropertyTablePropertyView, without the mess of templated + * types. + */ +typedef std::variant< + View, + View, + View, + View, + View, + View, + View, + View, + View, + View, + View, + View, + View, + View, + View, + View, + View, + View, + View, + View, + View, false>, + View, false>, + View, false>, + View, false>, + View, false>, + View, false>, + View, false>, + View, false>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View>, + View>, + View, false>, + View, false>, + View, false>, + View, false>, + View, false>, + View, false>, + View, false>, + View, false>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View>, + View>, + View, false>, + View, false>, + View, false>, + View, false>, + View, false>, + View, false>, + View, false>, + View, false>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View>, + View>, + View, false>, + View, false>, + View, false>, + View, false>, + View, false>, + View, false>, + View, false>, + View, false>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View>, + View>, + View, false>, + View, false>, + View, false>, + View, false>, + View, false>, + View, false>, + View, false>, + View, false>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View>, + View>, + View, false>, + View, false>, + View, false>, + View, false>, + View, false>, + View, false>, + View, false>, + View, false>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View>, + View>, + View, false>, + View, false>, + View, false>, + View, false>, + View, false>, + View, false>, + View, false>, + View, false>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View>, + View>, + View>, + View>, + View>, false>, + View>, false>, + View>, false>, + View>, false>, + View>, false>, + View>, false>, + View>, false>, + View>, false>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>>, + View>>, + View>, false>, + View>, false>, + View>, false>, + View>, false>, + View>, false>, + View>, false>, + View>, false>, + View>, false>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>>, + View>>, + View>, false>, + View>, false>, + View>, false>, + View>, false>, + View>, false>, + View>, false>, + View>, false>, + View>, false>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>>, + View>>, + View>, false>, + View>, false>, + View>, false>, + View>, false>, + View>, false>, + View>, false>, + View>, false>, + View>, false>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>>, + View>>, + View>, false>, + View>, false>, + View>, false>, + View>, false>, + View>, false>, + View>, false>, + View>, false>, + View>, false>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>>, + View>>, + View>, false>, + View>, false>, + View>, false>, + View>, false>, + View>, false>, + View>, false>, + View>, false>, + View>, false>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>>, + View>>> + PropertyTablePropertyViewType; + +#undef View + +} // namespace CesiumGltf diff --git a/CesiumGltf/include/CesiumGltf/PropertyTableView.h b/CesiumGltf/include/CesiumGltf/PropertyTableView.h index 1f5fb9a31..4688b9e4d 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTableView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTableView.h @@ -10,6 +10,7 @@ #include namespace CesiumGltf { + /** * @brief Indicates the status of a property table view. * @@ -107,21 +108,21 @@ class PropertyTableView { * @return A {@link PropertyTablePropertyView} of the property. If no valid property is * found, the property view will be invalid. */ - template - PropertyTablePropertyView + template + PropertyTablePropertyView getPropertyView(const std::string& propertyName) const { if (this->size() <= 0) { - return PropertyTablePropertyView( + return PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorInvalidPropertyTable); } const ClassProperty* pClassProperty = getClassProperty(propertyName); if (!pClassProperty) { - return PropertyTablePropertyView( + return PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorNonexistentProperty); } - return getPropertyViewImpl(propertyName, *pClassProperty); + return getPropertyViewImpl(propertyName, *pClassProperty); } /** @@ -170,47 +171,121 @@ class PropertyTableView { convertStringToPropertyComponentType(*pClassProperty->componentType); } + bool normalized = pClassProperty->normalized; + if (normalized) { + switch (componentType) { + case PropertyComponentType::Int8: + case PropertyComponentType::Uint8: + case PropertyComponentType::Int16: + case PropertyComponentType::Uint16: + case PropertyComponentType::Int32: + case PropertyComponentType::Uint32: + case PropertyComponentType::Int64: + case PropertyComponentType::Uint64: + break; + default: + callback( + propertyName, + PropertyTablePropertyView( + PropertyTablePropertyViewStatus::ErrorInvalidNormalization)); + return; + } + } + if (pClassProperty->array) { - getArrayPropertyViewImpl( - propertyName, - *pClassProperty, - type, - componentType, - std::forward(callback)); - } else if (type == PropertyType::Scalar) { - getScalarPropertyViewImpl( - propertyName, - *pClassProperty, - componentType, - std::forward(callback)); - } else if (isPropertyTypeVecN(type)) { - getVecNPropertyViewImpl( - propertyName, - *pClassProperty, - type, - componentType, - std::forward(callback)); - } else if (isPropertyTypeMatN(type)) { - getMatNPropertyViewImpl( - propertyName, - *pClassProperty, - type, - componentType, - std::forward(callback)); - } else if (type == PropertyType::String) { - callback( - propertyName, - getPropertyViewImpl(propertyName, *pClassProperty)); - } else if (type == PropertyType::Boolean) { + if (normalized) { + getArrayPropertyViewImpl( + propertyName, + *pClassProperty, + type, + componentType, + std::forward(callback)); + } else { + getArrayPropertyViewImpl( + propertyName, + *pClassProperty, + type, + componentType, + std::forward(callback)); + } + return; + } + + if (type == PropertyType::Scalar) { + if (normalized) { + getScalarPropertyViewImpl( + propertyName, + *pClassProperty, + componentType, + std::forward(callback)); + } else { + getScalarPropertyViewImpl( + propertyName, + *pClassProperty, + componentType, + std::forward(callback)); + } + return; + } + + if (isPropertyTypeVecN(type)) { + if (normalized) { + getVecNPropertyViewImpl( + propertyName, + *pClassProperty, + type, + componentType, + std::forward(callback)); + } else { + getVecNPropertyViewImpl( + propertyName, + *pClassProperty, + type, + componentType, + std::forward(callback)); + } + return; + } + + if (isPropertyTypeMatN(type)) { + if (normalized) { + getMatNPropertyViewImpl( + propertyName, + *pClassProperty, + type, + componentType, + std::forward(callback)); + } else { + getMatNPropertyViewImpl( + propertyName, + *pClassProperty, + type, + componentType, + std::forward(callback)); + } + return; + } + + if (type == PropertyType::String) { callback( propertyName, - getPropertyViewImpl(propertyName, *pClassProperty)); - } else { + getPropertyViewImpl( + propertyName, + *pClassProperty)); + return; + } + + if (type == PropertyType::Boolean) { callback( propertyName, - PropertyTablePropertyView( - PropertyTablePropertyViewStatus::ErrorTypeMismatch)); + getPropertyViewImpl(propertyName, *pClassProperty)); + return; } + + callback( + propertyName, + PropertyTablePropertyView( + PropertyTablePropertyViewStatus::ErrorTypeMismatch)); } /** @@ -220,9 +295,9 @@ class PropertyTableView { * * This method will validate the EXT_structural_metadata format to ensure * {@link PropertyTablePropertyView} retrieves the correct data. T must be one of the - * following: a scalar (uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t, - * uint64_t, int64_t, float, double), a glm vecN composed of one of the scalar - * types, a glm matN composed of one of the scalar types, bool, + * following: a scalar (uint8_t, int8_t, uint16_t, int16_t, uint32_t, + * int32_t, uint64_t, int64_t, float, double), a glm vecN composed of one of + * the scalar types, a glm matN composed of one of the scalar types, bool, * std::string_view, or {@link PropertyArrayView} with T as one of the * aforementioned types. If the property is invalid, an empty * {@link PropertyTablePropertyView} with an error status code will be passed to the @@ -240,7 +315,7 @@ class PropertyTableView { } private: - template + template void getScalarArrayPropertyViewImpl( const std::string& propertyName, const ClassProperty& classProperty, @@ -250,70 +325,70 @@ class PropertyTableView { case PropertyComponentType::Int8: callback( propertyName, - getPropertyViewImpl>( + getPropertyViewImpl, Normalized>( propertyName, classProperty)); break; case PropertyComponentType::Uint8: callback( propertyName, - getPropertyViewImpl>( + getPropertyViewImpl, Normalized>( propertyName, classProperty)); break; case PropertyComponentType::Int16: callback( propertyName, - getPropertyViewImpl>( + getPropertyViewImpl, Normalized>( propertyName, classProperty)); break; case PropertyComponentType::Uint16: callback( propertyName, - getPropertyViewImpl>( + getPropertyViewImpl, Normalized>( propertyName, classProperty)); break; case PropertyComponentType::Int32: callback( propertyName, - getPropertyViewImpl>( + getPropertyViewImpl, Normalized>( propertyName, classProperty)); break; case PropertyComponentType::Uint32: callback( propertyName, - getPropertyViewImpl>( + getPropertyViewImpl, Normalized>( propertyName, classProperty)); break; case PropertyComponentType::Int64: callback( propertyName, - getPropertyViewImpl>( + getPropertyViewImpl, Normalized>( propertyName, classProperty)); break; case PropertyComponentType::Uint64: callback( propertyName, - getPropertyViewImpl>( + getPropertyViewImpl, Normalized>( propertyName, classProperty)); break; case PropertyComponentType::Float32: callback( propertyName, - getPropertyViewImpl>( + getPropertyViewImpl, false>( propertyName, classProperty)); break; case PropertyComponentType::Float64: callback( propertyName, - getPropertyViewImpl>( + getPropertyViewImpl, false>( propertyName, classProperty)); break; @@ -326,7 +401,7 @@ class PropertyTableView { } } - template + template void getVecNArrayPropertyViewImpl( const std::string& propertyName, const ClassProperty& classProperty, @@ -336,70 +411,70 @@ class PropertyTableView { case PropertyComponentType::Int8: callback( propertyName, - getPropertyViewImpl>>( - propertyName, - classProperty)); + getPropertyViewImpl< + PropertyArrayView>, + Normalized>(propertyName, classProperty)); break; case PropertyComponentType::Uint8: callback( propertyName, - getPropertyViewImpl>>( - propertyName, - classProperty)); + getPropertyViewImpl< + PropertyArrayView>, + Normalized>(propertyName, classProperty)); break; case PropertyComponentType::Int16: callback( propertyName, - getPropertyViewImpl>>( - propertyName, - classProperty)); + getPropertyViewImpl< + PropertyArrayView>, + Normalized>(propertyName, classProperty)); break; case PropertyComponentType::Uint16: callback( propertyName, - getPropertyViewImpl>>( - propertyName, - classProperty)); + getPropertyViewImpl< + PropertyArrayView>, + Normalized>(propertyName, classProperty)); break; case PropertyComponentType::Int32: callback( propertyName, - getPropertyViewImpl>>( - propertyName, - classProperty)); + getPropertyViewImpl< + PropertyArrayView>, + Normalized>(propertyName, classProperty)); break; case PropertyComponentType::Uint32: callback( propertyName, - getPropertyViewImpl>>( - propertyName, - classProperty)); + getPropertyViewImpl< + PropertyArrayView>, + Normalized>(propertyName, classProperty)); break; case PropertyComponentType::Int64: callback( propertyName, - getPropertyViewImpl>>( - propertyName, - classProperty)); + getPropertyViewImpl< + PropertyArrayView>, + Normalized>(propertyName, classProperty)); break; case PropertyComponentType::Uint64: callback( propertyName, - getPropertyViewImpl>>( - propertyName, - classProperty)); + getPropertyViewImpl< + PropertyArrayView>, + Normalized>(propertyName, classProperty)); break; case PropertyComponentType::Float32: callback( propertyName, - getPropertyViewImpl>>( + getPropertyViewImpl>, false>( propertyName, classProperty)); break; case PropertyComponentType::Float64: callback( propertyName, - getPropertyViewImpl>>( + getPropertyViewImpl>, false>( propertyName, classProperty)); break; @@ -412,7 +487,7 @@ class PropertyTableView { } } - template + template void getVecNArrayPropertyViewImpl( const std::string& propertyName, const ClassProperty& classProperty, @@ -422,21 +497,21 @@ class PropertyTableView { glm::length_t N = getDimensionsFromPropertyType(type); switch (N) { case 2: - getVecNArrayPropertyViewImpl( + getVecNArrayPropertyViewImpl( propertyName, classProperty, componentType, std::forward(callback)); break; case 3: - getVecNArrayPropertyViewImpl( + getVecNArrayPropertyViewImpl( propertyName, classProperty, componentType, std::forward(callback)); break; case 4: - getVecNArrayPropertyViewImpl( + getVecNArrayPropertyViewImpl( propertyName, classProperty, componentType, @@ -451,7 +526,7 @@ class PropertyTableView { } } - template + template void getMatNArrayPropertyViewImpl( const std::string& propertyName, const ClassProperty& classProperty, @@ -461,70 +536,70 @@ class PropertyTableView { case PropertyComponentType::Int8: callback( propertyName, - getPropertyViewImpl>>( - propertyName, - classProperty)); + getPropertyViewImpl< + PropertyArrayView>, + Normalized>(propertyName, classProperty)); break; case PropertyComponentType::Uint8: callback( propertyName, - getPropertyViewImpl>>( - propertyName, - classProperty)); + getPropertyViewImpl< + PropertyArrayView>, + Normalized>(propertyName, classProperty)); break; case PropertyComponentType::Int16: callback( propertyName, - getPropertyViewImpl>>( - propertyName, - classProperty)); + getPropertyViewImpl< + PropertyArrayView>, + Normalized>(propertyName, classProperty)); break; case PropertyComponentType::Uint16: callback( propertyName, - getPropertyViewImpl>>( - propertyName, - classProperty)); + getPropertyViewImpl< + PropertyArrayView>, + Normalized>(propertyName, classProperty)); break; case PropertyComponentType::Int32: callback( propertyName, - getPropertyViewImpl>>( - propertyName, - classProperty)); + getPropertyViewImpl< + PropertyArrayView>, + Normalized>(propertyName, classProperty)); break; case PropertyComponentType::Uint32: callback( propertyName, - getPropertyViewImpl>>( - propertyName, - classProperty)); + getPropertyViewImpl< + PropertyArrayView>, + Normalized>(propertyName, classProperty)); break; case PropertyComponentType::Int64: callback( propertyName, - getPropertyViewImpl>>( - propertyName, - classProperty)); + getPropertyViewImpl< + PropertyArrayView>, + Normalized>(propertyName, classProperty)); break; case PropertyComponentType::Uint64: callback( propertyName, - getPropertyViewImpl>>( - propertyName, - classProperty)); + getPropertyViewImpl< + PropertyArrayView>, + Normalized>(propertyName, classProperty)); break; case PropertyComponentType::Float32: callback( propertyName, - getPropertyViewImpl>>( + getPropertyViewImpl>, false>( propertyName, classProperty)); break; case PropertyComponentType::Float64: callback( propertyName, - getPropertyViewImpl>>( + getPropertyViewImpl>, false>( propertyName, classProperty)); break; @@ -537,7 +612,7 @@ class PropertyTableView { } } - template + template void getMatNArrayPropertyViewImpl( const std::string& propertyName, const ClassProperty& classProperty, @@ -547,21 +622,21 @@ class PropertyTableView { const glm::length_t N = getDimensionsFromPropertyType(type); switch (N) { case 2: - getMatNArrayPropertyViewImpl( + getMatNArrayPropertyViewImpl( propertyName, classProperty, componentType, std::forward(callback)); break; case 3: - getMatNArrayPropertyViewImpl( + getMatNArrayPropertyViewImpl( propertyName, classProperty, componentType, std::forward(callback)); break; case 4: - getMatNArrayPropertyViewImpl( + getMatNArrayPropertyViewImpl( propertyName, classProperty, componentType, @@ -576,7 +651,7 @@ class PropertyTableView { } } - template + template void getArrayPropertyViewImpl( const std::string& propertyName, const ClassProperty& classProperty, @@ -584,20 +659,20 @@ class PropertyTableView { PropertyComponentType componentType, Callback&& callback) const { if (type == PropertyType::Scalar) { - getScalarArrayPropertyViewImpl( + getScalarArrayPropertyViewImpl( propertyName, classProperty, componentType, std::forward(callback)); } else if (isPropertyTypeVecN(type)) { - getVecNArrayPropertyViewImpl( + getVecNArrayPropertyViewImpl( propertyName, classProperty, type, componentType, std::forward(callback)); } else if (isPropertyTypeMatN(type)) { - getMatNArrayPropertyViewImpl( + getMatNArrayPropertyViewImpl( propertyName, classProperty, type, @@ -606,14 +681,14 @@ class PropertyTableView { } else if (type == PropertyType::Boolean) { callback( propertyName, - getPropertyViewImpl>( + getPropertyViewImpl, false>( propertyName, classProperty)); } else if (type == PropertyType::String) { callback( propertyName, - getPropertyViewImpl>( + getPropertyViewImpl, false>( propertyName, classProperty)); } else { @@ -624,7 +699,7 @@ class PropertyTableView { } } - template + template void getVecNPropertyViewImpl( const std::string& propertyName, const ClassProperty& classProperty, @@ -635,68 +710,70 @@ class PropertyTableView { case PropertyComponentType::Int8: callback( propertyName, - getPropertyViewImpl>( + getPropertyViewImpl, Normalized>( propertyName, classProperty)); break; case PropertyComponentType::Uint8: callback( propertyName, - getPropertyViewImpl>( + getPropertyViewImpl, Normalized>( propertyName, classProperty)); break; case PropertyComponentType::Int16: callback( propertyName, - getPropertyViewImpl>( + getPropertyViewImpl, Normalized>( propertyName, classProperty)); break; case PropertyComponentType::Uint16: callback( propertyName, - getPropertyViewImpl>( + getPropertyViewImpl, Normalized>( propertyName, classProperty)); break; case PropertyComponentType::Int32: callback( propertyName, - getPropertyViewImpl>( + getPropertyViewImpl, Normalized>( propertyName, classProperty)); break; case PropertyComponentType::Uint32: callback( propertyName, - getPropertyViewImpl>( + getPropertyViewImpl, Normalized>( propertyName, classProperty)); break; case PropertyComponentType::Int64: callback( propertyName, - getPropertyViewImpl>( + getPropertyViewImpl, Normalized>( propertyName, classProperty)); break; case PropertyComponentType::Uint64: callback( propertyName, - getPropertyViewImpl>( + getPropertyViewImpl, Normalized>( propertyName, classProperty)); break; case PropertyComponentType::Float32: callback( propertyName, - getPropertyViewImpl>(propertyName, classProperty)); + getPropertyViewImpl, false>( + propertyName, + classProperty)); break; case PropertyComponentType::Float64: callback( propertyName, - getPropertyViewImpl>( + getPropertyViewImpl, false>( propertyName, classProperty)); break; @@ -709,7 +786,7 @@ class PropertyTableView { } } - template + template void getVecNPropertyViewImpl( const std::string& propertyName, const ClassProperty& classProperty, @@ -719,21 +796,21 @@ class PropertyTableView { const glm::length_t N = getDimensionsFromPropertyType(type); switch (N) { case 2: - getVecNPropertyViewImpl( + getVecNPropertyViewImpl( propertyName, classProperty, componentType, std::forward(callback)); break; case 3: - getVecNPropertyViewImpl( + getVecNPropertyViewImpl( propertyName, classProperty, componentType, std::forward(callback)); break; case 4: - getVecNPropertyViewImpl( + getVecNPropertyViewImpl( propertyName, classProperty, componentType, @@ -748,7 +825,7 @@ class PropertyTableView { } } - template + template void getMatNPropertyViewImpl( const std::string& propertyName, const ClassProperty& classProperty, @@ -758,70 +835,70 @@ class PropertyTableView { case PropertyComponentType::Int8: callback( propertyName, - getPropertyViewImpl>( + getPropertyViewImpl, Normalized>( propertyName, classProperty)); break; case PropertyComponentType::Uint8: callback( propertyName, - getPropertyViewImpl>( + getPropertyViewImpl, Normalized>( propertyName, classProperty)); break; case PropertyComponentType::Int16: callback( propertyName, - getPropertyViewImpl>( + getPropertyViewImpl, Normalized>( propertyName, classProperty)); break; case PropertyComponentType::Uint16: callback( propertyName, - getPropertyViewImpl>( + getPropertyViewImpl, Normalized>( propertyName, classProperty)); break; case PropertyComponentType::Int32: callback( propertyName, - getPropertyViewImpl>( + getPropertyViewImpl, Normalized>( propertyName, classProperty)); break; case PropertyComponentType::Uint32: callback( propertyName, - getPropertyViewImpl>( + getPropertyViewImpl, Normalized>( propertyName, classProperty)); break; case PropertyComponentType::Int64: callback( propertyName, - getPropertyViewImpl>( + getPropertyViewImpl, Normalized>( propertyName, classProperty)); break; case PropertyComponentType::Uint64: callback( propertyName, - getPropertyViewImpl>( + getPropertyViewImpl, Normalized>( propertyName, classProperty)); break; case PropertyComponentType::Float32: callback( propertyName, - getPropertyViewImpl>( + getPropertyViewImpl, false>( propertyName, classProperty)); break; case PropertyComponentType::Float64: callback( propertyName, - getPropertyViewImpl>( + getPropertyViewImpl, false>( propertyName, classProperty)); break; @@ -834,7 +911,7 @@ class PropertyTableView { } } - template + template void getMatNPropertyViewImpl( const std::string& propertyName, const ClassProperty& classProperty, @@ -844,21 +921,21 @@ class PropertyTableView { glm::length_t N = getDimensionsFromPropertyType(type); switch (N) { case 2: - getMatNPropertyViewImpl( + getMatNPropertyViewImpl( propertyName, classProperty, componentType, std::forward(callback)); break; case 3: - getMatNPropertyViewImpl( + getMatNPropertyViewImpl( propertyName, classProperty, componentType, std::forward(callback)); break; case 4: - getMatNPropertyViewImpl( + getMatNPropertyViewImpl( propertyName, classProperty, componentType, @@ -873,7 +950,7 @@ class PropertyTableView { } } - template + template void getScalarPropertyViewImpl( const std::string& propertyName, const ClassProperty& classProperty, @@ -883,52 +960,66 @@ class PropertyTableView { case PropertyComponentType::Int8: callback( propertyName, - getPropertyViewImpl(propertyName, classProperty)); + getPropertyViewImpl(propertyName, classProperty)); return; case PropertyComponentType::Uint8: callback( propertyName, - getPropertyViewImpl(propertyName, classProperty)); + getPropertyViewImpl( + propertyName, + classProperty)); return; case PropertyComponentType::Int16: callback( propertyName, - getPropertyViewImpl(propertyName, classProperty)); + getPropertyViewImpl( + propertyName, + classProperty)); return; case PropertyComponentType::Uint16: callback( propertyName, - getPropertyViewImpl(propertyName, classProperty)); + getPropertyViewImpl( + propertyName, + classProperty)); break; case PropertyComponentType::Int32: callback( propertyName, - getPropertyViewImpl(propertyName, classProperty)); + getPropertyViewImpl( + propertyName, + classProperty)); break; case PropertyComponentType::Uint32: callback( propertyName, - getPropertyViewImpl(propertyName, classProperty)); + getPropertyViewImpl( + propertyName, + classProperty)); break; case PropertyComponentType::Int64: callback( propertyName, - getPropertyViewImpl(propertyName, classProperty)); + getPropertyViewImpl( + propertyName, + classProperty)); break; case PropertyComponentType::Uint64: callback( propertyName, - getPropertyViewImpl(propertyName, classProperty)); + getPropertyViewImpl( + propertyName, + classProperty)); break; case PropertyComponentType::Float32: callback( propertyName, - getPropertyViewImpl(propertyName, classProperty)); + getPropertyViewImpl(propertyName, classProperty)); break; case PropertyComponentType::Float64: callback( propertyName, - getPropertyViewImpl(propertyName, classProperty)); + getPropertyViewImpl(propertyName, classProperty)); break; default: callback( @@ -939,14 +1030,14 @@ class PropertyTableView { } } - template - PropertyTablePropertyView getPropertyViewImpl( + template + PropertyTablePropertyView getPropertyViewImpl( const std::string& propertyName, const ClassProperty& classProperty) const { auto propertyTablePropertyIter = _pPropertyTable->properties.find(propertyName); if (propertyTablePropertyIter == _pPropertyTable->properties.end()) { - return PropertyTablePropertyView( + return PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorNonexistentProperty); } @@ -954,7 +1045,7 @@ class PropertyTableView { propertyTablePropertyIter->second; if constexpr (IsMetadataNumeric::value || IsMetadataBoolean::value) { - return getNumericOrBooleanPropertyValues( + return getNumericOrBooleanPropertyValues( classProperty, propertyTableProperty); } @@ -963,49 +1054,58 @@ class PropertyTableView { return getStringPropertyValues(classProperty, propertyTableProperty); } - if constexpr ( - IsMetadataNumericArray::value || IsMetadataBooleanArray::value) { - return getPrimitiveArrayPropertyValues< - typename MetadataArrayType::type>( + if constexpr (IsMetadataBooleanArray::value) { + return getBooleanArrayPropertyValues( classProperty, propertyTableProperty); } + if constexpr (IsMetadataNumericArray::value) { + return getNumericArrayPropertyValues< + typename MetadataArrayType::type, + Normalized>(classProperty, propertyTableProperty); + } + if constexpr (IsMetadataStringArray::value) { return getStringArrayPropertyValues(classProperty, propertyTableProperty); } } - template - PropertyTablePropertyView getNumericOrBooleanPropertyValues( + template + PropertyTablePropertyView getNumericOrBooleanPropertyValues( const ClassProperty& classProperty, const PropertyTableProperty& propertyTableProperty) const { if (classProperty.array) { - return PropertyTablePropertyView( + return PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); } const PropertyType type = convertStringToPropertyType(classProperty.type); if (TypeToPropertyType::value != type) { - return PropertyTablePropertyView( + return PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorTypeMismatch); } const PropertyComponentType componentType = convertStringToPropertyComponentType( classProperty.componentType.value_or("")); if (TypeToPropertyType::component != componentType) { - return PropertyTablePropertyView( + return PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); } + if (classProperty.normalized != Normalized) { + return PropertyTablePropertyView( + PropertyTablePropertyViewStatus::ErrorInvalidNormalization); + } + gsl::span values; const auto status = getBufferSafe(propertyTableProperty.values, values); if (status != PropertyTablePropertyViewStatus::Valid) { - return PropertyTablePropertyView(status); + return PropertyTablePropertyView(status); } if (values.size() % sizeof(T) != 0) { - return PropertyTablePropertyView( + return PropertyTablePropertyView( PropertyTablePropertyViewStatus:: ErrorBufferViewSizeNotDivisibleByTypeSize); } @@ -1019,12 +1119,12 @@ class PropertyTableView { } if (values.size() < maxRequiredBytes) { - return PropertyTablePropertyView( + return PropertyTablePropertyView( PropertyTablePropertyViewStatus:: ErrorBufferViewSizeDoesNotMatchPropertyTableCount); } - return PropertyTablePropertyView( + return PropertyTablePropertyView( propertyTableProperty, classProperty, _pPropertyTable->count, @@ -1035,19 +1135,24 @@ class PropertyTableView { const ClassProperty& classProperty, const PropertyTableProperty& propertyTableProperty) const; - template - PropertyTablePropertyView> - getPrimitiveArrayPropertyValues( + PropertyTablePropertyView> + getBooleanArrayPropertyValues( + const ClassProperty& classProperty, + const PropertyTableProperty& propertyTableProperty) const; + + template + PropertyTablePropertyView, Normalized> + getNumericArrayPropertyValues( const ClassProperty& classProperty, const PropertyTableProperty& propertyTableProperty) const { if (!classProperty.array) { - return PropertyTablePropertyView>( + return PropertyTablePropertyView, Normalized>( PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); } const PropertyType type = convertStringToPropertyType(classProperty.type); if (TypeToPropertyType::value != type) { - return PropertyTablePropertyView>( + return PropertyTablePropertyView, Normalized>( PropertyTablePropertyViewStatus::ErrorTypeMismatch); } @@ -1055,63 +1160,57 @@ class PropertyTableView { convertStringToPropertyComponentType( classProperty.componentType.value_or("")); if (TypeToPropertyType::component != componentType) { - return PropertyTablePropertyView>( + return PropertyTablePropertyView, Normalized>( PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); } + if (classProperty.normalized != Normalized) { + return PropertyTablePropertyView, Normalized>( + PropertyTablePropertyViewStatus::ErrorInvalidNormalization); + } + gsl::span values; auto status = getBufferSafe(propertyTableProperty.values, values); if (status != PropertyTablePropertyViewStatus::Valid) { - return PropertyTablePropertyView>(status); + return PropertyTablePropertyView, Normalized>( + status); } if (values.size() % sizeof(T) != 0) { - return PropertyTablePropertyView>( + return PropertyTablePropertyView, Normalized>( PropertyTablePropertyViewStatus:: ErrorBufferViewSizeNotDivisibleByTypeSize); } const int64_t fixedLengthArrayCount = classProperty.count.value_or(0); if (fixedLengthArrayCount > 0 && propertyTableProperty.arrayOffsets >= 0) { - return PropertyTablePropertyView>( + return PropertyTablePropertyView, Normalized>( PropertyTablePropertyViewStatus:: ErrorArrayCountAndOffsetBufferCoexist); } if (fixedLengthArrayCount <= 0 && propertyTableProperty.arrayOffsets < 0) { - return PropertyTablePropertyView>( + return PropertyTablePropertyView, Normalized>( PropertyTablePropertyViewStatus:: ErrorArrayCountAndOffsetBufferDontExist); } // Handle fixed-length arrays if (fixedLengthArrayCount > 0) { - size_t maxRequiredBytes = 0; - if constexpr (IsMetadataBoolean::value) { - maxRequiredBytes = static_cast(glm::ceil( - static_cast( - _pPropertyTable->count * fixedLengthArrayCount) / - 8.0)); - } else { - maxRequiredBytes = static_cast( - _pPropertyTable->count * fixedLengthArrayCount * sizeof(T)); - } + size_t maxRequiredBytes = maxRequiredBytes = static_cast( + _pPropertyTable->count * fixedLengthArrayCount * sizeof(T)); if (values.size() < maxRequiredBytes) { - return PropertyTablePropertyView>( + return PropertyTablePropertyView, Normalized>( PropertyTablePropertyViewStatus:: ErrorBufferViewSizeDoesNotMatchPropertyTableCount); } - return PropertyTablePropertyView>( + return PropertyTablePropertyView, Normalized>( propertyTableProperty, classProperty, static_cast(_pPropertyTable->count), - values, - gsl::span(), - gsl::span(), - PropertyComponentType::None, - PropertyComponentType::None); + values); } // Handle variable-length arrays @@ -1119,11 +1218,11 @@ class PropertyTableView { convertArrayOffsetTypeStringToPropertyComponentType( propertyTableProperty.arrayOffsetType); if (arrayOffsetType == PropertyComponentType::None) { - return PropertyTablePropertyView>( + return PropertyTablePropertyView, Normalized>( PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); } - constexpr bool checkBitsSize = IsMetadataBoolean::value; + constexpr bool checkBitsSize = false; gsl::span arrayOffsets; status = getArrayOffsetsBufferSafe( propertyTableProperty.arrayOffsets, @@ -1133,18 +1232,29 @@ class PropertyTableView { checkBitsSize, arrayOffsets); if (status != PropertyTablePropertyViewStatus::Valid) { - return PropertyTablePropertyView>(status); + return PropertyTablePropertyView, Normalized>( + status); } - return PropertyTablePropertyView>( - propertyTableProperty, - classProperty, - static_cast(_pPropertyTable->count), - values, - arrayOffsets, - gsl::span(), - arrayOffsetType, - PropertyComponentType::None); + if constexpr (Normalized) { + return PropertyTablePropertyView, true>( + propertyTableProperty, + classProperty, + static_cast(_pPropertyTable->count), + values, + arrayOffsets, + arrayOffsetType); + } else { + return PropertyTablePropertyView, false>( + propertyTableProperty, + classProperty, + static_cast(_pPropertyTable->count), + values, + arrayOffsets, + gsl::span(), + arrayOffsetType, + PropertyComponentType::None); + } } PropertyTablePropertyView> diff --git a/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h index cf5c16aaa..731627a2d 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h @@ -20,73 +20,45 @@ namespace CesiumGltf { * corresponding property texture property. This enumeration provides the * reason. */ -enum class PropertyTexturePropertyViewStatus { - /** - * @brief This property view is valid and ready to use. - */ - Valid, - +class PropertyTexturePropertyViewStatus : public PropertyViewStatus { +public: /** * @brief This property view was initialized from an invalid * {@link PropertyTexture}. */ - ErrorInvalidPropertyTexture, - - /** - * @brief This property view is trying to view a property that does not exist - * in the {@link PropertyTexture}. - */ - ErrorNonexistentProperty, + static const int ErrorInvalidPropertyTexture = 12; /** * @brief This property view is associated with a {@link ClassProperty} of an * unsupported type. */ - ErrorUnsupportedProperty, - - /** - * @brief This property view's type does not match what is - * specified in {@link ClassProperty::type}. - */ - ErrorTypeMismatch, - - /** - * @brief This property view's component type does not match what - * is specified in {@link ClassProperty::componentType}. - */ - ErrorComponentTypeMismatch, - - /** - * @brief This property view differs from what is specified in - * {@link ClassProperty::array}. - */ - ErrorArrayTypeMismatch, + static const int ErrorUnsupportedProperty = 13; /** * @brief This property view does not have a valid texture index. */ - ErrorInvalidTexture, + static const int ErrorInvalidTexture = 14; /** * @brief This property view does not have a valid sampler index. */ - ErrorInvalidSampler, + static const int ErrorInvalidSampler = 15; /** * @brief This property view does not have a valid image index. */ - ErrorInvalidImage, + static const int ErrorInvalidImage = 16; /** * @brief This property is viewing an empty image. */ - ErrorEmptyImage, + static const int ErrorEmptyImage = 17; /** * @brief This property uses an image with multi-byte channels. Only * single-byte channels are supported. */ - ErrorInvalidBytesPerChannel, + static const int ErrorInvalidBytesPerChannel = 18; /** * @brief The channels of this property texture property are invalid. @@ -95,15 +67,15 @@ enum class PropertyTexturePropertyViewStatus { * more than four channels can be defined for specialized texture * formats, this implementation only supports four channels max. */ - ErrorInvalidChannels, + static const int ErrorInvalidChannels = 19; /** - * @brief The channels of this property texture property do not provide the - * exact number of bytes required by the property type. This may be because - * an incorrect number of channels was provided, or because the image itself - * has a different channel count / byte size than expected. + * @brief The channels of this property texture property do not provide + * the exact number of bytes required by the property type. This may be + * because an incorrect number of channels was provided, or because the + * image itself has a different channel count / byte size than expected. */ - ErrorChannelsAndTypeMismatch, + static const int ErrorChannelsAndTypeMismatch = 20; }; /** @@ -129,9 +101,9 @@ template class PropertyTexturePropertyView { /** * @brief Constructs an invalid instance for an erroneous property. * - * @param status The {@link PropertyTexturePropertyViewStatus} indicating the error with the property. + * @param status The code from {@link PropertyTexturePropertyViewStatus} indicating the error with the property. */ - PropertyTexturePropertyView(PropertyTexturePropertyViewStatus status) noexcept + PropertyTexturePropertyView(PropertyViewStatusType status) noexcept : _status(status), _pSampler(nullptr), _pImage(nullptr), @@ -145,7 +117,7 @@ template class PropertyTexturePropertyView { } /** - * @brief Construct a valid view of the data specified by a {@link PropertyTextureProperty}. + * @brief Construct a view of the data specified by a {@link PropertyTextureProperty}. * * @param pSampler A pointer to the sampler used by the property. * @param pImage A pointer to the image used by the property. @@ -248,9 +220,7 @@ template class PropertyTexturePropertyView { * * If invalid, this view cannot be sampled. */ - PropertyTexturePropertyViewStatus status() const noexcept { - return this->_status; - } + PropertyViewStatusType status() const noexcept { return this->_status; } /** * @brief Get the texture coordinate set index for this property. @@ -445,7 +415,7 @@ template class PropertyTexturePropertyView { return std::clamp(v, 0.0, 1.0); } - PropertyTexturePropertyViewStatus _status; + PropertyViewStatusType _status; const Sampler* _pSampler; const ImageCesium* _pImage; diff --git a/CesiumGltf/include/CesiumGltf/PropertyTextureView.h b/CesiumGltf/include/CesiumGltf/PropertyTextureView.h index eedde43e9..86f213af0 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTextureView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTextureView.h @@ -382,6 +382,7 @@ class PropertyTextureView { classProperty)); break; } + [[fallthrough]]; case PropertyComponentType::Uint16: if constexpr (N == 2) { callback( @@ -391,6 +392,7 @@ class PropertyTextureView { classProperty)); break; } + [[fallthrough]]; default: callback( propertyName, @@ -593,18 +595,17 @@ class PropertyTextureView { classProperty.normalized); } - PropertyTexturePropertyViewStatus getTextureSafe( + PropertyViewStatusType getTextureSafe( const int32_t textureIndex, int32_t& samplerIndex, int32_t& imageIndex) const noexcept; - PropertyTexturePropertyViewStatus + PropertyViewStatusType checkSampler(const int32_t samplerIndex) const noexcept; - PropertyTexturePropertyViewStatus - checkImage(const int32_t imageIndex) const noexcept; + PropertyViewStatusType checkImage(const int32_t imageIndex) const noexcept; - PropertyTexturePropertyViewStatus checkChannels( + PropertyViewStatusType checkChannels( const std::vector& channels, const ImageCesium& image) const noexcept; diff --git a/CesiumGltf/include/CesiumGltf/PropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyView.h index 4beb9aebf..804992f64 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyView.h @@ -1196,7 +1196,7 @@ template <> class PropertyView { * property. */ template -class PropertyView> { +class PropertyView, false> { public: /** * @brief Constructs an empty property instance. diff --git a/CesiumGltf/src/PropertyTableView.cpp b/CesiumGltf/src/PropertyTableView.cpp index 5ba0af589..e26b53bc4 100644 --- a/CesiumGltf/src/PropertyTableView.cpp +++ b/CesiumGltf/src/PropertyTableView.cpp @@ -326,6 +326,90 @@ PropertyTableView::getStringPropertyValues( offsetType); } +PropertyTablePropertyView> +PropertyTableView::getBooleanArrayPropertyValues( + const ClassProperty& classProperty, + const PropertyTableProperty& propertyTableProperty) const { + if (!classProperty.array) { + return PropertyTablePropertyView>( + PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); + } + + if (classProperty.type != ClassProperty::Type::BOOLEAN) { + return PropertyTablePropertyView>( + PropertyTablePropertyViewStatus::ErrorTypeMismatch); + } + + gsl::span values; + auto status = getBufferSafe(propertyTableProperty.values, values); + if (status != PropertyTablePropertyViewStatus::Valid) { + return PropertyTablePropertyView>(status); + } + + const int64_t fixedLengthArrayCount = classProperty.count.value_or(0); + if (fixedLengthArrayCount > 0 && propertyTableProperty.arrayOffsets >= 0) { + return PropertyTablePropertyView>( + PropertyTablePropertyViewStatus::ErrorArrayCountAndOffsetBufferCoexist); + } + + if (fixedLengthArrayCount <= 0 && propertyTableProperty.arrayOffsets < 0) { + return PropertyTablePropertyView>( + PropertyTablePropertyViewStatus:: + ErrorArrayCountAndOffsetBufferDontExist); + } + + // Handle fixed-length arrays + if (fixedLengthArrayCount > 0) { + size_t maxRequiredBytes = maxRequiredBytes = static_cast(glm::ceil( + static_cast(_pPropertyTable->count * fixedLengthArrayCount) / + 8.0)); + + if (values.size() < maxRequiredBytes) { + return PropertyTablePropertyView>( + PropertyTablePropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + } + + return PropertyTablePropertyView>( + propertyTableProperty, + classProperty, + static_cast(_pPropertyTable->count), + values); + } + + // Handle variable-length arrays + const PropertyComponentType arrayOffsetType = + convertArrayOffsetTypeStringToPropertyComponentType( + propertyTableProperty.arrayOffsetType); + if (arrayOffsetType == PropertyComponentType::None) { + return PropertyTablePropertyView>( + PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); + } + + constexpr bool checkBitsSize = true; + gsl::span arrayOffsets; + status = getArrayOffsetsBufferSafe( + propertyTableProperty.arrayOffsets, + arrayOffsetType, + values.size(), + static_cast(_pPropertyTable->count), + checkBitsSize, + arrayOffsets); + if (status != PropertyTablePropertyViewStatus::Valid) { + return PropertyTablePropertyView>(status); + } + + return PropertyTablePropertyView>( + propertyTableProperty, + classProperty, + static_cast(_pPropertyTable->count), + values, + arrayOffsets, + gsl::span(), + arrayOffsetType, + PropertyComponentType::None); +} + PropertyTablePropertyView> PropertyTableView::getStringArrayPropertyValues( const ClassProperty& classProperty, diff --git a/CesiumGltf/src/PropertyTextureView.cpp b/CesiumGltf/src/PropertyTextureView.cpp index d92c00bc2..c81247318 100644 --- a/CesiumGltf/src/PropertyTextureView.cpp +++ b/CesiumGltf/src/PropertyTextureView.cpp @@ -45,7 +45,7 @@ PropertyTextureView::getClassProperty(const std::string& propertyName) const { return &propertyIter->second; } -PropertyTexturePropertyViewStatus PropertyTextureView::getTextureSafe( +PropertyViewStatusType PropertyTextureView::getTextureSafe( const int32_t textureIndex, int32_t& samplerIndex, int32_t& imageIndex) const noexcept { @@ -61,7 +61,7 @@ PropertyTexturePropertyViewStatus PropertyTextureView::getTextureSafe( return PropertyTexturePropertyViewStatus::Valid; } -PropertyTexturePropertyViewStatus +PropertyViewStatusType PropertyTextureView::checkSampler(const int32_t samplerIndex) const noexcept { if (samplerIndex < 0 || static_cast(samplerIndex) >= _pModel->samplers.size()) { @@ -73,7 +73,7 @@ PropertyTextureView::checkSampler(const int32_t samplerIndex) const noexcept { return PropertyTexturePropertyViewStatus::Valid; } -PropertyTexturePropertyViewStatus +PropertyViewStatusType PropertyTextureView::checkImage(const int32_t imageIndex) const noexcept { if (imageIndex < 0 || static_cast(imageIndex) >= _pModel->images.size()) { @@ -94,7 +94,7 @@ PropertyTextureView::checkImage(const int32_t imageIndex) const noexcept { return PropertyTexturePropertyViewStatus::Valid; } -PropertyTexturePropertyViewStatus PropertyTextureView::checkChannels( +PropertyViewStatusType PropertyTextureView::checkChannels( const std::vector& channels, const ImageCesium& image) const noexcept { if (channels.size() <= 0 || channels.size() > 4) { diff --git a/CesiumGltf/test/TestPropertyTablePropertyView.cpp b/CesiumGltf/test/TestPropertyTablePropertyView.cpp index 016fd5bf5..065f0414a 100644 --- a/CesiumGltf/test/TestPropertyTablePropertyView.cpp +++ b/CesiumGltf/test/TestPropertyTablePropertyView.cpp @@ -3389,7 +3389,7 @@ TEST_CASE("Check fixed-length array of string") { auto values = *maybeValues; auto expectedValues = *expected[static_cast(i)]; for (int64_t j = 0; j < values.size(); ++j) { - REQUIRE(values[j] == expectedValues[j]); + REQUIRE(values[j] == expectedValues[static_cast(j)]); } } } @@ -3447,7 +3447,7 @@ TEST_CASE("Check fixed-length array of string") { auto values = *maybeValues; auto expectedValues = *expected[static_cast(i)]; for (int64_t j = 0; j < values.size(); ++j) { - REQUIRE(values[j] == expectedValues[j]); + REQUIRE(values[j] == expectedValues[static_cast(j)]); } } } @@ -3594,7 +3594,7 @@ TEST_CASE("Check variable-length string array PropertyTablePropertyView") { auto values = *maybeValues; auto expectedValues = *expected[static_cast(i)]; for (int64_t j = 0; j < values.size(); ++j) { - REQUIRE(values[j] == expectedValues[j]); + REQUIRE(values[j] == expectedValues[static_cast(j)]); } } } @@ -3655,7 +3655,7 @@ TEST_CASE("Check variable-length string array PropertyTablePropertyView") { auto values = *maybeValues; auto expectedValues = *expected[static_cast(i)]; for (int64_t j = 0; j < values.size(); ++j) { - REQUIRE(values[j] == expectedValues[j]); + REQUIRE(values[j] == expectedValues[static_cast(j)]); } } } diff --git a/CesiumGltf/test/TestPropertyTableView.cpp b/CesiumGltf/test/TestPropertyTableView.cpp index b79cf2e9e..b258becb2 100644 --- a/CesiumGltf/test/TestPropertyTableView.cpp +++ b/CesiumGltf/test/TestPropertyTableView.cpp @@ -1,3 +1,4 @@ +#include "CesiumGltf/PropertyTablePropertyViewType.h" #include "CesiumGltf/PropertyTableView.h" #include @@ -136,7 +137,7 @@ TEST_CASE("Test scalar PropertyTableProperty") { SECTION("Access correct type") { PropertyTablePropertyView uint32Property = - view.getPropertyView("TestClassProperty"); + view.getPropertyView("TestClassProperty"); REQUIRE(uint32Property.status() == PropertyTablePropertyViewStatus::Valid); REQUIRE(uint32Property.size() > 0); @@ -147,25 +148,25 @@ TEST_CASE("Test scalar PropertyTableProperty") { SECTION("Access wrong type") { PropertyTablePropertyView uvec3Invalid = - view.getPropertyView("TestClassProperty"); + view.getPropertyView("TestClassProperty"); REQUIRE( uvec3Invalid.status() == PropertyTablePropertyViewStatus::ErrorTypeMismatch); PropertyTablePropertyView u32mat3x3Invalid = - view.getPropertyView("TestClassProperty"); + view.getPropertyView("TestClassProperty"); REQUIRE( u32mat3x3Invalid.status() == PropertyTablePropertyViewStatus::ErrorTypeMismatch); PropertyTablePropertyView boolInvalid = - view.getPropertyView("TestClassProperty"); + view.getPropertyView("TestClassProperty"); REQUIRE( boolInvalid.status() == PropertyTablePropertyViewStatus::ErrorTypeMismatch); PropertyTablePropertyView stringInvalid = - view.getPropertyView("TestClassProperty"); + view.getPropertyView("TestClassProperty"); REQUIRE( stringInvalid.status() == PropertyTablePropertyViewStatus::ErrorTypeMismatch); @@ -173,19 +174,19 @@ TEST_CASE("Test scalar PropertyTableProperty") { SECTION("Access wrong component type") { PropertyTablePropertyView uint8Invalid = - view.getPropertyView("TestClassProperty"); + view.getPropertyView("TestClassProperty"); REQUIRE( uint8Invalid.status() == PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); PropertyTablePropertyView int32Invalid = - view.getPropertyView("TestClassProperty"); + view.getPropertyView("TestClassProperty"); REQUIRE( int32Invalid.status() == PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); PropertyTablePropertyView uint64Invalid = - view.getPropertyView("TestClassProperty"); + view.getPropertyView("TestClassProperty"); REQUIRE( uint64Invalid.status() == PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); @@ -193,7 +194,8 @@ TEST_CASE("Test scalar PropertyTableProperty") { SECTION("Access incorrectly as array") { PropertyTablePropertyView> uint32ArrayInvalid = - view.getPropertyView>("TestClassProperty"); + view.getPropertyView, false>( + "TestClassProperty"); REQUIRE( uint32ArrayInvalid.status() == PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); @@ -202,7 +204,7 @@ TEST_CASE("Test scalar PropertyTableProperty") { SECTION("Wrong buffer index") { model.bufferViews[valueBufferViewIndex].buffer = 2; PropertyTablePropertyView uint32Property = - view.getPropertyView("TestClassProperty"); + view.getPropertyView("TestClassProperty"); REQUIRE( uint32Property.status() == PropertyTablePropertyViewStatus::ErrorInvalidValueBuffer); @@ -211,7 +213,7 @@ TEST_CASE("Test scalar PropertyTableProperty") { SECTION("Wrong buffer view index") { propertyTableProperty.values = -1; PropertyTablePropertyView uint32Property = - view.getPropertyView("TestClassProperty"); + view.getPropertyView("TestClassProperty"); REQUIRE( uint32Property.status() == PropertyTablePropertyViewStatus::ErrorInvalidValueBufferView); @@ -220,7 +222,7 @@ TEST_CASE("Test scalar PropertyTableProperty") { SECTION("Buffer view points outside of the real buffer length") { model.buffers[valueBufferIndex].cesium.data.resize(12); PropertyTablePropertyView uint32Property = - view.getPropertyView("TestClassProperty"); + view.getPropertyView("TestClassProperty"); REQUIRE( uint32Property.status() == PropertyTablePropertyViewStatus::ErrorBufferViewOutOfBounds); @@ -229,7 +231,7 @@ TEST_CASE("Test scalar PropertyTableProperty") { SECTION("Buffer view length isn't multiple of sizeof(T)") { model.bufferViews[valueBufferViewIndex].byteLength = 13; PropertyTablePropertyView uint32Property = - view.getPropertyView("TestClassProperty"); + view.getPropertyView("TestClassProperty"); REQUIRE( uint32Property.status() == PropertyTablePropertyViewStatus:: @@ -239,7 +241,7 @@ TEST_CASE("Test scalar PropertyTableProperty") { SECTION("Buffer view length doesn't match with propertyTableCount") { model.bufferViews[valueBufferViewIndex].byteLength = 12; PropertyTablePropertyView uint32Property = - view.getPropertyView("TestClassProperty"); + view.getPropertyView("TestClassProperty"); REQUIRE( uint32Property.status() == PropertyTablePropertyViewStatus:: @@ -2337,6 +2339,13 @@ TEST_CASE("Test variable-length arrays of strings") { } } +template +void testFunction(PropertyTablePropertyView view) { + REQUIRE( + propertyValue.status() == + PropertyTablePropertyViewStatus::ErrorInvalidPropertyTable); +} + TEST_CASE("Test callback on invalid property table view") { Model model; ExtensionModelExtStructuralMetadata& metadata = @@ -2361,17 +2370,17 @@ TEST_CASE("Test callback on invalid property table view") { REQUIRE(!classProperty); uint32_t invokedCallbackCount = 0; - view.getPropertyView( - "TestClassProperty", - [&invokedCallbackCount]( - const std::string& /*propertyName*/, - auto propertyValue) mutable { - invokedCallbackCount++; - REQUIRE( - propertyValue.status() == - PropertyTablePropertyViewStatus::ErrorInvalidPropertyTable); - REQUIRE(propertyValue.size() == 0); - }); + auto callback = [&invokedCallbackCount]( + const std::string& /*propertyName*/, + auto property) mutable { + invokedCallbackCount++; + REQUIRE( + property.status() == + PropertyTablePropertyViewStatus::ErrorInvalidPropertyTable); + REQUIRE(property.size() == 0); + }; + + view.getPropertyView("TestClassProperty", callback); REQUIRE(invokedCallbackCount == 1); } @@ -2476,9 +2485,8 @@ TEST_CASE("Test callback for scalar PropertyTableProperty") { values[static_cast(i)]); } } else { - FAIL( - "getPropertyView returned PropertyTablePropertyView of incorrect " - "type for TestClassProperty."); + FAIL("getPropertyView returned PropertyTablePropertyView of " + "incorrect type for TestClassProperty."); } }); @@ -2545,9 +2553,8 @@ TEST_CASE("Test callback for vecN PropertyTableProperty") { values[static_cast(i)]); } } else { - FAIL( - "getPropertyView returned PropertyTablePropertyView of incorrect " - "type for TestClassProperty."); + FAIL("getPropertyView returned PropertyTablePropertyView of " + "incorrect type for TestClassProperty."); } }); @@ -2623,9 +2630,8 @@ TEST_CASE("Test callback for matN PropertyTableProperty") { values[static_cast(i)]); } } else { - FAIL( - "getPropertyView returned PropertyTablePropertyView of incorrect " - "type for TestClassProperty."); + FAIL("getPropertyView returned PropertyTablePropertyView of " + "incorrecttype for TestClassProperty."); } invokedCallbackCount++; }); @@ -2706,9 +2712,8 @@ TEST_CASE("Test callback for boolean PropertyTableProperty") { expected[static_cast(i)]); } } else { - FAIL( - "getPropertyView returned PropertyTablePropertyView of incorrect " - "type for TestClassProperty."); + FAIL("getPropertyView returned PropertyTablePropertyView of " + "incorrect type for TestClassProperty."); } }); @@ -2796,9 +2801,8 @@ TEST_CASE("Test callback for string PropertyTableProperty") { expected[static_cast(i)]); } } else { - FAIL( - "getPropertyView returned PropertyTablePropertyView of incorrect " - "type for TestClassProperty."); + FAIL("getPropertyView returned PropertyTablePropertyView of " + "incorrect type for TestClassProperty."); } }); @@ -2867,9 +2871,8 @@ TEST_CASE("Test callback for scalar array PropertyTableProperty") { } } } else { - FAIL( - "getPropertyView returned PropertyTablePropertyView of incorrect " - "type for TestClassProperty."); + FAIL("getPropertyView returned PropertyTablePropertyView of " + "incorrect type for TestClassProperty."); } }); @@ -2944,9 +2947,8 @@ TEST_CASE("Test callback for vecN array PropertyTableProperty") { } } } else { - FAIL( - "getPropertyView returned PropertyTablePropertyView of incorrect " - "type for TestClassProperty."); + FAIL("getPropertyView returned PropertyTablePropertyView of " + "incorrect type for TestClassProperty."); } }); @@ -3035,9 +3037,8 @@ TEST_CASE("Test callback for matN array PropertyTableProperty") { } } } else { - FAIL( - "getPropertyView returned PropertyTablePropertyView of incorrect " - "type for TestClassProperty."); + FAIL("getPropertyView returned PropertyTablePropertyView of " + "incorrect type for TestClassProperty."); } }); @@ -3126,9 +3127,8 @@ TEST_CASE("Test callback for boolean array PropertyTableProperty") { } } } else { - FAIL( - "getPropertyView returned PropertyTablePropertyView of incorrect " - "type for TestClassProperty."); + FAIL("getPropertyView returned PropertyTablePropertyView of " + "incorrect type for TestClassProperty."); } }); @@ -3237,9 +3237,8 @@ TEST_CASE("Test callback for string array PropertyTableProperty") { REQUIRE(v2[0] == "I love you, meat bags! ❤️"); REQUIRE(v2[1] == "Book in the freezer"); } else { - FAIL( - "getPropertyView returned PropertyTablePropertyView of incorrect " - "type for TestClassProperty."); + FAIL("getPropertyView returned PropertyTablePropertyView of " + "incorrect type for TestClassProperty."); } }); From a00b819dcae2b5fcf23c9dcac46e0d8230954415 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Thu, 24 Aug 2023 14:35:45 -0400 Subject: [PATCH 219/421] Finish up PropertyTableView tests --- .../CesiumGltf/PropertyTablePropertyView.h | 1 - ...ype.h => PropertyTablePropertyViewTypes.h} | 231 +- .../test/TestPropertyTablePropertyView.cpp | 10 +- CesiumGltf/test/TestPropertyTableView.cpp | 3732 ++++++++++++++--- 4 files changed, 3215 insertions(+), 759 deletions(-) rename CesiumGltf/include/CesiumGltf/{PropertyTablePropertyViewType.h => PropertyTablePropertyViewTypes.h} (95%) diff --git a/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h index 4c1c42cae..fe73675e8 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h @@ -567,7 +567,6 @@ class PropertyTablePropertyView _arrayOffsetType{arrayOffsetType}, _arrayOffsetTypeSize{getOffsetTypeSize(arrayOffsetType)} {} - /** * @brief Get the value of an element of the {@link PropertyTable}, * with normalization and other value transforms applied. In other words, the diff --git a/CesiumGltf/include/CesiumGltf/PropertyTablePropertyViewType.h b/CesiumGltf/include/CesiumGltf/PropertyTablePropertyViewTypes.h similarity index 95% rename from CesiumGltf/include/CesiumGltf/PropertyTablePropertyViewType.h rename to CesiumGltf/include/CesiumGltf/PropertyTablePropertyViewTypes.h index e879c1973..257c99615 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTablePropertyViewType.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTablePropertyViewTypes.h @@ -6,9 +6,9 @@ #include #include -#include #include #include +#include namespace CesiumGltf { @@ -16,7 +16,7 @@ namespace CesiumGltf { /** * @brief An alias for all of the potential types of - * {@link PropertyTablePropertyView} possible, with and without normalization. + * {@link PropertyTablePropertyView} possible without normalization. * Can be useful for applications that want to implement abstract * representations of PropertyTablePropertyView, without the mess of templated * types. @@ -30,14 +30,6 @@ typedef std::variant< View, View, View, - View, - View, - View, - View, - View, - View, - View, - View, View, View, View, @@ -50,14 +42,6 @@ typedef std::variant< View, false>, View, false>, View, false>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, View>, View>, View, false>, @@ -68,14 +52,6 @@ typedef std::variant< View, false>, View, false>, View, false>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, View>, View>, View, false>, @@ -86,14 +62,6 @@ typedef std::variant< View, false>, View, false>, View, false>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, View>, View>, View, false>, @@ -104,14 +72,6 @@ typedef std::variant< View, false>, View, false>, View, false>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, View>, View>, View, false>, @@ -122,14 +82,6 @@ typedef std::variant< View, false>, View, false>, View, false>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, View>, View>, View, false>, @@ -140,14 +92,6 @@ typedef std::variant< View, false>, View, false>, View, false>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, View>, View>, View, false>, @@ -158,14 +102,6 @@ typedef std::variant< View, false>, View, false>, View, false>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, View>, View>, View>, @@ -178,14 +114,6 @@ typedef std::variant< View>, false>, View>, false>, View>, false>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, View>>, View>>, View>, false>, @@ -196,14 +124,6 @@ typedef std::variant< View>, false>, View>, false>, View>, false>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, View>>, View>>, View>, false>, @@ -214,14 +134,6 @@ typedef std::variant< View>, false>, View>, false>, View>, false>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, View>>, View>>, View>, false>, @@ -232,14 +144,6 @@ typedef std::variant< View>, false>, View>, false>, View>, false>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, View>>, View>>, View>, false>, @@ -250,14 +154,6 @@ typedef std::variant< View>, false>, View>, false>, View>, false>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, View>>, View>>, View>, false>, @@ -268,6 +164,122 @@ typedef std::variant< View>, false>, View>, false>, View>, false>, + View>>, + View>>> + NonNormalizedPropertyTablePropertyView; + +/** + * @brief An alias for all of the potential types of + * {@link PropertyTablePropertyView} possible with normalization. + * Can be useful for applications that want to implement abstract + * representations of PropertyTablePropertyView, without the mess of templated + * types. + */ +typedef std::variant< + View, + View, + View, + View, + View, + View, + View, + View, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, + View>, true>, View>, true>, View>, true>, View>, true>, @@ -275,11 +287,8 @@ typedef std::variant< View>, true>, View>, true>, View>, true>, - View>, true>, - View>>, - View>>> - PropertyTablePropertyViewType; + View>, true>> + NormalizedPropertyTablePropertyView; #undef View - } // namespace CesiumGltf diff --git a/CesiumGltf/test/TestPropertyTablePropertyView.cpp b/CesiumGltf/test/TestPropertyTablePropertyView.cpp index 065f0414a..5ed7f2444 100644 --- a/CesiumGltf/test/TestPropertyTablePropertyView.cpp +++ b/CesiumGltf/test/TestPropertyTablePropertyView.cpp @@ -782,8 +782,8 @@ TEST_CASE("Check scalar PropertyTablePropertyView") { std::optional noData = -1; std::vector> expected{ std::nullopt, - 3, - 7, + static_cast(3), + static_cast(7), std::nullopt}; checkScalar( values, @@ -798,7 +798,11 @@ TEST_CASE("Check scalar PropertyTablePropertyView") { std::vector values{-1, 3, 7, -1}; std::optional noData = -1; std::optional defaultValue = 0; - std::vector> expected{0, 3, 7, 0}; + std::vector> expected{ + static_cast(0), + static_cast(3), + static_cast(7), + static_cast(0)}; checkScalar( values, expected, diff --git a/CesiumGltf/test/TestPropertyTableView.cpp b/CesiumGltf/test/TestPropertyTableView.cpp index b258becb2..2e9f9cd69 100644 --- a/CesiumGltf/test/TestPropertyTableView.cpp +++ b/CesiumGltf/test/TestPropertyTableView.cpp @@ -1,4 +1,4 @@ -#include "CesiumGltf/PropertyTablePropertyViewType.h" +#include "CesiumGltf/PropertyTablePropertyView.h" #include "CesiumGltf/PropertyTableView.h" #include @@ -134,39 +134,42 @@ TEST_CASE("Test scalar PropertyTableProperty") { REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT32); REQUIRE(classProperty->count == std::nullopt); REQUIRE(!classProperty->array); + REQUIRE(!classProperty->normalized); SECTION("Access correct type") { PropertyTablePropertyView uint32Property = - view.getPropertyView("TestClassProperty"); + view.getPropertyView("TestClassProperty"); REQUIRE(uint32Property.status() == PropertyTablePropertyViewStatus::Valid); REQUIRE(uint32Property.size() > 0); for (int64_t i = 0; i < uint32Property.size(); ++i) { REQUIRE(uint32Property.getRaw(i) == values[static_cast(i)]); + REQUIRE(uint32Property.get(i)); + REQUIRE(*uint32Property.get(i) == uint32Property.getRaw(i)); } } SECTION("Access wrong type") { PropertyTablePropertyView uvec3Invalid = - view.getPropertyView("TestClassProperty"); + view.getPropertyView("TestClassProperty"); REQUIRE( uvec3Invalid.status() == PropertyTablePropertyViewStatus::ErrorTypeMismatch); PropertyTablePropertyView u32mat3x3Invalid = - view.getPropertyView("TestClassProperty"); + view.getPropertyView("TestClassProperty"); REQUIRE( u32mat3x3Invalid.status() == PropertyTablePropertyViewStatus::ErrorTypeMismatch); PropertyTablePropertyView boolInvalid = - view.getPropertyView("TestClassProperty"); + view.getPropertyView("TestClassProperty"); REQUIRE( boolInvalid.status() == PropertyTablePropertyViewStatus::ErrorTypeMismatch); PropertyTablePropertyView stringInvalid = - view.getPropertyView("TestClassProperty"); + view.getPropertyView("TestClassProperty"); REQUIRE( stringInvalid.status() == PropertyTablePropertyViewStatus::ErrorTypeMismatch); @@ -174,37 +177,36 @@ TEST_CASE("Test scalar PropertyTableProperty") { SECTION("Access wrong component type") { PropertyTablePropertyView uint8Invalid = - view.getPropertyView("TestClassProperty"); + view.getPropertyView("TestClassProperty"); REQUIRE( uint8Invalid.status() == PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); PropertyTablePropertyView int32Invalid = - view.getPropertyView("TestClassProperty"); + view.getPropertyView("TestClassProperty"); REQUIRE( int32Invalid.status() == PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); PropertyTablePropertyView uint64Invalid = - view.getPropertyView("TestClassProperty"); + view.getPropertyView("TestClassProperty"); REQUIRE( uint64Invalid.status() == PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); } - SECTION("Access incorrectly as array") { - PropertyTablePropertyView> uint32ArrayInvalid = - view.getPropertyView, false>( - "TestClassProperty"); + SECTION("Access incorrectly as normalized") { + PropertyTablePropertyView uint32NormalizedInvalid = + view.getPropertyView("TestClassProperty"); REQUIRE( - uint32ArrayInvalid.status() == - PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); + uint32NormalizedInvalid.status() == + PropertyTablePropertyViewStatus::ErrorInvalidNormalization); } SECTION("Wrong buffer index") { model.bufferViews[valueBufferViewIndex].buffer = 2; PropertyTablePropertyView uint32Property = - view.getPropertyView("TestClassProperty"); + view.getPropertyView("TestClassProperty"); REQUIRE( uint32Property.status() == PropertyTablePropertyViewStatus::ErrorInvalidValueBuffer); @@ -213,7 +215,7 @@ TEST_CASE("Test scalar PropertyTableProperty") { SECTION("Wrong buffer view index") { propertyTableProperty.values = -1; PropertyTablePropertyView uint32Property = - view.getPropertyView("TestClassProperty"); + view.getPropertyView("TestClassProperty"); REQUIRE( uint32Property.status() == PropertyTablePropertyViewStatus::ErrorInvalidValueBufferView); @@ -222,7 +224,7 @@ TEST_CASE("Test scalar PropertyTableProperty") { SECTION("Buffer view points outside of the real buffer length") { model.buffers[valueBufferIndex].cesium.data.resize(12); PropertyTablePropertyView uint32Property = - view.getPropertyView("TestClassProperty"); + view.getPropertyView("TestClassProperty"); REQUIRE( uint32Property.status() == PropertyTablePropertyViewStatus::ErrorBufferViewOutOfBounds); @@ -231,7 +233,7 @@ TEST_CASE("Test scalar PropertyTableProperty") { SECTION("Buffer view length isn't multiple of sizeof(T)") { model.bufferViews[valueBufferViewIndex].byteLength = 13; PropertyTablePropertyView uint32Property = - view.getPropertyView("TestClassProperty"); + view.getPropertyView("TestClassProperty"); REQUIRE( uint32Property.status() == PropertyTablePropertyViewStatus:: @@ -241,7 +243,7 @@ TEST_CASE("Test scalar PropertyTableProperty") { SECTION("Buffer view length doesn't match with propertyTableCount") { model.bufferViews[valueBufferViewIndex].byteLength = 12; PropertyTablePropertyView uint32Property = - view.getPropertyView("TestClassProperty"); + view.getPropertyView("TestClassProperty"); REQUIRE( uint32Property.status() == PropertyTablePropertyViewStatus:: @@ -249,6 +251,159 @@ TEST_CASE("Test scalar PropertyTableProperty") { } } +TEST_CASE("Test scalar PropertyTableProperty (normalized)") { + Model model; + std::vector values = {-128, 0, 32, 2340, -1234, 127}; + + addBufferToModel(model, values); + size_t valueBufferIndex = model.buffers.size() - 1; + size_t valueBufferViewIndex = model.bufferViews.size() - 1; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::INT16; + testClassProperty.normalized = true; + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast(values.size()); + + PropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = static_cast(valueBufferViewIndex); + + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT16); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(classProperty->normalized); + REQUIRE(!classProperty->array); + + SECTION("Access correct type") { + PropertyTablePropertyView int16Property = + view.getPropertyView("TestClassProperty"); + REQUIRE(int16Property.status() == PropertyTablePropertyViewStatus::Valid); + REQUIRE(int16Property.size() > 0); + + for (int64_t i = 0; i < int16Property.size(); ++i) { + auto value = int16Property.getRaw(i); + REQUIRE(value == values[static_cast(i)]); + REQUIRE(int16Property.get(i)); + REQUIRE(*int16Property.get(i) == normalize(value)); + } + } + + SECTION("Access wrong type") { + PropertyTablePropertyView i16vec3Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + i16vec3Invalid.status() == + PropertyTablePropertyViewStatus::ErrorTypeMismatch); + + PropertyTablePropertyView i16mat3x3Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + i16mat3x3Invalid.status() == + PropertyTablePropertyViewStatus::ErrorTypeMismatch); + } + + SECTION("Access wrong component type") { + PropertyTablePropertyView uint16Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uint16Invalid.status() == + PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); + + PropertyTablePropertyView int32Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + int32Invalid.status() == + PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); + } + + SECTION("Access incorrectly as array") { + PropertyTablePropertyView, true> arrayInvalid = + view.getPropertyView, true>( + "TestClassProperty"); + REQUIRE( + arrayInvalid.status() == + PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); + } + + SECTION("Access incorrectly as non-normalized") { + PropertyTablePropertyView nonNormalizedInvalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + nonNormalizedInvalid.status() == + PropertyTablePropertyViewStatus::ErrorInvalidNormalization); + } + + SECTION("Access incorrectly as double") { + PropertyTablePropertyView doubleInvalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + doubleInvalid.status() == + PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); + } + + SECTION("Wrong buffer index") { + model.bufferViews[valueBufferViewIndex].buffer = 2; + PropertyTablePropertyView propertyView = + view.getPropertyView("TestClassProperty"); + REQUIRE( + propertyView.status() == + PropertyTablePropertyViewStatus::ErrorInvalidValueBuffer); + } + + SECTION("Wrong buffer view index") { + propertyTableProperty.values = -1; + PropertyTablePropertyView propertyView = + view.getPropertyView("TestClassProperty"); + REQUIRE( + propertyView.status() == + PropertyTablePropertyViewStatus::ErrorInvalidValueBufferView); + } + + SECTION("Buffer view points outside of the real buffer length") { + model.buffers[valueBufferIndex].cesium.data.resize(4); + PropertyTablePropertyView propertyView = + view.getPropertyView("TestClassProperty"); + REQUIRE( + propertyView.status() == + PropertyTablePropertyViewStatus::ErrorBufferViewOutOfBounds); + } + + SECTION("Buffer view length isn't multiple of sizeof(T)") { + model.bufferViews[valueBufferViewIndex].byteLength = 7; + PropertyTablePropertyView propertyView = + view.getPropertyView("TestClassProperty"); + REQUIRE( + propertyView.status() == PropertyTablePropertyViewStatus:: + ErrorBufferViewSizeNotDivisibleByTypeSize); + } + + SECTION("Buffer view length doesn't match with propertyTableCount") { + model.bufferViews[valueBufferViewIndex].byteLength = 2; + PropertyTablePropertyView propertyView = + view.getPropertyView("TestClassProperty"); + REQUIRE( + propertyView.status() == + PropertyTablePropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + } +} + TEST_CASE("Test vecN PropertyTableProperty") { Model model; std::vector values = { @@ -289,6 +444,7 @@ TEST_CASE("Test vecN PropertyTableProperty") { REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT32); REQUIRE(classProperty->count == std::nullopt); REQUIRE(!classProperty->array); + REQUIRE(!classProperty->normalized); SECTION("Access correct type") { PropertyTablePropertyView ivec3Property = @@ -298,6 +454,8 @@ TEST_CASE("Test vecN PropertyTableProperty") { for (int64_t i = 0; i < ivec3Property.size(); ++i) { REQUIRE(ivec3Property.getRaw(i) == values[static_cast(i)]); + REQUIRE(ivec3Property.get(i)); + REQUIRE(*ivec3Property.get(i) == ivec3Property.getRaw(i)); } } @@ -362,6 +520,14 @@ TEST_CASE("Test vecN PropertyTableProperty") { PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); } + SECTION("Access incorrectly as normalized") { + PropertyTablePropertyView normalizedInvalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + normalizedInvalid.status() == + PropertyTablePropertyViewStatus::ErrorInvalidNormalization); + } + SECTION("Wrong buffer index") { model.bufferViews[valueBufferViewIndex].buffer = 2; PropertyTablePropertyView ivec3Property = @@ -410,6 +576,170 @@ TEST_CASE("Test vecN PropertyTableProperty") { } } +TEST_CASE("Test vecN PropertyTableProperty (normalized)") { + Model model; + std::vector values = { + glm::ivec3(-12, 34, 30), + glm::ivec3(11, 73, 0), + glm::ivec3(-2, 6, 12), + glm::ivec3(-4, 8, -13)}; + + addBufferToModel(model, values); + size_t valueBufferIndex = model.buffers.size() - 1; + size_t valueBufferViewIndex = model.bufferViews.size() - 1; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::VEC3; + testClassProperty.componentType = ClassProperty::ComponentType::INT32; + testClassProperty.normalized = true; + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast(values.size()); + + PropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = static_cast(valueBufferViewIndex); + + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::VEC3); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT32); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->array); + REQUIRE(classProperty->normalized); + + SECTION("Access correct type") { + PropertyTablePropertyView ivec3Property = + view.getPropertyView("TestClassProperty"); + REQUIRE(ivec3Property.status() == PropertyTablePropertyViewStatus::Valid); + REQUIRE(ivec3Property.size() > 0); + + for (int64_t i = 0; i < ivec3Property.size(); ++i) { + auto value = ivec3Property.getRaw(i); + REQUIRE(value == values[static_cast(i)]); + REQUIRE(ivec3Property.get(i)); + REQUIRE(*ivec3Property.get(i) == normalize(value)); + } + } + + SECTION("Access wrong type") { + PropertyTablePropertyView int32Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + int32Invalid.status() == + PropertyTablePropertyViewStatus::ErrorTypeMismatch); + + PropertyTablePropertyView ivec2Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + ivec2Invalid.status() == + PropertyTablePropertyViewStatus::ErrorTypeMismatch); + + PropertyTablePropertyView i32mat3x3Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + i32mat3x3Invalid.status() == + PropertyTablePropertyViewStatus::ErrorTypeMismatch); + } + + SECTION("Access wrong component type") { + PropertyTablePropertyView u8vec3Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + u8vec3Invalid.status() == + PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); + + PropertyTablePropertyView i16vec3Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + i16vec3Invalid.status() == + PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); + } + + SECTION("Access incorrectly as array") { + PropertyTablePropertyView> ivec3ArrayInvalid = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + ivec3ArrayInvalid.status() == + PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); + } + + SECTION("Access incorrectly as non-normalized") { + PropertyTablePropertyView nonNormalizedInvalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + nonNormalizedInvalid.status() == + PropertyTablePropertyViewStatus::ErrorInvalidNormalization); + } + + SECTION("Access incorrectly as dvec3") { + PropertyTablePropertyView dvec3Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + dvec3Invalid.status() == + PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); + } + + SECTION("Wrong buffer index") { + model.bufferViews[valueBufferViewIndex].buffer = 2; + PropertyTablePropertyView ivec3Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + ivec3Property.status() == + PropertyTablePropertyViewStatus::ErrorInvalidValueBuffer); + } + + SECTION("Wrong buffer view index") { + propertyTableProperty.values = -1; + PropertyTablePropertyView ivec3Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + ivec3Property.status() == + PropertyTablePropertyViewStatus::ErrorInvalidValueBufferView); + } + + SECTION("Buffer view points outside of the real buffer length") { + model.buffers[valueBufferIndex].cesium.data.resize(12); + PropertyTablePropertyView ivec3Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + ivec3Property.status() == + PropertyTablePropertyViewStatus::ErrorBufferViewOutOfBounds); + } + + SECTION("Buffer view length isn't multiple of sizeof(T)") { + model.bufferViews[valueBufferViewIndex].byteLength = 11; + PropertyTablePropertyView ivec3Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + ivec3Property.status() == + PropertyTablePropertyViewStatus:: + ErrorBufferViewSizeNotDivisibleByTypeSize); + } + + SECTION("Buffer view length doesn't match with propertyTableCount") { + model.bufferViews[valueBufferViewIndex].byteLength = 12; + PropertyTablePropertyView ivec3Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + ivec3Property.status() == + PropertyTablePropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + } +} + TEST_CASE("Test matN PropertyTableProperty") { Model model; // clang-format off @@ -460,6 +790,7 @@ TEST_CASE("Test matN PropertyTableProperty") { REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT32); REQUIRE(classProperty->count == std::nullopt); REQUIRE(!classProperty->array); + REQUIRE(!classProperty->normalized); SECTION("Access correct type") { PropertyTablePropertyView u32mat2x2Property = @@ -470,6 +801,8 @@ TEST_CASE("Test matN PropertyTableProperty") { for (int64_t i = 0; i < u32mat2x2Property.size(); ++i) { REQUIRE(u32mat2x2Property.getRaw(i) == values[static_cast(i)]); + REQUIRE(u32mat2x2Property.get(i)); + REQUIRE(*u32mat2x2Property.get(i) == u32mat2x2Property.getRaw(i)); } } @@ -526,12 +859,21 @@ TEST_CASE("Test matN PropertyTableProperty") { } SECTION("Access incorrectly as array") { - PropertyTablePropertyView> - u32mat2x2ArrayInvalid = - view.getPropertyView>( + PropertyTablePropertyView> arrayInvalid = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayInvalid.status() == + PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); + } + + SECTION("Access incorrectly as normalized") { + PropertyTablePropertyView, true> + normalizedInvalid = + view.getPropertyView, true>( "TestClassProperty"); REQUIRE( - u32mat2x2ArrayInvalid.status() == + normalizedInvalid.status() == PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); } @@ -585,16 +927,194 @@ TEST_CASE("Test matN PropertyTableProperty") { } } -TEST_CASE("Test boolean PropertyTableProperty") { +TEST_CASE("Test matN PropertyTableProperty (normalized)") { Model model; - - int64_t instanceCount = 21; - std::vector expected; - std::vector values; - values.resize(3); - for (int64_t i = 0; i < instanceCount; ++i) { - if (i % 2 == 0) { - expected.emplace_back(true); + // clang-format off + std::vector values = { + glm::u32mat2x2( + 12, 34, + 30, 1), + glm::u32mat2x2( + 11, 8, + 73, 102), + glm::u32mat2x2( + 1, 0, + 63, 2), + glm::u32mat2x2( + 4, 8, + 3, 23)}; + // clang-format on + + addBufferToModel(model, values); + size_t valueBufferIndex = model.buffers.size() - 1; + size_t valueBufferViewIndex = model.bufferViews.size() - 1; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::MAT2; + testClassProperty.componentType = ClassProperty::ComponentType::UINT32; + testClassProperty.normalized = true; + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast(values.size()); + + PropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = static_cast(valueBufferViewIndex); + + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::MAT2); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT32); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->array); + REQUIRE(classProperty->normalized); + + SECTION("Access correct type") { + PropertyTablePropertyView u32mat2x2Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + u32mat2x2Property.status() == PropertyTablePropertyViewStatus::Valid); + REQUIRE(u32mat2x2Property.size() > 0); + + for (int64_t i = 0; i < u32mat2x2Property.size(); ++i) { + auto value = u32mat2x2Property.getRaw(i); + REQUIRE(value == values[static_cast(i)]); + REQUIRE(u32mat2x2Property.get(i)); + REQUIRE(*u32mat2x2Property.get(i) == normalize(value)); + } + } + + SECTION("Access wrong type") { + PropertyTablePropertyView uint32Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uint32Invalid.status() == + PropertyTablePropertyViewStatus::ErrorTypeMismatch); + + PropertyTablePropertyView uvec2Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uvec2Invalid.status() == + PropertyTablePropertyViewStatus::ErrorTypeMismatch); + + PropertyTablePropertyView u32mat4x4Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + u32mat4x4Invalid.status() == + PropertyTablePropertyViewStatus::ErrorTypeMismatch); + } + + SECTION("Access wrong component type") { + PropertyTablePropertyView u8mat2x2Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + u8mat2x2Invalid.status() == + PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); + + PropertyTablePropertyView i32mat2x2Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + i32mat2x2Invalid.status() == + PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); + } + + SECTION("Access incorrectly as array") { + PropertyTablePropertyView, true> + arrayInvalid = + view.getPropertyView, true>( + "TestClassProperty"); + REQUIRE( + arrayInvalid.status() == + PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); + } + + SECTION("Access incorrectly as non-normalized") { + PropertyTablePropertyView nonNormalizedInvalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + nonNormalizedInvalid.status() == + PropertyTablePropertyViewStatus::ErrorInvalidNormalization); + } + + SECTION("Access incorrectly as dmat2") { + PropertyTablePropertyView dmat2Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + dmat2Invalid.status() == + PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); + } + + SECTION("Wrong buffer index") { + model.bufferViews[valueBufferViewIndex].buffer = 2; + PropertyTablePropertyView u32mat2x2Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + u32mat2x2Property.status() == + PropertyTablePropertyViewStatus::ErrorInvalidValueBuffer); + } + + SECTION("Wrong buffer view index") { + propertyTableProperty.values = -1; + PropertyTablePropertyView u32mat2x2Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + u32mat2x2Property.status() == + PropertyTablePropertyViewStatus::ErrorInvalidValueBufferView); + } + + SECTION("Buffer view points outside of the real buffer length") { + model.buffers[valueBufferIndex].cesium.data.resize(sizeof(glm::u32mat2x2)); + PropertyTablePropertyView u32mat2x2Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + u32mat2x2Property.status() == + PropertyTablePropertyViewStatus::ErrorBufferViewOutOfBounds); + } + + SECTION("Buffer view length isn't multiple of sizeof(T)") { + model.bufferViews[valueBufferViewIndex].byteLength = + sizeof(glm::u32mat2x2) * 4 - 1; + PropertyTablePropertyView u32mat2x2Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + u32mat2x2Property.status() == + PropertyTablePropertyViewStatus:: + ErrorBufferViewSizeNotDivisibleByTypeSize); + } + + SECTION("Buffer view length doesn't match with propertyTableCount") { + model.bufferViews[valueBufferViewIndex].byteLength = sizeof(glm::u32mat2x2); + + PropertyTablePropertyView u32mat2x2Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + u32mat2x2Property.status() == + PropertyTablePropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + } +} + +TEST_CASE("Test boolean PropertyTableProperty") { + Model model; + + int64_t instanceCount = 21; + std::vector expected; + std::vector values; + values.resize(3); + for (int64_t i = 0; i < instanceCount; ++i) { + if (i % 2 == 0) { + expected.emplace_back(true); } else { expected.emplace_back(false); } @@ -644,6 +1164,8 @@ TEST_CASE("Test boolean PropertyTableProperty") { for (int64_t i = 0; i < boolProperty.size(); ++i) { bool expectedValue = expected[static_cast(i)]; REQUIRE(boolProperty.getRaw(i) == expectedValue); + REQUIRE(boolProperty.get(i)); + REQUIRE(*boolProperty.get(i) == expectedValue); } } @@ -727,6 +1249,8 @@ TEST_CASE("Test string PropertyTableProperty") { REQUIRE(stringProperty.status() == PropertyTablePropertyViewStatus::Valid); for (size_t i = 0; i < expected.size(); ++i) { REQUIRE(stringProperty.getRaw(static_cast(i)) == expected[i]); + REQUIRE(stringProperty.get(i)); + REQUIRE(*stringProperty.get(i) == expected[i]); } } @@ -841,6 +1365,7 @@ TEST_CASE("Test fixed-length scalar array") { REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT32); REQUIRE(classProperty->array); REQUIRE(classProperty->count == 3); + REQUIRE(!classProperty->normalized); SECTION("Access the right type") { PropertyTablePropertyView> arrayProperty = @@ -848,9 +1373,12 @@ TEST_CASE("Test fixed-length scalar array") { REQUIRE(arrayProperty.status() == PropertyTablePropertyViewStatus::Valid); for (int64_t i = 0; i < arrayProperty.size(); ++i) { - PropertyArrayView member = arrayProperty.getRaw(i); - for (int64_t j = 0; j < member.size(); ++j) { - REQUIRE(member[j] == values[static_cast(i * 3 + j)]); + PropertyArrayView array = arrayProperty.getRaw(i); + auto maybeArray = arrayProperty.get(i); + REQUIRE(maybeArray); + for (int64_t j = 0; j < array.size(); ++j) { + REQUIRE(array[j] == values[static_cast(i * 3 + j)]); + REQUIRE((*maybeArray)[j] == array[j]); } } } @@ -886,6 +1414,16 @@ TEST_CASE("Test fixed-length scalar array") { PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); } + SECTION("Incorrectly normalized") { + PropertyTablePropertyView, true> + normalizedInvalid = + view.getPropertyView, true>( + "TestClassProperty"); + REQUIRE( + normalizedInvalid.status() == + PropertyTablePropertyViewStatus::ErrorInvalidNormalization); + } + SECTION("Buffer size is not a multiple of type size") { model.bufferViews[valueBufferViewIndex].byteLength = 13; PropertyTablePropertyView> arrayProperty = @@ -896,7 +1434,7 @@ TEST_CASE("Test fixed-length scalar array") { ErrorBufferViewSizeNotDivisibleByTypeSize); } - SECTION("Negative component count") { + SECTION("Negative count") { testClassProperty.count = -1; PropertyTablePropertyView> arrayProperty = view.getPropertyView>("TestClassProperty"); @@ -916,6 +1454,137 @@ TEST_CASE("Test fixed-length scalar array") { } } +TEST_CASE("Test fixed-length scalar array (normalized)") { + Model model; + std::vector values = + {12, 34, 30, 11, 34, 34, 11, 33, 122, 33, 223, 11}; + + addBufferToModel(model, values); + size_t valueBufferViewIndex = model.bufferViews.size() - 1; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::UINT32; + testClassProperty.array = true; + testClassProperty.count = 3; + testClassProperty.normalized = true; + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast( + values.size() / static_cast(testClassProperty.count.value())); + + PropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = + static_cast(model.bufferViews.size() - 1); + + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT32); + REQUIRE(classProperty->array); + REQUIRE(classProperty->count == 3); + REQUIRE(classProperty->normalized); + + SECTION("Access the right type") { + PropertyTablePropertyView, true> arrayProperty = + view.getPropertyView, true>( + "TestClassProperty"); + REQUIRE(arrayProperty.status() == PropertyTablePropertyViewStatus::Valid); + + for (int64_t i = 0; i < arrayProperty.size(); ++i) { + PropertyArrayView array = arrayProperty.getRaw(i); + auto maybeArray = arrayProperty.get(i); + REQUIRE(maybeArray); + for (int64_t j = 0; j < array.size(); ++j) { + REQUIRE(array[j] == values[static_cast(i * 3 + j)]); + REQUIRE((*maybeArray)[j] == normalize(array[j])); + } + } + } + + SECTION("Wrong type") { + PropertyTablePropertyView, true> + uvec2ArrayInvalid = + view.getPropertyView, true>( + "TestClassProperty"); + REQUIRE( + uvec2ArrayInvalid.status() == + PropertyTablePropertyViewStatus::ErrorTypeMismatch); + } + + SECTION("Wrong component type") { + PropertyTablePropertyView, true> + int32ArrayInvalid = + view.getPropertyView, true>( + "TestClassProperty"); + REQUIRE( + int32ArrayInvalid.status() == + PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); + } + + SECTION("Not an array type") { + PropertyTablePropertyView uint32Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uint32Invalid.status() == + PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); + } + + SECTION("Incorrectly non-normalized") { + PropertyTablePropertyView> + nonNormalizedInvalid = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + nonNormalizedInvalid.status() == + PropertyTablePropertyViewStatus::ErrorInvalidNormalization); + } + + SECTION("Buffer size is not a multiple of type size") { + model.bufferViews[valueBufferViewIndex].byteLength = 13; + PropertyTablePropertyView, true> arrayProperty = + view.getPropertyView, true>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus:: + ErrorBufferViewSizeNotDivisibleByTypeSize); + } + + SECTION("Negative count") { + testClassProperty.count = -1; + PropertyTablePropertyView, true> arrayProperty = + view.getPropertyView, true>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == PropertyTablePropertyViewStatus:: + ErrorArrayCountAndOffsetBufferDontExist); + } + + SECTION("Value buffer doesn't fit into property table count") { + testClassProperty.count = 55; + PropertyTablePropertyView, true> arrayProperty = + view.getPropertyView, true>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + } +} + TEST_CASE("Test variable-length scalar array") { Model model; @@ -985,24 +1654,39 @@ TEST_CASE("Test variable-length scalar array") { REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT16); REQUIRE(classProperty->array); REQUIRE(!classProperty->count); + REQUIRE(!classProperty->normalized); SECTION("Access the correct type") { PropertyTablePropertyView> property = view.getPropertyView>("TestClassProperty"); REQUIRE(property.status() == PropertyTablePropertyViewStatus::Valid); for (size_t i = 0; i < expected.size(); ++i) { - PropertyArrayView valueMember = + PropertyArrayView array = property.getRaw(static_cast(i)); - REQUIRE(valueMember.size() == static_cast(expected[i].size())); + auto maybeArray = property.get(static_cast(i)); + REQUIRE(maybeArray); + REQUIRE(maybeArray->size() == array.size()); + REQUIRE(array.size() == static_cast(expected[i].size())); + for (size_t j = 0; j < expected[i].size(); ++j) { - REQUIRE(expected[i][j] == valueMember[static_cast(j)]); + REQUIRE(expected[i][j] == array[static_cast(j)]); + REQUIRE( + (*maybeArray)[static_cast(j)] == + array[static_cast(j)]); } } } - SECTION("Wrong offset type") { - propertyTableProperty.arrayOffsetType = - PropertyTableProperty::ArrayOffsetType::UINT8; - PropertyTablePropertyView> arrayProperty = + + SECTION("Incorrectly normalized") { + PropertyTablePropertyView> property = + view.getPropertyView>("TestClassProperty"); + REQUIRE(property.status() == PropertyTablePropertyViewStatus::Valid); + } + + SECTION("Wrong offset type") { + propertyTableProperty.arrayOffsetType = + PropertyTableProperty::ArrayOffsetType::UINT8; + PropertyTablePropertyView> arrayProperty = view.getPropertyView>("TestClassProperty"); REQUIRE( arrayProperty.status() == @@ -1070,159 +1754,32 @@ TEST_CASE("Test variable-length scalar array") { } } -TEST_CASE("Test fixed-length vecN array") { +TEST_CASE("Test variable-length scalar array (normalized)") { Model model; - std::vector values = { - glm::ivec3(12, 34, -30), - glm::ivec3(-2, 0, 1), - glm::ivec3(1, 2, 8), - glm::ivec3(-100, 84, 6), - glm::ivec3(2, -2, -2), - glm::ivec3(40, 61, 3), - }; - - addBufferToModel(model, values); - size_t valueBufferViewIndex = model.bufferViews.size() - 1; - - ExtensionModelExtStructuralMetadata& metadata = - model.addExtension(); - - Schema& schema = metadata.schema.emplace(); - Class& testClass = schema.classes["TestClass"]; - ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; - testClassProperty.type = ClassProperty::Type::VEC3; - testClassProperty.componentType = ClassProperty::ComponentType::INT32; - testClassProperty.array = true; - testClassProperty.count = 2; - - PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); - propertyTable.classProperty = "TestClass"; - propertyTable.count = static_cast( - values.size() / static_cast(testClassProperty.count.value())); - - PropertyTableProperty& propertyTableProperty = - propertyTable.properties["TestClassProperty"]; - propertyTableProperty.values = - static_cast(model.bufferViews.size() - 1); - - PropertyTableView view(model, propertyTable); - REQUIRE(view.status() == PropertyTableViewStatus::Valid); - REQUIRE(view.size() == propertyTable.count); - - const ClassProperty* classProperty = - view.getClassProperty("TestClassProperty"); - REQUIRE(classProperty); - REQUIRE(classProperty->type == ClassProperty::Type::VEC3); - REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT32); - REQUIRE(classProperty->array); - REQUIRE(classProperty->count == 2); - - SECTION("Access the right type") { - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE(arrayProperty.status() == PropertyTablePropertyViewStatus::Valid); - - for (int64_t i = 0; i < arrayProperty.size(); ++i) { - PropertyArrayView member = arrayProperty.getRaw(i); - for (int64_t j = 0; j < member.size(); ++j) { - REQUIRE(member[j] == values[static_cast(i * 2 + j)]); - } - } - } - - SECTION("Wrong type") { - PropertyTablePropertyView> int32ArrayInvalid = - view.getPropertyView>("TestClassProperty"); - REQUIRE( - int32ArrayInvalid.status() == - PropertyTablePropertyViewStatus::ErrorTypeMismatch); - - PropertyTablePropertyView> ivec2ArrayInvalid = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - ivec2ArrayInvalid.status() == - PropertyTablePropertyViewStatus::ErrorTypeMismatch); - } - - SECTION("Wrong component type") { - PropertyTablePropertyView> uvec3ArrayInvalid = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - uvec3ArrayInvalid.status() == - PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); - } - - SECTION("Not an array type") { - PropertyTablePropertyView ivec3Invalid = - view.getPropertyView("TestClassProperty"); - REQUIRE( - ivec3Invalid.status() == - PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); - } - - SECTION("Buffer size is not a multiple of type size") { - model.bufferViews[valueBufferViewIndex].byteLength = 13; - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus:: - ErrorBufferViewSizeNotDivisibleByTypeSize); - } - - SECTION("Negative component count") { - testClassProperty.count = -1; - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == PropertyTablePropertyViewStatus:: - ErrorArrayCountAndOffsetBufferDontExist); - } - - SECTION("Value buffer doesn't fit into property table count") { - testClassProperty.count = 55; - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus:: - ErrorBufferViewSizeDoesNotMatchPropertyTableCount); - } -} -TEST_CASE("Test variable-length vecN array") { - Model model; - // clang-format off - std::vector> expected{ - { glm::ivec3(12, 34, -30), glm::ivec3(-2, 0, 1) }, - { glm::ivec3(1, 2, 8), }, + std::vector> expected{ + {12, 33, 11, 344, 112, 444, 1}, {}, - { glm::ivec3(-100, 84, 6), glm::ivec3(2, -2, -2), glm::ivec3(40, 61, 3) }, - { glm::ivec3(-1, 4, -7) }, - }; - // clang-format on - + {}, + {122, 23, 333, 12}, + {}, + {333, 311, 22, 34}, + {}, + {33, 1888, 233, 33019}}; size_t numOfElements = 0; for (const auto& expectedMember : expected) { numOfElements += expectedMember.size(); } - std::vector values(numOfElements * sizeof(glm::ivec3)); + std::vector values(numOfElements * sizeof(uint16_t)); std::vector offsets((expected.size() + 1) * sizeof(uint64_t)); uint64_t* offsetValue = reinterpret_cast(offsets.data()); for (size_t i = 0; i < expected.size(); ++i) { std::memcpy( values.data() + offsetValue[i], expected[i].data(), - expected[i].size() * sizeof(glm::ivec3)); - offsetValue[i + 1] = - offsetValue[i] + expected[i].size() * sizeof(glm::ivec3); + expected[i].size() * sizeof(uint16_t)); + offsetValue[i + 1] = offsetValue[i] + expected[i].size() * sizeof(uint16_t); } addBufferToModel(model, values); @@ -1239,9 +1796,10 @@ TEST_CASE("Test variable-length vecN array") { Schema& schema = metadata.schema.emplace(); Class& testClass = schema.classes["TestClass"]; ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; - testClassProperty.type = ClassProperty::Type::VEC3; - testClassProperty.componentType = ClassProperty::ComponentType::INT32; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::UINT16; testClassProperty.array = true; + testClassProperty.normalized = true; PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); propertyTable.classProperty = "TestClass"; @@ -1262,31 +1820,46 @@ TEST_CASE("Test variable-length vecN array") { const ClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE(classProperty); - REQUIRE(classProperty->type == ClassProperty::Type::VEC3); - REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT32); + REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT16); REQUIRE(classProperty->array); REQUIRE(!classProperty->count); + REQUIRE(classProperty->normalized); SECTION("Access the correct type") { - PropertyTablePropertyView> property = - view.getPropertyView>( + PropertyTablePropertyView, true> property = + view.getPropertyView, true>( "TestClassProperty"); REQUIRE(property.status() == PropertyTablePropertyViewStatus::Valid); for (size_t i = 0; i < expected.size(); ++i) { - PropertyArrayView valueMember = + PropertyArrayView array = property.getRaw(static_cast(i)); - REQUIRE(valueMember.size() == static_cast(expected[i].size())); + auto maybeArray = property.get(static_cast(i)); + REQUIRE(maybeArray); + REQUIRE(maybeArray->size() == array.size()); + REQUIRE(array.size() == static_cast(expected[i].size())); + for (size_t j = 0; j < expected[i].size(); ++j) { - REQUIRE(expected[i][j] == valueMember[static_cast(j)]); + auto value = array[static_cast(j)]; + REQUIRE(expected[i][j] == value); + REQUIRE((*maybeArray)[static_cast(j)] == normalize(value)); } } } + SECTION("Incorrectly non-normalized") { + PropertyTablePropertyView> property = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + property.status() == + PropertyTablePropertyViewStatus::ErrorInvalidNormalization); + } + SECTION("Wrong offset type") { propertyTableProperty.arrayOffsetType = PropertyTableProperty::ArrayOffsetType::UINT8; - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>( + PropertyTablePropertyView, true> arrayProperty = + view.getPropertyView, true>( "TestClassProperty"); REQUIRE( arrayProperty.status() == @@ -1295,7 +1868,7 @@ TEST_CASE("Test variable-length vecN array") { propertyTableProperty.arrayOffsetType = PropertyTableProperty::ArrayOffsetType::UINT16; - arrayProperty = view.getPropertyView>( + arrayProperty = view.getPropertyView, true>( "TestClassProperty"); REQUIRE( arrayProperty.status() == @@ -1303,7 +1876,7 @@ TEST_CASE("Test variable-length vecN array") { ErrorBufferViewSizeDoesNotMatchPropertyTableCount); propertyTableProperty.arrayOffsetType = "NONSENSE"; - arrayProperty = view.getPropertyView>( + arrayProperty = view.getPropertyView, true>( "TestClassProperty"); REQUIRE( arrayProperty.status() == @@ -1312,7 +1885,7 @@ TEST_CASE("Test variable-length vecN array") { propertyTableProperty.arrayOffsetType = ""; propertyTableProperty.stringOffsetType = PropertyTableProperty::StringOffsetType::UINT64; - arrayProperty = view.getPropertyView>( + arrayProperty = view.getPropertyView, true>( "TestClassProperty"); REQUIRE( arrayProperty.status() == @@ -1325,8 +1898,8 @@ TEST_CASE("Test variable-length vecN array") { uint64_t* offset = reinterpret_cast( model.buffers[offsetBufferIndex].cesium.data.data()); offset[propertyTable.count] = 0; - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>( + PropertyTablePropertyView, true> arrayProperty = + view.getPropertyView, true>( "TestClassProperty"); REQUIRE( arrayProperty.status() == @@ -1338,8 +1911,8 @@ TEST_CASE("Test variable-length vecN array") { model.buffers[offsetBufferIndex].cesium.data.data()); offset[propertyTable.count] = static_cast(model.buffers[valueBufferIndex].byteLength + 4); - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>( + PropertyTablePropertyView, true> arrayProperty = + view.getPropertyView, true>( "TestClassProperty"); REQUIRE( arrayProperty.status() == @@ -1348,8 +1921,8 @@ TEST_CASE("Test variable-length vecN array") { SECTION("Count and offset buffer are both present") { testClassProperty.count = 3; - PropertyTablePropertyView> property = - view.getPropertyView>( + PropertyTablePropertyView, true> property = + view.getPropertyView, true>( "TestClassProperty"); REQUIRE( property.status() == @@ -1357,30 +1930,16 @@ TEST_CASE("Test variable-length vecN array") { } } -TEST_CASE("Test fixed-length matN array") { +TEST_CASE("Test fixed-length vecN array") { Model model; - // clang-format off - std::vector values = { - glm::i32mat2x2( - 12, 34, - -30, 20), - glm::i32mat2x2( - -2, -2, - 0, 1), - glm::i32mat2x2( - 1, 2, - 8, 5), - glm::i32mat2x2( - -100, 3, - 84, 6), - glm::i32mat2x2( - 2, 12, - -2, -2), - glm::i32mat2x2( - 40, 61, - 7, -3), + std::vector values = { + glm::ivec3(12, 34, -30), + glm::ivec3(-2, 0, 1), + glm::ivec3(1, 2, 8), + glm::ivec3(-100, 84, 6), + glm::ivec3(2, -2, -2), + glm::ivec3(40, 61, 3), }; - // clang-format on addBufferToModel(model, values); size_t valueBufferViewIndex = model.bufferViews.size() - 1; @@ -1391,7 +1950,7 @@ TEST_CASE("Test fixed-length matN array") { Schema& schema = metadata.schema.emplace(); Class& testClass = schema.classes["TestClass"]; ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; - testClassProperty.type = ClassProperty::Type::MAT2; + testClassProperty.type = ClassProperty::Type::VEC3; testClassProperty.componentType = ClassProperty::ComponentType::INT32; testClassProperty.array = true; testClassProperty.count = 2; @@ -1413,21 +1972,26 @@ TEST_CASE("Test fixed-length matN array") { const ClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE(classProperty); - REQUIRE(classProperty->type == ClassProperty::Type::MAT2); + REQUIRE(classProperty->type == ClassProperty::Type::VEC3); REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT32); REQUIRE(classProperty->array); REQUIRE(classProperty->count == 2); + REQUIRE(!classProperty->normalized); SECTION("Access the right type") { - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>( + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>( "TestClassProperty"); REQUIRE(arrayProperty.status() == PropertyTablePropertyViewStatus::Valid); for (int64_t i = 0; i < arrayProperty.size(); ++i) { - PropertyArrayView member = arrayProperty.getRaw(i); - for (int64_t j = 0; j < member.size(); ++j) { - REQUIRE(member[j] == values[static_cast(i * 2 + j)]); + PropertyArrayView array = arrayProperty.getRaw(i); + auto maybeArray = arrayProperty.get(i); + REQUIRE(maybeArray); + + for (int64_t j = 0; j < array.size(); ++j) { + REQUIRE(array[j] == values[static_cast(i * 2 + j)]); + REQUIRE((*maybeArray)[j] == array[j]); } } } @@ -1448,27 +2012,36 @@ TEST_CASE("Test fixed-length matN array") { } SECTION("Wrong component type") { - PropertyTablePropertyView> - u32mat2x2ArrayInvalid = - view.getPropertyView>( - "TestClassProperty"); + PropertyTablePropertyView> uvec3ArrayInvalid = + view.getPropertyView>( + "TestClassProperty"); REQUIRE( - u32mat2x2ArrayInvalid.status() == + uvec3ArrayInvalid.status() == PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); } SECTION("Not an array type") { - PropertyTablePropertyView ivec3Invalid = - view.getPropertyView("TestClassProperty"); + PropertyTablePropertyView ivec3Invalid = + view.getPropertyView("TestClassProperty"); REQUIRE( ivec3Invalid.status() == PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); } + SECTION("Incorrect normalization") { + PropertyTablePropertyView, true> + normalizedInvalid = + view.getPropertyView, true>( + "TestClassProperty"); + REQUIRE( + normalizedInvalid.status() == + PropertyTablePropertyViewStatus::ErrorInvalidNormalization); + } + SECTION("Buffer size is not a multiple of type size") { model.bufferViews[valueBufferViewIndex].byteLength = 13; - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>( + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>( "TestClassProperty"); REQUIRE( arrayProperty.status() == @@ -1476,10 +2049,10 @@ TEST_CASE("Test fixed-length matN array") { ErrorBufferViewSizeNotDivisibleByTypeSize); } - SECTION("Negative component count") { + SECTION("Negative count") { testClassProperty.count = -1; - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>( + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>( "TestClassProperty"); REQUIRE( arrayProperty.status() == PropertyTablePropertyViewStatus:: @@ -1488,9 +2061,9 @@ TEST_CASE("Test fixed-length matN array") { SECTION("Value buffer doesn't fit into property table count") { testClassProperty.count = 55; - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>( - "TestClassProperty"); + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>( + "TestClassProperty"); REQUIRE( arrayProperty.status() == PropertyTablePropertyViewStatus:: @@ -1498,53 +2071,182 @@ TEST_CASE("Test fixed-length matN array") { } } -TEST_CASE("Test variable-length matN array") { +TEST_CASE("Test fixed-length vecN array (normalized)") { + Model model; + std::vector values = { + glm::ivec3(12, 34, -30), + glm::ivec3(-2, 0, 1), + glm::ivec3(1, 2, 8), + glm::ivec3(-100, 84, 6), + glm::ivec3(2, -2, -2), + glm::ivec3(40, 61, 3), + }; + + addBufferToModel(model, values); + size_t valueBufferViewIndex = model.bufferViews.size() - 1; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::VEC3; + testClassProperty.componentType = ClassProperty::ComponentType::INT32; + testClassProperty.array = true; + testClassProperty.count = 2; + testClassProperty.normalized = true; + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast( + values.size() / static_cast(testClassProperty.count.value())); + + PropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = + static_cast(model.bufferViews.size() - 1); + + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::VEC3); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT32); + REQUIRE(classProperty->array); + REQUIRE(classProperty->count == 2); + REQUIRE(classProperty->normalized); + + SECTION("Access the right type") { + PropertyTablePropertyView, true> + arrayProperty = + view.getPropertyView, true>( + "TestClassProperty"); + REQUIRE(arrayProperty.status() == PropertyTablePropertyViewStatus::Valid); + + for (int64_t i = 0; i < arrayProperty.size(); ++i) { + PropertyArrayView array = arrayProperty.getRaw(i); + auto maybeArray = arrayProperty.get(i); + REQUIRE(maybeArray); + + for (int64_t j = 0; j < array.size(); ++j) { + REQUIRE(array[j] == values[static_cast(i * 2 + j)]); + REQUIRE((*maybeArray)[j] == normalize(array[j])); + } + } + } + + SECTION("Wrong type") { + PropertyTablePropertyView, true> + int32ArrayInvalid = + view.getPropertyView, true>( + "TestClassProperty"); + REQUIRE( + int32ArrayInvalid.status() == + PropertyTablePropertyViewStatus::ErrorTypeMismatch); + + PropertyTablePropertyView, true> + ivec2ArrayInvalid = + view.getPropertyView, true>( + "TestClassProperty"); + REQUIRE( + ivec2ArrayInvalid.status() == + PropertyTablePropertyViewStatus::ErrorTypeMismatch); + } + + SECTION("Wrong component type") { + PropertyTablePropertyView, true> + uvec3ArrayInvalid = + view.getPropertyView, true>( + "TestClassProperty"); + REQUIRE( + uvec3ArrayInvalid.status() == + PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); + } + + SECTION("Not an array type") { + PropertyTablePropertyView ivec3Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + ivec3Invalid.status() == + PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); + } + + SECTION("Incorrect non-normalization") { + PropertyTablePropertyView> normalizedInvalid = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + normalizedInvalid.status() == + PropertyTablePropertyViewStatus::ErrorInvalidNormalization); + } + + SECTION("Buffer size is not a multiple of type size") { + model.bufferViews[valueBufferViewIndex].byteLength = 13; + PropertyTablePropertyView, true> + arrayProperty = + view.getPropertyView, true>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus:: + ErrorBufferViewSizeNotDivisibleByTypeSize); + } + + SECTION("Negative count") { + testClassProperty.count = -1; + PropertyTablePropertyView, true> + arrayProperty = + view.getPropertyView, true>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == PropertyTablePropertyViewStatus:: + ErrorArrayCountAndOffsetBufferDontExist); + } + + SECTION("Value buffer doesn't fit into property table count") { + testClassProperty.count = 55; + PropertyTablePropertyView, true> + arrayProperty = + view.getPropertyView, true>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + } +} + +TEST_CASE("Test variable-length vecN array") { Model model; // clang-format off - std::vector data0{ - glm::i32mat2x2( - 3, -2, - 1, 0), - glm::i32mat2x2( - 40, 3, - 8, -9) - }; - std::vector data1{ - glm::i32mat2x2( - 1, 10, - 7, 8), - }; - std::vector data2{ - glm::i32mat2x2( - 18, 0, - 1, 17), - glm::i32mat2x2( - -4, -2, - -9, 1), - glm::i32mat2x2( - 1, 8, - -99, 3), - }; + std::vector> expected{ + { glm::ivec3(12, 34, -30), glm::ivec3(-2, 0, 1) }, + { glm::ivec3(1, 2, 8), }, + {}, + { glm::ivec3(-100, 84, 6), glm::ivec3(2, -2, -2), glm::ivec3(40, 61, 3) }, + { glm::ivec3(-1, 4, -7) }, + }; // clang-format on - std::vector> - expected{data0, {}, data1, data2, {}}; - size_t numOfElements = 0; for (const auto& expectedMember : expected) { numOfElements += expectedMember.size(); } - std::vector values(numOfElements * sizeof(glm::i32mat2x2)); + std::vector values(numOfElements * sizeof(glm::ivec3)); std::vector offsets((expected.size() + 1) * sizeof(uint64_t)); uint64_t* offsetValue = reinterpret_cast(offsets.data()); for (size_t i = 0; i < expected.size(); ++i) { std::memcpy( values.data() + offsetValue[i], expected[i].data(), - expected[i].size() * sizeof(glm::i32mat2x2)); + expected[i].size() * sizeof(glm::ivec3)); offsetValue[i + 1] = - offsetValue[i] + expected[i].size() * sizeof(glm::i32mat2x2); + offsetValue[i] + expected[i].size() * sizeof(glm::ivec3); } addBufferToModel(model, values); @@ -1561,7 +2263,7 @@ TEST_CASE("Test variable-length matN array") { Schema& schema = metadata.schema.emplace(); Class& testClass = schema.classes["TestClass"]; ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; - testClassProperty.type = ClassProperty::Type::MAT2; + testClassProperty.type = ClassProperty::Type::VEC3; testClassProperty.componentType = ClassProperty::ComponentType::INT32; testClassProperty.array = true; @@ -1584,31 +2286,45 @@ TEST_CASE("Test variable-length matN array") { const ClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE(classProperty); - REQUIRE(classProperty->type == ClassProperty::Type::MAT2); + REQUIRE(classProperty->type == ClassProperty::Type::VEC3); REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT32); REQUIRE(classProperty->array); REQUIRE(!classProperty->count); + REQUIRE(!classProperty->normalized); SECTION("Access the correct type") { - PropertyTablePropertyView> property = - view.getPropertyView>( + PropertyTablePropertyView> property = + view.getPropertyView>( "TestClassProperty"); REQUIRE(property.status() == PropertyTablePropertyViewStatus::Valid); for (size_t i = 0; i < expected.size(); ++i) { - PropertyArrayView valueMember = + PropertyArrayView array = property.getRaw(static_cast(i)); - REQUIRE(valueMember.size() == static_cast(expected[i].size())); + auto maybeArray = property.get(i); + REQUIRE(maybeArray); + REQUIRE(array.size() == static_cast(expected[i].size())); for (size_t j = 0; j < expected[i].size(); ++j) { - REQUIRE(expected[i][j] == valueMember[static_cast(j)]); + auto value = array[static_cast(j)]; + REQUIRE(expected[i][j] == value); + REQUIRE((*maybeArray)[j] == value); } } } + SECTION("Incorrectly normalized") { + PropertyTablePropertyView, true> property = + view.getPropertyView, true>( + "TestClassProperty"); + REQUIRE( + property.status() == + PropertyTablePropertyViewStatus::ErrorInvalidNormalization); + } + SECTION("Wrong offset type") { propertyTableProperty.arrayOffsetType = PropertyTableProperty::ArrayOffsetType::UINT8; - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>( + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>( "TestClassProperty"); REQUIRE( arrayProperty.status() == @@ -1617,7 +2333,7 @@ TEST_CASE("Test variable-length matN array") { propertyTableProperty.arrayOffsetType = PropertyTableProperty::ArrayOffsetType::UINT16; - arrayProperty = view.getPropertyView>( + arrayProperty = view.getPropertyView>( "TestClassProperty"); REQUIRE( arrayProperty.status() == @@ -1625,7 +2341,7 @@ TEST_CASE("Test variable-length matN array") { ErrorBufferViewSizeDoesNotMatchPropertyTableCount); propertyTableProperty.arrayOffsetType = "NONSENSE"; - arrayProperty = view.getPropertyView>( + arrayProperty = view.getPropertyView>( "TestClassProperty"); REQUIRE( arrayProperty.status() == @@ -1634,7 +2350,7 @@ TEST_CASE("Test variable-length matN array") { propertyTableProperty.arrayOffsetType = ""; propertyTableProperty.stringOffsetType = PropertyTableProperty::StringOffsetType::UINT64; - arrayProperty = view.getPropertyView>( + arrayProperty = view.getPropertyView>( "TestClassProperty"); REQUIRE( arrayProperty.status() == @@ -1647,8 +2363,8 @@ TEST_CASE("Test variable-length matN array") { uint64_t* offset = reinterpret_cast( model.buffers[offsetBufferIndex].cesium.data.data()); offset[propertyTable.count] = 0; - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>( + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>( "TestClassProperty"); REQUIRE( arrayProperty.status() == @@ -1660,8 +2376,8 @@ TEST_CASE("Test variable-length matN array") { model.buffers[offsetBufferIndex].cesium.data.data()); offset[propertyTable.count] = static_cast(model.buffers[valueBufferIndex].byteLength + 4); - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>( + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>( "TestClassProperty"); REQUIRE( arrayProperty.status() == @@ -1670,8 +2386,8 @@ TEST_CASE("Test variable-length matN array") { SECTION("Count and offset buffer are both present") { testClassProperty.count = 3; - PropertyTablePropertyView> property = - view.getPropertyView>( + PropertyTablePropertyView> property = + view.getPropertyView>( "TestClassProperty"); REQUIRE( property.status() == @@ -1679,35 +2395,42 @@ TEST_CASE("Test variable-length matN array") { } } -TEST_CASE("Test fixed-length boolean array") { +TEST_CASE("Test variable-length vecN array (normalized)") { Model model; + // clang-format off + std::vector> expected{ + { glm::ivec3(12, 34, -30), glm::ivec3(-2, 0, 1) }, + { glm::ivec3(1, 2, 8), }, + {}, + { glm::ivec3(-100, 84, 6), glm::ivec3(2, -2, -2), glm::ivec3(40, 61, 3) }, + { glm::ivec3(-1, 4, -7) }, + }; + // clang-format on - std::vector expected = { - true, - false, - false, - true, - false, - false, - true, - true, - true, - false, - false, - true}; - std::vector values; - size_t requiredBytesSize = static_cast( - glm::ceil(static_cast(expected.size()) / 8.0)); - values.resize(requiredBytesSize); + size_t numOfElements = 0; + for (const auto& expectedMember : expected) { + numOfElements += expectedMember.size(); + } + + std::vector values(numOfElements * sizeof(glm::ivec3)); + std::vector offsets((expected.size() + 1) * sizeof(uint64_t)); + uint64_t* offsetValue = reinterpret_cast(offsets.data()); for (size_t i = 0; i < expected.size(); ++i) { - uint8_t expectedValue = expected[i]; - size_t byteIndex = i / 8; - size_t bitIndex = i % 8; - values[byteIndex] = - static_cast((expectedValue << bitIndex) | values[byteIndex]); + std::memcpy( + values.data() + offsetValue[i], + expected[i].data(), + expected[i].size() * sizeof(glm::ivec3)); + offsetValue[i + 1] = + offsetValue[i] + expected[i].size() * sizeof(glm::ivec3); } addBufferToModel(model, values); + size_t valueBufferIndex = model.buffers.size() - 1; + size_t valueBufferViewIndex = model.bufferViews.size() - 1; + + addBufferToModel(model, offsets); + size_t offsetBufferIndex = model.buffers.size() - 1; + size_t offsetBufferViewIndex = model.bufferViews.size() - 1; ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); @@ -1715,138 +2438,14 @@ TEST_CASE("Test fixed-length boolean array") { Schema& schema = metadata.schema.emplace(); Class& testClass = schema.classes["TestClass"]; ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; - testClassProperty.type = ClassProperty::Type::BOOLEAN; + testClassProperty.type = ClassProperty::Type::VEC3; + testClassProperty.componentType = ClassProperty::ComponentType::INT32; testClassProperty.array = true; - testClassProperty.count = 3; + testClassProperty.normalized = true; PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); propertyTable.classProperty = "TestClass"; - propertyTable.count = static_cast( - expected.size() / static_cast(testClassProperty.count.value())); - - PropertyTableProperty& propertyTableProperty = - propertyTable.properties["TestClassProperty"]; - propertyTableProperty.values = - static_cast(model.bufferViews.size() - 1); - - PropertyTableView view(model, propertyTable); - REQUIRE(view.status() == PropertyTableViewStatus::Valid); - REQUIRE(view.size() == propertyTable.count); - - const ClassProperty* classProperty = - view.getClassProperty("TestClassProperty"); - REQUIRE(classProperty); - REQUIRE(classProperty->type == ClassProperty::Type::BOOLEAN); - REQUIRE(classProperty->array); - REQUIRE(classProperty->count == 3); - - SECTION("Access correct type") { - PropertyTablePropertyView> boolArrayProperty = - view.getPropertyView>("TestClassProperty"); - REQUIRE( - boolArrayProperty.status() == PropertyTablePropertyViewStatus::Valid); - REQUIRE(boolArrayProperty.size() == propertyTable.count); - REQUIRE(boolArrayProperty.size() > 0); - for (int64_t i = 0; i < boolArrayProperty.size(); ++i) { - PropertyArrayView valueMember = boolArrayProperty.getRaw(i); - for (int64_t j = 0; j < valueMember.size(); ++j) { - REQUIRE(valueMember[j] == expected[static_cast(i * 3 + j)]); - } - } - } - - SECTION("Wrong type") { - PropertyTablePropertyView> uint8ArrayInvalid = - view.getPropertyView>("TestClassProperty"); - REQUIRE( - uint8ArrayInvalid.status() == - PropertyTablePropertyViewStatus::ErrorTypeMismatch); - } - - SECTION("View is not array type") { - PropertyTablePropertyView boolInvalid = - view.getPropertyView("TestClassProperty"); - REQUIRE( - boolInvalid.status() == - PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); - } - - SECTION("Value buffer doesn't have enough required bytes") { - testClassProperty.count = 11; - PropertyTablePropertyView> boolArrayProperty = - view.getPropertyView>("TestClassProperty"); - REQUIRE( - boolArrayProperty.status() == - PropertyTablePropertyViewStatus:: - ErrorBufferViewSizeDoesNotMatchPropertyTableCount); - } - - SECTION("Count is negative") { - testClassProperty.count = -1; - PropertyTablePropertyView> boolArrayProperty = - view.getPropertyView>("TestClassProperty"); - REQUIRE( - boolArrayProperty.status() == - PropertyTablePropertyViewStatus:: - ErrorArrayCountAndOffsetBufferDontExist); - } -} - -TEST_CASE("Test variable-length boolean array") { - Model model; - - std::vector> expected{ - {true, false, true, true, false, true, true}, - {}, - {}, - {}, - {false, false, false, false}, - {true, false, true}, - {false}, - {true, true, true, true, true, false, false}}; - size_t numOfElements = 0; - for (const auto& expectedMember : expected) { - numOfElements += expectedMember.size(); - } - - size_t requiredBytesSize = - static_cast(glm::ceil(static_cast(numOfElements) / 8.0)); - std::vector values(requiredBytesSize); - std::vector offsets((expected.size() + 1) * sizeof(uint64_t)); - uint64_t* offsetValue = reinterpret_cast(offsets.data()); - size_t indexSoFar = 0; - for (size_t i = 0; i < expected.size(); ++i) { - for (size_t j = 0; j < expected[i].size(); ++j) { - uint8_t expectedValue = expected[i][j]; - size_t byteIndex = indexSoFar / 8; - size_t bitIndex = indexSoFar % 8; - values[byteIndex] = static_cast( - (expectedValue << bitIndex) | static_cast(values[byteIndex])); - ++indexSoFar; - } - offsetValue[i + 1] = offsetValue[i] + expected[i].size(); - } - - addBufferToModel(model, values); - size_t valueBufferIndex = model.buffers.size() - 1; - size_t valueBufferViewIndex = model.bufferViews.size() - 1; - - addBufferToModel(model, offsets); - size_t offsetBufferIndex = model.buffers.size() - 1; - size_t offsetBufferViewIndex = model.bufferViews.size() - 1; - - ExtensionModelExtStructuralMetadata& metadata = - model.addExtension(); - - Schema& schema = metadata.schema.emplace(); - Class& testClass = schema.classes["TestClass"]; - ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; - testClassProperty.type = ClassProperty::Type::BOOLEAN; - testClassProperty.array = true; - - PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); - propertyTable.classProperty = "TestClass"; - propertyTable.count = static_cast(expected.size()); + propertyTable.count = static_cast(expected.size()); PropertyTableProperty& propertyTableProperty = propertyTable.properties["TestClassProperty"]; @@ -1863,30 +2462,47 @@ TEST_CASE("Test variable-length boolean array") { const ClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE(classProperty); - REQUIRE(classProperty->type == ClassProperty::Type::BOOLEAN); + REQUIRE(classProperty->type == ClassProperty::Type::VEC3); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT32); REQUIRE(classProperty->array); REQUIRE(!classProperty->count); + REQUIRE(classProperty->normalized); - SECTION("Access correct type") { - PropertyTablePropertyView> boolArrayProperty = - view.getPropertyView>("TestClassProperty"); - REQUIRE( - boolArrayProperty.status() == PropertyTablePropertyViewStatus::Valid); + SECTION("Access the correct type") { + PropertyTablePropertyView, true> property = + view.getPropertyView, true>( + "TestClassProperty"); + REQUIRE(property.status() == PropertyTablePropertyViewStatus::Valid); for (size_t i = 0; i < expected.size(); ++i) { - PropertyArrayView arrayMember = - boolArrayProperty.getRaw(static_cast(i)); - REQUIRE(arrayMember.size() == static_cast(expected[i].size())); + PropertyArrayView array = + property.getRaw(static_cast(i)); + auto maybeArray = property.get(i); + REQUIRE(maybeArray); + REQUIRE(array.size() == static_cast(expected[i].size())); for (size_t j = 0; j < expected[i].size(); ++j) { - REQUIRE(expected[i][j] == arrayMember[static_cast(j)]); + auto value = array[static_cast(j)]; + REQUIRE(expected[i][j] == value); + REQUIRE((*maybeArray)[j] == normalize(value)); } } } + SECTION("Incorrectly non-normalized") { + PropertyTablePropertyView> property = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + property.status() == + PropertyTablePropertyViewStatus::ErrorInvalidNormalization); + } + SECTION("Wrong offset type") { propertyTableProperty.arrayOffsetType = PropertyTableProperty::ArrayOffsetType::UINT8; - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>("TestClassProperty"); + PropertyTablePropertyView, true> + arrayProperty = + view.getPropertyView, true>( + "TestClassProperty"); REQUIRE( arrayProperty.status() == PropertyTablePropertyViewStatus:: @@ -1894,16 +2510,16 @@ TEST_CASE("Test variable-length boolean array") { propertyTableProperty.arrayOffsetType = PropertyTableProperty::ArrayOffsetType::UINT16; - arrayProperty = - view.getPropertyView>("TestClassProperty"); + arrayProperty = view.getPropertyView, true>( + "TestClassProperty"); REQUIRE( arrayProperty.status() == PropertyTablePropertyViewStatus:: ErrorBufferViewSizeDoesNotMatchPropertyTableCount); propertyTableProperty.arrayOffsetType = "NONSENSE"; - arrayProperty = - view.getPropertyView>("TestClassProperty"); + arrayProperty = view.getPropertyView, true>( + "TestClassProperty"); REQUIRE( arrayProperty.status() == PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); @@ -1911,8 +2527,8 @@ TEST_CASE("Test variable-length boolean array") { propertyTableProperty.arrayOffsetType = ""; propertyTableProperty.stringOffsetType = PropertyTableProperty::StringOffsetType::UINT64; - arrayProperty = - view.getPropertyView>("TestClassProperty"); + arrayProperty = view.getPropertyView, true>( + "TestClassProperty"); REQUIRE( arrayProperty.status() == PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); @@ -1924,8 +2540,10 @@ TEST_CASE("Test variable-length boolean array") { uint64_t* offset = reinterpret_cast( model.buffers[offsetBufferIndex].cesium.data.data()); offset[propertyTable.count] = 0; - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>("TestClassProperty"); + PropertyTablePropertyView, true> + arrayProperty = + view.getPropertyView, true>( + "TestClassProperty"); REQUIRE( arrayProperty.status() == PropertyTablePropertyViewStatus::ErrorArrayOffsetsNotSorted); @@ -1934,82 +2552,76 @@ TEST_CASE("Test variable-length boolean array") { SECTION("Offset value points outside of value buffer") { uint64_t* offset = reinterpret_cast( model.buffers[offsetBufferIndex].cesium.data.data()); - offset[propertyTable.count] = static_cast( - model.buffers[valueBufferIndex].byteLength * 8 + 20); - PropertyTablePropertyView> arrayProperty = - view.getPropertyView>("TestClassProperty"); + offset[propertyTable.count] = + static_cast(model.buffers[valueBufferIndex].byteLength + 4); + PropertyTablePropertyView, true> + arrayProperty = + view.getPropertyView, true>( + "TestClassProperty"); REQUIRE( arrayProperty.status() == PropertyTablePropertyViewStatus::ErrorArrayOffsetOutOfBounds); } - SECTION("Count and offset buffer both present") { + SECTION("Count and offset buffer are both present") { testClassProperty.count = 3; - PropertyTablePropertyView> boolArrayProperty = - view.getPropertyView>("TestClassProperty"); + PropertyTablePropertyView, true> property = + view.getPropertyView, true>( + "TestClassProperty"); REQUIRE( - boolArrayProperty.status() == + property.status() == PropertyTablePropertyViewStatus::ErrorArrayCountAndOffsetBufferCoexist); } } -TEST_CASE("Test fixed-length arrays of strings") { +TEST_CASE("Test fixed-length matN array") { Model model; - - std::vector expected{ - "What's up", - "Breaking news!!! Aliens no longer attacks the US first", - "But they still abduct my cows! Those milk thiefs! 👽 🐮", - "I'm not crazy. My mother had me tested 🤪", - "I love you, meat bags! ❤️", - "Book in the freezer"}; - - size_t totalBytes = 0; - for (const std::string& expectedValue : expected) { - totalBytes += expectedValue.size(); - } - - std::vector offsets((expected.size() + 1) * sizeof(uint32_t)); - std::vector values(totalBytes); - uint32_t* offsetValue = reinterpret_cast(offsets.data()); - for (size_t i = 0; i < expected.size(); ++i) { - const std::string& expectedValue = expected[i]; - std::memcpy( - values.data() + offsetValue[i], - expectedValue.c_str(), - expectedValue.size()); - offsetValue[i + 1] = - offsetValue[i] + static_cast(expectedValue.size()); - } + // clang-format off + std::vector values = { + glm::i32mat2x2( + 12, 34, + -30, 20), + glm::i32mat2x2( + -2, -2, + 0, 1), + glm::i32mat2x2( + 1, 2, + 8, 5), + glm::i32mat2x2( + -100, 3, + 84, 6), + glm::i32mat2x2( + 2, 12, + -2, -2), + glm::i32mat2x2( + 40, 61, + 7, -3), + }; + // clang-format on addBufferToModel(model, values); size_t valueBufferViewIndex = model.bufferViews.size() - 1; - addBufferToModel(model, offsets); - size_t offsetBufferViewIndex = model.bufferViews.size() - 1; - ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); Schema& schema = metadata.schema.emplace(); Class& testClass = schema.classes["TestClass"]; ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; - testClassProperty.type = ClassProperty::Type::STRING; + testClassProperty.type = ClassProperty::Type::MAT2; + testClassProperty.componentType = ClassProperty::ComponentType::INT32; testClassProperty.array = true; testClassProperty.count = 2; PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); propertyTable.classProperty = "TestClass"; propertyTable.count = static_cast( - expected.size() / static_cast(testClassProperty.count.value())); + values.size() / static_cast(testClassProperty.count.value())); PropertyTableProperty& propertyTableProperty = propertyTable.properties["TestClassProperty"]; - propertyTableProperty.stringOffsetType = - PropertyTableProperty::StringOffsetType::UINT32; - propertyTableProperty.values = static_cast(valueBufferViewIndex); - propertyTableProperty.stringOffsets = - static_cast(offsetBufferViewIndex); + propertyTableProperty.values = + static_cast(model.bufferViews.size() - 1); PropertyTableView view(model, propertyTable); REQUIRE(view.status() == PropertyTableViewStatus::Valid); @@ -2018,32 +2630,1048 @@ TEST_CASE("Test fixed-length arrays of strings") { const ClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE(classProperty); - REQUIRE(classProperty->type == ClassProperty::Type::STRING); + REQUIRE(classProperty->type == ClassProperty::Type::MAT2); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT32); REQUIRE(classProperty->array); REQUIRE(classProperty->count == 2); + REQUIRE(!classProperty->normalized); - SECTION("Access correct type") { - PropertyTablePropertyView> - stringProperty = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE(stringProperty.status() == PropertyTablePropertyViewStatus::Valid); - REQUIRE(stringProperty.size() == 3); - - PropertyArrayView v0 = stringProperty.getRaw(0); - REQUIRE(v0.size() == 2); - REQUIRE(v0[0] == "What's up"); - REQUIRE(v0[1] == "Breaking news!!! Aliens no longer attacks the US first"); + SECTION("Access the right type") { + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE(arrayProperty.status() == PropertyTablePropertyViewStatus::Valid); - PropertyArrayView v1 = stringProperty.getRaw(1); - REQUIRE(v1.size() == 2); - REQUIRE(v1[0] == "But they still abduct my cows! Those milk thiefs! 👽 🐮"); + for (int64_t i = 0; i < arrayProperty.size(); ++i) { + PropertyArrayView member = arrayProperty.getRaw(i); + for (int64_t j = 0; j < member.size(); ++j) { + REQUIRE(member[j] == values[static_cast(i * 2 + j)]); + } + } + } + + SECTION("Wrong type") { + PropertyTablePropertyView> int32ArrayInvalid = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + int32ArrayInvalid.status() == + PropertyTablePropertyViewStatus::ErrorTypeMismatch); + + PropertyTablePropertyView> ivec2ArrayInvalid = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + ivec2ArrayInvalid.status() == + PropertyTablePropertyViewStatus::ErrorTypeMismatch); + } + + SECTION("Wrong component type") { + PropertyTablePropertyView> + u32mat2x2ArrayInvalid = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + u32mat2x2ArrayInvalid.status() == + PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); + } + + SECTION("Not an array type") { + PropertyTablePropertyView ivec3Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + ivec3Invalid.status() == + PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); + } + + SECTION("Incorrect normalization") { + PropertyTablePropertyView, true> + normalizedInvalid = + view.getPropertyView, true>( + "TestClassProperty"); + REQUIRE( + normalizedInvalid.status() == + PropertyTablePropertyViewStatus::ErrorInvalidNormalization); + } + + SECTION("Buffer size is not a multiple of type size") { + model.bufferViews[valueBufferViewIndex].byteLength = 13; + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus:: + ErrorBufferViewSizeNotDivisibleByTypeSize); + } + + SECTION("Negative count") { + testClassProperty.count = -1; + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == PropertyTablePropertyViewStatus:: + ErrorArrayCountAndOffsetBufferDontExist); + } + + SECTION("Value buffer doesn't fit into property table count") { + testClassProperty.count = 55; + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + } +} + +TEST_CASE("Test fixed-length matN array (normalized)") { + Model model; + // clang-format off + std::vector values = { + glm::i32mat2x2( + 12, 34, + -30, 20), + glm::i32mat2x2( + -2, -2, + 0, 1), + glm::i32mat2x2( + 1, 2, + 8, 5), + glm::i32mat2x2( + -100, 3, + 84, 6), + glm::i32mat2x2( + 2, 12, + -2, -2), + glm::i32mat2x2( + 40, 61, + 7, -3), + }; + // clang-format on + + addBufferToModel(model, values); + size_t valueBufferViewIndex = model.bufferViews.size() - 1; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::MAT2; + testClassProperty.componentType = ClassProperty::ComponentType::INT32; + testClassProperty.array = true; + testClassProperty.count = 2; + testClassProperty.normalized = true; + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast( + values.size() / static_cast(testClassProperty.count.value())); + + PropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = + static_cast(model.bufferViews.size() - 1); + + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::MAT2); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT32); + REQUIRE(classProperty->array); + REQUIRE(classProperty->count == 2); + REQUIRE(classProperty->normalized); + + SECTION("Access the right type") { + PropertyTablePropertyView, true> + arrayProperty = + view.getPropertyView, true>( + "TestClassProperty"); + REQUIRE(arrayProperty.status() == PropertyTablePropertyViewStatus::Valid); + + for (int64_t i = 0; i < arrayProperty.size(); ++i) { + PropertyArrayView array = arrayProperty.getRaw(i); + auto maybeArray = arrayProperty.get(i); + REQUIRE(maybeArray); + for (int64_t j = 0; j < array.size(); ++j) { + REQUIRE(array[j] == values[static_cast(i * 2 + j)]); + REQUIRE((*maybeArray)[j] == normalize(array[j])); + } + } + } + + SECTION("Wrong type") { + PropertyTablePropertyView, true> + int32ArrayInvalid = + view.getPropertyView, true>( + "TestClassProperty"); + REQUIRE( + int32ArrayInvalid.status() == + PropertyTablePropertyViewStatus::ErrorTypeMismatch); + + PropertyTablePropertyView, true> + ivec2ArrayInvalid = + view.getPropertyView, true>( + "TestClassProperty"); + REQUIRE( + ivec2ArrayInvalid.status() == + PropertyTablePropertyViewStatus::ErrorTypeMismatch); + } + + SECTION("Wrong component type") { + PropertyTablePropertyView, true> + u32mat2x2ArrayInvalid = + view.getPropertyView, true>( + "TestClassProperty"); + REQUIRE( + u32mat2x2ArrayInvalid.status() == + PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); + } + + SECTION("Not an array type") { + PropertyTablePropertyView ivec3Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + ivec3Invalid.status() == + PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); + } + + SECTION("Incorrect non-normalization") { + PropertyTablePropertyView> + nonNormalizedInvalid = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + nonNormalizedInvalid.status() == + PropertyTablePropertyViewStatus::ErrorInvalidNormalization); + } + + SECTION("Buffer size is not a multiple of type size") { + model.bufferViews[valueBufferViewIndex].byteLength = 13; + PropertyTablePropertyView, true> + arrayProperty = + view.getPropertyView, true>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus:: + ErrorBufferViewSizeNotDivisibleByTypeSize); + } + + SECTION("Negative count") { + testClassProperty.count = -1; + PropertyTablePropertyView, true> + arrayProperty = + view.getPropertyView, true>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == PropertyTablePropertyViewStatus:: + ErrorArrayCountAndOffsetBufferDontExist); + } + + SECTION("Value buffer doesn't fit into property table count") { + testClassProperty.count = 55; + PropertyTablePropertyView, true> + arrayProperty = + view.getPropertyView, true>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + } +} + +TEST_CASE("Test variable-length matN array") { + Model model; + // clang-format off + std::vector data0{ + glm::i32mat2x2( + 3, -2, + 1, 0), + glm::i32mat2x2( + 40, 3, + 8, -9) + }; + std::vector data1{ + glm::i32mat2x2( + 1, 10, + 7, 8), + }; + std::vector data2{ + glm::i32mat2x2( + 18, 0, + 1, 17), + glm::i32mat2x2( + -4, -2, + -9, 1), + glm::i32mat2x2( + 1, 8, + -99, 3), + }; + // clang-format on + + std::vector> + expected{data0, {}, data1, data2, {}}; + + size_t numOfElements = 0; + for (const auto& expectedMember : expected) { + numOfElements += expectedMember.size(); + } + + std::vector values(numOfElements * sizeof(glm::i32mat2x2)); + std::vector offsets((expected.size() + 1) * sizeof(uint64_t)); + uint64_t* offsetValue = reinterpret_cast(offsets.data()); + for (size_t i = 0; i < expected.size(); ++i) { + std::memcpy( + values.data() + offsetValue[i], + expected[i].data(), + expected[i].size() * sizeof(glm::i32mat2x2)); + offsetValue[i + 1] = + offsetValue[i] + expected[i].size() * sizeof(glm::i32mat2x2); + } + + addBufferToModel(model, values); + size_t valueBufferIndex = model.buffers.size() - 1; + size_t valueBufferViewIndex = model.bufferViews.size() - 1; + + addBufferToModel(model, offsets); + size_t offsetBufferIndex = model.buffers.size() - 1; + size_t offsetBufferViewIndex = model.bufferViews.size() - 1; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::MAT2; + testClassProperty.componentType = ClassProperty::ComponentType::INT32; + testClassProperty.array = true; + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast(expected.size()); + + PropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = static_cast(valueBufferViewIndex); + propertyTableProperty.arrayOffsets = + static_cast(offsetBufferViewIndex); + propertyTableProperty.arrayOffsetType = + PropertyTableProperty::ArrayOffsetType::UINT64; + + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::MAT2); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT32); + REQUIRE(classProperty->array); + REQUIRE(!classProperty->count); + REQUIRE(!classProperty->normalized); + + SECTION("Access the correct type") { + PropertyTablePropertyView> property = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE(property.status() == PropertyTablePropertyViewStatus::Valid); + for (size_t i = 0; i < expected.size(); ++i) { + PropertyArrayView array = + property.getRaw(static_cast(i)); + auto maybeArray = property.get(i); + REQUIRE(maybeArray); + REQUIRE(array.size() == static_cast(expected[i].size())); + for (size_t j = 0; j < expected[i].size(); ++j) { + auto value = array[static_cast(j)]; + REQUIRE(expected[i][j] == value); + REQUIRE((*maybeArray)[j] == value); + } + } + } + + SECTION("Incorrectly normalized") { + PropertyTablePropertyView, true> + property = + view.getPropertyView, true>( + "TestClassProperty"); + REQUIRE( + property.status() == + PropertyTablePropertyViewStatus::ErrorInvalidNormalization); + } + + SECTION("Wrong offset type") { + propertyTableProperty.arrayOffsetType = + PropertyTableProperty::ArrayOffsetType::UINT8; + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + + propertyTableProperty.arrayOffsetType = + PropertyTableProperty::ArrayOffsetType::UINT16; + arrayProperty = view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + + propertyTableProperty.arrayOffsetType = "NONSENSE"; + arrayProperty = view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); + + propertyTableProperty.arrayOffsetType = ""; + propertyTableProperty.stringOffsetType = + PropertyTableProperty::StringOffsetType::UINT64; + arrayProperty = view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); + } + + SECTION("Offset values are not sorted ascending") { + propertyTableProperty.arrayOffsetType = + PropertyTableProperty::ArrayOffsetType::UINT64; + uint64_t* offset = reinterpret_cast( + model.buffers[offsetBufferIndex].cesium.data.data()); + offset[propertyTable.count] = 0; + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus::ErrorArrayOffsetsNotSorted); + } + + SECTION("Offset value points outside of value buffer") { + uint64_t* offset = reinterpret_cast( + model.buffers[offsetBufferIndex].cesium.data.data()); + offset[propertyTable.count] = + static_cast(model.buffers[valueBufferIndex].byteLength + 4); + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus::ErrorArrayOffsetOutOfBounds); + } + + SECTION("Count and offset buffer are both present") { + testClassProperty.count = 3; + PropertyTablePropertyView> property = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + property.status() == + PropertyTablePropertyViewStatus::ErrorArrayCountAndOffsetBufferCoexist); + } +} + +TEST_CASE("Test variable-length matN array (normalized)") { + Model model; + // clang-format off + std::vector data0{ + glm::i32mat2x2( + 3, -2, + 1, 0), + glm::i32mat2x2( + 40, 3, + 8, -9) + }; + std::vector data1{ + glm::i32mat2x2( + 1, 10, + 7, 8), + }; + std::vector data2{ + glm::i32mat2x2( + 18, 0, + 1, 17), + glm::i32mat2x2( + -4, -2, + -9, 1), + glm::i32mat2x2( + 1, 8, + -99, 3), + }; + // clang-format on + + std::vector> + expected{data0, {}, data1, data2, {}}; + + size_t numOfElements = 0; + for (const auto& expectedMember : expected) { + numOfElements += expectedMember.size(); + } + + std::vector values(numOfElements * sizeof(glm::i32mat2x2)); + std::vector offsets((expected.size() + 1) * sizeof(uint64_t)); + uint64_t* offsetValue = reinterpret_cast(offsets.data()); + for (size_t i = 0; i < expected.size(); ++i) { + std::memcpy( + values.data() + offsetValue[i], + expected[i].data(), + expected[i].size() * sizeof(glm::i32mat2x2)); + offsetValue[i + 1] = + offsetValue[i] + expected[i].size() * sizeof(glm::i32mat2x2); + } + + addBufferToModel(model, values); + size_t valueBufferIndex = model.buffers.size() - 1; + size_t valueBufferViewIndex = model.bufferViews.size() - 1; + + addBufferToModel(model, offsets); + size_t offsetBufferIndex = model.buffers.size() - 1; + size_t offsetBufferViewIndex = model.bufferViews.size() - 1; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::MAT2; + testClassProperty.componentType = ClassProperty::ComponentType::INT32; + testClassProperty.array = true; + testClassProperty.normalized = true; + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast(expected.size()); + + PropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = static_cast(valueBufferViewIndex); + propertyTableProperty.arrayOffsets = + static_cast(offsetBufferViewIndex); + propertyTableProperty.arrayOffsetType = + PropertyTableProperty::ArrayOffsetType::UINT64; + + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::MAT2); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT32); + REQUIRE(classProperty->array); + REQUIRE(!classProperty->count); + REQUIRE(classProperty->normalized); + + SECTION("Access the correct type") { + PropertyTablePropertyView, true> + property = + view.getPropertyView, true>( + "TestClassProperty"); + REQUIRE(property.status() == PropertyTablePropertyViewStatus::Valid); + for (size_t i = 0; i < expected.size(); ++i) { + PropertyArrayView array = + property.getRaw(static_cast(i)); + auto maybeArray = property.get(i); + REQUIRE(maybeArray); + REQUIRE(array.size() == static_cast(expected[i].size())); + for (size_t j = 0; j < expected[i].size(); ++j) { + auto value = array[static_cast(j)]; + REQUIRE(expected[i][j] == value); + REQUIRE((*maybeArray)[j] == normalize(value)); + } + } + } + + SECTION("Incorrectly non-normalized") { + PropertyTablePropertyView> property = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + property.status() == + PropertyTablePropertyViewStatus::ErrorInvalidNormalization); + } + + SECTION("Wrong offset type") { + propertyTableProperty.arrayOffsetType = + PropertyTableProperty::ArrayOffsetType::UINT8; + PropertyTablePropertyView, true> + arrayProperty = + view.getPropertyView, true>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + + propertyTableProperty.arrayOffsetType = + PropertyTableProperty::ArrayOffsetType::UINT16; + arrayProperty = + view.getPropertyView, true>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + + propertyTableProperty.arrayOffsetType = "NONSENSE"; + arrayProperty = + view.getPropertyView, true>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); + + propertyTableProperty.arrayOffsetType = ""; + propertyTableProperty.stringOffsetType = + PropertyTableProperty::StringOffsetType::UINT64; + arrayProperty = + view.getPropertyView, true>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); + } + + SECTION("Offset values are not sorted ascending") { + propertyTableProperty.arrayOffsetType = + PropertyTableProperty::ArrayOffsetType::UINT64; + uint64_t* offset = reinterpret_cast( + model.buffers[offsetBufferIndex].cesium.data.data()); + offset[propertyTable.count] = 0; + PropertyTablePropertyView, true> + arrayProperty = + view.getPropertyView, true>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus::ErrorArrayOffsetsNotSorted); + } + + SECTION("Offset value points outside of value buffer") { + uint64_t* offset = reinterpret_cast( + model.buffers[offsetBufferIndex].cesium.data.data()); + offset[propertyTable.count] = + static_cast(model.buffers[valueBufferIndex].byteLength + 4); + PropertyTablePropertyView, true> + arrayProperty = + view.getPropertyView, true>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus::ErrorArrayOffsetOutOfBounds); + } + + SECTION("Count and offset buffer are both present") { + testClassProperty.count = 3; + PropertyTablePropertyView, true> + property = + view.getPropertyView, true>( + "TestClassProperty"); + REQUIRE( + property.status() == + PropertyTablePropertyViewStatus::ErrorArrayCountAndOffsetBufferCoexist); + } +} + +TEST_CASE("Test fixed-length boolean array") { + Model model; + + std::vector expected = { + true, + false, + false, + true, + false, + false, + true, + true, + true, + false, + false, + true}; + std::vector values; + size_t requiredBytesSize = static_cast( + glm::ceil(static_cast(expected.size()) / 8.0)); + values.resize(requiredBytesSize); + for (size_t i = 0; i < expected.size(); ++i) { + uint8_t expectedValue = expected[i]; + size_t byteIndex = i / 8; + size_t bitIndex = i % 8; + values[byteIndex] = + static_cast((expectedValue << bitIndex) | values[byteIndex]); + } + + addBufferToModel(model, values); + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::BOOLEAN; + testClassProperty.array = true; + testClassProperty.count = 3; + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast( + expected.size() / static_cast(testClassProperty.count.value())); + + PropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = + static_cast(model.bufferViews.size() - 1); + + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::BOOLEAN); + REQUIRE(classProperty->array); + REQUIRE(classProperty->count == 3); + + SECTION("Access correct type") { + PropertyTablePropertyView> boolArrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + boolArrayProperty.status() == PropertyTablePropertyViewStatus::Valid); + REQUIRE(boolArrayProperty.size() == propertyTable.count); + REQUIRE(boolArrayProperty.size() > 0); + for (int64_t i = 0; i < boolArrayProperty.size(); ++i) { + PropertyArrayView array = boolArrayProperty.getRaw(i); + auto maybeArray = boolArrayProperty.get(i); + REQUIRE(maybeArray); + for (int64_t j = 0; j < array.size(); ++j) { + REQUIRE(array[j] == expected[static_cast(i * 3 + j)]); + REQUIRE((*maybeArray)[j] == array[j]); + } + } + } + + SECTION("Wrong type") { + PropertyTablePropertyView> uint8ArrayInvalid = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + uint8ArrayInvalid.status() == + PropertyTablePropertyViewStatus::ErrorTypeMismatch); + } + + SECTION("View is not array type") { + PropertyTablePropertyView boolInvalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + boolInvalid.status() == + PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); + } + + SECTION("Value buffer doesn't have enough required bytes") { + testClassProperty.count = 11; + PropertyTablePropertyView> boolArrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + boolArrayProperty.status() == + PropertyTablePropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + } + + SECTION("Count is negative") { + testClassProperty.count = -1; + PropertyTablePropertyView> boolArrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + boolArrayProperty.status() == + PropertyTablePropertyViewStatus:: + ErrorArrayCountAndOffsetBufferDontExist); + } +} + +TEST_CASE("Test variable-length boolean array") { + Model model; + + std::vector> expected{ + {true, true, true, true, true}, + {}, + {}, + {}, + {false}, + {true, true}, + {false}, + {true, true, true, true, true}}; + size_t numOfElements = 0; + for (const auto& expectedMember : expected) { + numOfElements += expectedMember.size(); + } + + size_t requiredBytesSize = + static_cast(glm::ceil(static_cast(numOfElements) / 8.0)); + std::vector values(requiredBytesSize); + std::vector offsets((expected.size() + 1) * sizeof(uint64_t)); + uint64_t* offsetValue = reinterpret_cast(offsets.data()); + size_t indexSoFar = 0; + for (size_t i = 0; i < expected.size(); ++i) { + for (size_t j = 0; j < expected[i].size(); ++j) { + uint8_t expectedValue = expected[i][j]; + size_t byteIndex = indexSoFar / 8; + size_t bitIndex = indexSoFar % 8; + values[byteIndex] = static_cast( + (expectedValue << bitIndex) | static_cast(values[byteIndex])); + ++indexSoFar; + } + offsetValue[i + 1] = offsetValue[i] + expected[i].size(); + } + + addBufferToModel(model, values); + size_t valueBufferIndex = model.buffers.size() - 1; + size_t valueBufferViewIndex = model.bufferViews.size() - 1; + + addBufferToModel(model, offsets); + size_t offsetBufferIndex = model.buffers.size() - 1; + size_t offsetBufferViewIndex = model.bufferViews.size() - 1; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::BOOLEAN; + testClassProperty.array = true; + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast(expected.size()); + + PropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = static_cast(valueBufferViewIndex); + propertyTableProperty.arrayOffsets = + static_cast(offsetBufferViewIndex); + propertyTableProperty.arrayOffsetType = + PropertyTableProperty::ArrayOffsetType::UINT64; + + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::BOOLEAN); + REQUIRE(classProperty->array); + REQUIRE(!classProperty->count); + + SECTION("Access correct type") { + PropertyTablePropertyView> boolArrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + boolArrayProperty.status() == PropertyTablePropertyViewStatus::Valid); + for (size_t i = 0; i < expected.size(); ++i) { + PropertyArrayView array = + boolArrayProperty.getRaw(static_cast(i)); + auto maybeArray = boolArrayProperty.get(i); + REQUIRE(maybeArray); + REQUIRE(array.size() == static_cast(expected[i].size())); + for (size_t j = 0; j < expected[i].size(); ++j) { + auto value = array[static_cast(j)]; + REQUIRE(expected[i][j] == value); + REQUIRE((*maybeArray)[j] == value); + } + } + } + + SECTION("Wrong offset type") { + propertyTableProperty.arrayOffsetType = + PropertyTableProperty::ArrayOffsetType::UINT8; + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + + propertyTableProperty.arrayOffsetType = + PropertyTableProperty::ArrayOffsetType::UINT16; + arrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + + propertyTableProperty.arrayOffsetType = "NONSENSE"; + arrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); + + propertyTableProperty.arrayOffsetType = ""; + propertyTableProperty.stringOffsetType = + PropertyTableProperty::StringOffsetType::UINT64; + arrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); + } + + SECTION("Offset values are not sorted ascending") { + propertyTableProperty.arrayOffsetType = + PropertyTableProperty::ArrayOffsetType::UINT64; + uint64_t* offset = reinterpret_cast( + model.buffers[offsetBufferIndex].cesium.data.data()); + offset[propertyTable.count] = 0; + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus::ErrorArrayOffsetsNotSorted); + } + + SECTION("Offset value points outside of value buffer") { + uint64_t* offset = reinterpret_cast( + model.buffers[offsetBufferIndex].cesium.data.data()); + offset[propertyTable.count] = static_cast( + model.buffers[valueBufferIndex].byteLength * 8 + 20); + PropertyTablePropertyView> arrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus::ErrorArrayOffsetOutOfBounds); + } + + SECTION("Count and offset buffer both present") { + testClassProperty.count = 3; + PropertyTablePropertyView> boolArrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + boolArrayProperty.status() == + PropertyTablePropertyViewStatus::ErrorArrayCountAndOffsetBufferCoexist); + } +} + +TEST_CASE("Test fixed-length arrays of strings") { + Model model; + + std::vector expected{ + "What's up", + "Breaking news!!! Aliens no longer attacks the US first", + "But they still abduct my cows! Those milk thiefs! 👽 🐮", + "I'm not crazy. My mother had me tested 🤪", + "I love you, meat bags! ❤️", + "Book in the freezer"}; + + size_t totalBytes = 0; + for (const std::string& expectedValue : expected) { + totalBytes += expectedValue.size(); + } + + std::vector offsets((expected.size() + 1) * sizeof(uint32_t)); + std::vector values(totalBytes); + uint32_t* offsetValue = reinterpret_cast(offsets.data()); + for (size_t i = 0; i < expected.size(); ++i) { + const std::string& expectedValue = expected[i]; + std::memcpy( + values.data() + offsetValue[i], + expectedValue.c_str(), + expectedValue.size()); + offsetValue[i + 1] = + offsetValue[i] + static_cast(expectedValue.size()); + } + + addBufferToModel(model, values); + size_t valueBufferViewIndex = model.bufferViews.size() - 1; + + addBufferToModel(model, offsets); + size_t offsetBufferViewIndex = model.bufferViews.size() - 1; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::STRING; + testClassProperty.array = true; + testClassProperty.count = 2; + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast( + expected.size() / static_cast(testClassProperty.count.value())); + + PropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.stringOffsetType = + PropertyTableProperty::StringOffsetType::UINT32; + propertyTableProperty.values = static_cast(valueBufferViewIndex); + propertyTableProperty.stringOffsets = + static_cast(offsetBufferViewIndex); + + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::STRING); + REQUIRE(classProperty->array); + REQUIRE(classProperty->count == 2); + + SECTION("Access correct type") { + PropertyTablePropertyView> + stringProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE(stringProperty.status() == PropertyTablePropertyViewStatus::Valid); + REQUIRE(stringProperty.size() == 3); + + PropertyArrayView v0 = stringProperty.getRaw(0); + REQUIRE(v0.size() == 2); + REQUIRE(v0[0] == "What's up"); + REQUIRE(v0[1] == "Breaking news!!! Aliens no longer attacks the US first"); + + PropertyArrayView v1 = stringProperty.getRaw(1); + REQUIRE(v1.size() == 2); + REQUIRE(v1[0] == "But they still abduct my cows! Those milk thiefs! 👽 🐮"); REQUIRE(v1[1] == "I'm not crazy. My mother had me tested 🤪"); PropertyArrayView v2 = stringProperty.getRaw(2); REQUIRE(v2.size() == 2); REQUIRE(v2[0] == "I love you, meat bags! ❤️"); REQUIRE(v2[1] == "Book in the freezer"); + + for (int64_t i = 0; i < stringProperty.size(); i++) { + auto maybeValue = stringProperty.get(i); + REQUIRE(maybeValue); + + auto value = stringProperty.getRaw(i); + REQUIRE(maybeValue->size() == value.size()); + for (int64_t j = 0; j < value.size(); j++) { + REQUIRE((*maybeValue)[j] == value[j]); + } + } } SECTION("Array type mismatch") { @@ -2201,8 +3829,11 @@ TEST_CASE("Test variable-length arrays of strings") { for (size_t i = 0; i < expected.size(); ++i) { PropertyArrayView stringArray = stringProperty.getRaw(static_cast(i)); + auto maybeArray = stringProperty.get(static_cast(i)); + REQUIRE(maybeArray); for (size_t j = 0; j < expected[i].size(); ++j) { REQUIRE(stringArray[static_cast(j)] == expected[i][j]); + REQUIRE((*maybeArray)[static_cast(j)] == expected[i][j]); } } } @@ -2250,100 +3881,475 @@ TEST_CASE("Test variable-length arrays of strings") { PropertyTablePropertyViewStatus:: ErrorBufferViewSizeDoesNotMatchPropertyTableCount); - propertyTableProperty.stringOffsetType = - PropertyTableProperty::StringOffsetType::UINT16; - arrayProperty = view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus:: - ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + propertyTableProperty.stringOffsetType = + PropertyTableProperty::StringOffsetType::UINT16; + arrayProperty = view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus:: + ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + + propertyTableProperty.stringOffsetType = "NONSENSE"; + arrayProperty = view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus::ErrorInvalidStringOffsetType); + propertyTableProperty.stringOffsetType = + PropertyTableProperty::StringOffsetType::UINT32; + } + + SECTION("Array offset values are not sorted ascending") { + uint32_t* offset = reinterpret_cast( + model.buffers[arrayOffsetBuffer].cesium.data.data()); + offset[0] = static_cast(1000); + PropertyTablePropertyView> + arrayProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus::ErrorArrayOffsetsNotSorted); + offset[0] = 0; + } + + SECTION("String offset values are not sorted ascending") { + uint32_t* offset = reinterpret_cast( + model.buffers[stringOffsetBuffer].cesium.data.data()); + offset[0] = static_cast(1000); + PropertyTablePropertyView> + arrayProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus::ErrorStringOffsetsNotSorted); + offset[0] = 0; + } + + SECTION("Array offset value points outside of value buffer") { + uint32_t* offset = reinterpret_cast( + model.buffers[arrayOffsetBuffer].cesium.data.data()); + uint32_t previousValue = offset[propertyTable.count]; + offset[propertyTable.count] = static_cast(100000); + PropertyTablePropertyView> + arrayProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus::ErrorArrayOffsetOutOfBounds); + offset[propertyTable.count] = previousValue; + } + + SECTION("String offset value points outside of value buffer") { + uint32_t* offset = reinterpret_cast( + model.buffers[stringOffsetBuffer].cesium.data.data()); + uint32_t previousValue = offset[6]; + offset[6] = static_cast(100000); + PropertyTablePropertyView> + arrayProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayProperty.status() == + PropertyTablePropertyViewStatus::ErrorStringOffsetOutOfBounds); + offset[6] = previousValue; + } + + SECTION("Count and offset buffer both present") { + testClassProperty.count = 3; + PropertyTablePropertyView> + boolArrayProperty = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + boolArrayProperty.status() == + PropertyTablePropertyViewStatus::ErrorArrayCountAndOffsetBufferCoexist); + } +} + +TEST_CASE("Test with property offset, scale, min, max") { + Model model; + std::vector values = {1.0f, 2.0f, 3.0f, 4.0f}; + const float offset = 0.5f; + const float scale = 2.0f; + const float min = 3.5f; + const float max = 8.5f; + + addBufferToModel(model, values); + size_t valueBufferViewIndex = model.bufferViews.size() - 1; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::FLOAT32; + testClassProperty.offset = offset; + testClassProperty.scale = scale; + testClassProperty.min = min; + testClassProperty.max = max; + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast(values.size()); + + PropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = static_cast(valueBufferViewIndex); + + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); + REQUIRE( + classProperty->componentType == ClassProperty::ComponentType::FLOAT32); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->array); + REQUIRE(!classProperty->normalized); + REQUIRE(classProperty->offset); + REQUIRE(classProperty->scale); + REQUIRE(classProperty->min); + REQUIRE(classProperty->max); + + SECTION("Use class property values") { + PropertyTablePropertyView propertyView = + view.getPropertyView("TestClassProperty"); + REQUIRE(propertyView.status() == PropertyTablePropertyViewStatus::Valid); + REQUIRE(propertyView.size() > 0); + REQUIRE(propertyView.offset()); + REQUIRE(*propertyView.offset() == offset); + REQUIRE(propertyView.scale()); + REQUIRE(*propertyView.scale() == scale); + REQUIRE(propertyView.min()); + REQUIRE(*propertyView.min() == min); + REQUIRE(propertyView.max()); + REQUIRE(*propertyView.max() == max); + + for (int64_t i = 0; i < propertyView.size(); ++i) { + REQUIRE(propertyView.getRaw(i) == values[static_cast(i)]); + REQUIRE(propertyView.get(i)); + REQUIRE(*propertyView.get(i) == propertyView.getRaw(i) * scale + offset); + } + } + + SECTION("Use own property values") { + const float newOffset = 1.0f; + const float newScale = -1.0f; + const float newMin = -3.0f; + const float newMax = 0.0f; + propertyTableProperty.offset = newOffset; + propertyTableProperty.scale = newScale; + propertyTableProperty.min = newMin; + propertyTableProperty.max = newMax; + + PropertyTablePropertyView propertyView = + view.getPropertyView("TestClassProperty"); + REQUIRE(propertyView.status() == PropertyTablePropertyViewStatus::Valid); + REQUIRE(propertyView.size() > 0); + REQUIRE(propertyView.offset()); + REQUIRE(*propertyView.offset() == newOffset); + REQUIRE(propertyView.scale()); + REQUIRE(*propertyView.scale() == newScale); + REQUIRE(propertyView.min()); + REQUIRE(*propertyView.min() == newMin); + REQUIRE(propertyView.max()); + REQUIRE(*propertyView.max() == newMax); + + for (int64_t i = 0; i < propertyView.size(); ++i) { + REQUIRE(propertyView.getRaw(i) == values[static_cast(i)]); + REQUIRE(propertyView.get(i)); + REQUIRE( + *propertyView.get(i) == + propertyView.getRaw(i) * newScale + newOffset); + } + } +} + +TEST_CASE("Test with property offset, scale, min, max (normalized)") { + Model model; + std::vector values = {-128, 0, 32, 127}; + const double offset = 0.5; + const double scale = 2.0; + const double min = 1.5; + const double max = 2.5; + + addBufferToModel(model, values); + size_t valueBufferViewIndex = model.bufferViews.size() - 1; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::INT8; + testClassProperty.normalized = true; + testClassProperty.offset = offset; + testClassProperty.scale = scale; + testClassProperty.min = min; + testClassProperty.max = max; + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast(values.size()); + + PropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = static_cast(valueBufferViewIndex); + + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT8); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->array); + REQUIRE(classProperty->normalized); + REQUIRE(classProperty->offset); + REQUIRE(classProperty->scale); + REQUIRE(classProperty->min); + REQUIRE(classProperty->max); + + SECTION("Use class property values") { + PropertyTablePropertyView propertyView = + view.getPropertyView("TestClassProperty"); + REQUIRE(propertyView.status() == PropertyTablePropertyViewStatus::Valid); + REQUIRE(propertyView.size() > 0); + REQUIRE(propertyView.offset()); + REQUIRE(*propertyView.offset() == offset); + REQUIRE(propertyView.scale()); + REQUIRE(*propertyView.scale() == scale); + REQUIRE(propertyView.min()); + REQUIRE(*propertyView.min() == min); + REQUIRE(propertyView.max()); + REQUIRE(*propertyView.max() == max); + + for (int64_t i = 0; i < propertyView.size(); ++i) { + REQUIRE(propertyView.getRaw(i) == values[static_cast(i)]); + REQUIRE(propertyView.get(i)); + REQUIRE( + *propertyView.get(i) == + normalize(propertyView.getRaw(i)) * scale + offset); + } + } + + SECTION("Use own property values") { + const double newOffset = -0.5; + const double newScale = 1.0; + const double newMin = -1.5; + const double newMax = 0.5; + propertyTableProperty.offset = newOffset; + propertyTableProperty.scale = newScale; + propertyTableProperty.min = newMin; + propertyTableProperty.max = newMax; + + PropertyTablePropertyView propertyView = + view.getPropertyView("TestClassProperty"); + REQUIRE(propertyView.status() == PropertyTablePropertyViewStatus::Valid); + REQUIRE(propertyView.size() > 0); + REQUIRE(propertyView.offset()); + REQUIRE(*propertyView.offset() == newOffset); + REQUIRE(propertyView.scale()); + REQUIRE(*propertyView.scale() == newScale); + REQUIRE(propertyView.min()); + REQUIRE(*propertyView.min() == newMin); + REQUIRE(propertyView.max()); + REQUIRE(*propertyView.max() == newMax); + + for (int64_t i = 0; i < propertyView.size(); ++i) { + REQUIRE(propertyView.getRaw(i) == values[static_cast(i)]); + REQUIRE(propertyView.get(i)); + REQUIRE( + *propertyView.get(i) == + normalize(propertyView.getRaw(i)) * newScale + newOffset); + } + } +} + +TEST_CASE("Test with property noData value") { + Model model; + std::vector values = {-128, 0, 32, -128, 127}; + const int8_t noData = -128; + + addBufferToModel(model, values); + size_t valueBufferViewIndex = model.bufferViews.size() - 1; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::INT8; + testClassProperty.noData = noData; + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast(values.size()); + + PropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = static_cast(valueBufferViewIndex); + + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); - propertyTableProperty.stringOffsetType = "NONSENSE"; - arrayProperty = view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus::ErrorInvalidStringOffsetType); - propertyTableProperty.stringOffsetType = - PropertyTableProperty::StringOffsetType::UINT32; + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT8); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->array); + REQUIRE(!classProperty->normalized); + REQUIRE(classProperty->noData); + + SECTION("Without default value") { + PropertyTablePropertyView propertyView = + view.getPropertyView("TestClassProperty"); + REQUIRE(propertyView.status() == PropertyTablePropertyViewStatus::Valid); + REQUIRE(propertyView.size() > 0); + REQUIRE(propertyView.noData()); + REQUIRE(*propertyView.noData() == noData); + + for (int64_t i = 0; i < propertyView.size(); ++i) { + REQUIRE(propertyView.getRaw(i) == values[static_cast(i)]); + if (propertyView.getRaw(i) == noData) { + REQUIRE(!propertyView.get(i)); + } else { + REQUIRE(propertyView.get(i)); + REQUIRE(*propertyView.get(i) == propertyView.getRaw(i)); + } + } } - SECTION("Array offset values are not sorted ascending") { - uint32_t* offset = reinterpret_cast( - model.buffers[arrayOffsetBuffer].cesium.data.data()); - offset[0] = static_cast(1000); - PropertyTablePropertyView> - arrayProperty = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus::ErrorArrayOffsetsNotSorted); - offset[0] = 0; + SECTION("With default value") { + const int8_t defaultValue = 100; + testClassProperty.defaultProperty = defaultValue; + classProperty = view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty->defaultProperty); + PropertyTablePropertyView propertyView = + view.getPropertyView("TestClassProperty"); + REQUIRE(propertyView.status() == PropertyTablePropertyViewStatus::Valid); + REQUIRE(propertyView.size() > 0); + REQUIRE(propertyView.noData()); + REQUIRE(*propertyView.noData() == noData); + REQUIRE(propertyView.defaultValue()); + REQUIRE(*propertyView.defaultValue() == defaultValue); + + for (int64_t i = 0; i < propertyView.size(); ++i) { + REQUIRE(propertyView.getRaw(i) == values[static_cast(i)]); + REQUIRE(propertyView.get(i)); + if (propertyView.getRaw(i) == noData) { + REQUIRE(*propertyView.get(i) == defaultValue); + } else { + REQUIRE(*propertyView.get(i) == propertyView.getRaw(i)); + } + } } +} - SECTION("String offset values are not sorted ascending") { - uint32_t* offset = reinterpret_cast( - model.buffers[stringOffsetBuffer].cesium.data.data()); - offset[0] = static_cast(1000); - PropertyTablePropertyView> - arrayProperty = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus::ErrorStringOffsetsNotSorted); - offset[0] = 0; - } +TEST_CASE("Test with property noData value (normalized)") { + Model model; + std::vector values = {-128, 0, 32, -128, 127}; + const int8_t noData = -128; - SECTION("Array offset value points outside of value buffer") { - uint32_t* offset = reinterpret_cast( - model.buffers[arrayOffsetBuffer].cesium.data.data()); - uint32_t previousValue = offset[propertyTable.count]; - offset[propertyTable.count] = static_cast(100000); - PropertyTablePropertyView> - arrayProperty = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus::ErrorArrayOffsetOutOfBounds); - offset[propertyTable.count] = previousValue; - } + addBufferToModel(model, values); + size_t valueBufferViewIndex = model.bufferViews.size() - 1; - SECTION("String offset value points outside of value buffer") { - uint32_t* offset = reinterpret_cast( - model.buffers[stringOffsetBuffer].cesium.data.data()); - uint32_t previousValue = offset[6]; - offset[6] = static_cast(100000); - PropertyTablePropertyView> - arrayProperty = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus::ErrorStringOffsetOutOfBounds); - offset[6] = previousValue; - } + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); - SECTION("Count and offset buffer both present") { - testClassProperty.count = 3; - PropertyTablePropertyView> - boolArrayProperty = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - boolArrayProperty.status() == - PropertyTablePropertyViewStatus::ErrorArrayCountAndOffsetBufferCoexist); + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::INT8; + testClassProperty.normalized = true; + testClassProperty.noData = noData; + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast(values.size()); + + PropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = static_cast(valueBufferViewIndex); + + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT8); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->array); + REQUIRE(classProperty->normalized); + REQUIRE(classProperty->noData); + + SECTION("Without default value") { + PropertyTablePropertyView propertyView = + view.getPropertyView("TestClassProperty"); + REQUIRE(propertyView.status() == PropertyTablePropertyViewStatus::Valid); + REQUIRE(propertyView.size() > 0); + REQUIRE(propertyView.noData()); + REQUIRE(*propertyView.noData() == noData); + + for (int64_t i = 0; i < propertyView.size(); ++i) { + REQUIRE(propertyView.getRaw(i) == values[static_cast(i)]); + if (propertyView.getRaw(i) == noData) { + REQUIRE(!propertyView.get(i)); + } else { + REQUIRE(propertyView.get(i)); + REQUIRE(*propertyView.get(i) == normalize(propertyView.getRaw(i))); + } + } } -} -template -void testFunction(PropertyTablePropertyView view) { - REQUIRE( - propertyValue.status() == - PropertyTablePropertyViewStatus::ErrorInvalidPropertyTable); + SECTION("With default value") { + const double defaultValue = 10.5; + testClassProperty.defaultProperty = defaultValue; + classProperty = view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty->defaultProperty); + + PropertyTablePropertyView propertyView = + view.getPropertyView("TestClassProperty"); + REQUIRE(propertyView.status() == PropertyTablePropertyViewStatus::Valid); + REQUIRE(propertyView.size() > 0); + REQUIRE(propertyView.noData()); + REQUIRE(*propertyView.noData() == noData); + REQUIRE(propertyView.defaultValue()); + REQUIRE(*propertyView.defaultValue() == defaultValue); + + for (int64_t i = 0; i < propertyView.size(); ++i) { + REQUIRE(propertyView.getRaw(i) == values[static_cast(i)]); + REQUIRE(propertyView.get(i)); + if (propertyView.getRaw(i) == noData) { + REQUIRE(*propertyView.get(i) == defaultValue); + } else { + REQUIRE(*propertyView.get(i) == normalize(propertyView.getRaw(i))); + } + } + } } TEST_CASE("Test callback on invalid property table view") { @@ -2426,12 +4432,157 @@ TEST_CASE("Test callback for invalid PropertyTableProperty") { view.getPropertyView("InvalidProperty", testCallback); view.getPropertyView("NonexistentProperty", testCallback); - REQUIRE(invokedCallbackCount == 2); + REQUIRE(invokedCallbackCount == 2); +} + +TEST_CASE("Test callback for scalar PropertyTableProperty") { + Model model; + std::vector values = {12, 34, 30, 11, 34, 34, 11, 33, 122, 33}; + + addBufferToModel(model, values); + size_t valueBufferViewIndex = model.bufferViews.size() - 1; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::UINT32; + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast(values.size()); + + PropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = static_cast(valueBufferViewIndex); + + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT32); + REQUIRE(!classProperty->array); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->normalized); + + uint32_t invokedCallbackCount = 0; + view.getPropertyView( + "TestClassProperty", + [&values, &invokedCallbackCount]( + const std::string& /*propertyName*/, + auto propertyValue) mutable { + invokedCallbackCount++; + if constexpr (std::is_same_v< + PropertyTablePropertyView, + decltype(propertyValue)>) { + REQUIRE( + propertyValue.status() == PropertyTablePropertyViewStatus::Valid); + REQUIRE(propertyValue.size() > 0); + + for (int64_t i = 0; i < propertyValue.size(); ++i) { + auto expectedValue = values[static_cast(i)]; + REQUIRE( + static_cast(propertyValue.getRaw(i)) == + expectedValue); + + auto maybeValue = propertyValue.get(i); + REQUIRE(maybeValue); + REQUIRE(*maybeValue == expectedValue); + } + } else { + FAIL("getPropertyView returned PropertyTablePropertyView of " + "incorrect type for TestClassProperty."); + } + }); + + REQUIRE(invokedCallbackCount == 1); +} + +TEST_CASE("Test callback for scalar PropertyTableProperty (normalized)") { + Model model; + std::vector values = {12, 34, 30, 11, 34, 34, 11, 33, 122, 33}; + + addBufferToModel(model, values); + size_t valueBufferViewIndex = model.bufferViews.size() - 1; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::UINT32; + testClassProperty.normalized = true; + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast(values.size()); + + PropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = static_cast(valueBufferViewIndex); + + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT32); + REQUIRE(!classProperty->array); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(classProperty->normalized); + + uint32_t invokedCallbackCount = 0; + view.getPropertyView( + "TestClassProperty", + [&values, &invokedCallbackCount]( + const std::string& /*propertyName*/, + auto propertyValue) mutable { + invokedCallbackCount++; + if constexpr (std::is_same_v< + PropertyTablePropertyView, + decltype(propertyValue)>) { + REQUIRE( + propertyValue.status() == PropertyTablePropertyViewStatus::Valid); + REQUIRE(propertyValue.size() > 0); + + for (int64_t i = 0; i < propertyValue.size(); ++i) { + auto expectedValue = values[static_cast(i)]; + REQUIRE( + static_cast(propertyValue.getRaw(i)) == + expectedValue); + + auto maybeValue = propertyValue.get(i); + REQUIRE(maybeValue); + REQUIRE(*maybeValue == normalize(expectedValue)); + } + } else { + FAIL("getPropertyView returned PropertyTablePropertyView of " + "incorrect type for TestClassProperty."); + } + }); + + REQUIRE(invokedCallbackCount == 1); } -TEST_CASE("Test callback for scalar PropertyTableProperty") { +TEST_CASE("Test callback for vecN PropertyTableProperty") { Model model; - std::vector values = {12, 34, 30, 11, 34, 34, 11, 33, 122, 33}; + std::vector values = { + glm::ivec3(-12, 34, 30), + glm::ivec3(11, 73, 0), + glm::ivec3(-2, 6, 12), + glm::ivec3(-4, 8, -13)}; addBufferToModel(model, values); size_t valueBufferViewIndex = model.bufferViews.size() - 1; @@ -2442,8 +4593,8 @@ TEST_CASE("Test callback for scalar PropertyTableProperty") { Schema& schema = metadata.schema.emplace(); Class& testClass = schema.classes["TestClass"]; ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; - testClassProperty.type = ClassProperty::Type::SCALAR; - testClassProperty.componentType = ClassProperty::ComponentType::UINT32; + testClassProperty.type = ClassProperty::Type::VEC3; + testClassProperty.componentType = ClassProperty::ComponentType::INT32; PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); propertyTable.classProperty = "TestClass"; @@ -2460,10 +4611,11 @@ TEST_CASE("Test callback for scalar PropertyTableProperty") { const ClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE(classProperty); - REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); - REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT32); - REQUIRE(!classProperty->array); + REQUIRE(classProperty->type == ClassProperty::Type::VEC3); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT32); REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->array); + REQUIRE(!classProperty->normalized); uint32_t invokedCallbackCount = 0; view.getPropertyView( @@ -2472,17 +4624,22 @@ TEST_CASE("Test callback for scalar PropertyTableProperty") { const std::string& /*propertyName*/, auto propertyValue) mutable { invokedCallbackCount++; + REQUIRE( + propertyValue.status() == PropertyTablePropertyViewStatus::Valid); + REQUIRE(propertyValue.size() > 0); + if constexpr (std::is_same_v< - PropertyTablePropertyView, + PropertyTablePropertyView, decltype(propertyValue)>) { - REQUIRE( - propertyValue.status() == PropertyTablePropertyViewStatus::Valid); - REQUIRE(propertyValue.size() > 0); - for (int64_t i = 0; i < propertyValue.size(); ++i) { + auto expectedValue = values[static_cast(i)]; REQUIRE( - static_cast(propertyValue.getRaw(i)) == - values[static_cast(i)]); + static_cast(propertyValue.getRaw(i)) == + expectedValue); + + auto maybeValue = propertyValue.get(i); + REQUIRE(maybeValue); + REQUIRE(*maybeValue == expectedValue); } } else { FAIL("getPropertyView returned PropertyTablePropertyView of " @@ -2493,7 +4650,7 @@ TEST_CASE("Test callback for scalar PropertyTableProperty") { REQUIRE(invokedCallbackCount == 1); } -TEST_CASE("Test callback for vecN PropertyTableProperty") { +TEST_CASE("Test callback for vecN PropertyTableProperty (normalized)") { Model model; std::vector values = { glm::ivec3(-12, 34, 30), @@ -2512,6 +4669,7 @@ TEST_CASE("Test callback for vecN PropertyTableProperty") { ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; testClassProperty.type = ClassProperty::Type::VEC3; testClassProperty.componentType = ClassProperty::ComponentType::INT32; + testClassProperty.normalized = true; PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); propertyTable.classProperty = "TestClass"; @@ -2532,6 +4690,7 @@ TEST_CASE("Test callback for vecN PropertyTableProperty") { REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT32); REQUIRE(classProperty->count == std::nullopt); REQUIRE(!classProperty->array); + REQUIRE(classProperty->normalized); uint32_t invokedCallbackCount = 0; view.getPropertyView( @@ -2545,12 +4704,17 @@ TEST_CASE("Test callback for vecN PropertyTableProperty") { REQUIRE(propertyValue.size() > 0); if constexpr (std::is_same_v< - PropertyTablePropertyView, + PropertyTablePropertyView, decltype(propertyValue)>) { for (int64_t i = 0; i < propertyValue.size(); ++i) { + auto expectedValue = values[static_cast(i)]; REQUIRE( static_cast(propertyValue.getRaw(i)) == - values[static_cast(i)]); + expectedValue); + + auto maybeValue = propertyValue.get(i); + REQUIRE(maybeValue); + REQUIRE(*maybeValue == normalize(expectedValue)); } } else { FAIL("getPropertyView returned PropertyTablePropertyView of " @@ -2610,6 +4774,7 @@ TEST_CASE("Test callback for matN PropertyTableProperty") { REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT32); REQUIRE(classProperty->count == std::nullopt); REQUIRE(!classProperty->array); + REQUIRE(!classProperty->normalized); uint32_t invokedCallbackCount = 0; view.getPropertyView( @@ -2625,13 +4790,103 @@ TEST_CASE("Test callback for matN PropertyTableProperty") { PropertyTablePropertyView, decltype(propertyValue)>) { for (int64_t i = 0; i < propertyValue.size(); ++i) { + auto expectedValue = values[static_cast(i)]; + REQUIRE( + static_cast(propertyValue.getRaw(i)) == + expectedValue); + + auto maybeValue = propertyValue.get(i); + REQUIRE(maybeValue); + REQUIRE(*maybeValue == expectedValue); + } + } else { + FAIL("getPropertyView returned PropertyTablePropertyView of " + "incorrect type for TestClassProperty."); + } + invokedCallbackCount++; + }); + + REQUIRE(invokedCallbackCount == 1); +} + +TEST_CASE("Test callback for matN PropertyTableProperty (normalized)") { + Model model; + // clang-format off + std::vector values = { + glm::u32mat2x2( + 12, 34, + 30, 1), + glm::u32mat2x2( + 11, 8, + 73, 102), + glm::u32mat2x2( + 1, 0, + 63, 2), + glm::u32mat2x2( + 4, 8, + 3, 23)}; + // clang-format on + + addBufferToModel(model, values); + size_t valueBufferViewIndex = model.bufferViews.size() - 1; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::MAT2; + testClassProperty.componentType = ClassProperty::ComponentType::UINT32; + testClassProperty.normalized = true; + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast(values.size()); + + PropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = static_cast(valueBufferViewIndex); + + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::MAT2); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT32); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->array); + REQUIRE(classProperty->normalized); + + uint32_t invokedCallbackCount = 0; + view.getPropertyView( + "TestClassProperty", + [&values, &invokedCallbackCount]( + const std::string& /*propertyName*/, + auto propertyValue) mutable { + REQUIRE( + propertyValue.status() == PropertyTablePropertyViewStatus::Valid); + REQUIRE(propertyValue.size() > 0); + + if constexpr (std::is_same_v< + PropertyTablePropertyView, + decltype(propertyValue)>) { + for (int64_t i = 0; i < propertyValue.size(); ++i) { + auto expectedValue = values[static_cast(i)]; REQUIRE( static_cast(propertyValue.getRaw(i)) == - values[static_cast(i)]); + expectedValue); + + auto maybeValue = propertyValue.get(i); + REQUIRE(maybeValue); + REQUIRE(*maybeValue == normalize(expectedValue)); } } else { FAIL("getPropertyView returned PropertyTablePropertyView of " - "incorrecttype for TestClassProperty."); + "incorrect type for TestClassProperty."); } invokedCallbackCount++; }); @@ -2707,9 +4962,13 @@ TEST_CASE("Test callback for boolean PropertyTableProperty") { PropertyTablePropertyView, decltype(propertyValue)>) { for (int64_t i = 0; i < propertyValue.size(); ++i) { + auto expectedValue = expected[static_cast(i)]; REQUIRE( - static_cast(propertyValue.getRaw(i)) == - expected[static_cast(i)]); + static_cast(propertyValue.getRaw(i)) == expectedValue); + + auto maybeValue = propertyValue.get(i); + REQUIRE(maybeValue); + REQUIRE(*maybeValue == expectedValue); } } else { FAIL("getPropertyView returned PropertyTablePropertyView of " @@ -2796,9 +5055,14 @@ TEST_CASE("Test callback for string PropertyTableProperty") { PropertyTablePropertyView, decltype(propertyValue)>) { for (int64_t i = 0; i < propertyValue.size(); ++i) { + auto expectedValue = expected[static_cast(i)]; REQUIRE( static_cast(propertyValue.getRaw(i)) == - expected[static_cast(i)]); + expectedValue); + + auto maybeValue = propertyValue.get(i); + REQUIRE(maybeValue); + REQUIRE(*maybeValue == expectedValue); } } else { FAIL("getPropertyView returned PropertyTablePropertyView of " @@ -2848,6 +5112,7 @@ TEST_CASE("Test callback for scalar array PropertyTableProperty") { REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT32); REQUIRE(classProperty->array); REQUIRE(classProperty->count == 3); + REQUIRE(!classProperty->normalized); uint32_t invokedCallbackCount = 0; view.getPropertyView( @@ -2865,9 +5130,87 @@ TEST_CASE("Test callback for scalar array PropertyTableProperty") { PropertyArrayView>, decltype(propertyValue)>) { for (int64_t i = 0; i < propertyValue.size(); ++i) { - PropertyArrayView member = propertyValue.getRaw(i); - for (int64_t j = 0; j < member.size(); ++j) { - REQUIRE(member[j] == values[static_cast(i * 3 + j)]); + PropertyArrayView array = propertyValue.getRaw(i); + auto maybeArray = propertyValue.get(i); + REQUIRE(maybeArray); + for (int64_t j = 0; j < array.size(); ++j) { + REQUIRE(array[j] == values[static_cast(i * 3 + j)]); + REQUIRE((*maybeArray)[j] == array[j]); + } + } + } else { + FAIL("getPropertyView returned PropertyTablePropertyView of " + "incorrect type for TestClassProperty."); + } + }); + + REQUIRE(invokedCallbackCount == 1); +} + +TEST_CASE("Test callback for scalar array PropertyTableProperty (normalized)") { + Model model; + std::vector values = + {12, 34, 30, 11, 34, 34, 11, 33, 122, 33, 223, 11}; + + addBufferToModel(model, values); + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::UINT32; + testClassProperty.array = true; + testClassProperty.count = 3; + testClassProperty.normalized = true; + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast( + values.size() / static_cast(testClassProperty.count.value())); + + PropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = + static_cast(model.bufferViews.size() - 1); + + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT32); + REQUIRE(classProperty->array); + REQUIRE(classProperty->count == 3); + REQUIRE(classProperty->normalized); + + uint32_t invokedCallbackCount = 0; + view.getPropertyView( + "TestClassProperty", + [&values, &invokedCallbackCount]( + const std::string& /*propertyName*/, + auto propertyValue) { + invokedCallbackCount++; + REQUIRE( + propertyValue.status() == PropertyTablePropertyViewStatus::Valid); + REQUIRE(propertyValue.size() > 0); + + if constexpr ( + std::is_same_v< + PropertyTablePropertyView, true>, + decltype(propertyValue)>) { + for (int64_t i = 0; i < propertyValue.size(); ++i) { + PropertyArrayView array = propertyValue.getRaw(i); + auto maybeArray = propertyValue.get(i); + REQUIRE(maybeArray); + for (int64_t j = 0; j < array.size(); ++j) { + REQUIRE(array[j] == values[static_cast(i * 3 + j)]); + REQUIRE((*maybeArray)[j] == normalize(array[j])); } } } else { @@ -2924,6 +5267,7 @@ TEST_CASE("Test callback for vecN array PropertyTableProperty") { REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT32); REQUIRE(classProperty->array); REQUIRE(classProperty->count == 2); + REQUIRE(!classProperty->normalized); uint32_t invokedCallbackCount = 0; view.getPropertyView( @@ -2941,9 +5285,95 @@ TEST_CASE("Test callback for vecN array PropertyTableProperty") { PropertyArrayView>, decltype(propertyValue)>) { for (int64_t i = 0; i < propertyValue.size(); ++i) { - PropertyArrayView member = propertyValue.getRaw(i); - for (int64_t j = 0; j < member.size(); ++j) { - REQUIRE(member[j] == values[static_cast(i * 2 + j)]); + PropertyArrayView array = propertyValue.getRaw(i); + auto maybeArray = propertyValue.get(i); + REQUIRE(maybeArray); + + for (int64_t j = 0; j < array.size(); ++j) { + REQUIRE(array[j] == values[static_cast(i * 2 + j)]); + REQUIRE((*maybeArray)[j] == array[j]); + } + } + } else { + FAIL("getPropertyView returned PropertyTablePropertyView of " + "incorrect type for TestClassProperty."); + } + }); + + REQUIRE(invokedCallbackCount == 1); +} + +TEST_CASE("Test callback for vecN array PropertyTableProperty (normalized)") { + Model model; + std::vector values = { + glm::ivec3(12, 34, -30), + glm::ivec3(-2, 0, 1), + glm::ivec3(1, 2, 8), + glm::ivec3(-100, 84, 6), + glm::ivec3(2, -2, -2), + glm::ivec3(40, 61, 3), + }; + + addBufferToModel(model, values); + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::VEC3; + testClassProperty.componentType = ClassProperty::ComponentType::INT32; + testClassProperty.array = true; + testClassProperty.count = 2; + testClassProperty.normalized = true; + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast( + values.size() / static_cast(testClassProperty.count.value())); + + PropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = + static_cast(model.bufferViews.size() - 1); + + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::VEC3); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT32); + REQUIRE(classProperty->array); + REQUIRE(classProperty->count == 2); + REQUIRE(classProperty->normalized); + + uint32_t invokedCallbackCount = 0; + view.getPropertyView( + "TestClassProperty", + [&values, &invokedCallbackCount]( + const std::string& /*propertyName*/, + auto propertyValue) { + invokedCallbackCount++; + REQUIRE( + propertyValue.status() == PropertyTablePropertyViewStatus::Valid); + REQUIRE(propertyValue.size() > 0); + + if constexpr ( + std::is_same_v< + PropertyTablePropertyView, true>, + decltype(propertyValue)>) { + for (int64_t i = 0; i < propertyValue.size(); ++i) { + PropertyArrayView array = propertyValue.getRaw(i); + auto maybeArray = propertyValue.get(i); + REQUIRE(maybeArray); + + for (int64_t j = 0; j < array.size(); ++j) { + REQUIRE(array[j] == values[static_cast(i * 2 + j)]); + REQUIRE((*maybeArray)[j] == normalize(array[j])); } } } else { @@ -3121,9 +5551,12 @@ TEST_CASE("Test callback for boolean array PropertyTableProperty") { PropertyTablePropertyView>, decltype(propertyValue)>) { for (int64_t i = 0; i < propertyValue.size(); ++i) { - PropertyArrayView member = propertyValue.getRaw(i); - for (int64_t j = 0; j < member.size(); ++j) { - REQUIRE(member[j] == expected[static_cast(i * 3 + j)]); + PropertyArrayView array = propertyValue.getRaw(i); + auto maybeArray = propertyValue.get(i); + REQUIRE(maybeArray); + for (int64_t j = 0; j < array.size(); ++j) { + REQUIRE(array[j] == expected[static_cast(i * 3 + j)]); + REQUIRE((*maybeArray)[j] == array[j]); } } } else { @@ -3236,6 +5669,17 @@ TEST_CASE("Test callback for string array PropertyTableProperty") { REQUIRE(v2.size() == 2); REQUIRE(v2[0] == "I love you, meat bags! ❤️"); REQUIRE(v2[1] == "Book in the freezer"); + + for (int64_t i = 0; i < propertyValue.size(); i++) { + auto maybeValue = propertyValue.get(i); + REQUIRE(maybeValue); + + auto value = propertyValue.getRaw(i); + REQUIRE(maybeValue->size() == value.size()); + for (int64_t j = 0; j < value.size(); j++) { + REQUIRE((*maybeValue)[j] == value[j]); + } + } } else { FAIL("getPropertyView returned PropertyTablePropertyView of " "incorrect type for TestClassProperty."); From 76427497ca3ae10eb8359654976b140341fe2bf4 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Thu, 24 Aug 2023 18:33:56 -0400 Subject: [PATCH 220/421] Rewrite property texture property tests --- .../include/CesiumGltf/PropertyTableView.h | 6 +- .../CesiumGltf/PropertyTexturePropertyView.h | 552 ++++- CesiumGltf/src/PropertyTableView.cpp | 4 +- .../test/TestPropertyTablePropertyView.cpp | 228 +- .../test/TestPropertyTexturePropertyView.cpp | 1902 +++++++++++------ 5 files changed, 1797 insertions(+), 895 deletions(-) diff --git a/CesiumGltf/include/CesiumGltf/PropertyTableView.h b/CesiumGltf/include/CesiumGltf/PropertyTableView.h index 4688b9e4d..c1aac5bf8 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTableView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTableView.h @@ -1209,7 +1209,7 @@ class PropertyTableView { return PropertyTablePropertyView, Normalized>( propertyTableProperty, classProperty, - static_cast(_pPropertyTable->count), + _pPropertyTable->count, values); } @@ -1240,7 +1240,7 @@ class PropertyTableView { return PropertyTablePropertyView, true>( propertyTableProperty, classProperty, - static_cast(_pPropertyTable->count), + _pPropertyTable->count, values, arrayOffsets, arrayOffsetType); @@ -1248,7 +1248,7 @@ class PropertyTableView { return PropertyTablePropertyView, false>( propertyTableProperty, classProperty, - static_cast(_pPropertyTable->count), + _pPropertyTable->count, values, arrayOffsets, gsl::span(), diff --git a/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h index 731627a2d..f49b27baf 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h @@ -2,6 +2,7 @@ #include "CesiumGltf/ImageCesium.h" #include "CesiumGltf/PropertyTextureProperty.h" +#include "CesiumGltf/PropertyTransformations.h" #include "CesiumGltf/PropertyTypeTraits.h" #include "CesiumGltf/PropertyView.h" #include "CesiumGltf/Sampler.h" @@ -78,25 +79,101 @@ class PropertyTexturePropertyViewStatus : public PropertyViewStatus { static const int ErrorChannelsAndTypeMismatch = 20; }; +namespace { +template +ElementType assembleScalarValue(const std::vector& bytes) { + if constexpr (std::is_same_v) { + assert( + bytes.size() == sizeof(float) && + "Not enough channel inputs to construct a float."); + uint32_t resultAsUint = 0; + for (size_t i = 0; i < bytes.size(); i++) { + resultAsUint |= static_cast(bytes[i]) << i * 8; + } + + // Reinterpret the bits as a float. + return *reinterpret_cast(&resultAsUint); + } + + if constexpr (IsMetadataInteger::value) { + using UintType = std::make_unsigned_t; + UintType resultAsUint = 0; + for (size_t i = 0; i < bytes.size(); i++) { + resultAsUint |= static_cast(bytes[i]) << i * 8; + } + + // Reinterpret the bits with the correct signedness. + return *reinterpret_cast(&resultAsUint); + } +} + +double applySamplerWrapS(const double u, const int32_t wrapS) { + if (wrapS == Sampler::WrapS::REPEAT) { + double integral = 0; + double fraction = std::modf(u, &integral); + return fraction < 0 ? 1.0 - fraction : fraction; + } + + if (wrapS == Sampler::WrapS::MIRRORED_REPEAT) { + double integral = 0; + double fraction = std::abs(std::modf(u, &integral)); + int64_t integer = static_cast(std::abs(integral)); + // If the integer part is odd, the direction is reversed. + return integer % 2 == 1 ? 1.0 - fraction : fraction; + } + + return std::clamp(u, 0.0, 1.0); +} + +double applySamplerWrapT(const double v, const int32_t wrapT) { + if (wrapT == Sampler::WrapT::REPEAT) { + double integral = 0; + double fraction = std::modf(v, &integral); + return fraction < 0 ? 1.0 - fraction : fraction; + } + + if (wrapT == Sampler::WrapT::MIRRORED_REPEAT) { + double integral = 0; + double fraction = std::abs(std::modf(v, &integral)); + int64_t integer = static_cast(std::abs(integral)); + // If the integer part is odd, the direction is reversed. + return integer % 2 == 1 ? 1.0 - fraction : fraction; + } + + return std::clamp(v, 0.0, 1.0); +} +} // namespace + /** * @brief A view of the data specified by a {@link PropertyTextureProperty}. * * Provides utilities to sample the property texture property using texture * coordinates. */ -template class PropertyTexturePropertyView { +template +class PropertyTexturePropertyView; + +/** + * @brief A view of the non-normalized data specified by a + * {@link PropertyTextureProperty}. + * + * Provides utilities to sample the property texture property using texture + * coordinates. + */ +template +class PropertyTexturePropertyView + : public PropertyView { public: /** * @brief Constructs an invalid instance for a non-existent property. */ PropertyTexturePropertyView() noexcept - : _status(PropertyTexturePropertyViewStatus::ErrorNonexistentProperty), + : PropertyView(), _pSampler(nullptr), _pImage(nullptr), _texCoordSetIndex(0), _channels(), - _swizzle(""), - _normalized(false) {} + _swizzle("") {} /** * @brief Constructs an invalid instance for an erroneous property. @@ -104,13 +181,12 @@ template class PropertyTexturePropertyView { * @param status The code from {@link PropertyTexturePropertyViewStatus} indicating the error with the property. */ PropertyTexturePropertyView(PropertyViewStatusType status) noexcept - : _status(status), + : PropertyView(), _pSampler(nullptr), _pImage(nullptr), _texCoordSetIndex(0), _channels(), - _swizzle(""), - _normalized(false) { + _swizzle("") { assert( _status != PropertyTexturePropertyViewStatus::Valid && "An empty property view should not be constructed with a valid status"); @@ -119,6 +195,8 @@ template class PropertyTexturePropertyView { /** * @brief Construct a view of the data specified by a {@link PropertyTextureProperty}. * + * @param property The {@link PropertyTextureProperty} + * @param classProperty The {@link ClassProperty} this property conforms to. * @param pSampler A pointer to the sampler used by the property. * @param pImage A pointer to the image used by the property. * @param texCoordSetIndex The value of {@link PropertyTextureProperty::texcoord}. @@ -126,18 +204,22 @@ template class PropertyTexturePropertyView { * @param normalized Whether this property has a normalized integer type. */ PropertyTexturePropertyView( + const PropertyTextureProperty& property, + const ClassProperty& classProperty, const Sampler& sampler, const ImageCesium& image, int64_t texCoordSetIndex, - const std::vector& channels, - bool normalized) noexcept - : _status(PropertyTexturePropertyViewStatus::Valid), + const std::vector& channels) noexcept + : PropertyView(classProperty, property), _pSampler(&sampler), _pImage(&image), _texCoordSetIndex(texCoordSetIndex), _channels(channels), - _swizzle(""), - _normalized(normalized) { + _swizzle("") { + if (this->_status != PropertyTexturePropertyViewStatus::Valid) { + return; + } + for (size_t i = 0; i < _channels.size(); ++i) { switch (_channels[i]) { case 0: @@ -159,8 +241,48 @@ template class PropertyTexturePropertyView { } /** - * @brief Gets the property value for the given texture coordinates. The - * sampler's wrapping mode will be used when sampling the texture. + * @brief Gets the raw value of the property for the given texture + * coordinates with all value transforms applied. That is, if the property + * specifies an offset and scale, they will be applied to the value before the + * value is returned. The sampler's wrapping mode will be used when sampling + * the texture. + * + * If this property has a specified "no data" value, and the retrieved element + * is equal to that value, then this will return the property's specified + * default value. If the property did not provide a default value, this + * returns std::nullopt. + * + * @param u The u-component of the texture coordinates. + * @param v The v-component of the texture coordinates. + * + * @return The value of the element, or std::nullopt if it matches the "no + * data" value + */ + std::optional get(double u, double v) const noexcept { + ElementType value = getRaw(u, v); + + if (this->noData() && value == *(this->noData())) { + return this->defaultValue(); + } + + if constexpr (IsMetadataNumeric::value) { + value = transformValue(value, this->offset(), this->scale()); + } + + if constexpr (IsMetadataNumericArray::value) { + value = transformArray(value, this->offset(), this->scale()); + } + + return value; + } + + /** + * @brief Gets the raw value of the property for the given texture + * coordinates. The sampler's wrapping mode will be used when sampling the + * texture. + * + * If this property has a specified "no data" value, the raw value will still + * be returned, even if it equals the "no data" value. * * @param u The u-component of the texture coordinates. * @param v The v-component of the texture coordinates. @@ -168,7 +290,7 @@ template class PropertyTexturePropertyView { * @return The value at the nearest pixel to the texture coordinates. */ - ElementType get(double u, double v) const noexcept { + ElementType getRaw(double u, double v) const noexcept { assert( _status == PropertyTexturePropertyViewStatus::Valid && "Check the status() first to make sure view is valid"); @@ -215,13 +337,6 @@ template class PropertyTexturePropertyView { return assembleValueFromChannels(channelValues); } - /** - * @brief Get the status of this view. - * - * If invalid, this view cannot be sampled. - */ - PropertyViewStatusType status() const noexcept { return this->_status; } - /** * @brief Get the texture coordinate set index for this property. */ @@ -229,11 +344,6 @@ template class PropertyTexturePropertyView { return this->_texCoordSetIndex; } - /** - * @brief Whether the component type for this property should be normalized. - */ - bool isNormalized() const noexcept { return this->_normalized; } - /** * @brief Get the image containing this property's data. * @@ -260,7 +370,7 @@ template class PropertyTexturePropertyView { assert(bytes.size() > 0 && "Channel input must have at least one value."); if constexpr (IsMetadataScalar::value) { - return assembleScalarValue(bytes); + return assembleScalarValue(bytes); } if constexpr (IsMetadataVecN::value) { @@ -273,33 +383,6 @@ template class PropertyTexturePropertyView { } } - ElementType - assembleScalarValue(const std::vector& bytes) const noexcept { - if constexpr (std::is_same_v) { - assert( - bytes.size() == sizeof(float) && - "Not enough channel inputs to construct a float."); - uint32_t resultAsUint = 0; - for (size_t i = 0; i < bytes.size(); i++) { - resultAsUint |= static_cast(bytes[i]) << i * 8; - } - - // Reinterpret the bits as a float. - return *reinterpret_cast(&resultAsUint); - } - - if constexpr (IsMetadataInteger::value) { - using UintType = std::make_unsigned_t; - UintType resultAsUint = 0; - for (size_t i = 0; i < bytes.size(); i++) { - resultAsUint |= static_cast(bytes[i]) << i * 8; - } - - // Reinterpret the bits with the correct signedness. - return *reinterpret_cast(&resultAsUint); - } - } - ElementType assembleVecNValue(const std::vector& bytes) const noexcept { const glm::length_t N = @@ -379,49 +462,356 @@ template class PropertyTexturePropertyView { return PropertyArrayView(std::move(result)); } - double applySamplerWrapS(const double u, const int32_t wrapS) const noexcept { - if (wrapS == Sampler::WrapS::REPEAT) { - double integral = 0; - double fraction = std::modf(u, &integral); - return fraction < 0 ? 1.0 - fraction : fraction; + const Sampler* _pSampler; + const ImageCesium* _pImage; + int64_t _texCoordSetIndex; + std::vector _channels; + std::string _swizzle; +}; + +/** + * @brief A view of the normalized data specified by a + * {@link PropertyTextureProperty}. + * + * Provides utilities to sample the property texture property using texture + * coordinates. + */ +template +class PropertyTexturePropertyView + : public PropertyView { +private: + using NormalizedType = typename TypeToNormalizedType::type; + +public: + /** + * @brief Constructs an invalid instance for a non-existent property. + */ + PropertyTexturePropertyView() noexcept + : PropertyView(), + _pSampler(nullptr), + _pImage(nullptr), + _texCoordSetIndex(0), + _channels(), + _swizzle("") {} + + /** + * @brief Constructs an invalid instance for an erroneous property. + * + * @param status The code from {@link PropertyTexturePropertyViewStatus} indicating the error with the property. + */ + PropertyTexturePropertyView(PropertyViewStatusType status) noexcept + : PropertyView(), + _pSampler(nullptr), + _pImage(nullptr), + _texCoordSetIndex(0), + _channels(), + _swizzle("") { + assert( + _status != PropertyTexturePropertyViewStatus::Valid && + "An empty property view should not be constructed with a valid status"); + } + + /** + * @brief Construct a view of the data specified by a {@link PropertyTextureProperty}. + * + * @param property The {@link PropertyTextureProperty} + * @param classProperty The {@link ClassProperty} this property conforms to. + * @param pSampler A pointer to the sampler used by the property. + * @param pImage A pointer to the image used by the property. + * @param texCoordSetIndex The value of {@link PropertyTextureProperty::texcoord}. + * @param channels The value of {@link PropertyTextureProperty::channels}. + * @param normalized Whether this property has a normalized integer type. + */ + PropertyTexturePropertyView( + const PropertyTextureProperty& property, + const ClassProperty& classProperty, + const Sampler& sampler, + const ImageCesium& image, + int64_t texCoordSetIndex, + const std::vector& channels) noexcept + : PropertyView(classProperty, property), + _pSampler(&sampler), + _pImage(&image), + _texCoordSetIndex(texCoordSetIndex), + _channels(channels), + _swizzle("") { + if (this->_status != PropertyTexturePropertyViewStatus::Valid) { + return; } - if (wrapS == Sampler::WrapS::MIRRORED_REPEAT) { - double integral = 0; - double fraction = std::abs(std::modf(u, &integral)); - int64_t integer = static_cast(std::abs(integral)); - // If the integer part is odd, the direction is reversed. - return integer % 2 == 1 ? 1.0 - fraction : fraction; + for (size_t i = 0; i < _channels.size(); ++i) { + switch (_channels[i]) { + case 0: + _swizzle += "r"; + break; + case 1: + _swizzle += "g"; + break; + case 2: + _swizzle += "b"; + break; + case 3: + _swizzle += "a"; + break; + default: + assert(false && "A valid channels vector must be passed to the view."); + } } + } + + /** + * @brief Gets the value of the property for the given texture coordinates + * with all value transforms applied. That is, if the property specifies an + * offset and scale, they will be applied to the value before the value is + * returned. The sampler's wrapping mode will be used when sampling the + * texture. + * + * If this property has a specified "no data" value, and the retrieved element + * is equal to that value, then this will return the property's specified + * default value. If the property did not provide a default value, this + * returns std::nullopt. + * + * @param u The u-component of the texture coordinates. + * @param v The v-component of the texture coordinates. + * + * @return The value of the element, or std::nullopt if it matches the "no + * data" value + */ + std::optional get(double u, double v) const noexcept { + ElementType value = getRaw(u, v); - return std::clamp(u, 0.0, 1.0); + if (this->noData() && value == *(this->noData())) { + return this->defaultValue(); + } + + if constexpr (IsMetadataScalar::value) { + return transformValue( + normalize(value), + this->offset(), + this->scale()); + } + + if constexpr (IsMetadataVecN::value) { + constexpr glm::length_t N = ElementType::length(); + using T = typename ElementType::value_type; + using NormalizedT = typename NormalizedType::value_type; + return transformValue>( + normalize(value), + this->offset(), + this->scale()); + } + + if constexpr (IsMetadataArray::value) { + using ArrayElementType = typename MetadataArrayType::type; + if constexpr (IsMetadataScalar::value) { + return transformNormalizedArray( + value, + this->offset(), + this->scale()); + } + + if constexpr (IsMetadataVecN::value) { + constexpr glm::length_t N = ArrayElementType::length(); + using T = typename ArrayElementType::value_type; + return transformNormalizedVecNArray( + value, + this->offset(), + this->scale()); + } + } } - double applySamplerWrapT(const double v, const int32_t wrapT) const noexcept { - if (wrapT == Sampler::WrapT::REPEAT) { - double integral = 0; - double fraction = std::modf(v, &integral); - return fraction < 0 ? 1.0 - fraction : fraction; + /** + * @brief Gets the raw value of the property for the given texture + * coordinates. The sampler's wrapping mode will be used when sampling the + * texture. + * + * If this property has a specified "no data" value, the raw value will still + * be returned, even if it equals the "no data" value. + * + * @param u The u-component of the texture coordinates. + * @param v The v-component of the texture coordinates. + * + * @return The value at the nearest pixel to the texture coordinates. + */ + + ElementType getRaw(double u, double v) const noexcept { + assert( + _status == PropertyTexturePropertyViewStatus::Valid && + "Check the status() first to make sure view is valid"); + + double wrappedU = applySamplerWrapS(u, this->_pSampler->wrapS); + double wrappedV = applySamplerWrapT(v, this->_pSampler->wrapT); + + // TODO: account for sampler's filter (can be nearest or linear) + + // For nearest filtering, std::floor is used instead of std::round. + // This is because filtering is supposed to consider the pixel centers. But + // memory access here acts as sampling the beginning of the pixel. Example: + // 0.4 * 2 = 0.8. In a 2x1 pixel image, that should be closer to the left + // pixel's center. But it will round to 1.0 which corresponds to the right + // pixel. So the right pixel has a bigger range than the left one, which is + // incorrect. + double xCoord = std::floor(wrappedU * this->_pImage->width); + double yCoord = std::floor(wrappedV * this->_pImage->height); + + // Clamp to ensure no out-of-bounds data access + int64_t x = std::clamp( + static_cast(xCoord), + static_cast(0), + static_cast(this->_pImage->width) - 1); + int64_t y = std::clamp( + static_cast(yCoord), + static_cast(0), + static_cast(this->_pImage->height) - 1); + + int64_t pixelIndex = this->_pImage->bytesPerChannel * + this->_pImage->channels * + (y * this->_pImage->width + x); + + // TODO: Currently stb only outputs uint8 pixel types. If that + // changes this should account for additional pixel byte sizes. + const uint8_t* pValue = reinterpret_cast( + this->_pImage->pixelData.data() + pixelIndex); + + std::vector channelValues(this->_channels.size()); + for (size_t i = 0; i < this->_channels.size(); i++) { + channelValues[i] = *(pValue + this->_channels[i]); } - if (wrapT == Sampler::WrapT::MIRRORED_REPEAT) { - double integral = 0; - double fraction = std::abs(std::modf(v, &integral)); - int64_t integer = static_cast(std::abs(integral)); - // If the integer part is odd, the direction is reversed. - return integer % 2 == 1 ? 1.0 - fraction : fraction; + return assembleValueFromChannels(channelValues); + } + + /** + * @brief Get the texture coordinate set index for this property. + */ + int64_t getTexCoordSetIndex() const noexcept { + return this->_texCoordSetIndex; + } + + /** + * @brief Get the image containing this property's data. + * + * This will be nullptr if the property texture property view runs into + * problems during construction. + */ + const ImageCesium* getImage() const noexcept { return this->_pImage; } + + /** + * @brief Gets the channels of this property texture property. + */ + const std::vector& getChannels() const noexcept { + return this->_channels; + } + + /** + * @brief Gets this property's channels as a swizzle string. + */ + const std::string& getSwizzle() const noexcept { return this->_swizzle; } + +private: + ElementType + assembleValueFromChannels(const std::vector& bytes) const noexcept { + assert(bytes.size() > 0 && "Channel input must have at least one value."); + + if constexpr (IsMetadataScalar::value) { + return assembleScalarValue(bytes); } - return std::clamp(v, 0.0, 1.0); + if constexpr (IsMetadataVecN::value) { + return assembleVecNValue(bytes); + } + + if constexpr (IsMetadataArray::value) { + return assembleArrayValue::type>( + bytes); + } + } + + ElementType + assembleVecNValue(const std::vector& bytes) const noexcept { + const glm::length_t N = + getDimensionsFromPropertyType(TypeToPropertyType::value); + switch (N) { + case 2: + return assembleVecNValueImpl<2, typename ElementType::value_type>(bytes); + case 3: + return assembleVecNValueImpl<3, typename ElementType::value_type>(bytes); + case 4: + return assembleVecNValueImpl<4, typename ElementType::value_type>(bytes); + default: + return ElementType(); + } + } + + template + ElementType + assembleVecNValueImpl(const std::vector& bytes) const noexcept { + ElementType result = ElementType(); + assert( + sizeof(T) <= 2 && + "Components cannot be larger than two bytes in size."); + + if constexpr (std::is_same_v) { + assert(N == 2 && "Only vec2s can contain two-byte integer components."); + uint16_t x = static_cast(bytes[0]) | + (static_cast(bytes[1]) << 8); + uint16_t y = static_cast(bytes[2]) | + (static_cast(bytes[3]) << 8); + + result[0] = *reinterpret_cast(&x); + result[1] = *reinterpret_cast(&y); + } + + if constexpr (std::is_same_v) { + assert(N == 2 && "Only vec2s can contain two-byte integer components."); + result[0] = static_cast(bytes[0]) | + (static_cast(bytes[1]) << 8); + result[1] = static_cast(bytes[2]) | + (static_cast(bytes[3]) << 8); + } + + if constexpr (std::is_same_v) { + for (size_t i = 0; i < bytes.size(); i++) { + result[i] = *reinterpret_cast(&bytes[i]); + } + } + + if constexpr (std::is_same_v) { + for (size_t i = 0; i < bytes.size(); i++) { + result[i] = bytes[i]; + } + } + + return result; } - PropertyViewStatusType _status; + template + PropertyArrayView + assembleArrayValue(const std::vector& bytes) const noexcept { + std::vector result(bytes.size() / sizeof(T)); + + if constexpr (sizeof(T) == 2) { + for (int i = 0, b = 0; i < result.size(); i++, b += 2) { + using UintType = std::make_unsigned_t; + UintType resultAsUint = static_cast(bytes[b]) | + (static_cast(bytes[b + 1]) << 8); + result[i] = *reinterpret_cast(&resultAsUint); + } + } else { + for (size_t i = 0; i < bytes.size(); i++) { + result[i] = *reinterpret_cast(&bytes[i]); + } + } + + return PropertyArrayView(std::move(result)); + } const Sampler* _pSampler; const ImageCesium* _pImage; int64_t _texCoordSetIndex; std::vector _channels; std::string _swizzle; - bool _normalized; }; + } // namespace CesiumGltf diff --git a/CesiumGltf/src/PropertyTableView.cpp b/CesiumGltf/src/PropertyTableView.cpp index e26b53bc4..f2456cf77 100644 --- a/CesiumGltf/src/PropertyTableView.cpp +++ b/CesiumGltf/src/PropertyTableView.cpp @@ -373,7 +373,7 @@ PropertyTableView::getBooleanArrayPropertyValues( return PropertyTablePropertyView>( propertyTableProperty, classProperty, - static_cast(_pPropertyTable->count), + _pPropertyTable->count, values); } @@ -402,7 +402,7 @@ PropertyTableView::getBooleanArrayPropertyValues( return PropertyTablePropertyView>( propertyTableProperty, classProperty, - static_cast(_pPropertyTable->count), + _pPropertyTable->count, values, arrayOffsets, gsl::span(), diff --git a/CesiumGltf/test/TestPropertyTablePropertyView.cpp b/CesiumGltf/test/TestPropertyTablePropertyView.cpp index 5ed7f2444..d6fb63878 100644 --- a/CesiumGltf/test/TestPropertyTablePropertyView.cpp +++ b/CesiumGltf/test/TestPropertyTablePropertyView.cpp @@ -53,7 +53,7 @@ template static void checkNumeric(const std::vector& expected) { } template -static void checkScalar( +static void checkNumeric( const std::vector& values, const std::vector>& expected, const std::optional offset = std::nullopt, @@ -85,6 +85,7 @@ static void checkScalar( gsl::span(data.data(), data.size())); REQUIRE(property.size() == static_cast(expected.size())); + REQUIRE(!property.normalized()); for (int64_t i = 0; i < property.size(); ++i) { REQUIRE(property.getRaw(i) == values[static_cast(i)]); @@ -97,90 +98,10 @@ static void checkScalar( } } -template -static void checkVecN( - const std::vector>& values, - const std::vector>>& expected, - const std::optional offset = std::nullopt, - const std::optional scale = std::nullopt, - const std::optional noData = std::nullopt, - const std::optional defaultValue = std::nullopt) { - std::vector data; - data.resize(values.size() * sizeof(glm::vec)); - std::memcpy(data.data(), values.data(), data.size()); - - PropertyTableProperty propertyTableProperty; - ClassProperty classProperty; - classProperty.type = - convertPropertyTypeToString(TypeToPropertyType>::value); - - PropertyComponentType componentType = TypeToPropertyType::component; - classProperty.componentType = - convertPropertyComponentTypeToString(componentType); - - classProperty.offset = offset; - classProperty.scale = scale; - classProperty.noData = noData; - classProperty.defaultProperty = defaultValue; - - PropertyTablePropertyView> property( - propertyTableProperty, - classProperty, - static_cast(expected.size()), - gsl::span(data.data(), data.size())); - - REQUIRE(property.size() == static_cast(expected.size())); - - for (int64_t i = 0; i < property.size(); ++i) { - REQUIRE(property.getRaw(i) == values[static_cast(i)]); - REQUIRE(property.get(i) == expected[static_cast(i)]); - } -} - -template -static void checkMatN( - const std::vector>& values, - const std::vector>>& expected, - const std::optional offset = std::nullopt, - const std::optional scale = std::nullopt, - const std::optional noData = std::nullopt, - const std::optional defaultValue = std::nullopt) { - std::vector data; - data.resize(values.size() * sizeof(glm::mat)); - std::memcpy(data.data(), values.data(), data.size()); - - PropertyTableProperty propertyTableProperty; - ClassProperty classProperty; - classProperty.type = - convertPropertyTypeToString(TypeToPropertyType>::value); - - PropertyComponentType componentType = TypeToPropertyType::component; - classProperty.componentType = - convertPropertyComponentTypeToString(componentType); - - classProperty.offset = offset; - classProperty.scale = scale; - classProperty.noData = noData; - classProperty.defaultProperty = defaultValue; - - PropertyTablePropertyView> property( - propertyTableProperty, - classProperty, - static_cast(expected.size()), - gsl::span(data.data(), data.size())); - - REQUIRE(property.size() == static_cast(expected.size())); - - for (int64_t i = 0; i < property.size(); ++i) { - REQUIRE(property.getRaw(i) == values[static_cast(i)]); - REQUIRE(property.get(i) == expected[static_cast(i)]); - } -} - -template -static void checkNormalizedScalar( +template ::type> +static void checkNormalizedNumeric( const std::vector& values, - const std::vector>& expected, + const std::vector>& expected, const std::optional offset = std::nullopt, const std::optional scale = std::nullopt, const std::optional noData = std::nullopt, @@ -212,90 +133,7 @@ static void checkNormalizedScalar( gsl::span(data.data(), data.size())); REQUIRE(property.size() == static_cast(expected.size())); - - for (int64_t i = 0; i < property.size(); ++i) { - REQUIRE(property.getRaw(i) == values[static_cast(i)]); - REQUIRE(property.get(i) == expected[static_cast(i)]); - } -} - -template -static void checkNormalizedVecN( - const std::vector>& values, - const std::vector>>& expected, - const std::optional offset = std::nullopt, - const std::optional scale = std::nullopt, - const std::optional noData = std::nullopt, - const std::optional defaultValue = std::nullopt) { - std::vector data; - data.resize(values.size() * sizeof(glm::vec)); - std::memcpy(data.data(), values.data(), data.size()); - - PropertyTableProperty propertyTableProperty; - ClassProperty classProperty; - classProperty.type = - convertPropertyTypeToString(TypeToPropertyType>::value); - - PropertyComponentType componentType = TypeToPropertyType::component; - classProperty.componentType = - convertPropertyComponentTypeToString(componentType); - - classProperty.normalized = true; - - classProperty.offset = offset; - classProperty.scale = scale; - classProperty.noData = noData; - classProperty.defaultProperty = defaultValue; - - PropertyTablePropertyView, true> property( - propertyTableProperty, - classProperty, - static_cast(expected.size()), - gsl::span(data.data(), data.size())); - - REQUIRE(property.size() == static_cast(expected.size())); - - for (int64_t i = 0; i < property.size(); ++i) { - REQUIRE(property.getRaw(i) == values[static_cast(i)]); - REQUIRE(property.get(i) == expected[static_cast(i)]); - } -} - -template -static void checkNormalizedMatN( - const std::vector>& values, - const std::vector>>& expected, - const std::optional offset = std::nullopt, - const std::optional scale = std::nullopt, - const std::optional noData = std::nullopt, - const std::optional defaultValue = std::nullopt) { - std::vector data; - data.resize(values.size() * sizeof(glm::mat)); - std::memcpy(data.data(), values.data(), data.size()); - - PropertyTableProperty propertyTableProperty; - ClassProperty classProperty; - classProperty.type = - convertPropertyTypeToString(TypeToPropertyType>::value); - - PropertyComponentType componentType = TypeToPropertyType::component; - classProperty.componentType = - convertPropertyComponentTypeToString(componentType); - - classProperty.normalized = true; - - classProperty.offset = offset; - classProperty.scale = scale; - classProperty.noData = noData; - classProperty.defaultProperty = defaultValue; - - PropertyTablePropertyView, true> property( - propertyTableProperty, - classProperty, - static_cast(expected.size()), - gsl::span(data.data(), data.size())); - - REQUIRE(property.size() == static_cast(expected.size())); + REQUIRE(property.normalized()); for (int64_t i = 0; i < property.size(); ++i) { REQUIRE(property.getRaw(i) == values[static_cast(i)]); @@ -744,7 +582,7 @@ TEST_CASE("Check scalar PropertyTablePropertyView") { 64.0 / 255.0, 128.0 / 255.0, 1.0}; - checkNormalizedScalar(values, expected); + checkNormalizedNumeric(values, expected); } SECTION("Normalized Int16") { @@ -754,7 +592,7 @@ TEST_CASE("Check scalar PropertyTablePropertyView") { 0.0, 16384.0 / 32767.0, 1.0}; - checkNormalizedScalar(values, expected); + checkNormalizedNumeric(values, expected); } SECTION("Float with Offset / Scale") { @@ -762,7 +600,7 @@ TEST_CASE("Check scalar PropertyTablePropertyView") { std::optional offset = 1.0f; std::optional scale = 2.0f; std::vector> expected{26.0f, -24.0f, -9.0f, 14.5f}; - checkScalar(values, expected, offset, scale); + checkNumeric(values, expected, offset, scale); } SECTION("Normalized Uint8 with Offset and Scale") { @@ -774,7 +612,7 @@ TEST_CASE("Check scalar PropertyTablePropertyView") { 1 + 2 * (64.0 / 255.0), 1 + 2 * (128.0 / 255.0), 3.0}; - checkNormalizedScalar(values, expected, offset, scale); + checkNormalizedNumeric(values, expected, offset, scale); } SECTION("Int16 with NoData") { @@ -785,7 +623,7 @@ TEST_CASE("Check scalar PropertyTablePropertyView") { static_cast(3), static_cast(7), std::nullopt}; - checkScalar( + checkNumeric( values, expected, std::nullopt, @@ -803,7 +641,7 @@ TEST_CASE("Check scalar PropertyTablePropertyView") { static_cast(3), static_cast(7), static_cast(0)}; - checkScalar( + checkNumeric( values, expected, std::nullopt, @@ -823,7 +661,7 @@ TEST_CASE("Check scalar PropertyTablePropertyView") { 1 + 2 * (64.0 / 255.0), 1 + 2 * (128.0 / 255.0), 3.0}; - checkNormalizedScalar( + checkNormalizedNumeric( values, expected, offset, @@ -915,7 +753,7 @@ TEST_CASE("Check vecN PropertyTablePropertyView") { glm::dvec2(0.0, 64.0 / 255.0), glm::dvec2(128.0 / 255.0, 1.0), glm::dvec2(1.0, 0.0)}; - checkNormalizedVecN(values, expected); + checkNormalizedNumeric(values, expected); } SECTION("Normalized Int16 Vec2") { @@ -928,7 +766,7 @@ TEST_CASE("Check vecN PropertyTablePropertyView") { glm::dvec2(16384.0 / 32767.0, 1.0), glm::dvec2(1.0, -1.0), }; - checkNormalizedVecN(values, expected); + checkNormalizedNumeric(values, expected); } SECTION("Float Vec3 with Offset / Scale") { @@ -944,7 +782,7 @@ TEST_CASE("Check vecN PropertyTablePropertyView") { glm::vec3(14.0f, 4.0f, 11.0f), glm::vec3(17.0f, -1.0f, 5.0f), }; - checkVecN(values, expected, offset, scale); + checkNumeric(values, expected, offset, scale); } SECTION("Normalized Uint8 Vec2 with Offset and Scale") { @@ -958,7 +796,7 @@ TEST_CASE("Check vecN PropertyTablePropertyView") { glm::dvec2(0.0, 1 + (64.0 / 255.0)), glm::dvec2(2 * (128.0 / 255.0), 2.0), glm::dvec2(2.0, 1.0)}; - checkNormalizedVecN(values, expected, offset, scale); + checkNormalizedNumeric(values, expected, offset, scale); } SECTION("Int16 Vec2 with NoData") { @@ -971,7 +809,7 @@ TEST_CASE("Check vecN PropertyTablePropertyView") { glm::i16vec2(-1, 3), std::nullopt, glm::i16vec2(7, 0)}; - checkVecN<2, glm::i16>( + checkNumeric( values, expected, std::nullopt, @@ -991,7 +829,7 @@ TEST_CASE("Check vecN PropertyTablePropertyView") { glm::i16vec2(-1, 3), glm::i16vec2(0, 1), glm::i16vec2(7, 0)}; - checkVecN<2, glm::i16>( + checkNumeric( values, expected, std::nullopt, @@ -1015,7 +853,13 @@ TEST_CASE("Check vecN PropertyTablePropertyView") { glm::dvec2(2 * (128.0 / 255.0), 2.0), glm::dvec2(2.0, 1.0), glm::dvec2(5.0, 15.0)}; - checkNormalizedVecN(values, expected, offset, scale, noData, defaultValue); + checkNormalizedNumeric( + values, + expected, + offset, + scale, + noData, + defaultValue); } SECTION("Overrides class property values") { @@ -1148,7 +992,7 @@ TEST_CASE("Check matN PropertyTablePropertyView") { 1.0, 0.0, 128.0 / 255.0, 0.0)}; // clang-format on - checkNormalizedMatN(values, expected); + checkNormalizedNumeric(values, expected); } SECTION("Normalized Int16 Mat2") { @@ -1169,7 +1013,7 @@ TEST_CASE("Check matN PropertyTablePropertyView") { 1.0, -1.0), }; // clang-format on - checkNormalizedMatN(values, expected); + checkNormalizedNumeric(values, expected); } SECTION("Float Mat2 with Offset / Scale") { @@ -1203,7 +1047,7 @@ TEST_CASE("Check matN PropertyTablePropertyView") { 3.0f, 3.0f), }; // clang-format on - checkMatN(values, expected, offset, scale); + checkNumeric(values, expected, offset, scale); } SECTION("Normalized Uint8 Mat2 with Offset and Scale") { @@ -1229,7 +1073,7 @@ TEST_CASE("Check matN PropertyTablePropertyView") { 2.0, 1.0, 1.0, 0.0)}; // clang-format on - checkNormalizedMatN(values, expected, offset, scale); + checkNormalizedNumeric(values, expected, offset, scale); } SECTION("Int16 Mat3 with NoData") { @@ -1257,7 +1101,7 @@ TEST_CASE("Check matN PropertyTablePropertyView") { values[0], values[1], std::nullopt}; - checkMatN<3, glm::i16>( + checkNumeric( values, expected, std::nullopt, @@ -1294,7 +1138,7 @@ TEST_CASE("Check matN PropertyTablePropertyView") { values[0], values[1], glm::i16mat3x3(1)}; - checkMatN<3, glm::i16>( + checkNumeric( values, expected, std::nullopt, @@ -1335,7 +1179,13 @@ TEST_CASE("Check matN PropertyTablePropertyView") { 2.0, 1.0, 1.0, 0.0)}; // clang-format on - checkNormalizedMatN(values, expected, offset, scale, noData, defaultValue); + checkNormalizedNumeric( + values, + expected, + offset, + scale, + noData, + defaultValue); } SECTION("Overrides class property values") { diff --git a/CesiumGltf/test/TestPropertyTexturePropertyView.cpp b/CesiumGltf/test/TestPropertyTexturePropertyView.cpp index 7a329d00f..4cad0637f 100644 --- a/CesiumGltf/test/TestPropertyTexturePropertyView.cpp +++ b/CesiumGltf/test/TestPropertyTexturePropertyView.cpp @@ -10,75 +10,479 @@ #include using namespace CesiumGltf; +using namespace CesiumUtility; + +std::vector texCoords{ + glm::dvec2(0, 0), + glm::dvec2(0.5, 0), + glm::dvec2(0, 0.5), + glm::dvec2(0.5, 0.5)}; + +template +void checkTextureValues( + const std::vector& data, + const std::vector& expected) { + PropertyTextureProperty property; + ClassProperty classProperty; + classProperty.type = + convertPropertyTypeToString(TypeToPropertyType::value); + + PropertyComponentType componentType = TypeToPropertyType::component; + classProperty.componentType = + convertPropertyComponentTypeToString(componentType); + + Sampler sampler; + ImageCesium image; + image.width = 2; + image.height = 2; + image.channels = static_cast(sizeof(T)); + image.bytesPerChannel = 1; + + std::vector& imageData = image.pixelData; + imageData.resize(data.size()); + std::memcpy(imageData.data(), data.data(), data.size()); + + std::vector channels; + for (int32_t i = 0; i < image.channels; i++) { + channels.push_back(i); + } -TEST_CASE("Check scalar PropertyTexturePropertyView") { - SECTION("uint8_t") { - Sampler sampler; - ImageCesium image; - image.width = 2; - image.height = 2; - image.channels = 1; - image.bytesPerChannel = 1; + PropertyTexturePropertyView + view(property, classProperty, sampler, image, 0, channels); + switch (sizeof(T)) { + case 1: + CHECK(view.getSwizzle() == "r"); + break; + case 2: + CHECK(view.getSwizzle() == "rg"); + break; + case 3: + CHECK(view.getSwizzle() == "rgb"); + break; + case 4: + CHECK(view.getSwizzle() == "rgba"); + break; + default: + FAIL("Invalid property texture property view type"); + } - std::vector data{12, 33, 56, 67}; - std::vector& imageData = image.pixelData; - imageData.resize(data.size()); - std::memcpy(imageData.data(), data.data(), data.size()); + REQUIRE(!view.normalized()); - std::vector channels{0}; + for (size_t i = 0; i < texCoords.size(); i++) { + glm::dvec2 uv = texCoords[i]; + auto value = view.getRaw(uv[0], uv[1]); + REQUIRE(value == expected[i]); - PropertyTexturePropertyView - view(sampler, image, 0, channels, false); + auto maybeValue = view.get(uv[0], uv[1]); + REQUIRE(maybeValue); + REQUIRE(*maybeValue == expected[i]); + } +} + +template +void checkTextureValues( + const std::vector& data, + const std::vector& expectedRaw, + const std::vector>& expectedTransformed, + const std::optional offset = std::nullopt, + const std::optional scale = std::nullopt, + const std::optional noData = std::nullopt, + const std::optional defaultValue = std::nullopt) { + PropertyTextureProperty property; + ClassProperty classProperty; + classProperty.type = + convertPropertyTypeToString(TypeToPropertyType::value); + + PropertyComponentType componentType = TypeToPropertyType::component; + classProperty.componentType = + convertPropertyComponentTypeToString(componentType); + + classProperty.offset = offset; + classProperty.scale = scale; + classProperty.noData = noData; + classProperty.defaultProperty = defaultValue; + + Sampler sampler; + ImageCesium image; + image.width = 2; + image.height = 2; + image.channels = static_cast(sizeof(T)); + image.bytesPerChannel = 1; + + std::vector& imageData = image.pixelData; + imageData.resize(data.size()); + std::memcpy(imageData.data(), data.data(), data.size()); + + std::vector channels; + for (int32_t i = 0; i < image.channels; i++) { + channels.push_back(i); + } + + PropertyTexturePropertyView + view(property, classProperty, sampler, image, 0, channels); + switch (sizeof(T)) { + case 1: CHECK(view.getSwizzle() == "r"); + break; + case 2: + CHECK(view.getSwizzle() == "rg"); + break; + case 3: + CHECK(view.getSwizzle() == "rgb"); + break; + case 4: + CHECK(view.getSwizzle() == "rgba"); + break; + default: + FAIL("Invalid property texture property view type"); + } + + REQUIRE(!view.normalized()); + + for (size_t i = 0; i < texCoords.size(); i++) { + glm::dvec2 uv = texCoords[i]; + auto value = view.getRaw(uv[0], uv[1]); + REQUIRE(value == expectedRaw[i]); - std::vector values{ - view.get(0, 0), - view.get(0.5, 0), - view.get(0, 0.5), - view.get(0.5, 0.5)}; - for (size_t i = 0; i < values.size(); i++) { - REQUIRE(values[i] == data[i]); + auto maybeValue = view.get(uv[0], uv[1]); + REQUIRE(maybeValue == expectedTransformed[i]); + } +} + +template ::type> +void checkNormalizedTextureValues( + const std::vector& data, + const std::vector& expectedRaw, + const std::vector>& expectedTransformed, + const std::optional offset = std::nullopt, + const std::optional scale = std::nullopt, + const std::optional noData = std::nullopt, + const std::optional defaultValue = std::nullopt) { + PropertyTextureProperty property; + ClassProperty classProperty; + classProperty.type = + convertPropertyTypeToString(TypeToPropertyType::value); + + PropertyComponentType componentType = TypeToPropertyType::component; + classProperty.componentType = + convertPropertyComponentTypeToString(componentType); + + classProperty.normalized = true; + + classProperty.offset = offset; + classProperty.scale = scale; + classProperty.noData = noData; + classProperty.defaultProperty = defaultValue; + + Sampler sampler; + ImageCesium image; + image.width = 2; + image.height = 2; + image.channels = static_cast(sizeof(T)); + image.bytesPerChannel = 1; + + std::vector& imageData = image.pixelData; + imageData.resize(data.size()); + std::memcpy(imageData.data(), data.data(), data.size()); + + std::vector channels; + for (int32_t i = 0; i < image.channels; i++) { + channels.push_back(i); + } + + PropertyTexturePropertyView + view(property, classProperty, sampler, image, 0, channels); + switch (sizeof(T)) { + case 1: + CHECK(view.getSwizzle() == "r"); + break; + case 2: + CHECK(view.getSwizzle() == "rg"); + break; + case 3: + CHECK(view.getSwizzle() == "rgb"); + break; + case 4: + CHECK(view.getSwizzle() == "rgba"); + break; + default: + FAIL("Invalid property texture property view type"); + } + + REQUIRE(view.normalized()); + + for (size_t i = 0; i < texCoords.size(); i++) { + glm::dvec2 uv = texCoords[i]; + auto value = view.getRaw(uv[0], uv[1]); + REQUIRE(value == expectedRaw[i]); + + auto maybeValue = view.get(uv[0], uv[1]); + REQUIRE(maybeValue == expectedTransformed[i]); + } +} + +template +void checkTextureArrayValues( + const std::vector& data, + const int64_t count, + const std::vector>& expected) { + PropertyTextureProperty property; + ClassProperty classProperty; + classProperty.type = + convertPropertyTypeToString(TypeToPropertyType::value); + + PropertyComponentType componentType = TypeToPropertyType::component; + classProperty.componentType = + convertPropertyComponentTypeToString(componentType); + + classProperty.array = true; + classProperty.count = count; + + Sampler sampler; + ImageCesium image; + image.width = 2; + image.height = 2; + image.channels = + static_cast(count) * static_cast(sizeof(T)); + image.bytesPerChannel = 1; + + std::vector& imageData = image.pixelData; + imageData.resize(data.size()); + std::memcpy(imageData.data(), data.data(), data.size()); + + std::vector channels; + for (int32_t i = 0; i < image.channels; i++) { + channels.push_back(i); + } + + PropertyTexturePropertyView> + view(property, classProperty, sampler, image, 0, channels); + switch (image.channels) { + case 1: + CHECK(view.getSwizzle() == "r"); + break; + case 2: + CHECK(view.getSwizzle() == "rg"); + break; + case 3: + CHECK(view.getSwizzle() == "rgb"); + break; + case 4: + CHECK(view.getSwizzle() == "rgba"); + break; + default: + FAIL("Unsupported count value"); + } + + REQUIRE(!view.normalized()); + + for (size_t i = 0; i < texCoords.size(); i++) { + const std::vector& expectedValue = expected[i]; + glm::dvec2 uv = texCoords[i]; + + auto value = view.getRaw(uv[0], uv[1]); + REQUIRE(static_cast(value.size()) == expectedValue.size()); + + auto maybeValue = view.get(uv[0], uv[1]); + REQUIRE(maybeValue); + REQUIRE(maybeValue->size() == value.size()); + for (int64_t j = 0; j < value.size(); j++) { + REQUIRE(value[j] == expectedValue[static_cast(j)]); + REQUIRE((*maybeValue)[j] == value[j]); } } +} - SECTION("int8_t") { - Sampler sampler; - ImageCesium image; - image.width = 2; - image.height = 2; - image.channels = 1; - image.bytesPerChannel = 1; +template +void checkTextureArrayValues( + const std::vector& data, + const int64_t count, + const std::vector>& expectedRaw, + const std::vector>>& expectedTransformed, + const std::optional noData = std::nullopt, + const std::optional defaultValue = std::nullopt) { + PropertyTextureProperty property; + ClassProperty classProperty; + classProperty.type = + convertPropertyTypeToString(TypeToPropertyType::value); + + PropertyComponentType componentType = TypeToPropertyType::component; + classProperty.componentType = + convertPropertyComponentTypeToString(componentType); + + classProperty.array = true; + classProperty.count = count; + classProperty.noData = noData; + classProperty.defaultProperty = defaultValue; + + Sampler sampler; + ImageCesium image; + image.width = 2; + image.height = 2; + image.channels = + static_cast(count) * static_cast(sizeof(T)); + image.bytesPerChannel = 1; + + std::vector& imageData = image.pixelData; + imageData.resize(data.size()); + std::memcpy(imageData.data(), data.data(), data.size()); + + std::vector channels; + for (int32_t i = 0; i < image.channels; i++) { + channels.push_back(i); + } - std::vector data{255, 0, 223, 67}; - std::vector& imageData = image.pixelData; - imageData.resize(data.size()); - std::memcpy(imageData.data(), data.data(), data.size()); + PropertyTexturePropertyView> + view(property, classProperty, sampler, image, 0, channels); + switch (count) { + case 1: + CHECK(view.getSwizzle() == "r"); + break; + case 2: + CHECK(view.getSwizzle() == "rg"); + break; + case 3: + CHECK(view.getSwizzle() == "rgb"); + break; + case 4: + CHECK(view.getSwizzle() == "rgba"); + break; + default: + FAIL("Unsupported count value"); + } + + REQUIRE(!view.normalized()); + + for (size_t i = 0; i < texCoords.size(); i++) { + auto expectedRawValue = expectedRaw[i]; + glm::dvec2 uv = texCoords[i]; + + // Check raw values first + auto rawValue = view.getRaw(uv[0], uv[1]); + REQUIRE(static_cast(rawValue.size()) == expectedRawValue.size()); + for (int64_t j = 0; j < rawValue.size(); j++) { + REQUIRE(rawValue[j] == expectedRawValue[static_cast(j)]); + } + + // Check transformed values + auto maybeValue = view.get(uv[0], uv[1]); + if (!maybeValue) { + REQUIRE(!expectedTransformed[i]); + continue; + } - std::vector channels{0}; + auto expectedValue = *(expectedTransformed[i]); + REQUIRE(maybeValue->size() == static_cast(expectedValue.size())); + for (int64_t j = 0; j < maybeValue->size(); j++) { + REQUIRE((*maybeValue)[j] == expectedValue[static_cast(j)]); + } + } +} - PropertyTexturePropertyView - view(sampler, image, 0, channels, false); +template ::type> +void checkNormalizedTextureArrayValues( + const std::vector& data, + const int64_t count, + const std::vector>& expectedRaw, + const std::vector>>& expectedTransformed, + const std::optional offset = std::nullopt, + const std::optional scale = std::nullopt, + const std::optional noData = std::nullopt, + const std::optional defaultValue = std::nullopt) { + PropertyTextureProperty property; + ClassProperty classProperty; + classProperty.type = + convertPropertyTypeToString(TypeToPropertyType::value); + + PropertyComponentType componentType = TypeToPropertyType::component; + classProperty.componentType = + convertPropertyComponentTypeToString(componentType); + + classProperty.array = true; + classProperty.count = count; + classProperty.normalized = true; + classProperty.offset = offset; + classProperty.scale = scale; + classProperty.noData = noData; + classProperty.defaultProperty = defaultValue; + + Sampler sampler; + ImageCesium image; + image.width = 2; + image.height = 2; + image.channels = + static_cast(count) * static_cast(sizeof(T)); + image.bytesPerChannel = 1; + + std::vector& imageData = image.pixelData; + imageData.resize(data.size()); + std::memcpy(imageData.data(), data.data(), data.size()); + + std::vector channels; + for (int32_t i = 0; i < image.channels; i++) { + channels.push_back(i); + } + + PropertyTexturePropertyView, true> + view(property, classProperty, sampler, image, 0, channels); + switch (image.channels) { + case 1: CHECK(view.getSwizzle() == "r"); + break; + case 2: + CHECK(view.getSwizzle() == "rg"); + break; + case 3: + CHECK(view.getSwizzle() == "rgb"); + break; + case 4: + CHECK(view.getSwizzle() == "rgba"); + break; + default: + FAIL("Unsupported count value"); + } - std::vector expected{-1, 0, -33, 67}; - std::vector values{ - view.get(0, 0), - view.get(0.5, 0), - view.get(0, 0.5), - view.get(0.5, 0.5)}; - for (size_t i = 0; i < values.size(); i++) { - REQUIRE(values[i] == expected[i]); + REQUIRE(view.normalized()); + + for (size_t i = 0; i < texCoords.size(); i++) { + auto expectedRawValue = expectedRaw[i]; + glm::dvec2 uv = texCoords[i]; + + // Check raw values first + auto rawValue = view.getRaw(uv[0], uv[1]); + REQUIRE(static_cast(rawValue.size()) == expectedRawValue.size()); + for (int64_t j = 0; j < rawValue.size(); j++) { + REQUIRE(rawValue[j] == expectedRawValue[static_cast(j)]); + } + + // Check transformed values + auto maybeValue = view.get(uv[0], uv[1]); + if (!maybeValue) { + REQUIRE(!expectedTransformed[i]); + continue; + } + + auto expectedValue = *(expectedTransformed[i]); + REQUIRE(maybeValue->size() == static_cast(expectedValue.size())); + for (int64_t j = 0; j < maybeValue->size(); j++) { + REQUIRE((*maybeValue)[j] == expectedValue[j]); } } +} - SECTION("uint16_t") { - Sampler sampler; - ImageCesium image; - image.width = 2; - image.height = 2; - image.channels = 2; - image.bytesPerChannel = 1; +TEST_CASE("Check scalar PropertyTexturePropertyView") { + SECTION("uint8_t") { + std::vector data{12, 33, 56, 67}; + checkTextureValues(data, data); + } + SECTION("int8_t") { + std::vector data{255, 0, 223, 67}; + std::vector expected{-1, 0, -33, 67}; + checkTextureValues(data, expected); + } + + SECTION("uint16_t") { // clang-format off std::vector data{ 28, 0, @@ -86,36 +490,11 @@ TEST_CASE("Check scalar PropertyTexturePropertyView") { 0, 3, 182, 1}; // clang-format on - - std::vector& imageData = image.pixelData; - imageData.resize(data.size()); - std::memcpy(imageData.data(), data.data(), data.size()); - - std::vector channels{0, 1}; - - PropertyTexturePropertyView - view(sampler, image, 0, channels, false); - CHECK(view.getSwizzle() == "rg"); - std::vector expected{28, 257, 768, 438}; - std::vector values{ - view.get(0, 0), - view.get(0.5, 0), - view.get(0, 0.5), - view.get(0.5, 0.5)}; - for (size_t i = 0; i < values.size(); i++) { - REQUIRE(values[i] == expected[i]); - } + checkTextureValues(data, expected); } SECTION("int16_t") { - Sampler sampler; - ImageCesium image; - image.width = 2; - image.height = 2; - image.channels = 2; - image.bytesPerChannel = 1; - // clang-format off std::vector data{ 255, 255, @@ -123,36 +502,11 @@ TEST_CASE("Check scalar PropertyTexturePropertyView") { 0, 3, 182, 1}; // clang-format on - - std::vector& imageData = image.pixelData; - imageData.resize(data.size()); - std::memcpy(imageData.data(), data.data(), data.size()); - - std::vector channels{0, 1}; - - PropertyTexturePropertyView - view(sampler, image, 0, channels, false); - CHECK(view.getSwizzle() == "rg"); - std::vector expected{-1, -32511, 768, 438}; - std::vector values{ - view.get(0, 0), - view.get(0.5, 0), - view.get(0, 0.5), - view.get(0.5, 0.5)}; - for (size_t i = 0; i < values.size(); i++) { - REQUIRE(values[i] == expected[i]); - } + checkTextureValues(data, expected); } SECTION("uint32_t") { - Sampler sampler; - ImageCesium image; - image.width = 2; - image.height = 2; - image.channels = 4; - image.bytesPerChannel = 1; - // clang-format off std::vector data{ 0, 0, 0, 1, @@ -160,36 +514,11 @@ TEST_CASE("Check scalar PropertyTexturePropertyView") { 20, 2, 2, 0, 8, 1, 0, 1}; // clang-format on - - std::vector& imageData = image.pixelData; - imageData.resize(data.size()); - std::memcpy(imageData.data(), data.data(), data.size()); - - std::vector channels{0, 1, 2, 3}; - - PropertyTexturePropertyView - view(sampler, image, 0, channels, false); - CHECK(view.getSwizzle() == "rgba"); - std::vector expected{16777216, 65545, 131604, 16777480}; - std::vector values{ - view.get(0, 0), - view.get(0.5, 0), - view.get(0, 0.5), - view.get(0.5, 0.5)}; - for (size_t i = 0; i < values.size(); i++) { - REQUIRE(values[i] == expected[i]); - } + checkTextureValues(data, expected); } SECTION("int32_t") { - Sampler sampler; - ImageCesium image; - image.width = 2; - image.height = 2; - image.channels = 4; - image.bytesPerChannel = 1; - // clang-format off std::vector data{ 255, 255, 255, 255, @@ -197,35 +526,11 @@ TEST_CASE("Check scalar PropertyTexturePropertyView") { 20, 2, 2, 255, 8, 1, 0, 1}; // clang-format on - - std::vector& imageData = image.pixelData; - imageData.resize(data.size()); - std::memcpy(imageData.data(), data.data(), data.size()); - - std::vector channels{0, 1, 2, 3}; - - PropertyTexturePropertyView - view(sampler, image, 0, channels, false); - CHECK(view.getSwizzle() == "rgba"); std::vector expected{-1, 65545, -16645612, 16777480}; - std::vector values{ - view.get(0, 0), - view.get(0.5, 0), - view.get(0, 0.5), - view.get(0.5, 0.5)}; - for (size_t i = 0; i < values.size(); i++) { - REQUIRE(values[i] == expected[i]); - } + checkTextureValues(data, expected); } SECTION("float") { - Sampler sampler; - ImageCesium image; - image.width = 2; - image.height = 2; - image.channels = 4; - image.bytesPerChannel = 1; - // clang-format off std::vector data{ 0, 0, 0, 1, @@ -234,15 +539,6 @@ TEST_CASE("Check scalar PropertyTexturePropertyView") { 8, 1, 0, 1}; // clang-format on - std::vector& imageData = image.pixelData; - imageData.resize(data.size()); - std::memcpy(imageData.data(), data.data(), data.size()); - - std::vector channels{0, 1, 2, 3}; - - PropertyTexturePropertyView view(sampler, image, 0, channels, false); - CHECK(view.getSwizzle() == "rgba"); - std::vector expectedUint{16777216, 65545, 131604, 16777480}; std::vector expected(expectedUint.size()); for (size_t i = 0; i < expectedUint.size(); i++) { @@ -250,67 +546,177 @@ TEST_CASE("Check scalar PropertyTexturePropertyView") { expected[i] = value; } - std::vector values{ - view.get(0, 0), - view.get(0.5, 0), - view.get(0, 0.5), - view.get(0.5, 0.5)}; - for (size_t i = 0; i < values.size(); i++) { - REQUIRE(values[i] == expected[i]); + checkTextureValues(data, expected); + } + + SECTION("float with offset / scale") { + // clang-format off + std::vector data{ + 0, 0, 0, 1, + 9, 0, 1, 0, + 20, 2, 2, 0, + 8, 1, 0, 1}; + // clang-format on + + std::vector expectedUint{16777216, 65545, 131604, 16777480}; + std::vector expectedRaw(expectedUint.size()); + std::vector> expectedTransformed(expectedUint.size()); + + const float offset = 1.0f; + const float scale = 2.0f; + + for (size_t i = 0; i < expectedUint.size(); i++) { + float value = *reinterpret_cast(&expectedUint[i]); + expectedRaw[i] = value; + expectedTransformed[i] = value * scale + offset; } + + checkTextureValues(data, expectedRaw, expectedTransformed, offset, scale); + } + + SECTION("uint8_t with noData") { + std::vector data{12, 33, 0, 128, 0, 56, 67}; + const uint8_t noData = 0; + std::vector> expected{ + data[0], + data[1], + std::nullopt, + data[3], + std::nullopt, + data[5], + data[6]}; + checkTextureValues( + data, + data, + expected, + std::nullopt, + std::nullopt, + noData); + } + + SECTION("uint8_t with noData and defaultValue") { + std::vector data{12, 33, 0, 128, 0, 56, 67}; + const uint8_t noData = 0; + const uint8_t defaultValue = 255; + std::vector> expected{ + data[0], + data[1], + defaultValue, + data[3], + defaultValue, + data[5], + data[6]}; + checkTextureValues( + data, + data, + expected, + std::nullopt, + std::nullopt, + noData, + defaultValue); } } -TEST_CASE("Check vecN PropertyTexturePropertyView") { - SECTION("glm::u8vec2") { - Sampler sampler; - ImageCesium image; - image.width = 2; - image.height = 2; - image.channels = 2; - image.bytesPerChannel = 1; +TEST_CASE("Check scalar PropertyTexturePropertyView (normalized)") { + SECTION("uint8_t") { + std::vector data{12, 33, 56, 67}; + std::vector> expected{ + 12.0 / 255.0, + 33 / 255.0, + 56 / 255.0, + 67 / 255.0}; + checkNormalizedTextureValues(data, data, expected); + } + SECTION("int16_t") { // clang-format off std::vector data{ - 28, 0, - 1, 1, + 255, 255, + 1, 129, 0, 3, 182, 1}; // clang-format on + std::vector expectedRaw{-1, -32511, 768, 438}; + std::vector> expectedTransformed{ + normalize(expectedRaw[0]), + normalize(expectedRaw[1]), + normalize(expectedRaw[2]), + normalize(expectedRaw[3])}; + checkNormalizedTextureValues(data, expectedRaw, expectedTransformed); + } - std::vector& imageData = image.pixelData; - imageData.resize(data.size()); - std::memcpy(imageData.data(), data.data(), data.size()); + SECTION("uint32_t") { + // clang-format off + std::vector data{ + 0, 0, 0, 1, + 9, 0, 1, 0, + 20, 2, 2, 0, + 8, 1, 0, 1}; + // clang-format on + std::vector expectedRaw{16777216, 65545, 131604, 16777480}; + std::vector> expectedTransformed{ + normalize(expectedRaw[0]), + normalize(expectedRaw[1]), + normalize(expectedRaw[2]), + normalize(expectedRaw[3])}; + checkNormalizedTextureValues(data, expectedRaw, expectedTransformed); + } - std::vector channels{0, 1}; + SECTION("uint8_t with offset / scale") { + std::vector data{12, 33, 56, 67}; + const double offset = 1.0; + const double scale = 2.0; + std::vector> expected{ + normalize(data[0]) * scale + offset, + normalize(data[1]) * scale + offset, + normalize(data[2]) * scale + offset, + normalize(data[3]) * scale + offset, + }; + checkNormalizedTextureValues(data, data, expected, offset, scale); + } - PropertyTexturePropertyView - view(sampler, image, 0, channels, false); - CHECK(view.getSwizzle() == "rg"); + SECTION("uint8_t with all properties") { + std::vector data{12, 33, 56, 0, 67}; + const double offset = 1.0; + const double scale = 2.0; + const uint8_t noData = 0; + const double defaultValue = 10.0; + std::vector> expected{ + normalize(data[0]) * scale + offset, + normalize(data[1]) * scale + offset, + normalize(data[2]) * scale + offset, + 10.0, + normalize(data[4]) * scale + offset, + }; + checkNormalizedTextureValues( + data, + data, + expected, + offset, + scale, + noData, + defaultValue); + } +} +TEST_CASE("Check vecN PropertyTexturePropertyView") { + SECTION("glm::u8vec2") { + // clang-format off + std::vector data{ + 28, 0, + 1, 1, + 0, 3, + 182, 1}; + // clang-format on std::vector expected{ glm::u8vec2(28, 0), glm::u8vec2(1, 1), glm::u8vec2(0, 3), glm::u8vec2(182, 1)}; - std::vector values{ - view.get(0, 0), - view.get(0.5, 0), - view.get(0, 0.5), - view.get(0.5, 0.5)}; - for (size_t i = 0; i < values.size(); i++) { - REQUIRE(values[i] == expected[i]); - } + checkTextureValues(data, expected); } SECTION("glm::i8vec2") { - Sampler sampler; - ImageCesium image; - image.width = 2; - image.height = 2; - image.channels = 2; - image.bytesPerChannel = 1; - // clang-format off std::vector data{ 28, 255, @@ -318,40 +724,15 @@ TEST_CASE("Check vecN PropertyTexturePropertyView") { 0, 3, 182, 1}; // clang-format on - - std::vector& imageData = image.pixelData; - imageData.resize(data.size()); - std::memcpy(imageData.data(), data.data(), data.size()); - - std::vector channels{0, 1}; - - PropertyTexturePropertyView - view(sampler, image, 0, channels, false); - CHECK(view.getSwizzle() == "rg"); - std::vector expected{ glm::i8vec2(28, -1), glm::i8vec2(-2, 1), glm::i8vec2(0, 3), glm::i8vec2(-74, 1)}; - std::vector values{ - view.get(0, 0), - view.get(0.5, 0), - view.get(0, 0.5), - view.get(0.5, 0.5)}; - for (size_t i = 0; i < values.size(); i++) { - REQUIRE(values[i] == expected[i]); - } + checkTextureValues(data, expected); } SECTION("glm::u8vec3") { - Sampler sampler; - ImageCesium image; - image.width = 2; - image.height = 2; - image.channels = 3; - image.bytesPerChannel = 1; - // clang-format off std::vector data{ 1, 2, 3, @@ -359,40 +740,15 @@ TEST_CASE("Check vecN PropertyTexturePropertyView") { 7, 8, 9, 0, 5, 2}; // clang-format on - - std::vector& imageData = image.pixelData; - imageData.resize(data.size()); - std::memcpy(imageData.data(), data.data(), data.size()); - - std::vector channels{0, 1, 2}; - - PropertyTexturePropertyView - view(sampler, image, 0, channels, false); - CHECK(view.getSwizzle() == "rgb"); - std::vector expected{ glm::u8vec3(1, 2, 3), glm::u8vec3(4, 5, 6), glm::u8vec3(7, 8, 9), glm::u8vec3(0, 5, 2)}; - std::vector values{ - view.get(0, 0), - view.get(0.5, 0), - view.get(0, 0.5), - view.get(0.5, 0.5)}; - for (size_t i = 0; i < values.size(); i++) { - REQUIRE(values[i] == expected[i]); - } + checkTextureValues(data, expected); } SECTION("glm::i8vec3") { - Sampler sampler; - ImageCesium image; - image.width = 2; - image.height = 2; - image.channels = 3; - image.bytesPerChannel = 1; - // clang-format off std::vector data{ 255, 2, 3, @@ -400,40 +756,15 @@ TEST_CASE("Check vecN PropertyTexturePropertyView") { 7, 8, 159, 0, 5, 2}; // clang-format on - - std::vector& imageData = image.pixelData; - imageData.resize(data.size()); - std::memcpy(imageData.data(), data.data(), data.size()); - - std::vector channels{0, 1, 2}; - - PropertyTexturePropertyView - view(sampler, image, 0, channels, false); - CHECK(view.getSwizzle() == "rgb"); - std::vector expected{ glm::i8vec3(-1, 2, 3), glm::i8vec3(4, -2, 6), glm::i8vec3(7, 8, -97), glm::i8vec3(0, 5, 2)}; - std::vector values{ - view.get(0, 0), - view.get(0.5, 0), - view.get(0, 0.5), - view.get(0.5, 0.5)}; - for (size_t i = 0; i < values.size(); i++) { - REQUIRE(values[i] == expected[i]); - } + checkTextureValues(data, expected); } SECTION("glm::u8vec4") { - Sampler sampler; - ImageCesium image; - image.width = 2; - image.height = 2; - image.channels = 4; - image.bytesPerChannel = 1; - // clang-format off std::vector data{ 1, 2, 3, 0, @@ -441,40 +772,15 @@ TEST_CASE("Check vecN PropertyTexturePropertyView") { 7, 8, 9, 3, 0, 5, 2, 27}; // clang-format on - - std::vector& imageData = image.pixelData; - imageData.resize(data.size()); - std::memcpy(imageData.data(), data.data(), data.size()); - - std::vector channels{0, 1, 2, 3}; - - PropertyTexturePropertyView - view(sampler, image, 0, channels, false); - CHECK(view.getSwizzle() == "rgba"); - std::vector expected{ glm::u8vec4(1, 2, 3, 0), glm::u8vec4(4, 5, 6, 11), glm::u8vec4(7, 8, 9, 3), glm::u8vec4(0, 5, 2, 27)}; - std::vector values{ - view.get(0, 0), - view.get(0.5, 0), - view.get(0, 0.5), - view.get(0.5, 0.5)}; - for (size_t i = 0; i < values.size(); i++) { - REQUIRE(values[i] == expected[i]); - } + checkTextureValues(data, expected); } SECTION("glm::i8vec4") { - Sampler sampler; - ImageCesium image; - image.width = 2; - image.height = 2; - image.channels = 4; - image.bytesPerChannel = 1; - // clang-format off std::vector data{ 1, 200, 3, 0, @@ -482,40 +788,15 @@ TEST_CASE("Check vecN PropertyTexturePropertyView") { 129, 8, 9, 3, 0, 155, 2, 27}; // clang-format on - - std::vector& imageData = image.pixelData; - imageData.resize(data.size()); - std::memcpy(imageData.data(), data.data(), data.size()); - - std::vector channels{0, 1, 2, 3}; - - PropertyTexturePropertyView - view(sampler, image, 0, channels, false); - CHECK(view.getSwizzle() == "rgba"); - std::vector expected{ glm::i8vec4(1, -56, 3, 0), glm::i8vec4(4, 5, 6, -5), glm::i8vec4(-127, 8, 9, 3), glm::i8vec4(0, -101, 2, 27)}; - std::vector values{ - view.get(0, 0), - view.get(0.5, 0), - view.get(0, 0.5), - view.get(0.5, 0.5)}; - for (size_t i = 0; i < values.size(); i++) { - REQUIRE(values[i] == expected[i]); - } + checkTextureValues(data, expected); } SECTION("glm::u16vec2") { - Sampler sampler; - ImageCesium image; - image.width = 2; - image.height = 2; - image.channels = 4; - image.bytesPerChannel = 1; - // clang-format off std::vector data{ 0, 0, 0, 1, @@ -523,40 +804,160 @@ TEST_CASE("Check vecN PropertyTexturePropertyView") { 20, 2, 2, 0, 8, 1, 0, 1}; // clang-format on - - std::vector& imageData = image.pixelData; - imageData.resize(data.size()); - std::memcpy(imageData.data(), data.data(), data.size()); - - std::vector channels{0, 1, 2, 3}; - - PropertyTexturePropertyView - view(sampler, image, 0, channels, false); - CHECK(view.getSwizzle() == "rgba"); - std::vector expected{ glm::u16vec2(0, 256), glm::u16vec2(9, 1), glm::u16vec2(532, 2), glm::u16vec2(264, 256)}; - std::vector values{ - view.get(0, 0), - view.get(0.5, 0), - view.get(0, 0.5), - view.get(0.5, 0.5)}; - for (size_t i = 0; i < values.size(); i++) { - REQUIRE(values[i] == expected[i]); - } + checkTextureValues(data, expected); } SECTION("glm::i16vec2") { - Sampler sampler; - ImageCesium image; - image.width = 2; - image.height = 2; - image.channels = 4; - image.bytesPerChannel = 1; + // clang-format off + std::vector data{ + 255, 255, 0, 1, + 9, 0, 146, 195, + 20, 2, 2, 0, + 8, 1, 255, 1}; + // clang-format on + std::vector expected{ + glm::i16vec2(-1, 256), + glm::i16vec2(9, -15470), + glm::i16vec2(532, 2), + glm::i16vec2(264, 511)}; + checkTextureValues(data, expected); + } + SECTION("glm::i8vec2 with noData") { + // clang-format off + std::vector data{ + 28, 255, + 254, 1, + 0, 3, + 0, 0, + 182, 1}; + // clang-format on + std::optional noData = JsonValue::Array{0, 0}; + std::vector expectedRaw{ + glm::i8vec2(28, -1), + glm::i8vec2(-2, 1), + glm::i8vec2(0, 3), + glm::i8vec2(0), + glm::i8vec2(-74, 1)}; + std::vector> expectedTransformed{ + glm::i8vec2(28, -1), + glm::i8vec2(-2, 1), + glm::i8vec2(0, 3), + std::nullopt, + glm::i8vec2(-74, 1)}; + checkTextureValues( + data, + expectedRaw, + expectedTransformed, + std::nullopt, + std::nullopt, + noData); + } + + SECTION("glm::i8vec2 with defaultValue") { + // clang-format off + std::vector data{ + 28, 255, + 254, 1, + 0, 3, + 0, 0, + 182, 1}; + // clang-format on + std::optional noData = JsonValue::Array{0, 0}; + std::optional defaultValue = JsonValue::Array{127, 127}; + std::vector expectedRaw{ + glm::i8vec2(28, -1), + glm::i8vec2(-2, 1), + glm::i8vec2(0, 3), + glm::i8vec2(0, 0), + glm::i8vec2(-74, 1)}; + std::vector> expectedTransformed{ + glm::i8vec2(28, -1), + glm::i8vec2(-2, 1), + glm::i8vec2(0, 3), + glm::i8vec2(127, 127), + glm::i8vec2(-74, 1)}; + checkTextureValues( + data, + expectedRaw, + expectedTransformed, + std::nullopt, + std::nullopt, + noData, + defaultValue); + } +} + +TEST_CASE("Check vecN PropertyTexturePropertyView (normalized)") { + SECTION("glm::i8vec2") { + // clang-format off + std::vector data{ + 28, 255, + 254, 1, + 0, 3, + 182, 1}; + // clang-format on + std::vector expectedRaw{ + glm::i8vec2(28, -1), + glm::i8vec2(-2, 1), + glm::i8vec2(0, 3), + glm::i8vec2(-74, 1)}; + std::vector> expectedTransformed{ + normalize(expectedRaw[0]), + normalize(expectedRaw[1]), + normalize(expectedRaw[2]), + normalize(expectedRaw[3])}; + checkNormalizedTextureValues(data, expectedRaw, expectedTransformed); + } + + SECTION("glm::u8vec3") { + // clang-format off + std::vector data{ + 1, 2, 3, + 4, 5, 6, + 7, 8, 9, + 0, 5, 2}; + // clang-format on + std::vector expectedRaw{ + glm::u8vec3(1, 2, 3), + glm::u8vec3(4, 5, 6), + glm::u8vec3(7, 8, 9), + glm::u8vec3(0, 5, 2)}; + std::vector> expectedTransformed{ + normalize(expectedRaw[0]), + normalize(expectedRaw[1]), + normalize(expectedRaw[2]), + normalize(expectedRaw[3])}; + checkNormalizedTextureValues(data, expectedRaw, expectedTransformed); + } + + SECTION("glm::u8vec4") { + // clang-format off + std::vector data{ + 1, 2, 3, 0, + 4, 5, 6, 11, + 7, 8, 9, 3, + 0, 5, 2, 27}; + // clang-format on + std::vector expectedRaw{ + glm::u8vec4(1, 2, 3, 0), + glm::u8vec4(4, 5, 6, 11), + glm::u8vec4(7, 8, 9, 3), + glm::u8vec4(0, 5, 2, 27)}; + std::vector> expectedTransformed{ + normalize(expectedRaw[0]), + normalize(expectedRaw[1]), + normalize(expectedRaw[2]), + normalize(expectedRaw[3])}; + checkNormalizedTextureValues(data, expectedRaw, expectedTransformed); + } + + SECTION("glm::i16vec2") { // clang-format off std::vector data{ 255, 255, 0, 1, @@ -564,42 +965,92 @@ TEST_CASE("Check vecN PropertyTexturePropertyView") { 20, 2, 2, 0, 8, 1, 255, 1}; // clang-format on + std::vector expectedRaw{ + glm::i16vec2(-1, 256), + glm::i16vec2(9, -15470), + glm::i16vec2(532, 2), + glm::i16vec2(264, 511)}; + std::vector> expectedTransformed{ + normalize(expectedRaw[0]), + normalize(expectedRaw[1]), + normalize(expectedRaw[2]), + normalize(expectedRaw[3])}; + checkNormalizedTextureValues(data, expectedRaw, expectedTransformed); + } - std::vector& imageData = image.pixelData; - imageData.resize(data.size()); - std::memcpy(imageData.data(), data.data(), data.size()); + SECTION("glm::i8vec2 with offset / scale") { + // clang-format off + std::vector data{ + 28, 255, + 254, 1, + 0, 3, + 0, 0, + 182, 1}; + // clang-format on + const glm::dvec2 offset(-1.0, 4.0); + const glm::dvec2 scale(2.0, 1.0); - std::vector channels{0, 1, 2, 3}; + std::vector expectedRaw{ + glm::i8vec2(28, -1), + glm::i8vec2(-2, 1), + glm::i8vec2(0, 3), + glm::i8vec2(0), + glm::i8vec2(-74, 1)}; + std::vector> expectedTransformed{ + normalize(expectedRaw[0]) * scale + offset, + normalize(expectedRaw[1]) * scale + offset, + normalize(expectedRaw[2]) * scale + offset, + normalize(expectedRaw[3]) * scale + offset, + normalize(expectedRaw[4]) * scale + offset, + }; + checkNormalizedTextureValues( + data, + expectedRaw, + expectedTransformed, + JsonValue::Array{offset[0], offset[1]}, + JsonValue::Array{scale[0], scale[1]}); + } - PropertyTexturePropertyView - view(sampler, image, 0, channels, false); - CHECK(view.getSwizzle() == "rgba"); + SECTION("glm::i8vec2 with all properties") { + // clang-format off + std::vector data{ + 28, 255, + 254, 1, + 0, 3, + 0, 0, + 182, 1}; + // clang-format on + const glm::dvec2 offset(-1.0, 4.0); + const glm::dvec2 scale(2.0, 1.0); + const glm::i8vec2 noData(0); + const glm::dvec2 defaultValue(100.0, 5.5); - std::vector expected{ - glm::i16vec2(-1, 256), - glm::i16vec2(9, -15470), - glm::i16vec2(532, 2), - glm::i16vec2(264, 511)}; - std::vector values{ - view.get(0, 0), - view.get(0.5, 0), - view.get(0, 0.5), - view.get(0.5, 0.5)}; - for (size_t i = 0; i < values.size(); i++) { - REQUIRE(values[i] == expected[i]); - } + std::vector expectedRaw{ + glm::i8vec2(28, -1), + glm::i8vec2(-2, 1), + glm::i8vec2(0, 3), + glm::i8vec2(0), + glm::i8vec2(-74, 1)}; + std::vector> expectedTransformed{ + normalize(expectedRaw[0]) * scale + offset, + normalize(expectedRaw[1]) * scale + offset, + normalize(expectedRaw[2]) * scale + offset, + defaultValue, + normalize(expectedRaw[4]) * scale + offset, + }; + checkNormalizedTextureValues( + data, + expectedRaw, + expectedTransformed, + JsonValue::Array{offset[0], offset[1]}, + JsonValue::Array{scale[0], scale[1]}, + JsonValue::Array{noData[0], noData[1]}, + JsonValue::Array{defaultValue[0], defaultValue[1]}); } } TEST_CASE("Check array PropertyTexturePropertyView") { SECTION("uint8_t array") { - Sampler sampler; - ImageCesium image; - image.width = 2; - image.height = 2; - image.channels = 4; - image.bytesPerChannel = 1; - // clang-format off std::vector data{ 1, 2, 3, 0, @@ -608,43 +1059,15 @@ TEST_CASE("Check array PropertyTexturePropertyView") { 0, 5, 2, 27}; // clang-format on - std::vector& imageData = image.pixelData; - imageData.resize(data.size()); - std::memcpy(imageData.data(), data.data(), data.size()); - - std::vector channels{0, 1, 2, 3}; - - PropertyTexturePropertyView> - view(sampler, image, 0, channels, false); - CHECK(view.getSwizzle() == "rgba"); - - std::vector> values{ - view.get(0, 0), - view.get(0.5, 0), - view.get(0, 0.5), - view.get(0.5, 0.5)}; - - int64_t size = static_cast(values.size()); - for (int64_t i = 0; i < size; i++) { - auto dataStart = data.begin() + i * 4; - std::vector expected(dataStart, dataStart + 4); - - const PropertyArrayView& value = values[static_cast(i)]; - REQUIRE(static_cast(value.size()) == expected.size()); - for (int64_t j = 0; j < value.size(); j++) { - REQUIRE(value[j] == expected[static_cast(j)]); - } - } + std::vector> expected{ + {1, 2, 3, 0}, + {4, 5, 6, 11}, + {7, 8, 9, 3}, + {0, 5, 2, 27}}; + checkTextureArrayValues(data, 4, expected); } SECTION("int8_t array") { - Sampler sampler; - ImageCesium image; - image.width = 2; - image.height = 2; - image.channels = 4; - image.bytesPerChannel = 1; - // clang-format off std::vector data{ 1, 200, 3, 0, @@ -652,46 +1075,15 @@ TEST_CASE("Check array PropertyTexturePropertyView") { 129, 8, 9, 3, 0, 155, 2, 27}; // clang-format on - - std::vector& imageData = image.pixelData; - imageData.resize(data.size()); - std::memcpy(imageData.data(), data.data(), data.size()); - - std::vector channels{0, 1, 2, 3}; - - PropertyTexturePropertyView> - view(sampler, image, 0, channels, false); - CHECK(view.getSwizzle() == "rgba"); - std::vector> expected{ {1, -56, 3, 0}, {4, 5, 6, -5}, {-127, 8, 9, 3}, {0, -101, 2, 27}}; - std::vector> values{ - view.get(0, 0), - view.get(0.5, 0), - view.get(0, 0.5), - view.get(0.5, 0.5)}; - - for (size_t i = 0; i < values.size(); i++) { - std::vector& expectedValue = expected[i]; - const PropertyArrayView& value = values[i]; - REQUIRE(static_cast(value.size()) == expected.size()); - for (int64_t j = 0; j < value.size(); j++) { - REQUIRE(value[j] == expectedValue[static_cast(j)]); - } - } + checkTextureArrayValues(data, 4, expected); } SECTION("uint16_t array") { - Sampler sampler; - ImageCesium image; - image.width = 2; - image.height = 2; - image.channels = 4; - image.bytesPerChannel = 1; - // clang-format off std::vector data{ 0, 0, 0, 1, @@ -699,47 +1091,15 @@ TEST_CASE("Check array PropertyTexturePropertyView") { 20, 2, 2, 0, 8, 1, 0, 1}; // clang-format on - - std::vector& imageData = image.pixelData; - imageData.resize(data.size()); - std::memcpy(imageData.data(), data.data(), data.size()); - - std::vector channels{0, 1, 2, 3}; - - PropertyTexturePropertyView> - view(sampler, image, 0, channels, false); - CHECK(view.getSwizzle() == "rgba"); - - std::vector> values{ - view.get(0, 0), - view.get(0.5, 0), - view.get(0, 0.5), - view.get(0.5, 0.5)}; - std::vector> expected{ {0, 256}, {9, 1}, {532, 2}, {264, 256}}; - - for (size_t i = 0; i < expected.size(); i++) { - std::vector& expectedValue = expected[i]; - const PropertyArrayView& value = values[i]; - REQUIRE(static_cast(value.size()) == expectedValue.size()); - for (int64_t j = 0; j < value.size(); j++) { - REQUIRE(value[j] == expectedValue[static_cast(j)]); - } - } + checkTextureArrayValues(data, 2, expected); } SECTION("int16_t array") { - Sampler sampler; - ImageCesium image; - image.width = 2; - image.height = 2; - image.channels = 4; - image.bytesPerChannel = 1; - // clang-format off std::vector data{ 255, 255, 0, 1, @@ -747,40 +1107,332 @@ TEST_CASE("Check array PropertyTexturePropertyView") { 20, 2, 2, 0, 8, 255, 0, 1}; // clang-format on + std::vector> expected{ + {-1, 256}, + {9, -15470}, + {532, 2}, + {-248, 256}}; + checkTextureArrayValues(data, 2, expected); + } + + SECTION("uint8_t array with noData") { + // clang-format off + std::vector data{ + 1, 2, 3, 0, + 4, 5, 6, 11, + 7, 8, 9, 3, + 0, 0, 0, 0,}; + // clang-format on - std::vector& imageData = image.pixelData; - imageData.resize(data.size()); - std::memcpy(imageData.data(), data.data(), data.size()); + std::optional noData = JsonValue::Array{0, 0, 0, 0}; + std::vector> expectedRaw{ + {1, 2, 3, 0}, + {4, 5, 6, 11}, + {7, 8, 9, 3}, + {0, 0, 0, 0}}; + std::vector>> expectedTransformed{ + std::vector{1, 2, 3, 0}, + std::vector{4, 5, 6, 11}, + std::vector{7, 8, 9, 3}, + std::nullopt}; + checkTextureArrayValues(data, 4, expectedRaw, expectedTransformed, noData); + } - std::vector channels{0, 1, 2, 3}; + SECTION("uint8_t array with noData and defaultValue") { + // clang-format off + std::vector data{ + 1, 2, 3, 0, + 4, 5, 6, 11, + 7, 8, 9, 3, + 0, 0, 0, 0,}; + // clang-format on - PropertyTexturePropertyView> - view(sampler, image, 0, channels, false); - CHECK(view.getSwizzle() == "rgba"); + std::optional noData = JsonValue::Array{0, 0, 0, 0}; + std::optional defaultValue = JsonValue::Array{255, 8, 12, 5}; + std::vector> expectedRaw{ + {1, 2, 3, 0}, + {4, 5, 6, 11}, + {7, 8, 9, 3}, + {0, 0, 0, 0}}; + std::vector>> expectedTransformed{ + std::vector{1, 2, 3, 0}, + std::vector{4, 5, 6, 11}, + std::vector{7, 8, 9, 3}, + std::vector{255, 8, 12, 5}}; + checkTextureArrayValues( + data, + 4, + expectedRaw, + expectedTransformed, + noData, + defaultValue); + } +} - std::vector> expected{ +TEST_CASE("Check array PropertyTexturePropertyView (normalized)") { + SECTION("uint8_t array") { + // clang-format off + std::vector data{ + 1, 2, 3, 0, + 4, 5, 6, 11, + 7, 8, 9, 3, + 0, 5, 2, 27}; + // clang-format on + std::vector> expectedRaw{ + {1, 2, 3, 0}, + {4, 5, 6, 11}, + {7, 8, 9, 3}, + {0, 5, 2, 27}}; + std::vector>> expectedTransformed( + expectedRaw.size()); + + for (size_t i = 0; i < expectedRaw.size(); i++) { + auto rawValues = expectedRaw[i]; + std::vector transformedValues(rawValues.size()); + for (size_t j = 0; j < rawValues.size(); j++) { + transformedValues[j] = normalize(rawValues[j]); + } + expectedTransformed[i] = transformedValues; + } + checkNormalizedTextureArrayValues( + data, + 4, + expectedRaw, + expectedTransformed); + } + + SECTION("int16_t array") { + // clang-format off + std::vector data{ + 255, 255, 0, 1, + 9, 0, 146, 195, + 20, 2, 2, 0, + 8, 255, 0, 1}; + // clang-format on + std::vector> expectedRaw{ {-1, 256}, {9, -15470}, {532, 2}, {-248, 256}}; - std::vector> values{ - view.get(0, 0), - view.get(0.5, 0), - view.get(0, 0.5), - view.get(0.5, 0.5)}; - for (size_t i = 0; i < values.size(); i++) { - std::vector& expectedValue = expected[i]; - const PropertyArrayView& value = values[i]; - REQUIRE(static_cast(value.size()) == expectedValue.size()); - for (int64_t j = 0; j < value.size(); j++) { - REQUIRE(value[j] == expectedValue[static_cast(j)]); + std::vector>> expectedTransformed( + expectedRaw.size()); + + for (size_t i = 0; i < expectedRaw.size(); i++) { + auto rawValues = expectedRaw[i]; + std::vector transformedValues(rawValues.size()); + for (size_t j = 0; j < rawValues.size(); j++) { + transformedValues[j] = normalize(rawValues[j]); + } + expectedTransformed[i] = transformedValues; + } + + checkNormalizedTextureArrayValues( + data, + 2, + expectedRaw, + expectedTransformed); + } + + SECTION("uint8_t array with offset / scale") { + // clang-format off + std::vector data{ + 1, 2, 3, 0, + 4, 5, 6, 11, + 7, 8, 9, 3, + 0, 0, 0, 0,}; + // clang-format on + + const std::vector offset{1, 2, 0, 4}; + const std::vector scale{1, -1, 3, -2}; + std::vector> expectedRaw{ + {1, 2, 3, 0}, + {4, 5, 6, 11}, + {7, 8, 9, 3}, + {0, 0, 0, 0}}; + std::vector>> expectedTransformed( + expectedRaw.size()); + + for (size_t i = 0; i < expectedRaw.size(); i++) { + auto rawValues = expectedRaw[i]; + std::vector transformedValues(rawValues.size()); + for (size_t j = 0; j < rawValues.size(); j++) { + transformedValues[j] = normalize(rawValues[j]) * scale[j] + offset[j]; + } + expectedTransformed[i] = transformedValues; + } + + checkNormalizedTextureArrayValues( + data, + 4, + expectedRaw, + expectedTransformed, + JsonValue::Array{offset[0], offset[1], offset[2], offset[3]}, + JsonValue::Array{scale[0], scale[1], scale[2], scale[3]}); + } + + SECTION("uint8_t array with noData") { + // clang-format off + std::vector data{ + 1, 2, 3, 0, + 4, 5, 6, 11, + 7, 8, 9, 3, + 0, 0, 0, 0,}; + // clang-format on + + std::optional noData = JsonValue::Array{0, 0, 0, 0}; + std::vector> expectedRaw{ + {1, 2, 3, 0}, + {4, 5, 6, 11}, + {7, 8, 9, 3}, + {0, 0, 0, 0}}; + std::vector>> expectedTransformed( + expectedRaw.size()); + + for (size_t i = 0; i < expectedRaw.size() - 1; i++) { + auto rawValues = expectedRaw[i]; + std::vector transformedValues(rawValues.size()); + for (size_t j = 0; j < rawValues.size(); j++) { + transformedValues[j] = normalize(rawValues[j]); + } + expectedTransformed[i] = transformedValues; + } + + expectedTransformed[3] = std::nullopt; + + checkNormalizedTextureArrayValues( + data, + 4, + expectedRaw, + expectedTransformed, + std::nullopt, + std::nullopt, + noData); + } + + SECTION("uint8_t array with all properties") { + // clang-format off + std::vector data{ + 1, 2, 3, 0, + 4, 5, 6, 11, + 7, 8, 9, 3, + 0, 0, 0, 0,}; + // clang-format on + + const std::vector offset{1, 2, 0, 4}; + const std::vector scale{1, -1, 3, -2}; + std::optional noData = JsonValue::Array{0, 0, 0, 0}; + std::optional defaultValue = + JsonValue::Array{1.0, 2.0, 3.0, 4.0}; + + std::vector> expectedRaw{ + {1, 2, 3, 0}, + {4, 5, 6, 11}, + {7, 8, 9, 3}, + {0, 0, 0, 0}}; + std::vector>> expectedTransformed( + expectedRaw.size()); + + for (size_t i = 0; i < expectedRaw.size() - 1; i++) { + auto rawValues = expectedRaw[i]; + std::vector transformedValues(rawValues.size()); + for (size_t j = 0; j < rawValues.size(); j++) { + transformedValues[j] = normalize(rawValues[j]) * scale[j] + offset[j]; } + expectedTransformed[i] = transformedValues; } + + expectedTransformed[3] = std::vector{1.0, 2.0, 3.0, 4.0}; + + checkNormalizedTextureArrayValues( + data, + 4, + expectedRaw, + expectedTransformed, + JsonValue::Array{offset[0], offset[1], offset[2], offset[3]}, + JsonValue::Array{scale[0], scale[1], scale[2], scale[3]}, + noData, + defaultValue); + } +} + +TEST_CASE("Check that property values override class property values") { + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::SCALAR; + classProperty.componentType = ClassProperty::ComponentType::FLOAT32; + + classProperty.offset = 0.0f; + classProperty.scale = 1.0f; + classProperty.min = -10.0f; + classProperty.max = 10.0f; + + Sampler sampler; + ImageCesium image; + image.width = 2; + image.height = 2; + image.channels = 4; + image.bytesPerChannel = 1; + + // clang-format off + std::vector data{ + 0, 0, 0, 1, + 9, 0, 1, 0, + 20, 2, 2, 0, + 8, 1, 0, 1}; + // clang-format on + + const float offset = 1.0f; + const float scale = 2.0f; + std::vector expectedUint{16777216, 65545, 131604, 16777480}; + std::vector expectedRaw(expectedUint.size()); + std::vector> expectedTransformed(expectedUint.size()); + for (size_t i = 0; i < expectedUint.size(); i++) { + float value = *reinterpret_cast(&expectedUint[i]); + expectedRaw[i] = value; + expectedTransformed[i] = value * scale + offset; + } + + std::vector& imageData = image.pixelData; + imageData.resize(data.size()); + std::memcpy(imageData.data(), data.data(), data.size()); + + std::vector channels{0, 1, 2, 3}; + + PropertyTextureProperty property; + property.offset = offset; + property.scale = scale; + property.min = std::numeric_limits::lowest(); + property.max = std::numeric_limits::max(); + + PropertyTexturePropertyView + view(property, classProperty, sampler, image, 0, channels); + CHECK(view.getSwizzle() == "rgba"); + + REQUIRE(view.offset()); + REQUIRE(*view.offset() == offset); + REQUIRE(view.scale()); + REQUIRE(*view.scale() == scale); + REQUIRE(view.min()); + REQUIRE(*view.min() == std::numeric_limits::lowest()); + REQUIRE(view.max()); + REQUIRE(*view.max() == std::numeric_limits::max()); + + for (size_t i = 0; i < texCoords.size(); i++) { + glm::dvec2 uv = texCoords[i]; + auto value = view.getRaw(uv[0], uv[1]); + REQUIRE(value == expectedRaw[i]); + + auto maybeValue = view.get(uv[0], uv[1]); + REQUIRE(maybeValue == expectedTransformed[i]); } } TEST_CASE("Check that non-adjacent channels resolve to expected output") { SECTION("single-byte scalar") { + PropertyTextureProperty property; + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::SCALAR; + classProperty.componentType = ClassProperty::ComponentType::UINT8; + Sampler sampler; ImageCesium image; image.width = 2; @@ -802,22 +1454,28 @@ TEST_CASE("Check that non-adjacent channels resolve to expected output") { std::vector channels{3}; - PropertyTexturePropertyView - view(sampler, image, 0, channels, false); + PropertyTexturePropertyView + view(property, classProperty, sampler, image, 0, channels); CHECK(view.getSwizzle() == "a"); - std::vector expected{3, 4, 0, 1}; - std::vector values{ - view.get(0, 0), - view.get(0.5, 0), - view.get(0, 0.5), - view.get(0.5, 0.5)}; - for (size_t i = 0; i < values.size(); i++) { - REQUIRE(values[i] == expected[i]); + std::vector expected{3, 4, 0, 1}; + for (size_t i = 0; i < expected.size(); i++) { + glm::dvec2 uv = texCoords[i]; + auto value = view.getRaw(uv[0], uv[1]); + REQUIRE(value == expected[i]); + + auto maybeValue = view.get(uv[0], uv[1]); + REQUIRE(maybeValue); + REQUIRE(*maybeValue == expected[i]); } } SECTION("multi-byte scalar") { + PropertyTextureProperty property; + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::SCALAR; + classProperty.componentType = ClassProperty::ComponentType::UINT16; + Sampler sampler; ImageCesium image; image.width = 2; @@ -839,21 +1497,27 @@ TEST_CASE("Check that non-adjacent channels resolve to expected output") { std::vector channels{2, 0}; PropertyTexturePropertyView - view(sampler, image, 0, channels, false); + view(property, classProperty, sampler, image, 0, channels); CHECK(view.getSwizzle() == "br"); std::vector expected{2, 259, 257, 520}; - std::vector values{ - view.get(0, 0), - view.get(0.5, 0), - view.get(0, 0.5), - view.get(0.5, 0.5)}; - for (size_t i = 0; i < values.size(); i++) { - REQUIRE(values[i] == expected[i]); + for (size_t i = 0; i < expected.size(); i++) { + glm::dvec2 uv = texCoords[i]; + auto value = view.getRaw(uv[0], uv[1]); + REQUIRE(value == expected[i]); + + auto maybeValue = view.get(uv[0], uv[1]); + REQUIRE(maybeValue); + REQUIRE(*maybeValue == expected[i]); } } SECTION("vecN") { + PropertyTextureProperty property; + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::VEC3; + classProperty.componentType = ClassProperty::ComponentType::UINT8; + Sampler sampler; ImageCesium image; image.width = 2; @@ -875,7 +1539,7 @@ TEST_CASE("Check that non-adjacent channels resolve to expected output") { std::vector channels{3, 2, 1}; PropertyTexturePropertyView - view(sampler, image, 0, channels, false); + view(property, classProperty, sampler, image, 0, channels); CHECK(view.getSwizzle() == "abg"); std::vector expected{ @@ -883,17 +1547,24 @@ TEST_CASE("Check that non-adjacent channels resolve to expected output") { glm::u8vec3(4, 3, 2), glm::u8vec3(0, 1, 0), glm::u8vec3(1, 8, 3)}; - std::vector values{ - view.get(0, 0), - view.get(0.5, 0), - view.get(0, 0.5), - view.get(0.5, 0.5)}; - for (size_t i = 0; i < values.size(); i++) { - REQUIRE(values[i] == expected[i]); + for (size_t i = 0; i < expected.size(); i++) { + glm::dvec2 uv = texCoords[i]; + auto value = view.getRaw(uv[0], uv[1]); + REQUIRE(value == expected[i]); + + auto maybeValue = view.get(uv[0], uv[1]); + REQUIRE(maybeValue); + REQUIRE(*maybeValue == expected[i]); } } SECTION("array") { + PropertyTextureProperty property; + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::SCALAR; + classProperty.componentType = ClassProperty::ComponentType::UINT8; + classProperty.array = true; + Sampler sampler; ImageCesium image; image.width = 2; @@ -916,15 +1587,9 @@ TEST_CASE("Check that non-adjacent channels resolve to expected output") { std::vector channels{1, 0, 3, 2}; PropertyTexturePropertyView> - view(sampler, image, 0, channels, false); + view(property, classProperty, sampler, image, 0, channels); CHECK(view.getSwizzle() == "grab"); - std::vector> values{ - view.get(0, 0), - view.get(0.5, 0), - view.get(0, 0.5), - view.get(0.5, 0.5)}; - std::vector> expected{ {2, 1, 0, 3}, {5, 4, 11, 6}, @@ -933,45 +1598,63 @@ TEST_CASE("Check that non-adjacent channels resolve to expected output") { for (size_t i = 0; i < expected.size(); i++) { std::vector& expectedValue = expected[i]; - const PropertyArrayView& value = values[i]; + glm::dvec2 uv = texCoords[i]; + + auto value = view.getRaw(uv[0], uv[1]); REQUIRE(static_cast(value.size()) == expectedValue.size()); + + auto maybeValue = view.get(uv[0], uv[1]); + REQUIRE(maybeValue); + REQUIRE(maybeValue->size() == value.size()); for (int64_t j = 0; j < value.size(); j++) { REQUIRE(value[j] == expectedValue[static_cast(j)]); + REQUIRE((*maybeValue)[j] == value[j]); } } } } TEST_CASE("Check sampling with different sampler values") { + PropertyTextureProperty property; + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::SCALAR; + classProperty.componentType = ClassProperty::ComponentType::UINT8; + + ImageCesium image; + image.width = 2; + image.height = 2; + image.channels = 1; + image.bytesPerChannel = 1; + + std::vector data{12, 33, 56, 67}; + std::vector& imageData = image.pixelData; + imageData.resize(data.size()); + std::memcpy(imageData.data(), data.data(), data.size()); + + std::vector channels{0}; + SECTION("REPEAT") { Sampler sampler; sampler.wrapS = Sampler::WrapS::REPEAT; sampler.wrapT = Sampler::WrapT::REPEAT; - ImageCesium image; - image.width = 2; - image.height = 2; - image.channels = 1; - image.bytesPerChannel = 1; - - std::vector data{12, 33, 56, 67}; - std::vector& imageData = image.pixelData; - imageData.resize(data.size()); - std::memcpy(imageData.data(), data.data(), data.size()); - - std::vector channels{0}; - PropertyTexturePropertyView - view(sampler, image, 0, channels, false); + view(property, classProperty, sampler, image, 0, channels); CHECK(view.getSwizzle() == "r"); - std::vector values{ - view.get(1.0, 0), - view.get(-1.5, 0), - view.get(0, -0.5), - view.get(1.5, -0.5)}; - for (size_t i = 0; i < values.size(); i++) { - REQUIRE(values[i] == data[i]); + std::vector uvs{ + glm::dvec2(1.0, 0), + glm::dvec2(-1.5, 0), + glm::dvec2(0, -0.5), + glm::dvec2(1.5, -0.5)}; + for (size_t i = 0; i < uvs.size(); i++) { + glm::dvec2 uv = uvs[i]; + auto value = view.getRaw(uv[0], uv[1]); + REQUIRE(value == data[i]); + + auto maybeValue = view.get(uv[0], uv[1]); + REQUIRE(maybeValue); + REQUIRE(*maybeValue == data[i]); } } @@ -983,30 +1666,23 @@ TEST_CASE("Check sampling with different sampler values") { // MIRRORED: | 1 2 3 | 3 2 1 | // Sampling 0.6 is equal to sampling 1.4 or -0.6. - ImageCesium image; - image.width = 2; - image.height = 2; - image.channels = 1; - image.bytesPerChannel = 1; - - std::vector data{12, 33, 56, 67}; - std::vector& imageData = image.pixelData; - imageData.resize(data.size()); - std::memcpy(imageData.data(), data.data(), data.size()); - - std::vector channels{0}; - PropertyTexturePropertyView - view(sampler, image, 0, channels, false); + view(property, classProperty, sampler, image, 0, channels); CHECK(view.getSwizzle() == "r"); - std::vector values{ - view.get(2.0, 0), - view.get(-0.75, 0), - view.get(0, 1.25), - view.get(-1.25, 2.75)}; - for (size_t i = 0; i < values.size(); i++) { - REQUIRE(values[i] == data[i]); + std::vector uvs{ + glm::dvec2(2.0, 0), + glm::dvec2(-0.75, 0), + glm::dvec2(0, 1.25), + glm::dvec2(-1.25, 2.75)}; + for (size_t i = 0; i < uvs.size(); i++) { + glm::dvec2 uv = uvs[i]; + auto value = view.getRaw(uv[0], uv[1]); + REQUIRE(value == data[i]); + + auto maybeValue = view.get(uv[0], uv[1]); + REQUIRE(maybeValue); + REQUIRE(*maybeValue == data[i]); } } @@ -1015,30 +1691,23 @@ TEST_CASE("Check sampling with different sampler values") { sampler.wrapS = Sampler::WrapS::CLAMP_TO_EDGE; sampler.wrapT = Sampler::WrapT::CLAMP_TO_EDGE; - ImageCesium image; - image.width = 2; - image.height = 2; - image.channels = 1; - image.bytesPerChannel = 1; - - std::vector data{12, 33, 56, 67}; - std::vector& imageData = image.pixelData; - imageData.resize(data.size()); - std::memcpy(imageData.data(), data.data(), data.size()); - - std::vector channels{0}; - PropertyTexturePropertyView - view(sampler, image, 0, channels, false); + view(property, classProperty, sampler, image, 0, channels); CHECK(view.getSwizzle() == "r"); - std::vector values{ - view.get(-1.0, 0), - view.get(1.4, 0), - view.get(0, 2.0), - view.get(1.5, 1.5)}; - for (size_t i = 0; i < values.size(); i++) { - REQUIRE(values[i] == data[i]); + std::vector uvs{ + glm::dvec2(-1.0, 0), + glm::dvec2(1.4, 0), + glm::dvec2(0, 2.0), + glm::dvec2(1.5, 1.5)}; + for (size_t i = 0; i < uvs.size(); i++) { + glm::dvec2 uv = uvs[i]; + auto value = view.getRaw(uv[0], uv[1]); + REQUIRE(value == data[i]); + + auto maybeValue = view.get(uv[0], uv[1]); + REQUIRE(maybeValue); + REQUIRE(*maybeValue == data[i]); } } @@ -1047,30 +1716,23 @@ TEST_CASE("Check sampling with different sampler values") { sampler.wrapS = Sampler::WrapS::REPEAT; sampler.wrapT = Sampler::WrapT::CLAMP_TO_EDGE; - ImageCesium image; - image.width = 2; - image.height = 2; - image.channels = 1; - image.bytesPerChannel = 1; - - std::vector data{12, 33, 56, 67}; - std::vector& imageData = image.pixelData; - imageData.resize(data.size()); - std::memcpy(imageData.data(), data.data(), data.size()); - - std::vector channels{0}; - PropertyTexturePropertyView - view(sampler, image, 0, channels, false); + view(property, classProperty, sampler, image, 0, channels); CHECK(view.getSwizzle() == "r"); - std::vector values{ - view.get(1.0, 0), - view.get(-1.5, -1.0), - view.get(0, 1.5), - view.get(1.5, 1.5)}; - for (size_t i = 0; i < values.size(); i++) { - REQUIRE(values[i] == data[i]); + std::vector uvs{ + glm::dvec2(1.0, 0), + glm::dvec2(-1.5, -1.0), + glm::dvec2(0, 1.5), + glm::dvec2(1.5, 1.5)}; + for (size_t i = 0; i < uvs.size(); i++) { + glm::dvec2 uv = uvs[i]; + auto value = view.getRaw(uv[0], uv[1]); + REQUIRE(value == data[i]); + + auto maybeValue = view.get(uv[0], uv[1]); + REQUIRE(maybeValue); + REQUIRE(*maybeValue == data[i]); } } } From ed2bdbd55319dc6b5e40edc086b70fd26efe6029 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Thu, 24 Aug 2023 20:52:45 -0400 Subject: [PATCH 221/421] Update PropertyTextureView --- CHANGES.md | 6 +- .../CesiumGltf/PropertyTexturePropertyView.h | 10 +- .../include/CesiumGltf/PropertyTextureView.h | 251 ++- CesiumGltf/test/TestPropertyTableView.cpp | 8 +- .../test/TestPropertyTexturePropertyView.cpp | 84 +- CesiumGltf/test/TestPropertyTextureView.cpp | 1547 +++++++++++++++-- 6 files changed, 1679 insertions(+), 227 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 178e949bf..e21ada456 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -12,20 +12,20 @@ - Replaced `FeatureIDTextureView` with `FeatureIdTextureView`, which views a `FeatureIdTexture` in `EXT_mesh_features`. Feature ID textures from `EXT_feature_metadata` are no longer supported. - Renamed `FeatureIDTextureViewStatus` to `FeatureIdTextureViewStatus` for consistency. - Replaced `MetadataFeatureTableView` with `PropertyTableView`, which views a `PropertyTable` in `EXT_structural_metadata`. -- Replaced `MetadataPropertyView` with `PropertyTablePropertyView`, which is a templated view of a `PropertyTableProperty` in `EXT_structural_metadata`. This takes two template parameters: a typename `T` , and a `bool` indicating whether or not the values are normalized. +- Replaced `MetadataPropertyView` with `PropertyTablePropertyView`, which is a view of a `PropertyTableProperty` in `EXT_structural_metadata`. This takes two template parameters: a typename `T` , and a `bool` indicating whether or not the values are normalized. - Renamed `MetadataArrayView` to `PropertyArrayView`. - Replaced `FeatureTextureView` with `PropertyTextureView`, which views a `PropertyTexture` in `EXT_structural_metadata`. - Renamed `FeatureTextureViewStatus` to `PropertyTextureViewStatus`. -- Replaced `FeatureTexturePropertyView` with `PropertyTexturePropertyView`, which is a templated view of a `PropertyTextureProperty` in `EXT_structural_metadata`. +- Replaced `FeatureTexturePropertyView` with `PropertyTexturePropertyView`, which is a view of a `PropertyTextureProperty` in `EXT_structural_metadata`. This takes two template parameters: a typename `T` , and a `bool` indicating whether or not the values are normalized. - Removed `FeatureTexturePropertyComponentType`, `FeatureTexturePropertyChannelOffsets`, and `FeatureTexturePropertyValue`. `PropertyTextureProperty` retrieves the values with the type indicated by its class property. - Renamed `FeatureTexturePropertyViewStatus` to `PropertyTexturePropertyViewStatus`. - Refactored `PropertyType` to reflect the values of `type` in a `ClassProperty` from `EXT_structural_metadata`. ##### Additions :tada: +- Added `PropertyView`, which acts as a base class for all metadata property views. This takes two template parameters: a type `T` , and a `bool` indicating whether or not the values are normalized. - Added `PropertyTableViewStatus` to indicate whether a `PropertyTableView` is valid. - Added `PropertyComponentType` to reflect the values of `componentType` in a `ClassProperty` from `EXT_structural_metadata`. -- Added `PropertyView`, which acts as a base class for all metadata property views. This takes two template parameters: a typename `T` , and a `bool` indicating whether or not the values are normalized. ### v0.25.0 - 2023-06-01 diff --git a/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h index f49b27baf..e71dc1976 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h @@ -181,7 +181,7 @@ class PropertyTexturePropertyView * @param status The code from {@link PropertyTexturePropertyViewStatus} indicating the error with the property. */ PropertyTexturePropertyView(PropertyViewStatusType status) noexcept - : PropertyView(), + : PropertyView(status), _pSampler(nullptr), _pImage(nullptr), _texCoordSetIndex(0), @@ -208,12 +208,11 @@ class PropertyTexturePropertyView const ClassProperty& classProperty, const Sampler& sampler, const ImageCesium& image, - int64_t texCoordSetIndex, const std::vector& channels) noexcept : PropertyView(classProperty, property), _pSampler(&sampler), _pImage(&image), - _texCoordSetIndex(texCoordSetIndex), + _texCoordSetIndex(property.texCoord), _channels(channels), _swizzle("") { if (this->_status != PropertyTexturePropertyViewStatus::Valid) { @@ -500,7 +499,7 @@ class PropertyTexturePropertyView * @param status The code from {@link PropertyTexturePropertyViewStatus} indicating the error with the property. */ PropertyTexturePropertyView(PropertyViewStatusType status) noexcept - : PropertyView(), + : PropertyView(status), _pSampler(nullptr), _pImage(nullptr), _texCoordSetIndex(0), @@ -527,12 +526,11 @@ class PropertyTexturePropertyView const ClassProperty& classProperty, const Sampler& sampler, const ImageCesium& image, - int64_t texCoordSetIndex, const std::vector& channels) noexcept : PropertyView(classProperty, property), _pSampler(&sampler), _pImage(&image), - _texCoordSetIndex(texCoordSetIndex), + _texCoordSetIndex(property.texCoord), _channels(channels), _swizzle("") { if (this->_status != PropertyTexturePropertyViewStatus::Valid) { diff --git a/CesiumGltf/include/CesiumGltf/PropertyTextureView.h b/CesiumGltf/include/CesiumGltf/PropertyTextureView.h index 86f213af0..1a56fa2ec 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTextureView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTextureView.h @@ -91,21 +91,21 @@ class PropertyTextureView { * @return A {@link PropertyTexturePropertyView} of the property. If no valid property is * found, the property view will be invalid. */ - template - PropertyTexturePropertyView + template + PropertyTexturePropertyView getPropertyView(const std::string& propertyName) const { if (this->_status != PropertyTextureViewStatus::Valid) { - return PropertyTexturePropertyView( + return PropertyTexturePropertyView( PropertyTexturePropertyViewStatus::ErrorInvalidPropertyTexture); } const ClassProperty* pClassProperty = getClassProperty(propertyName); if (!pClassProperty) { - return PropertyTexturePropertyView( + return PropertyTexturePropertyView( PropertyTexturePropertyViewStatus::ErrorNonexistentProperty); } - return getPropertyViewImpl(propertyName, *pClassProperty); + return getPropertyViewImpl(propertyName, *pClassProperty); } /** @@ -152,34 +152,68 @@ class PropertyTextureView { componentType = convertStringToPropertyComponentType(*pClassProperty->componentType); } + bool normalized = pClassProperty->normalized; if (pClassProperty->array) { - getArrayPropertyViewImpl( - propertyName, - *pClassProperty, - type, - componentType, - std::forward(callback)); - } else if (type == PropertyType::Scalar) { - getScalarPropertyViewImpl( - propertyName, - *pClassProperty, - componentType, - std::forward(callback)); - } else if (isPropertyTypeVecN(type)) { - getVecNPropertyViewImpl( - propertyName, - *pClassProperty, - type, - componentType, - std::forward(callback)); - } else { - callback( - propertyName, - PropertyTexturePropertyView( - PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty)); + if (normalized) { + getArrayPropertyViewImpl( + propertyName, + *pClassProperty, + type, + componentType, + std::forward(callback)); + } else { + getArrayPropertyViewImpl( + propertyName, + *pClassProperty, + type, + componentType, + std::forward(callback)); + } + return; + } + + if (type == PropertyType::Scalar) { + if (normalized) { + getScalarPropertyViewImpl( + propertyName, + *pClassProperty, + componentType, + std::forward(callback)); + } else { + getScalarPropertyViewImpl( + propertyName, + *pClassProperty, + componentType, + std::forward(callback)); + } + return; + } + + if (isPropertyTypeVecN(type)) { + if (normalized) { + getVecNPropertyViewImpl( + propertyName, + *pClassProperty, + type, + componentType, + std::forward(callback)); + } else { + getVecNPropertyViewImpl( + propertyName, + *pClassProperty, + type, + componentType, + std::forward(callback)); + } return; } + + callback( + propertyName, + PropertyTexturePropertyView( + PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty)); + return; } /** @@ -207,14 +241,14 @@ class PropertyTextureView { } private: - template - PropertyTexturePropertyView getPropertyViewImpl( + template + PropertyTexturePropertyView getPropertyViewImpl( const std::string& propertyName, const ClassProperty& classProperty) const { auto propertyTexturePropertyIter = _pPropertyTexture->properties.find(propertyName); if (propertyTexturePropertyIter == _pPropertyTexture->properties.end()) { - return PropertyTexturePropertyView( + return PropertyTexturePropertyView( PropertyTexturePropertyViewStatus::ErrorNonexistentProperty); } @@ -222,23 +256,25 @@ class PropertyTextureView { propertyTexturePropertyIter->second; if constexpr (IsMetadataScalar::value) { - return createScalarPropertyView( + return createScalarPropertyView( classProperty, propertyTextureProperty); } if constexpr (IsMetadataVecN::value) { - return createVecNPropertyView(classProperty, propertyTextureProperty); + return createVecNPropertyView( + classProperty, + propertyTextureProperty); } if constexpr (IsMetadataArray::value) { - return createArrayPropertyView::type>( - classProperty, - propertyTextureProperty); + return createArrayPropertyView< + typename MetadataArrayType::type, + Normalized>(classProperty, propertyTextureProperty); } } - template + template void getArrayPropertyViewImpl( const std::string& propertyName, const ClassProperty& classProperty, @@ -267,28 +303,28 @@ class PropertyTextureView { case PropertyComponentType::Int8: callback( propertyName, - getPropertyViewImpl>( + getPropertyViewImpl, Normalized>( propertyName, classProperty)); break; case PropertyComponentType::Uint8: callback( propertyName, - getPropertyViewImpl>( + getPropertyViewImpl, Normalized>( propertyName, classProperty)); break; case PropertyComponentType::Int16: callback( propertyName, - getPropertyViewImpl>( + getPropertyViewImpl, Normalized>( propertyName, classProperty)); break; case PropertyComponentType::Uint16: callback( propertyName, - getPropertyViewImpl>( + getPropertyViewImpl, Normalized>( propertyName, classProperty)); break; @@ -301,7 +337,7 @@ class PropertyTextureView { } } - template + template void getScalarPropertyViewImpl( const std::string& propertyName, const ClassProperty& classProperty, @@ -311,37 +347,47 @@ class PropertyTextureView { case PropertyComponentType::Int8: callback( propertyName, - getPropertyViewImpl(propertyName, classProperty)); + getPropertyViewImpl(propertyName, classProperty)); return; case PropertyComponentType::Uint8: callback( propertyName, - getPropertyViewImpl(propertyName, classProperty)); + getPropertyViewImpl( + propertyName, + classProperty)); return; case PropertyComponentType::Int16: callback( propertyName, - getPropertyViewImpl(propertyName, classProperty)); + getPropertyViewImpl( + propertyName, + classProperty)); return; case PropertyComponentType::Uint16: callback( propertyName, - getPropertyViewImpl(propertyName, classProperty)); + getPropertyViewImpl( + propertyName, + classProperty)); break; case PropertyComponentType::Int32: callback( propertyName, - getPropertyViewImpl(propertyName, classProperty)); + getPropertyViewImpl( + propertyName, + classProperty)); break; case PropertyComponentType::Uint32: callback( propertyName, - getPropertyViewImpl(propertyName, classProperty)); + getPropertyViewImpl( + propertyName, + classProperty)); break; case PropertyComponentType::Float32: callback( propertyName, - getPropertyViewImpl(propertyName, classProperty)); + getPropertyViewImpl(propertyName, classProperty)); break; default: callback( @@ -352,7 +398,7 @@ class PropertyTextureView { } } - template + template void getVecNPropertyViewImpl( const std::string& propertyName, const ClassProperty& classProperty, @@ -362,14 +408,14 @@ class PropertyTextureView { case PropertyComponentType::Int8: callback( propertyName, - getPropertyViewImpl>( + getPropertyViewImpl, Normalized>( propertyName, classProperty)); break; case PropertyComponentType::Uint8: callback( propertyName, - getPropertyViewImpl>( + getPropertyViewImpl, Normalized>( propertyName, classProperty)); break; @@ -377,7 +423,7 @@ class PropertyTextureView { if constexpr (N == 2) { callback( propertyName, - getPropertyViewImpl>( + getPropertyViewImpl, Normalized>( propertyName, classProperty)); break; @@ -387,7 +433,7 @@ class PropertyTextureView { if constexpr (N == 2) { callback( propertyName, - getPropertyViewImpl>( + getPropertyViewImpl, Normalized>( propertyName, classProperty)); break; @@ -402,7 +448,7 @@ class PropertyTextureView { } } - template + template void getVecNPropertyViewImpl( const std::string& propertyName, const ClassProperty& classProperty, @@ -412,21 +458,21 @@ class PropertyTextureView { const glm::length_t N = getDimensionsFromPropertyType(type); switch (N) { case 2: - getVecNPropertyViewImpl( + getVecNPropertyViewImpl( propertyName, classProperty, componentType, std::forward(callback)); break; case 3: - getVecNPropertyViewImpl( + getVecNPropertyViewImpl( propertyName, classProperty, componentType, std::forward(callback)); break; case 4: - getVecNPropertyViewImpl( + getVecNPropertyViewImpl( propertyName, classProperty, componentType, @@ -441,18 +487,18 @@ class PropertyTextureView { } } - template - PropertyTexturePropertyView createScalarPropertyView( + template + PropertyTexturePropertyView createScalarPropertyView( const ClassProperty& classProperty, const PropertyTextureProperty& propertyTextureProperty) const { if (classProperty.array) { - return PropertyTexturePropertyView( + return PropertyTexturePropertyView( PropertyTexturePropertyViewStatus::ErrorArrayTypeMismatch); } const PropertyType type = convertStringToPropertyType(classProperty.type); if (TypeToPropertyType::value != type) { - return PropertyTexturePropertyView( + return PropertyTexturePropertyView( PropertyTexturePropertyViewStatus::ErrorTypeMismatch); } @@ -460,31 +506,39 @@ class PropertyTextureView { convertStringToPropertyComponentType( classProperty.componentType.value_or("")); if (TypeToPropertyType::component != componentType) { - return PropertyTexturePropertyView( + return PropertyTexturePropertyView( PropertyTexturePropertyViewStatus::ErrorComponentTypeMismatch); } + if (classProperty.normalized != Normalized) { + return PropertyTexturePropertyView( + PropertyTexturePropertyViewStatus::ErrorInvalidNormalization); + } + // Only up to four bytes of image data are supported. if constexpr (sizeof(T) <= 4) { - return createPropertyViewImpl( + return createPropertyViewImpl( classProperty, propertyTextureProperty, sizeof(T)); + } else { + return PropertyTexturePropertyView( + PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty); } } - template - PropertyTexturePropertyView createVecNPropertyView( + template + PropertyTexturePropertyView createVecNPropertyView( const ClassProperty& classProperty, const PropertyTextureProperty& propertyTextureProperty) const { if (classProperty.array) { - return PropertyTexturePropertyView( + return PropertyTexturePropertyView( PropertyTexturePropertyViewStatus::ErrorArrayTypeMismatch); } const PropertyType type = convertStringToPropertyType(classProperty.type); if (TypeToPropertyType::value != type) { - return PropertyTexturePropertyView( + return PropertyTexturePropertyView( PropertyTexturePropertyViewStatus::ErrorTypeMismatch); } @@ -492,31 +546,40 @@ class PropertyTextureView { convertStringToPropertyComponentType( classProperty.componentType.value_or("")); if (TypeToPropertyType::component != componentType) { - return PropertyTexturePropertyView( + return PropertyTexturePropertyView( PropertyTexturePropertyViewStatus::ErrorComponentTypeMismatch); } + if (classProperty.normalized != Normalized) { + return PropertyTexturePropertyView( + PropertyTexturePropertyViewStatus::ErrorInvalidNormalization); + } + // Only up to four bytes of image data are supported. if constexpr (sizeof(T) <= 4) { - return createPropertyViewImpl( + return createPropertyViewImpl( classProperty, propertyTextureProperty, sizeof(T)); + } else { + return PropertyTexturePropertyView( + PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty); } } - template - PropertyTexturePropertyView> createArrayPropertyView( + template + PropertyTexturePropertyView, Normalized> + createArrayPropertyView( const ClassProperty& classProperty, const PropertyTextureProperty& propertyTextureProperty) const { if (!classProperty.array) { - return PropertyTexturePropertyView>( + return PropertyTexturePropertyView, Normalized>( PropertyTexturePropertyViewStatus::ErrorArrayTypeMismatch); } const PropertyType type = convertStringToPropertyType(classProperty.type); if (TypeToPropertyType::value != type) { - return PropertyTexturePropertyView>( + return PropertyTexturePropertyView, Normalized>( PropertyTexturePropertyViewStatus::ErrorTypeMismatch); } @@ -524,34 +587,42 @@ class PropertyTextureView { convertStringToPropertyComponentType( classProperty.componentType.value_or("")); if (TypeToPropertyType::component != componentType) { - return PropertyTexturePropertyView>( + return PropertyTexturePropertyView, Normalized>( PropertyTexturePropertyViewStatus::ErrorComponentTypeMismatch); } + if (classProperty.normalized != Normalized) { + return PropertyTexturePropertyView, Normalized>( + PropertyTexturePropertyViewStatus::ErrorInvalidNormalization); + } + // Only scalar arrays are supported. The scalar component type must not // exceed two bytes. if constexpr (IsMetadataScalar::value && sizeof(T) <= 4) { // Only up to four elements are supported. int64_t count = classProperty.count.value_or(0); if (count <= 0 || count > 4) { - return PropertyTexturePropertyView>( + return PropertyTexturePropertyView, Normalized>( PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty); } if (count * sizeof(T) > 4) { - return PropertyTexturePropertyView>( + return PropertyTexturePropertyView, Normalized>( PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty); } - return createPropertyViewImpl>( + return createPropertyViewImpl, Normalized>( classProperty, propertyTextureProperty, count * sizeof(T)); + } else { + return PropertyTexturePropertyView, Normalized>( + PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty); } } - template - PropertyTexturePropertyView createPropertyViewImpl( + template + PropertyTexturePropertyView createPropertyViewImpl( const ClassProperty& classProperty, const PropertyTextureProperty& propertyTextureProperty, size_t elementSize) const { @@ -562,17 +633,17 @@ class PropertyTextureView { getTextureSafe(propertyTextureProperty.index, samplerIndex, imageIndex); if (status != PropertyTexturePropertyViewStatus::Valid) { - return PropertyTexturePropertyView(status); + return PropertyTexturePropertyView(status); } status = checkSampler(samplerIndex); if (status != PropertyTexturePropertyViewStatus::Valid) { - return PropertyTexturePropertyView(status); + return PropertyTexturePropertyView(status); } status = checkImage(imageIndex); if (status != PropertyTexturePropertyViewStatus::Valid) { - return PropertyTexturePropertyView(status); + return PropertyTexturePropertyView(status); } const ImageCesium& image = _pModel->images[imageIndex].cesium; @@ -580,19 +651,19 @@ class PropertyTextureView { status = checkChannels(channels, image); if (status != PropertyTexturePropertyViewStatus::Valid) { - return PropertyTexturePropertyView(status); + return PropertyTexturePropertyView(status); } if (channels.size() * image.bytesPerChannel != elementSize) { return PropertyTexturePropertyViewStatus::ErrorChannelsAndTypeMismatch; } - return PropertyTexturePropertyView( + return PropertyTexturePropertyView( + propertyTextureProperty, + classProperty, _pModel->samplers[samplerIndex], image, - propertyTextureProperty.texCoord, - channels, - classProperty.normalized); + channels); } PropertyViewStatusType getTextureSafe( @@ -614,5 +685,5 @@ class PropertyTextureView { const Class* _pClass; PropertyTextureViewStatus _status; -}; +}; // namespace CesiumGltf } // namespace CesiumGltf diff --git a/CesiumGltf/test/TestPropertyTableView.cpp b/CesiumGltf/test/TestPropertyTableView.cpp index 2e9f9cd69..3bb8ca0e8 100644 --- a/CesiumGltf/test/TestPropertyTableView.cpp +++ b/CesiumGltf/test/TestPropertyTableView.cpp @@ -3970,7 +3970,7 @@ TEST_CASE("Test variable-length arrays of strings") { } } -TEST_CASE("Test with property offset, scale, min, max") { +TEST_CASE("Test with PropertyTableProperty offset, scale, min, max") { Model model; std::vector values = {1.0f, 2.0f, 3.0f, 4.0f}; const float offset = 0.5f; @@ -4074,7 +4074,7 @@ TEST_CASE("Test with property offset, scale, min, max") { } } -TEST_CASE("Test with property offset, scale, min, max (normalized)") { +TEST_CASE("Test with PropertyTableProperty offset, scale, min, max (normalized)") { Model model; std::vector values = {-128, 0, 32, 127}; const double offset = 0.5; @@ -4180,7 +4180,7 @@ TEST_CASE("Test with property offset, scale, min, max (normalized)") { } } -TEST_CASE("Test with property noData value") { +TEST_CASE("Test with PropertyTableProperty noData value") { Model model; std::vector values = {-128, 0, 32, -128, 127}; const int8_t noData = -128; @@ -4265,7 +4265,7 @@ TEST_CASE("Test with property noData value") { } } -TEST_CASE("Test with property noData value (normalized)") { +TEST_CASE("Test with PropertyTableProperty noData value (normalized)") { Model model; std::vector values = {-128, 0, 32, -128, 127}; const int8_t noData = -128; diff --git a/CesiumGltf/test/TestPropertyTexturePropertyView.cpp b/CesiumGltf/test/TestPropertyTexturePropertyView.cpp index 4cad0637f..2b1889a64 100644 --- a/CesiumGltf/test/TestPropertyTexturePropertyView.cpp +++ b/CesiumGltf/test/TestPropertyTexturePropertyView.cpp @@ -12,12 +12,6 @@ using namespace CesiumGltf; using namespace CesiumUtility; -std::vector texCoords{ - glm::dvec2(0, 0), - glm::dvec2(0.5, 0), - glm::dvec2(0, 0.5), - glm::dvec2(0.5, 0.5)}; - template void checkTextureValues( const std::vector& data, @@ -48,7 +42,7 @@ void checkTextureValues( } PropertyTexturePropertyView - view(property, classProperty, sampler, image, 0, channels); + view(property, classProperty, sampler, image, channels); switch (sizeof(T)) { case 1: CHECK(view.getSwizzle() == "r"); @@ -68,6 +62,12 @@ void checkTextureValues( REQUIRE(!view.normalized()); + std::vector texCoords{ + glm::dvec2(0, 0), + glm::dvec2(0.5, 0), + glm::dvec2(0, 0.5), + glm::dvec2(0.5, 0.5)}; + for (size_t i = 0; i < texCoords.size(); i++) { glm::dvec2 uv = texCoords[i]; auto value = view.getRaw(uv[0], uv[1]); @@ -119,7 +119,7 @@ void checkTextureValues( } PropertyTexturePropertyView - view(property, classProperty, sampler, image, 0, channels); + view(property, classProperty, sampler, image, channels); switch (sizeof(T)) { case 1: CHECK(view.getSwizzle() == "r"); @@ -139,6 +139,12 @@ void checkTextureValues( REQUIRE(!view.normalized()); + std::vector texCoords{ + glm::dvec2(0, 0), + glm::dvec2(0.5, 0), + glm::dvec2(0, 0.5), + glm::dvec2(0.5, 0.5)}; + for (size_t i = 0; i < texCoords.size(); i++) { glm::dvec2 uv = texCoords[i]; auto value = view.getRaw(uv[0], uv[1]); @@ -191,7 +197,7 @@ void checkNormalizedTextureValues( } PropertyTexturePropertyView - view(property, classProperty, sampler, image, 0, channels); + view(property, classProperty, sampler, image, channels); switch (sizeof(T)) { case 1: CHECK(view.getSwizzle() == "r"); @@ -211,6 +217,12 @@ void checkNormalizedTextureValues( REQUIRE(view.normalized()); + std::vector texCoords{ + glm::dvec2(0, 0), + glm::dvec2(0.5, 0), + glm::dvec2(0, 0.5), + glm::dvec2(0.5, 0.5)}; + for (size_t i = 0; i < texCoords.size(); i++) { glm::dvec2 uv = texCoords[i]; auto value = view.getRaw(uv[0], uv[1]); @@ -256,7 +268,7 @@ void checkTextureArrayValues( } PropertyTexturePropertyView> - view(property, classProperty, sampler, image, 0, channels); + view(property, classProperty, sampler, image, channels); switch (image.channels) { case 1: CHECK(view.getSwizzle() == "r"); @@ -276,6 +288,12 @@ void checkTextureArrayValues( REQUIRE(!view.normalized()); + std::vector texCoords{ + glm::dvec2(0, 0), + glm::dvec2(0.5, 0), + glm::dvec2(0, 0.5), + glm::dvec2(0.5, 0.5)}; + for (size_t i = 0; i < texCoords.size(); i++) { const std::vector& expectedValue = expected[i]; glm::dvec2 uv = texCoords[i]; @@ -333,7 +351,7 @@ void checkTextureArrayValues( } PropertyTexturePropertyView> - view(property, classProperty, sampler, image, 0, channels); + view(property, classProperty, sampler, image, channels); switch (count) { case 1: CHECK(view.getSwizzle() == "r"); @@ -353,6 +371,12 @@ void checkTextureArrayValues( REQUIRE(!view.normalized()); + std::vector texCoords{ + glm::dvec2(0, 0), + glm::dvec2(0.5, 0), + glm::dvec2(0, 0.5), + glm::dvec2(0.5, 0.5)}; + for (size_t i = 0; i < texCoords.size(); i++) { auto expectedRawValue = expectedRaw[i]; glm::dvec2 uv = texCoords[i]; @@ -424,7 +448,7 @@ void checkNormalizedTextureArrayValues( } PropertyTexturePropertyView, true> - view(property, classProperty, sampler, image, 0, channels); + view(property, classProperty, sampler, image, channels); switch (image.channels) { case 1: CHECK(view.getSwizzle() == "r"); @@ -444,6 +468,12 @@ void checkNormalizedTextureArrayValues( REQUIRE(view.normalized()); + std::vector texCoords{ + glm::dvec2(0, 0), + glm::dvec2(0.5, 0), + glm::dvec2(0, 0.5), + glm::dvec2(0.5, 0.5)}; + for (size_t i = 0; i < texCoords.size(); i++) { auto expectedRawValue = expectedRaw[i]; glm::dvec2 uv = texCoords[i]; @@ -1404,7 +1434,7 @@ TEST_CASE("Check that property values override class property values") { property.max = std::numeric_limits::max(); PropertyTexturePropertyView - view(property, classProperty, sampler, image, 0, channels); + view(property, classProperty, sampler, image, channels); CHECK(view.getSwizzle() == "rgba"); REQUIRE(view.offset()); @@ -1416,6 +1446,12 @@ TEST_CASE("Check that property values override class property values") { REQUIRE(view.max()); REQUIRE(*view.max() == std::numeric_limits::max()); + std::vector texCoords{ + glm::dvec2(0, 0), + glm::dvec2(0.5, 0), + glm::dvec2(0, 0.5), + glm::dvec2(0.5, 0.5)}; + for (size_t i = 0; i < texCoords.size(); i++) { glm::dvec2 uv = texCoords[i]; auto value = view.getRaw(uv[0], uv[1]); @@ -1427,6 +1463,12 @@ TEST_CASE("Check that property values override class property values") { } TEST_CASE("Check that non-adjacent channels resolve to expected output") { + std::vector texCoords{ + glm::dvec2(0, 0), + glm::dvec2(0.5, 0), + glm::dvec2(0, 0.5), + glm::dvec2(0.5, 0.5)}; + SECTION("single-byte scalar") { PropertyTextureProperty property; ClassProperty classProperty; @@ -1455,7 +1497,7 @@ TEST_CASE("Check that non-adjacent channels resolve to expected output") { std::vector channels{3}; PropertyTexturePropertyView - view(property, classProperty, sampler, image, 0, channels); + view(property, classProperty, sampler, image, channels); CHECK(view.getSwizzle() == "a"); std::vector expected{3, 4, 0, 1}; @@ -1497,7 +1539,7 @@ TEST_CASE("Check that non-adjacent channels resolve to expected output") { std::vector channels{2, 0}; PropertyTexturePropertyView - view(property, classProperty, sampler, image, 0, channels); + view(property, classProperty, sampler, image, channels); CHECK(view.getSwizzle() == "br"); std::vector expected{2, 259, 257, 520}; @@ -1539,7 +1581,7 @@ TEST_CASE("Check that non-adjacent channels resolve to expected output") { std::vector channels{3, 2, 1}; PropertyTexturePropertyView - view(property, classProperty, sampler, image, 0, channels); + view(property, classProperty, sampler, image, channels); CHECK(view.getSwizzle() == "abg"); std::vector expected{ @@ -1587,7 +1629,7 @@ TEST_CASE("Check that non-adjacent channels resolve to expected output") { std::vector channels{1, 0, 3, 2}; PropertyTexturePropertyView> - view(property, classProperty, sampler, image, 0, channels); + view(property, classProperty, sampler, image, channels); CHECK(view.getSwizzle() == "grab"); std::vector> expected{ @@ -1639,7 +1681,7 @@ TEST_CASE("Check sampling with different sampler values") { sampler.wrapT = Sampler::WrapT::REPEAT; PropertyTexturePropertyView - view(property, classProperty, sampler, image, 0, channels); + view(property, classProperty, sampler, image, channels); CHECK(view.getSwizzle() == "r"); std::vector uvs{ @@ -1667,7 +1709,7 @@ TEST_CASE("Check sampling with different sampler values") { // Sampling 0.6 is equal to sampling 1.4 or -0.6. PropertyTexturePropertyView - view(property, classProperty, sampler, image, 0, channels); + view(property, classProperty, sampler, image, channels); CHECK(view.getSwizzle() == "r"); std::vector uvs{ @@ -1692,7 +1734,7 @@ TEST_CASE("Check sampling with different sampler values") { sampler.wrapT = Sampler::WrapT::CLAMP_TO_EDGE; PropertyTexturePropertyView - view(property, classProperty, sampler, image, 0, channels); + view(property, classProperty, sampler, image, channels); CHECK(view.getSwizzle() == "r"); std::vector uvs{ @@ -1717,7 +1759,7 @@ TEST_CASE("Check sampling with different sampler values") { sampler.wrapT = Sampler::WrapT::CLAMP_TO_EDGE; PropertyTexturePropertyView - view(property, classProperty, sampler, image, 0, channels); + view(property, classProperty, sampler, image, channels); CHECK(view.getSwizzle() == "r"); std::vector uvs{ diff --git a/CesiumGltf/test/TestPropertyTextureView.cpp b/CesiumGltf/test/TestPropertyTextureView.cpp index d442ee364..af6e61e13 100644 --- a/CesiumGltf/test/TestPropertyTextureView.cpp +++ b/CesiumGltf/test/TestPropertyTextureView.cpp @@ -159,21 +159,27 @@ TEST_CASE("Test scalar PropertyTextureProperty") { REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT8); REQUIRE(classProperty->count == std::nullopt); REQUIRE(!classProperty->array); + REQUIRE(!classProperty->normalized); SECTION("Access correct type") { PropertyTexturePropertyView uint8Property = view.getPropertyView("TestClassProperty"); REQUIRE(uint8Property.status() == PropertyTexturePropertyViewStatus::Valid); - std::vector values{ - uint8Property.get(0.0, 0.0), - uint8Property.get(0.5, 0.0), - uint8Property.get(0.0, 0.5), - uint8Property.get(0.5, 0.5), - }; + std::vector texCoords{ + glm::dvec2(0, 0), + glm::dvec2(0.5, 0), + glm::dvec2(0, 0.5), + glm::dvec2(0.5, 0.5)}; - for (size_t i = 0; i < values.size(); ++i) { - REQUIRE(values[i] == data[i]); + for (size_t i = 0; i < texCoords.size(); ++i) { + glm::dvec2 uv = texCoords[i]; + auto value = uint8Property.getRaw(uv[0], uv[1]); + REQUIRE(value == data[i]); + + auto maybeValue = uint8Property.get(uv[0], uv[1]); + REQUIRE(maybeValue); + REQUIRE(*maybeValue == data[i]); } } @@ -213,6 +219,14 @@ TEST_CASE("Test scalar PropertyTextureProperty") { PropertyTexturePropertyViewStatus::ErrorArrayTypeMismatch); } + SECTION("Access incorrectly as normalized") { + PropertyTexturePropertyView normalizedInvalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + normalizedInvalid.status() == + PropertyTexturePropertyViewStatus::ErrorInvalidNormalization); + } + SECTION("Channel and type mismatch") { model.images[imageIndex].cesium.channels = 2; propertyTextureProperty.channels = {0, 1}; @@ -287,6 +301,195 @@ TEST_CASE("Test scalar PropertyTextureProperty") { } } +TEST_CASE("Test scalar PropertyTextureProperty (normalized)") { + Model model; + std::vector data = {12, 34, 30, 11}; + + addTextureToModel( + model, + Sampler::WrapS::CLAMP_TO_EDGE, + Sampler::WrapS::CLAMP_TO_EDGE, + 2, + 2, + 1, + data); + size_t textureIndex = model.textures.size() - 1; + size_t imageIndex = model.images.size() - 1; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::UINT8; + testClassProperty.normalized = true; + + PropertyTexture& propertyTexture = metadata.propertyTextures.emplace_back(); + propertyTexture.classProperty = "TestClass"; + + PropertyTextureProperty& propertyTextureProperty = + propertyTexture.properties["TestClassProperty"]; + propertyTextureProperty.index = static_cast(textureIndex); + propertyTextureProperty.texCoord = 0; + propertyTextureProperty.channels = {0}; + + PropertyTextureView view(model, propertyTexture); + REQUIRE(view.status() == PropertyTextureViewStatus::Valid); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT8); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->array); + REQUIRE(classProperty->normalized); + + SECTION("Access correct type") { + PropertyTexturePropertyView uint8Property = + view.getPropertyView("TestClassProperty"); + REQUIRE(uint8Property.status() == PropertyTexturePropertyViewStatus::Valid); + + std::vector texCoords{ + glm::dvec2(0, 0), + glm::dvec2(0.5, 0), + glm::dvec2(0, 0.5), + glm::dvec2(0.5, 0.5)}; + + for (size_t i = 0; i < texCoords.size(); ++i) { + glm::dvec2 uv = texCoords[i]; + auto value = uint8Property.getRaw(uv[0], uv[1]); + REQUIRE(value == data[i]); + + auto maybeValue = uint8Property.get(uv[0], uv[1]); + REQUIRE(maybeValue); + REQUIRE(*maybeValue == normalize(data[i])); + } + } + + SECTION("Access wrong type") { + PropertyTexturePropertyView u8vec2Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + u8vec2Invalid.status() == + PropertyTexturePropertyViewStatus::ErrorTypeMismatch); + } + + SECTION("Access wrong component type") { + PropertyTexturePropertyView uint16Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uint16Invalid.status() == + PropertyTexturePropertyViewStatus::ErrorComponentTypeMismatch); + + PropertyTexturePropertyView int32Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + int32Invalid.status() == + PropertyTexturePropertyViewStatus::ErrorComponentTypeMismatch); + } + + SECTION("Access incorrectly as array") { + PropertyTexturePropertyView, true> arrayInvalid = + view.getPropertyView, true>( + "TestClassProperty"); + REQUIRE( + arrayInvalid.status() == + PropertyTexturePropertyViewStatus::ErrorArrayTypeMismatch); + } + + SECTION("Access incorrectly as non-normalized") { + PropertyTexturePropertyView normalizedInvalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + normalizedInvalid.status() == + PropertyTexturePropertyViewStatus::ErrorInvalidNormalization); + } + + SECTION("Access incorrectly as double") { + PropertyTexturePropertyView doubleInvalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + doubleInvalid.status() == + PropertyTexturePropertyViewStatus::ErrorComponentTypeMismatch); + } + + SECTION("Channel and type mismatch") { + model.images[imageIndex].cesium.channels = 2; + propertyTextureProperty.channels = {0, 1}; + PropertyTexturePropertyView uint8Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uint8Property.status() == + PropertyTexturePropertyViewStatus::ErrorChannelsAndTypeMismatch); + } + + SECTION("Invalid channel values") { + propertyTextureProperty.channels = {5}; + PropertyTexturePropertyView uint8Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uint8Property.status() == + PropertyTexturePropertyViewStatus::ErrorInvalidChannels); + } + + SECTION("Zero channel values") { + propertyTextureProperty.channels.clear(); + PropertyTexturePropertyView uint8Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uint8Property.status() == + PropertyTexturePropertyViewStatus::ErrorInvalidChannels); + } + + SECTION("Invalid bytes per channel") { + model.images[imageIndex].cesium.bytesPerChannel = 2; + PropertyTexturePropertyView uint8Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uint8Property.status() == + PropertyTexturePropertyViewStatus::ErrorInvalidBytesPerChannel); + } + + SECTION("Empty image") { + model.images[imageIndex].cesium.width = 0; + PropertyTexturePropertyView uint8Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uint8Property.status() == + PropertyTexturePropertyViewStatus::ErrorEmptyImage); + } + + SECTION("Wrong image index") { + model.textures[textureIndex].source = 1; + PropertyTexturePropertyView uint8Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uint8Property.status() == + PropertyTexturePropertyViewStatus::ErrorInvalidImage); + } + + SECTION("Wrong sampler index") { + model.textures[textureIndex].sampler = 1; + PropertyTexturePropertyView uint8Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uint8Property.status() == + PropertyTexturePropertyViewStatus::ErrorInvalidSampler); + } + + SECTION("Wrong texture index") { + propertyTextureProperty.index = 1; + PropertyTexturePropertyView uint8Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uint8Property.status() == + PropertyTexturePropertyViewStatus::ErrorInvalidTexture); + } +} + TEST_CASE("Test vecN PropertyTextureProperty") { Model model; std::vector data = {12, 34, 10, 3, 40, 0, 30, 11}; @@ -330,6 +533,7 @@ TEST_CASE("Test vecN PropertyTextureProperty") { REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT8); REQUIRE(classProperty->count == std::nullopt); REQUIRE(!classProperty->array); + REQUIRE(!classProperty->normalized); SECTION("Access correct type") { PropertyTexturePropertyView u8vec2Property = @@ -337,19 +541,25 @@ TEST_CASE("Test vecN PropertyTextureProperty") { REQUIRE( u8vec2Property.status() == PropertyTexturePropertyViewStatus::Valid); + std::vector texCoords{ + glm::dvec2(0, 0), + glm::dvec2(0.5, 0), + glm::dvec2(0, 0.5), + glm::dvec2(0.5, 0.5)}; + std::vector expected{ glm::u8vec2(12, 34), glm::u8vec2(10, 3), glm::u8vec2(40, 0), glm::u8vec2(30, 11)}; - std::vector values{ - u8vec2Property.get(0.0, 0.0), - u8vec2Property.get(0.5, 0.0), - u8vec2Property.get(0.0, 0.5), - u8vec2Property.get(0.5, 0.5), - }; - for (size_t i = 0; i < values.size(); ++i) { - REQUIRE(values[i] == expected[i]); + for (size_t i = 0; i < texCoords.size(); ++i) { + glm::dvec2 uv = texCoords[i]; + auto value = u8vec2Property.getRaw(uv[0], uv[1]); + REQUIRE(value == expected[i]); + + auto maybeValue = u8vec2Property.get(uv[0], uv[1]); + REQUIRE(maybeValue); + REQUIRE(*maybeValue == expected[i]); } } @@ -381,6 +591,23 @@ TEST_CASE("Test vecN PropertyTextureProperty") { PropertyTexturePropertyViewStatus::ErrorComponentTypeMismatch); } + SECTION("Access incorrectly as array") { + PropertyTexturePropertyView> arrayInvalid = + view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + arrayInvalid.status() == + PropertyTexturePropertyViewStatus::ErrorArrayTypeMismatch); + } + + SECTION("Access incorrectly as normalized") { + PropertyTexturePropertyView normalizedInvalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + normalizedInvalid.status() == + PropertyTexturePropertyViewStatus::ErrorInvalidNormalization); + } + SECTION("Channel and type mismatch") { model.images[imageIndex].cesium.channels = 4; propertyTextureProperty.channels = {0, 1, 2, 3}; @@ -410,16 +637,9 @@ TEST_CASE("Test vecN PropertyTextureProperty") { } } -TEST_CASE("Test array PropertyTextureProperty") { +TEST_CASE("Test vecN PropertyTextureProperty (normalized)") { Model model; - // clang-format off - std::vector data = { - 12, 34, 10, - 40, 0, 30, - 80, 4, 2, - 6, 3, 4, - }; - // clang-format on + std::vector data = {12, 34, 10, 3, 40, 0, 30, 11}; addTextureToModel( model, @@ -427,7 +647,7 @@ TEST_CASE("Test array PropertyTextureProperty") { Sampler::WrapS::CLAMP_TO_EDGE, 2, 2, - 3, + 2, data); size_t textureIndex = model.textures.size() - 1; size_t imageIndex = model.images.size() - 1; @@ -438,10 +658,9 @@ TEST_CASE("Test array PropertyTextureProperty") { Schema& schema = metadata.schema.emplace(); Class& testClass = schema.classes["TestClass"]; ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; - testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.type = ClassProperty::Type::VEC2; testClassProperty.componentType = ClassProperty::ComponentType::UINT8; - testClassProperty.array = true; - testClassProperty.count = 3; + testClassProperty.normalized = true; PropertyTexture& propertyTexture = metadata.propertyTextures.emplace_back(); propertyTexture.classProperty = "TestClass"; @@ -450,7 +669,7 @@ TEST_CASE("Test array PropertyTextureProperty") { propertyTexture.properties["TestClassProperty"]; propertyTextureProperty.index = static_cast(textureIndex); propertyTextureProperty.texCoord = 0; - propertyTextureProperty.channels = {0, 1, 2}; + propertyTextureProperty.channels = {0, 1}; PropertyTextureView view(model, propertyTexture); REQUIRE(view.status() == PropertyTextureViewStatus::Valid); @@ -458,92 +677,900 @@ TEST_CASE("Test array PropertyTextureProperty") { const ClassProperty* classProperty = view.getClassProperty("TestClassProperty"); REQUIRE(classProperty); - REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); + REQUIRE(classProperty->type == ClassProperty::Type::VEC2); REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT8); - REQUIRE(classProperty->array); - REQUIRE(classProperty->count == 3); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->array); + REQUIRE(classProperty->normalized); SECTION("Access correct type") { - PropertyTexturePropertyView> uint8ArrayProperty = - view.getPropertyView>("TestClassProperty"); + PropertyTexturePropertyView u8vec2Property = + view.getPropertyView("TestClassProperty"); REQUIRE( - uint8ArrayProperty.status() == - PropertyTexturePropertyViewStatus::Valid); + u8vec2Property.status() == PropertyTexturePropertyViewStatus::Valid); - std::vector> values{ - uint8ArrayProperty.get(0.0, 0.0), - uint8ArrayProperty.get(0.5, 0.0), - uint8ArrayProperty.get(0.0, 0.5), - uint8ArrayProperty.get(0.5, 0.5), - }; + std::vector texCoords{ + glm::dvec2(0, 0), + glm::dvec2(0.5, 0), + glm::dvec2(0, 0.5), + glm::dvec2(0.5, 0.5)}; - int64_t size = static_cast(values.size()); - for (int64_t i = 0; i < size; ++i) { - auto dataStart = data.begin() + i * 3; - std::vector expected(dataStart, dataStart + 3); - const PropertyArrayView& value = values[static_cast(i)]; - REQUIRE(static_cast(value.size()) == expected.size()); - for (int64_t j = 0; j < value.size(); j++) { - REQUIRE(value[j] == expected[static_cast(j)]); - } + std::vector expected{ + glm::u8vec2(12, 34), + glm::u8vec2(10, 3), + glm::u8vec2(40, 0), + glm::u8vec2(30, 11)}; + for (size_t i = 0; i < texCoords.size(); ++i) { + glm::dvec2 uv = texCoords[i]; + auto value = u8vec2Property.getRaw(uv[0], uv[1]); + REQUIRE(value == expected[i]); + + auto maybeValue = u8vec2Property.get(uv[0], uv[1]); + REQUIRE(maybeValue); + REQUIRE(*maybeValue == normalize(expected[i])); } } + SECTION("Access wrong type") { + PropertyTexturePropertyView uint8Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uint8Invalid.status() == + PropertyTexturePropertyViewStatus::ErrorTypeMismatch); + + PropertyTexturePropertyView u8vec3Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + u8vec3Invalid.status() == + PropertyTexturePropertyViewStatus::ErrorTypeMismatch); + } + SECTION("Access wrong component type") { - PropertyTexturePropertyView> int8ArrayInvalid = - view.getPropertyView>("TestClassProperty"); + PropertyTexturePropertyView u16vec2Invalid = + view.getPropertyView("TestClassProperty"); REQUIRE( - int8ArrayInvalid.status() == + u16vec2Invalid.status() == PropertyTexturePropertyViewStatus::ErrorComponentTypeMismatch); - PropertyTexturePropertyView> - uint16ArrayInvalid = view.getPropertyView>( - "TestClassProperty"); + PropertyTexturePropertyView i8vec2Invalid = + view.getPropertyView("TestClassProperty"); REQUIRE( - uint16ArrayInvalid.status() == + i8vec2Invalid.status() == PropertyTexturePropertyViewStatus::ErrorComponentTypeMismatch); } - SECTION("Access incorrectly as non-array") { - PropertyTexturePropertyView uint8Invalid = - view.getPropertyView("TestClassProperty"); + SECTION("Access incorrectly as array") { + PropertyTexturePropertyView, true> + arrayInvalid = + view.getPropertyView, true>( + "TestClassProperty"); REQUIRE( - uint8Invalid.status() == + arrayInvalid.status() == PropertyTexturePropertyViewStatus::ErrorArrayTypeMismatch); + } - PropertyTexturePropertyView u8vec3Invalid = - view.getPropertyView("TestClassProperty"); + SECTION("Access incorrectly as non-normalized") { + PropertyTexturePropertyView normalizedInvalid = + view.getPropertyView("TestClassProperty"); REQUIRE( - u8vec3Invalid.status() == - PropertyTexturePropertyViewStatus::ErrorArrayTypeMismatch); + normalizedInvalid.status() == + PropertyTexturePropertyViewStatus::ErrorInvalidNormalization); + } + + SECTION("Access incorrectly as dvec2") { + PropertyTexturePropertyView normalizedInvalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + normalizedInvalid.status() == + PropertyTexturePropertyViewStatus::ErrorComponentTypeMismatch); } SECTION("Channel and type mismatch") { model.images[imageIndex].cesium.channels = 4; propertyTextureProperty.channels = {0, 1, 2, 3}; - PropertyTexturePropertyView> uint8ArrayProperty = - view.getPropertyView>("TestClassProperty"); + PropertyTexturePropertyView u8vec2Property = + view.getPropertyView("TestClassProperty"); REQUIRE( - uint8ArrayProperty.status() == + u8vec2Property.status() == PropertyTexturePropertyViewStatus::ErrorChannelsAndTypeMismatch); } SECTION("Invalid channel values") { - propertyTextureProperty.channels = {0, 4, 1}; - PropertyTexturePropertyView> uint8ArrayProperty = - view.getPropertyView>("TestClassProperty"); + propertyTextureProperty.channels = {0, 4}; + PropertyTexturePropertyView u8vec2Property = + view.getPropertyView("TestClassProperty"); REQUIRE( - uint8ArrayProperty.status() == + u8vec2Property.status() == PropertyTexturePropertyViewStatus::ErrorInvalidChannels); } - SECTION("Invalid bytes per channel") { - model.images[imageIndex].cesium.bytesPerChannel = 2; - PropertyTexturePropertyView> uint8ArrayProperty = - view.getPropertyView>("TestClassProperty"); - REQUIRE( - uint8ArrayProperty.status() == - PropertyTexturePropertyViewStatus::ErrorInvalidBytesPerChannel); + SECTION("Invalid bytes per channel") { + model.images[imageIndex].cesium.bytesPerChannel = 2; + PropertyTexturePropertyView u8vec2Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + u8vec2Property.status() == + PropertyTexturePropertyViewStatus::ErrorInvalidBytesPerChannel); + } +} + +TEST_CASE("Test array PropertyTextureProperty") { + Model model; + // clang-format off + std::vector data = { + 12, 34, 10, + 40, 0, 30, + 80, 4, 2, + 6, 3, 4, + }; + // clang-format on + + addTextureToModel( + model, + Sampler::WrapS::CLAMP_TO_EDGE, + Sampler::WrapS::CLAMP_TO_EDGE, + 2, + 2, + 3, + data); + size_t textureIndex = model.textures.size() - 1; + size_t imageIndex = model.images.size() - 1; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::UINT8; + testClassProperty.array = true; + testClassProperty.count = 3; + + PropertyTexture& propertyTexture = metadata.propertyTextures.emplace_back(); + propertyTexture.classProperty = "TestClass"; + + PropertyTextureProperty& propertyTextureProperty = + propertyTexture.properties["TestClassProperty"]; + propertyTextureProperty.index = static_cast(textureIndex); + propertyTextureProperty.texCoord = 0; + propertyTextureProperty.channels = {0, 1, 2}; + + PropertyTextureView view(model, propertyTexture); + REQUIRE(view.status() == PropertyTextureViewStatus::Valid); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT8); + REQUIRE(classProperty->array); + REQUIRE(classProperty->count == 3); + REQUIRE(!classProperty->normalized); + + SECTION("Access correct type") { + PropertyTexturePropertyView> uint8ArrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + uint8ArrayProperty.status() == + PropertyTexturePropertyViewStatus::Valid); + + std::vector texCoords{ + glm::dvec2(0, 0), + glm::dvec2(0.5, 0), + glm::dvec2(0, 0.5), + glm::dvec2(0.5, 0.5)}; + + int64_t size = static_cast(texCoords.size()); + for (int64_t i = 0; i < size; ++i) { + glm::dvec2 uv = texCoords[static_cast(i)]; + + auto dataStart = data.begin() + i * 3; + std::vector expected(dataStart, dataStart + 3); + + const PropertyArrayView& value = + uint8ArrayProperty.getRaw(uv[0], uv[1]); + REQUIRE(static_cast(value.size()) == expected.size()); + for (int64_t j = 0; j < value.size(); j++) { + REQUIRE(value[j] == expected[static_cast(j)]); + } + + auto maybeValue = uint8ArrayProperty.get(uv[0], uv[1]); + REQUIRE(maybeValue); + for (int64_t j = 0; j < maybeValue->size(); j++) { + REQUIRE((*maybeValue)[j] == value[j]); + } + } + } + + SECTION("Access wrong component type") { + PropertyTexturePropertyView> int8ArrayInvalid = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + int8ArrayInvalid.status() == + PropertyTexturePropertyViewStatus::ErrorComponentTypeMismatch); + + PropertyTexturePropertyView> + uint16ArrayInvalid = view.getPropertyView>( + "TestClassProperty"); + REQUIRE( + uint16ArrayInvalid.status() == + PropertyTexturePropertyViewStatus::ErrorComponentTypeMismatch); + } + + SECTION("Access incorrectly as non-array") { + PropertyTexturePropertyView uint8Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uint8Invalid.status() == + PropertyTexturePropertyViewStatus::ErrorArrayTypeMismatch); + + PropertyTexturePropertyView u8vec3Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + u8vec3Invalid.status() == + PropertyTexturePropertyViewStatus::ErrorArrayTypeMismatch); + } + + SECTION("Access incorrectly as normalized") { + PropertyTexturePropertyView, true> + normalizedInvalid = + view.getPropertyView, true>( + "TestClassProperty"); + REQUIRE( + normalizedInvalid.status() == + PropertyTexturePropertyViewStatus::ErrorInvalidNormalization); + } + + SECTION("Channel and type mismatch") { + model.images[imageIndex].cesium.channels = 4; + propertyTextureProperty.channels = {0, 1, 2, 3}; + PropertyTexturePropertyView> uint8ArrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + uint8ArrayProperty.status() == + PropertyTexturePropertyViewStatus::ErrorChannelsAndTypeMismatch); + } + + SECTION("Invalid channel values") { + propertyTextureProperty.channels = {0, 4, 1}; + PropertyTexturePropertyView> uint8ArrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + uint8ArrayProperty.status() == + PropertyTexturePropertyViewStatus::ErrorInvalidChannels); + } + + SECTION("Invalid bytes per channel") { + model.images[imageIndex].cesium.bytesPerChannel = 2; + PropertyTexturePropertyView> uint8ArrayProperty = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + uint8ArrayProperty.status() == + PropertyTexturePropertyViewStatus::ErrorInvalidBytesPerChannel); + } +} + +TEST_CASE("Test array PropertyTextureProperty (normalized)") { + Model model; + // clang-format off + std::vector data = { + 12, 34, 10, + 40, 0, 30, + 80, 4, 2, + 6, 3, 4, + }; + // clang-format on + + addTextureToModel( + model, + Sampler::WrapS::CLAMP_TO_EDGE, + Sampler::WrapS::CLAMP_TO_EDGE, + 2, + 2, + 3, + data); + size_t textureIndex = model.textures.size() - 1; + size_t imageIndex = model.images.size() - 1; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::UINT8; + testClassProperty.array = true; + testClassProperty.count = 3; + testClassProperty.normalized = true; + + PropertyTexture& propertyTexture = metadata.propertyTextures.emplace_back(); + propertyTexture.classProperty = "TestClass"; + + PropertyTextureProperty& propertyTextureProperty = + propertyTexture.properties["TestClassProperty"]; + propertyTextureProperty.index = static_cast(textureIndex); + propertyTextureProperty.texCoord = 0; + propertyTextureProperty.channels = {0, 1, 2}; + + PropertyTextureView view(model, propertyTexture); + REQUIRE(view.status() == PropertyTextureViewStatus::Valid); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT8); + REQUIRE(classProperty->array); + REQUIRE(classProperty->count == 3); + REQUIRE(classProperty->normalized); + + SECTION("Access correct type") { + PropertyTexturePropertyView, true> + uint8ArrayProperty = + view.getPropertyView, true>( + "TestClassProperty"); + REQUIRE( + uint8ArrayProperty.status() == + PropertyTexturePropertyViewStatus::Valid); + + std::vector texCoords{ + glm::dvec2(0, 0), + glm::dvec2(0.5, 0), + glm::dvec2(0, 0.5), + glm::dvec2(0.5, 0.5)}; + + int64_t size = static_cast(texCoords.size()); + for (int64_t i = 0; i < size; ++i) { + glm::dvec2 uv = texCoords[static_cast(i)]; + + auto dataStart = data.begin() + i * 3; + std::vector expected(dataStart, dataStart + 3); + + const PropertyArrayView& value = + uint8ArrayProperty.getRaw(uv[0], uv[1]); + REQUIRE(static_cast(value.size()) == expected.size()); + for (int64_t j = 0; j < value.size(); j++) { + REQUIRE(value[j] == expected[static_cast(j)]); + } + + auto maybeValue = uint8ArrayProperty.get(uv[0], uv[1]); + REQUIRE(maybeValue); + for (int64_t j = 0; j < maybeValue->size(); j++) { + REQUIRE((*maybeValue)[j] == normalize(value[j])); + } + } + } + + SECTION("Access wrong component type") { + PropertyTexturePropertyView, true> + int8ArrayInvalid = + view.getPropertyView, true>( + "TestClassProperty"); + REQUIRE( + int8ArrayInvalid.status() == + PropertyTexturePropertyViewStatus::ErrorComponentTypeMismatch); + + PropertyTexturePropertyView, true> + uint16ArrayInvalid = + view.getPropertyView, true>( + "TestClassProperty"); + REQUIRE( + uint16ArrayInvalid.status() == + PropertyTexturePropertyViewStatus::ErrorComponentTypeMismatch); + } + + SECTION("Access incorrectly as non-array") { + PropertyTexturePropertyView uint8Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uint8Invalid.status() == + PropertyTexturePropertyViewStatus::ErrorArrayTypeMismatch); + + PropertyTexturePropertyView u8vec3Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + u8vec3Invalid.status() == + PropertyTexturePropertyViewStatus::ErrorArrayTypeMismatch); + } + + SECTION("Access incorrectly as normalized") { + PropertyTexturePropertyView> normalizedInvalid = + view.getPropertyView>("TestClassProperty"); + REQUIRE( + normalizedInvalid.status() == + PropertyTexturePropertyViewStatus::ErrorInvalidNormalization); + } + + SECTION("Channel and type mismatch") { + model.images[imageIndex].cesium.channels = 4; + propertyTextureProperty.channels = {0, 1, 2, 3}; + PropertyTexturePropertyView, true> + uint8ArrayProperty = + view.getPropertyView, true>( + "TestClassProperty"); + REQUIRE( + uint8ArrayProperty.status() == + PropertyTexturePropertyViewStatus::ErrorChannelsAndTypeMismatch); + } + + SECTION("Invalid channel values") { + propertyTextureProperty.channels = {0, 4, 1}; + PropertyTexturePropertyView, true> + uint8ArrayProperty = + view.getPropertyView, true>( + "TestClassProperty"); + REQUIRE( + uint8ArrayProperty.status() == + PropertyTexturePropertyViewStatus::ErrorInvalidChannels); + } + + SECTION("Invalid bytes per channel") { + model.images[imageIndex].cesium.bytesPerChannel = 2; + PropertyTexturePropertyView, true> + uint8ArrayProperty = + view.getPropertyView, true>( + "TestClassProperty"); + REQUIRE( + uint8ArrayProperty.status() == + PropertyTexturePropertyViewStatus::ErrorInvalidBytesPerChannel); + } +} + +TEST_CASE("Test with PropertyTextureProperty offset, scale, min, max") { + Model model; + // clang-format off + std::vector data{ + 0, 0, 0, 1, + 9, 0, 1, 0, + 20, 2, 2, 0, + 8, 1, 0, 1}; + // clang-format on + + std::vector expectedUint{16777216, 65545, 131604, 16777480}; + + const float offset = 1.0f; + const float scale = 2.0f; + const float min = -10.0f; + const float max = 10.0f; + + addTextureToModel( + model, + Sampler::WrapS::CLAMP_TO_EDGE, + Sampler::WrapS::CLAMP_TO_EDGE, + 2, + 2, + 4, + data); + size_t textureIndex = model.textures.size() - 1; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::FLOAT32; + testClassProperty.offset = offset; + testClassProperty.scale = scale; + testClassProperty.min = min; + testClassProperty.max = max; + + PropertyTexture& propertyTexture = metadata.propertyTextures.emplace_back(); + propertyTexture.classProperty = "TestClass"; + + PropertyTextureProperty& propertyTextureProperty = + propertyTexture.properties["TestClassProperty"]; + propertyTextureProperty.index = static_cast(textureIndex); + propertyTextureProperty.texCoord = 0; + propertyTextureProperty.channels = {0, 1, 2, 3}; + + PropertyTextureView view(model, propertyTexture); + REQUIRE(view.status() == PropertyTextureViewStatus::Valid); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); + REQUIRE( + classProperty->componentType == ClassProperty::ComponentType::FLOAT32); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->array); + REQUIRE(!classProperty->normalized); + REQUIRE(classProperty->offset); + REQUIRE(classProperty->scale); + REQUIRE(classProperty->min); + REQUIRE(classProperty->max); + + std::vector texCoords{ + glm::dvec2(0, 0), + glm::dvec2(0.5, 0), + glm::dvec2(0, 0.5), + glm::dvec2(0.5, 0.5)}; + + SECTION("Use class property values") { + PropertyTexturePropertyView property = + view.getPropertyView("TestClassProperty"); + REQUIRE(property.status() == PropertyTexturePropertyViewStatus::Valid); + REQUIRE(property.offset()); + REQUIRE(*property.offset() == offset); + REQUIRE(property.scale()); + REQUIRE(*property.scale() == scale); + REQUIRE(property.min()); + REQUIRE(*property.min() == min); + REQUIRE(property.max()); + REQUIRE(*property.max() == max); + + std::vector expectedRaw(expectedUint.size()); + std::vector> expectedTransformed(expectedUint.size()); + for (size_t i = 0; i < expectedUint.size(); i++) { + float value = *reinterpret_cast(&expectedUint[i]); + expectedRaw[i] = value; + expectedTransformed[i] = value * scale + offset; + } + + for (size_t i = 0; i < texCoords.size(); ++i) { + glm::dvec2 uv = texCoords[i]; + auto value = property.getRaw(uv[0], uv[1]); + REQUIRE(value == expectedRaw[i]); + + auto maybeValue = property.get(uv[0], uv[1]); + REQUIRE(maybeValue == expectedTransformed[i]); + } + } + + SECTION("Use own property values") { + const float newOffset = 1.0f; + const float newScale = -1.0f; + const float newMin = -3.0f; + const float newMax = 0.0f; + propertyTextureProperty.offset = newOffset; + propertyTextureProperty.scale = newScale; + propertyTextureProperty.min = newMin; + propertyTextureProperty.max = newMax; + + PropertyTexturePropertyView property = + view.getPropertyView("TestClassProperty"); + REQUIRE(property.status() == PropertyTexturePropertyViewStatus::Valid); + REQUIRE(property.offset()); + REQUIRE(*property.offset() == newOffset); + REQUIRE(property.scale()); + REQUIRE(*property.scale() == newScale); + REQUIRE(property.min()); + REQUIRE(*property.min() == newMin); + REQUIRE(property.max()); + REQUIRE(*property.max() == newMax); + + std::vector expectedRaw(expectedUint.size()); + std::vector> expectedTransformed(expectedUint.size()); + for (size_t i = 0; i < expectedUint.size(); i++) { + float value = *reinterpret_cast(&expectedUint[i]); + expectedRaw[i] = value; + expectedTransformed[i] = value * newScale + newOffset; + } + + for (size_t i = 0; i < texCoords.size(); ++i) { + glm::dvec2 uv = texCoords[i]; + auto value = property.getRaw(uv[0], uv[1]); + REQUIRE(value == expectedRaw[i]); + + auto maybeValue = property.get(uv[0], uv[1]); + REQUIRE(maybeValue == expectedTransformed[i]); + } + } +} + +TEST_CASE( + "Test with PropertyTextureProperty offset, scale, min, max (normalized)") { + Model model; + std::vector data = {12, 34, 30, 11}; + + const double offset = 1.0; + const double scale = 2.0; + const double min = 1.0; + const double max = 3.0; + + addTextureToModel( + model, + Sampler::WrapS::CLAMP_TO_EDGE, + Sampler::WrapS::CLAMP_TO_EDGE, + 2, + 2, + 1, + data); + size_t textureIndex = model.textures.size() - 1; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::UINT8; + testClassProperty.normalized = true; + testClassProperty.offset = offset; + testClassProperty.scale = scale; + testClassProperty.min = min; + testClassProperty.max = max; + + PropertyTexture& propertyTexture = metadata.propertyTextures.emplace_back(); + propertyTexture.classProperty = "TestClass"; + + PropertyTextureProperty& propertyTextureProperty = + propertyTexture.properties["TestClassProperty"]; + propertyTextureProperty.index = static_cast(textureIndex); + propertyTextureProperty.texCoord = 0; + propertyTextureProperty.channels = {0}; + + PropertyTextureView view(model, propertyTexture); + REQUIRE(view.status() == PropertyTextureViewStatus::Valid); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT8); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->array); + REQUIRE(classProperty->normalized); + + std::vector texCoords{ + glm::dvec2(0, 0), + glm::dvec2(0.5, 0), + glm::dvec2(0, 0.5), + glm::dvec2(0.5, 0.5)}; + + SECTION("Use class property values") { + PropertyTexturePropertyView property = + view.getPropertyView("TestClassProperty"); + REQUIRE(property.status() == PropertyTexturePropertyViewStatus::Valid); + REQUIRE(property.offset()); + REQUIRE(*property.offset() == offset); + REQUIRE(property.scale()); + REQUIRE(*property.scale() == scale); + REQUIRE(property.min()); + REQUIRE(*property.min() == min); + REQUIRE(property.max()); + REQUIRE(*property.max() == max); + + for (size_t i = 0; i < texCoords.size(); ++i) { + glm::dvec2 uv = texCoords[i]; + auto value = property.getRaw(uv[0], uv[1]); + REQUIRE(value == data[i]); + + auto maybeValue = property.get(uv[0], uv[1]); + REQUIRE(maybeValue); + REQUIRE(*maybeValue == normalize(data[i]) * scale + offset); + } + } + + SECTION("Use own property values") { + const double newOffset = 2.0; + const double newScale = 5.0; + const double newMin = 10.0; + const double newMax = 11.0; + propertyTextureProperty.offset = newOffset; + propertyTextureProperty.scale = newScale; + propertyTextureProperty.min = newMin; + propertyTextureProperty.max = newMax; + + PropertyTexturePropertyView property = + view.getPropertyView("TestClassProperty"); + REQUIRE(property.status() == PropertyTexturePropertyViewStatus::Valid); + REQUIRE(property.offset()); + REQUIRE(*property.offset() == newOffset); + REQUIRE(property.scale()); + REQUIRE(*property.scale() == newScale); + REQUIRE(property.min()); + REQUIRE(*property.min() == newMin); + REQUIRE(property.max()); + REQUIRE(*property.max() == newMax); + + for (size_t i = 0; i < texCoords.size(); ++i) { + glm::dvec2 uv = texCoords[i]; + auto value = property.getRaw(uv[0], uv[1]); + REQUIRE(value == data[i]); + + auto maybeValue = property.get(uv[0], uv[1]); + REQUIRE(maybeValue); + REQUIRE(*maybeValue == normalize(data[i]) * newScale + newOffset); + } + } +} + +TEST_CASE("Test with PropertyTextureProperty noData") { + Model model; + std::vector data = {12, 34, 30, 11}; + const uint8_t noData = 34; + + addTextureToModel( + model, + Sampler::WrapS::CLAMP_TO_EDGE, + Sampler::WrapS::CLAMP_TO_EDGE, + 2, + 2, + 1, + data); + size_t textureIndex = model.textures.size() - 1; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::UINT8; + testClassProperty.noData = noData; + + PropertyTexture& propertyTexture = metadata.propertyTextures.emplace_back(); + propertyTexture.classProperty = "TestClass"; + + PropertyTextureProperty& propertyTextureProperty = + propertyTexture.properties["TestClassProperty"]; + propertyTextureProperty.index = static_cast(textureIndex); + propertyTextureProperty.texCoord = 0; + propertyTextureProperty.channels = {0}; + + PropertyTextureView view(model, propertyTexture); + REQUIRE(view.status() == PropertyTextureViewStatus::Valid); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT8); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->array); + REQUIRE(!classProperty->normalized); + + std::vector texCoords{ + glm::dvec2(0, 0), + glm::dvec2(0.5, 0), + glm::dvec2(0, 0.5), + glm::dvec2(0.5, 0.5)}; + + SECTION("Without default value") { + PropertyTexturePropertyView property = + view.getPropertyView("TestClassProperty"); + REQUIRE(property.status() == PropertyTexturePropertyViewStatus::Valid); + + for (size_t i = 0; i < texCoords.size(); ++i) { + glm::dvec2 uv = texCoords[i]; + auto value = property.getRaw(uv[0], uv[1]); + REQUIRE(value == data[i]); + + auto maybeValue = property.get(uv[0], uv[1]); + if (value == noData) { + REQUIRE(!maybeValue); + } else { + REQUIRE(maybeValue); + REQUIRE(*maybeValue == data[i]); + } + } + } + + SECTION("With default value") { + const uint8_t defaultValue = 255; + testClassProperty.defaultProperty = defaultValue; + + PropertyTexturePropertyView property = + view.getPropertyView("TestClassProperty"); + REQUIRE(property.status() == PropertyTexturePropertyViewStatus::Valid); + + for (size_t i = 0; i < texCoords.size(); ++i) { + glm::dvec2 uv = texCoords[i]; + auto value = property.getRaw(uv[0], uv[1]); + REQUIRE(value == data[i]); + + auto maybeValue = property.get(uv[0], uv[1]); + REQUIRE(maybeValue); + if (value == noData) { + REQUIRE(*maybeValue == defaultValue); + } else { + REQUIRE(*maybeValue == data[i]); + } + } + } +} + +TEST_CASE("Test with PropertyTextureProperty noData (normalized)") { + Model model; + std::vector data = {12, 34, 30, 11}; + const uint8_t noData = 34; + + addTextureToModel( + model, + Sampler::WrapS::CLAMP_TO_EDGE, + Sampler::WrapS::CLAMP_TO_EDGE, + 2, + 2, + 1, + data); + size_t textureIndex = model.textures.size() - 1; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::UINT8; + testClassProperty.normalized = true; + testClassProperty.noData = noData; + + PropertyTexture& propertyTexture = metadata.propertyTextures.emplace_back(); + propertyTexture.classProperty = "TestClass"; + + PropertyTextureProperty& propertyTextureProperty = + propertyTexture.properties["TestClassProperty"]; + propertyTextureProperty.index = static_cast(textureIndex); + propertyTextureProperty.texCoord = 0; + propertyTextureProperty.channels = {0}; + + PropertyTextureView view(model, propertyTexture); + REQUIRE(view.status() == PropertyTextureViewStatus::Valid); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT8); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->array); + REQUIRE(classProperty->normalized); + + std::vector texCoords{ + glm::dvec2(0, 0), + glm::dvec2(0.5, 0), + glm::dvec2(0, 0.5), + glm::dvec2(0.5, 0.5)}; + + SECTION("Without default value") { + PropertyTexturePropertyView property = + view.getPropertyView("TestClassProperty"); + REQUIRE(property.status() == PropertyTexturePropertyViewStatus::Valid); + + for (size_t i = 0; i < texCoords.size(); ++i) { + glm::dvec2 uv = texCoords[i]; + auto value = property.getRaw(uv[0], uv[1]); + REQUIRE(value == data[i]); + + auto maybeValue = property.get(uv[0], uv[1]); + if (value == noData) { + REQUIRE(!maybeValue); + } else { + REQUIRE(maybeValue); + REQUIRE(*maybeValue == normalize(data[i])); + } + } + } + + SECTION("With default value") { + const double defaultValue = -1.0; + testClassProperty.defaultProperty = defaultValue; + + PropertyTexturePropertyView property = + view.getPropertyView("TestClassProperty"); + REQUIRE(property.status() == PropertyTexturePropertyViewStatus::Valid); + + for (size_t i = 0; i < texCoords.size(); ++i) { + glm::dvec2 uv = texCoords[i]; + auto value = property.getRaw(uv[0], uv[1]); + REQUIRE(value == data[i]); + + auto maybeValue = property.get(uv[0], uv[1]); + REQUIRE(maybeValue); + if (value == noData) { + REQUIRE(*maybeValue == defaultValue); + } else { + REQUIRE(*maybeValue == normalize(data[i])); + } + } } } @@ -666,14 +1693,15 @@ TEST_CASE("Test callback for scalar PropertyTextureProperty") { REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT16); REQUIRE(classProperty->count == std::nullopt); REQUIRE(!classProperty->array); + REQUIRE(!classProperty->normalized); - std::vector expected{-1, 268, 542, -256}; - std::vector texCoords{ - glm::vec2(0, 0), - glm::vec2(0.5, 0), - glm::vec2(0, 0.5), - glm::vec2(0.5, 0.5)}; + std::vector texCoords{ + glm::dvec2(0, 0), + glm::dvec2(0.5, 0), + glm::dvec2(0, 0.5), + glm::dvec2(0.5, 0.5)}; + std::vector expected{-1, 268, 542, -256}; uint32_t invokedCallbackCount = 0; view.getPropertyView( "TestClassProperty", @@ -689,8 +1717,97 @@ TEST_CASE("Test callback for scalar PropertyTextureProperty") { PropertyTexturePropertyViewStatus::Valid); for (size_t i = 0; i < expected.size(); ++i) { - glm::vec2& texCoord = texCoords[i]; - REQUIRE(propertyValue.get(texCoord[0], texCoord[1]) == expected[i]); + glm::dvec2& uv = texCoords[i]; + auto value = propertyValue.getRaw(uv[0], uv[1]); + REQUIRE(value == expected[i]); + + auto maybeValue = propertyValue.get(uv[0], uv[1]); + REQUIRE(maybeValue); + REQUIRE(*maybeValue == expected[i]); + } + } else { + FAIL("getPropertyView returned PropertyTexturePropertyView of " + "incorrect type for TestClassProperty."); + } + }); + + REQUIRE(invokedCallbackCount == 1); +} + +TEST_CASE("Test callback for scalar PropertyTextureProperty (normalized)") { + Model model; + std::vector data = {255, 255, 12, 1, 30, 2, 0, 255}; + + addTextureToModel( + model, + Sampler::WrapS::CLAMP_TO_EDGE, + Sampler::WrapS::CLAMP_TO_EDGE, + 2, + 2, + 2, + data); + size_t textureIndex = model.textures.size() - 1; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::INT16; + testClassProperty.normalized = true; + + PropertyTexture& propertyTexture = metadata.propertyTextures.emplace_back(); + propertyTexture.classProperty = "TestClass"; + + PropertyTextureProperty& propertyTextureProperty = + propertyTexture.properties["TestClassProperty"]; + propertyTextureProperty.index = static_cast(textureIndex); + propertyTextureProperty.texCoord = 0; + propertyTextureProperty.channels = {0, 1}; + + PropertyTextureView view(model, propertyTexture); + REQUIRE(view.status() == PropertyTextureViewStatus::Valid); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT16); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->array); + REQUIRE(classProperty->normalized); + + std::vector texCoords{ + glm::dvec2(0, 0), + glm::dvec2(0.5, 0), + glm::dvec2(0, 0.5), + glm::dvec2(0.5, 0.5)}; + + std::vector expected{-1, 268, 542, -256}; + uint32_t invokedCallbackCount = 0; + view.getPropertyView( + "TestClassProperty", + [&expected, &texCoords, &invokedCallbackCount]( + const std::string& /*propertyName*/, + auto propertyValue) mutable { + invokedCallbackCount++; + if constexpr (std::is_same_v< + PropertyTexturePropertyView, + decltype(propertyValue)>) { + REQUIRE( + propertyValue.status() == + PropertyTexturePropertyViewStatus::Valid); + + for (size_t i = 0; i < expected.size(); ++i) { + glm::dvec2& uv = texCoords[i]; + auto value = propertyValue.getRaw(uv[0], uv[1]); + REQUIRE(value == expected[i]); + + auto maybeValue = propertyValue.get(uv[0], uv[1]); + REQUIRE(maybeValue); + REQUIRE(*maybeValue == normalize(expected[i])); } } else { FAIL("getPropertyView returned PropertyTexturePropertyView of " @@ -749,17 +1866,19 @@ TEST_CASE("Test callback for vecN PropertyTextureProperty") { REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT8); REQUIRE(classProperty->count == std::nullopt); REQUIRE(!classProperty->array); + REQUIRE(!classProperty->normalized); std::vector expected{ glm::i8vec2(-1, -1), glm::i8vec2(12, 1), glm::i8vec2(30, 2), glm::i8vec2(0, -1)}; - std::vector texCoords{ - glm::vec2(0, 0), - glm::vec2(0.5, 0), - glm::vec2(0, 0.5), - glm::vec2(0.5, 0.5)}; + + std::vector texCoords{ + glm::dvec2(0, 0), + glm::dvec2(0.5, 0), + glm::dvec2(0, 0.5), + glm::dvec2(0.5, 0.5)}; uint32_t invokedCallbackCount = 0; view.getPropertyView( @@ -776,8 +1895,108 @@ TEST_CASE("Test callback for vecN PropertyTextureProperty") { PropertyTexturePropertyViewStatus::Valid); for (size_t i = 0; i < expected.size(); ++i) { - glm::vec2& texCoord = texCoords[i]; - REQUIRE(propertyValue.get(texCoord[0], texCoord[1]) == expected[i]); + glm::dvec2& uv = texCoords[i]; + auto value = propertyValue.getRaw(uv[0], uv[1]); + REQUIRE(value == expected[i]); + + auto maybeValue = propertyValue.get(uv[0], uv[1]); + REQUIRE(maybeValue); + REQUIRE(*maybeValue == expected[i]); + } + } else { + FAIL("getPropertyView returned PropertyTexturePropertyView of " + "incorrect type for TestClassProperty."); + } + }); + + REQUIRE(invokedCallbackCount == 1); +} + +TEST_CASE("Test callback for vecN PropertyTextureProperty (normalized)") { + Model model; + // clang-format off + std::vector data = { + 255, 255, + 12, 1, + 30, 2, + 0, 255}; + // clang-format on + + addTextureToModel( + model, + Sampler::WrapS::CLAMP_TO_EDGE, + Sampler::WrapS::CLAMP_TO_EDGE, + 2, + 2, + 2, + data); + size_t textureIndex = model.textures.size() - 1; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::VEC2; + testClassProperty.componentType = ClassProperty::ComponentType::INT8; + testClassProperty.normalized = true; + + PropertyTexture& propertyTexture = metadata.propertyTextures.emplace_back(); + propertyTexture.classProperty = "TestClass"; + + PropertyTextureProperty& propertyTextureProperty = + propertyTexture.properties["TestClassProperty"]; + propertyTextureProperty.index = static_cast(textureIndex); + propertyTextureProperty.texCoord = 0; + propertyTextureProperty.channels = {0, 1}; + + PropertyTextureView view(model, propertyTexture); + REQUIRE(view.status() == PropertyTextureViewStatus::Valid); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::VEC2); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT8); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->array); + REQUIRE(classProperty->normalized); + + std::vector expected{ + glm::i8vec2(-1, -1), + glm::i8vec2(12, 1), + glm::i8vec2(30, 2), + glm::i8vec2(0, -1)}; + + std::vector texCoords{ + glm::dvec2(0, 0), + glm::dvec2(0.5, 0), + glm::dvec2(0, 0.5), + glm::dvec2(0.5, 0.5)}; + + uint32_t invokedCallbackCount = 0; + view.getPropertyView( + "TestClassProperty", + [&expected, &texCoords, &invokedCallbackCount]( + const std::string& /*propertyName*/, + auto propertyValue) mutable { + invokedCallbackCount++; + if constexpr (std::is_same_v< + PropertyTexturePropertyView, + decltype(propertyValue)>) { + REQUIRE( + propertyValue.status() == + PropertyTexturePropertyViewStatus::Valid); + + for (size_t i = 0; i < expected.size(); ++i) { + glm::dvec2& uv = texCoords[i]; + auto value = propertyValue.getRaw(uv[0], uv[1]); + REQUIRE(value == expected[i]); + + auto maybeValue = propertyValue.get(uv[0], uv[1]); + REQUIRE(maybeValue); + REQUIRE(*maybeValue == normalize(expected[i])); } } else { FAIL("getPropertyView returned PropertyTexturePropertyView of " @@ -838,17 +2057,19 @@ TEST_CASE("Test callback for array PropertyTextureProperty") { REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT16); REQUIRE(classProperty->array); REQUIRE(classProperty->count == 2); + REQUIRE(!classProperty->normalized); std::vector> expected{ {254, 509}, {522, 808}, {30, 512}, {522, 1279}}; - std::vector texCoords{ - glm::vec2(0, 0), - glm::vec2(0.5, 0), - glm::vec2(0, 0.5), - glm::vec2(0.5, 0.5)}; + + std::vector texCoords{ + glm::dvec2(0, 0), + glm::dvec2(0.5, 0), + glm::dvec2(0, 0.5), + glm::dvec2(0.5, 0.5)}; uint32_t invokedCallbackCount = 0; view.getPropertyView( @@ -867,14 +2088,134 @@ TEST_CASE("Test callback for array PropertyTextureProperty") { for (size_t i = 0; i < expected.size(); ++i) { std::vector& expectedArray = expected[i]; - glm::vec2& texCoord = texCoords[i]; + glm::dvec2& uv = texCoords[i]; + PropertyArrayView array = + propertyValue.getRaw(uv[0], uv[1]); + + REQUIRE(static_cast(array.size()) == expectedArray.size()); + for (int64_t j = 0; j < array.size(); j++) { + REQUIRE(array[j] == expectedArray[static_cast(j)]); + } + + auto maybeArray = propertyValue.get(uv[0], uv[1]); + REQUIRE(maybeArray); + REQUIRE( + static_cast(maybeArray->size()) == + expectedArray.size()); + for (int64_t j = 0; j < array.size(); j++) { + REQUIRE( + (*maybeArray)[j] == expectedArray[static_cast(j)]); + } + } + } else { + FAIL("getPropertyView returned PropertyTexturePropertyView of " + "incorrect type for TestClassProperty."); + } + }); + + REQUIRE(invokedCallbackCount == 1); +} + +TEST_CASE("Test callback for array PropertyTextureProperty (normalized)") { + Model model; + // clang-format off + std::vector data = { + 254, 0, 253, 1, + 10, 2, 40, 3, + 30, 0, 0, 2, + 10, 2, 255, 4}; + // clang-format on + + addTextureToModel( + model, + Sampler::WrapS::CLAMP_TO_EDGE, + Sampler::WrapS::CLAMP_TO_EDGE, + 2, + 2, + 4, + data); + size_t textureIndex = model.textures.size() - 1; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::UINT16; + testClassProperty.array = true; + testClassProperty.count = 2; + testClassProperty.normalized = true; + + PropertyTexture& propertyTexture = metadata.propertyTextures.emplace_back(); + propertyTexture.classProperty = "TestClass"; + + PropertyTextureProperty& propertyTextureProperty = + propertyTexture.properties["TestClassProperty"]; + propertyTextureProperty.index = static_cast(textureIndex); + propertyTextureProperty.texCoord = 0; + propertyTextureProperty.channels = {0, 1, 2, 3}; + + PropertyTextureView view(model, propertyTexture); + REQUIRE(view.status() == PropertyTextureViewStatus::Valid); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT16); + REQUIRE(classProperty->array); + REQUIRE(classProperty->count == 2); + REQUIRE(classProperty->normalized); + + std::vector> expected{ + {254, 509}, + {522, 808}, + {30, 512}, + {522, 1279}}; + + std::vector texCoords{ + glm::dvec2(0, 0), + glm::dvec2(0.5, 0), + glm::dvec2(0, 0.5), + glm::dvec2(0.5, 0.5)}; + + uint32_t invokedCallbackCount = 0; + view.getPropertyView( + "TestClassProperty", + [&expected, &texCoords, &invokedCallbackCount]( + const std::string& /*propertyName*/, + auto propertyValue) mutable { + invokedCallbackCount++; + if constexpr ( + std::is_same_v< + PropertyTexturePropertyView, true>, + decltype(propertyValue)>) { + REQUIRE( + propertyValue.status() == + PropertyTexturePropertyViewStatus::Valid); + + for (size_t i = 0; i < expected.size(); ++i) { + std::vector& expectedArray = expected[i]; + glm::dvec2& uv = texCoords[i]; PropertyArrayView array = - propertyValue.get(texCoord[0], texCoord[1]); + propertyValue.getRaw(uv[0], uv[1]); REQUIRE(static_cast(array.size()) == expectedArray.size()); for (int64_t j = 0; j < array.size(); j++) { REQUIRE(array[j] == expectedArray[static_cast(j)]); } + + auto maybeArray = propertyValue.get(uv[0], uv[1]); + REQUIRE(maybeArray); + REQUIRE( + static_cast(maybeArray->size()) == + expectedArray.size()); + for (int64_t j = 0; j < array.size(); j++) { + auto rawValue = expectedArray[static_cast(j)]; + REQUIRE((*maybeArray)[j] == normalize(rawValue)); + } } } else { FAIL("getPropertyView returned PropertyTexturePropertyView of " From 2babed53b106290bc0906b1bd037e4d606412951 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Fri, 25 Aug 2023 15:13:05 +1000 Subject: [PATCH 222/421] Add tests of unknown properties in glTF. --- CesiumGltfReader/test/TestGltfReader.cpp | 53 ++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/CesiumGltfReader/test/TestGltfReader.cpp b/CesiumGltfReader/test/TestGltfReader.cpp index 56660f56b..679b9403e 100644 --- a/CesiumGltfReader/test/TestGltfReader.cpp +++ b/CesiumGltfReader/test/TestGltfReader.cpp @@ -609,3 +609,56 @@ TEST_CASE("Can correctly interpret mipmaps in KTX2 files") { } } } + +TEST_CASE("Can read unknown properties from a glTF") { + const std::string s = R"( + { + "someUnknownProperty": "test", + "asset": { + "unknownInsideKnown": "this works too" + } + } + )"; + + GltfReaderOptions options; + GltfReader reader; + + reader.getOptions().setCaptureUnknownProperties(true); + + GltfReaderResult result = reader.readGltf( + gsl::span(reinterpret_cast(s.c_str()), s.size()), + options); + REQUIRE(result.model.has_value()); + + auto unknownIt1 = result.model->unknownProperties.find("someUnknownProperty"); + REQUIRE(unknownIt1 != result.model->unknownProperties.end()); + CHECK(unknownIt1->second.getStringOrDefault("") == "test"); + + auto unknownIt2 = + result.model->asset.unknownProperties.find("unknownInsideKnown"); + REQUIRE(unknownIt2 != result.model->asset.unknownProperties.end()); + CHECK(unknownIt2->second.getStringOrDefault("") == "this works too"); +} + +TEST_CASE("Ignores unknown properties if requested") { + const std::string s = R"( + { + "someUnknownProperty": "test", + "asset": { + "unknownInsideKnown": "this works too" + } + } + )"; + + GltfReaderOptions options; + GltfReader reader; + + reader.getOptions().setCaptureUnknownProperties(false); + + GltfReaderResult result = reader.readGltf( + gsl::span(reinterpret_cast(s.c_str()), s.size()), + options); + REQUIRE(result.model.has_value()); + CHECK(result.model->unknownProperties.empty()); + CHECK(result.model->asset.unknownProperties.empty()); +} From d5f6944580366a7a59dfe91ba6fa573524cf3d9a Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Fri, 25 Aug 2023 15:30:32 +1000 Subject: [PATCH 223/421] Formatting. --- CesiumGltf/test/TestPropertyTableView.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CesiumGltf/test/TestPropertyTableView.cpp b/CesiumGltf/test/TestPropertyTableView.cpp index 3bb8ca0e8..747256fe1 100644 --- a/CesiumGltf/test/TestPropertyTableView.cpp +++ b/CesiumGltf/test/TestPropertyTableView.cpp @@ -4074,7 +4074,8 @@ TEST_CASE("Test with PropertyTableProperty offset, scale, min, max") { } } -TEST_CASE("Test with PropertyTableProperty offset, scale, min, max (normalized)") { +TEST_CASE( + "Test with PropertyTableProperty offset, scale, min, max (normalized)") { Model model; std::vector values = {-128, 0, 32, 127}; const double offset = 0.5; From 3e5d630a1fa043ed28a732e8f7be0c6302747d5c Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Fri, 25 Aug 2023 16:17:11 -0400 Subject: [PATCH 224/421] First round of cleanup / PR feedback --- .../include/CesiumGltf/PropertyArrayView.h | 8 +- .../CesiumGltf/PropertyTablePropertyView.h | 48 +- .../include/CesiumGltf/PropertyTableView.h | 32 +- .../CesiumGltf/PropertyTexturePropertyView.h | 376 +++----- .../include/CesiumGltf/PropertyTextureView.h | 74 +- CesiumGltf/include/CesiumGltf/PropertyView.h | 20 +- CesiumGltf/src/FeatureIdTextureView.cpp | 4 +- CesiumGltf/src/PropertyTableView.cpp | 2 +- .../src/PropertyTexturePropertyView.cpp | 23 + CesiumGltf/test/TestPropertyTableView.cpp | 832 ++++-------------- CesiumGltf/test/TestPropertyTextureView.cpp | 326 +++---- 11 files changed, 550 insertions(+), 1195 deletions(-) create mode 100644 CesiumGltf/src/PropertyTexturePropertyView.cpp diff --git a/CesiumGltf/include/CesiumGltf/PropertyArrayView.h b/CesiumGltf/include/CesiumGltf/PropertyArrayView.h index 80d8bbc61..bc58d0160 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyArrayView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyArrayView.h @@ -81,7 +81,7 @@ template <> class PropertyArrayView { /** * @brief Constructs an empty array view. */ - PropertyArrayView() : _values{} {} + PropertyArrayView() : _values{}, _bitOffset{0}, _size{0} {} /** * @brief Constructs an array view from a buffer. @@ -123,8 +123,8 @@ template <> class PropertyArrayView { private: gsl::span _values; - int64_t _bitOffset = 0; - int64_t _size = 0; + int64_t _bitOffset; + int64_t _size; }; template <> class PropertyArrayView { @@ -154,7 +154,7 @@ template <> class PropertyArrayView { : _values{values}, _stringOffsets{stringOffsets}, _stringOffsetType{stringOffsetType}, - _size(size) {} + _size{size} {} std::string_view operator[](int64_t index) const noexcept { const size_t currentOffset = diff --git a/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h index fe73675e8..1a86d0972 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h @@ -18,112 +18,114 @@ namespace CesiumGltf { * @brief Indicates the status of a property table property view. * * The {@link PropertyTablePropertyView} constructor always completes successfully. - * However, it may not always reflect the actual content of the {@link PropertyTableProperty}, - * but instead indicate that its {@link PropertyTablePropertyView::size} is 0. + * However, it may not always reflect the actual content of the + * {@link PropertyTableProperty}, but instead indicate that its + * {@link PropertyTablePropertyView::size} is 0. * This enumeration provides the reason. */ class PropertyTablePropertyViewStatus : public PropertyViewStatus { public: /** - * @brief This property view was initialized from an invalid {@link PropertyTable}. + * @brief This property view was initialized from an invalid + * {@link PropertyTable}. */ - static const PropertyViewStatusType ErrorInvalidPropertyTable = 12; + static const PropertyViewStatusType ErrorInvalidPropertyTable = 13; /** * @brief This property view does not have a valid value buffer view index. */ - static const PropertyViewStatusType ErrorInvalidValueBufferView = 13; + static const PropertyViewStatusType ErrorInvalidValueBufferView = 14; /** * @brief This array property view does not have a valid array offset buffer * view index. */ - static const PropertyViewStatusType ErrorInvalidArrayOffsetBufferView = 14; + static const PropertyViewStatusType ErrorInvalidArrayOffsetBufferView = 15; /** * @brief This string property view does not have a valid string offset buffer * view index. */ - static const PropertyViewStatusType ErrorInvalidStringOffsetBufferView = 15; + static const PropertyViewStatusType ErrorInvalidStringOffsetBufferView = 16; /** * @brief This property view has a valid value buffer view, but the buffer * view specifies an invalid buffer index. */ - static const PropertyViewStatusType ErrorInvalidValueBuffer = 16; + static const PropertyViewStatusType ErrorInvalidValueBuffer = 17; /** * @brief This property view has a valid array string buffer view, but the * buffer view specifies an invalid buffer index. */ - static const PropertyViewStatusType ErrorInvalidArrayOffsetBuffer = 17; + static const PropertyViewStatusType ErrorInvalidArrayOffsetBuffer = 18; /** * @brief This property view has a valid string offset buffer view, but the * buffer view specifies an invalid buffer index. */ - static const PropertyViewStatusType ErrorInvalidStringOffsetBuffer = 18; + static const PropertyViewStatusType ErrorInvalidStringOffsetBuffer = 19; /** * @brief This property view has a buffer view that points outside the bounds * of its target buffer. */ - static const PropertyViewStatusType ErrorBufferViewOutOfBounds = 19; + static const PropertyViewStatusType ErrorBufferViewOutOfBounds = 20; /** * @brief This property view has an invalid buffer view; its length is not * a multiple of the size of its type / offset type. */ static const PropertyViewStatusType - ErrorBufferViewSizeNotDivisibleByTypeSize = 20; + ErrorBufferViewSizeNotDivisibleByTypeSize = 21; /** * @brief This property view has an invalid buffer view; its length does not * match the size of the property table. */ static const PropertyViewStatusType - ErrorBufferViewSizeDoesNotMatchPropertyTableCount = 21; + ErrorBufferViewSizeDoesNotMatchPropertyTableCount = 22; /** * @brief This array property view has both a fixed length and an offset * buffer view defined. */ static const PropertyViewStatusType ErrorArrayCountAndOffsetBufferCoexist = - 22; + 23; /** * @brief This array property view has neither a fixed length nor an offset * buffer view defined. */ static const PropertyViewStatusType ErrorArrayCountAndOffsetBufferDontExist = - 23; + 24; /** * @brief This property view has an unknown array offset type. */ - static const PropertyViewStatusType ErrorInvalidArrayOffsetType = 24; + static const PropertyViewStatusType ErrorInvalidArrayOffsetType = 25; /** * @brief This property view has an unknown string offset type. */ - static const PropertyViewStatusType ErrorInvalidStringOffsetType = 25; + static const PropertyViewStatusType ErrorInvalidStringOffsetType = 26; /** * @brief This property view's array offset values are not sorted in ascending * order. */ - static const PropertyViewStatusType ErrorArrayOffsetsNotSorted = 26; + static const PropertyViewStatusType ErrorArrayOffsetsNotSorted = 27; /** * @brief This property view's string offset values are not sorted in * ascending order. */ - static const PropertyViewStatusType ErrorStringOffsetsNotSorted = 27; + static const PropertyViewStatusType ErrorStringOffsetsNotSorted = 28; /** * @brief This property view has an array offset that is out of bounds. */ - static const PropertyViewStatusType ErrorArrayOffsetOutOfBounds = 28; + static const PropertyViewStatusType ErrorArrayOffsetOutOfBounds = 29; /** * @brief This property view has a string offset that is out of bounds. @@ -131,8 +133,7 @@ class PropertyTablePropertyViewStatus : public PropertyViewStatus { static const PropertyViewStatusType ErrorStringOffsetOutOfBounds = 29; }; -namespace { -int64_t getOffsetTypeSize(PropertyComponentType offsetType) noexcept { +inline int64_t getOffsetTypeSize(PropertyComponentType offsetType) noexcept { switch (offsetType) { case PropertyComponentType::Uint8: return sizeof(uint8_t); @@ -146,7 +147,6 @@ int64_t getOffsetTypeSize(PropertyComponentType offsetType) noexcept { return 0; } } -} // namespace /** * @brief A view on the data of the {@link PropertyTableProperty} that is created @@ -282,7 +282,7 @@ class PropertyTablePropertyView std::optional get(int64_t index) const noexcept { ElementType value = getRaw(index); - if (this->noData() && value == *(this->noData())) { + if (value == this->noData()) { return this->defaultValue(); } diff --git a/CesiumGltf/include/CesiumGltf/PropertyTableView.h b/CesiumGltf/include/CesiumGltf/PropertyTableView.h index c1aac5bf8..bd18c705e 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTableView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTableView.h @@ -104,6 +104,16 @@ class PropertyTableView { * std::string_view, or {@link PropertyArrayView} with T as one of the * aforementioned types. * + * If T does not match the type specified by the class property, this returns + * an invalid PropertyTexturePropertyView. Likewise, if the value of + * Normalized + * does not match the value of {@ClassProperty::normalized} for that class property, + * this returns an invalid property view. Only types with integer components + * may be normalized. + * + * @tparam T The C++ type corresponding to the type of the data retrieved. + * @tparam Normalized Whether the property is normalized. Only applicable to + * types with integer components. * @param propertyName The name of the property to retrieve data from * @return A {@link PropertyTablePropertyView} of the property. If no valid property is * found, the property view will be invalid. @@ -136,9 +146,11 @@ class PropertyTableView { * uint64_t, int64_t, float, double), a glm vecN composed of one of the scalar * types, a glm matN composed of one of the scalar types, bool, * std::string_view, or {@link PropertyArrayView} with T as one of the - * aforementioned types. If the property is invalid, an empty - * {@link PropertyTablePropertyView} with an error status will be passed to the - * callback. Otherwise, a valid property view will be passed to the callback. + * aforementioned types. + * + * If the property is invalid, an empty {@link PropertyTablePropertyView} with an + * error status will be passed to the callback. Otherwise, a valid property + * view will be passed to the callback. * * @param propertyName The name of the property to retrieve data from * @tparam callback A callback function that accepts a property name and a @@ -184,6 +196,7 @@ class PropertyTableView { case PropertyComponentType::Uint64: break; default: + // Only integer components may be normalized. callback( propertyName, PropertyTablePropertyView( @@ -299,10 +312,11 @@ class PropertyTableView { * int32_t, uint64_t, int64_t, float, double), a glm vecN composed of one of * the scalar types, a glm matN composed of one of the scalar types, bool, * std::string_view, or {@link PropertyArrayView} with T as one of the - * aforementioned types. If the property is invalid, an empty - * {@link PropertyTablePropertyView} with an error status code will be passed to the - * callback. Otherwise, a valid property view will be passed to - * the callback. + * aforementioned types. + * + * If the property is invalid, an empty {@link PropertyTablePropertyView} with + * an error status code will be passed to the callback. Otherwise, a valid + * property view will be passed to the callback. * * @param propertyName The name of the property to retrieve data from * @tparam callback A callback function that accepts property name and @@ -1095,7 +1109,7 @@ class PropertyTableView { if (classProperty.normalized != Normalized) { return PropertyTablePropertyView( - PropertyTablePropertyViewStatus::ErrorInvalidNormalization); + PropertyTablePropertyViewStatus::ErrorNormalizationMismatch); } gsl::span values; @@ -1166,7 +1180,7 @@ class PropertyTableView { if (classProperty.normalized != Normalized) { return PropertyTablePropertyView, Normalized>( - PropertyTablePropertyViewStatus::ErrorInvalidNormalization); + PropertyTablePropertyViewStatus::ErrorNormalizationMismatch); } gsl::span values; diff --git a/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h index e71dc1976..6b191caff 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h @@ -27,39 +27,39 @@ class PropertyTexturePropertyViewStatus : public PropertyViewStatus { * @brief This property view was initialized from an invalid * {@link PropertyTexture}. */ - static const int ErrorInvalidPropertyTexture = 12; + static const int ErrorInvalidPropertyTexture = 13; /** * @brief This property view is associated with a {@link ClassProperty} of an * unsupported type. */ - static const int ErrorUnsupportedProperty = 13; + static const int ErrorUnsupportedProperty = 14; /** * @brief This property view does not have a valid texture index. */ - static const int ErrorInvalidTexture = 14; + static const int ErrorInvalidTexture = 15; /** * @brief This property view does not have a valid sampler index. */ - static const int ErrorInvalidSampler = 15; + static const int ErrorInvalidSampler = 16; /** * @brief This property view does not have a valid image index. */ - static const int ErrorInvalidImage = 16; + static const int ErrorInvalidImage = 17; /** * @brief This property is viewing an empty image. */ - static const int ErrorEmptyImage = 17; + static const int ErrorEmptyImage = 18; /** * @brief This property uses an image with multi-byte channels. Only * single-byte channels are supported. */ - static const int ErrorInvalidBytesPerChannel = 18; + static const int ErrorInvalidBytesPerChannel = 19; /** * @brief The channels of this property texture property are invalid. @@ -68,7 +68,7 @@ class PropertyTexturePropertyViewStatus : public PropertyViewStatus { * more than four channels can be defined for specialized texture * formats, this implementation only supports four channels max. */ - static const int ErrorInvalidChannels = 19; + static const int ErrorInvalidChannels = 20; /** * @brief The channels of this property texture property do not provide @@ -76,12 +76,30 @@ class PropertyTexturePropertyViewStatus : public PropertyViewStatus { * because an incorrect number of channels was provided, or because the * image itself has a different channel count / byte size than expected. */ - static const int ErrorChannelsAndTypeMismatch = 20; + static const int ErrorChannelsAndTypeMismatch = 21; }; -namespace { template -ElementType assembleScalarValue(const std::vector& bytes) { +ElementType +assembleValueFromChannels(const std::vector& bytes) noexcept { + assert(bytes.size() > 0 && "Channel input must have at least one value."); + + if constexpr (IsMetadataScalar::value) { + return assembleScalarValue(bytes); + } + + if constexpr (IsMetadataVecN::value) { + return assembleVecNValue(bytes); + } + + if constexpr (IsMetadataArray::value) { + return assembleArrayValue::type>( + bytes); + } +} + +template +ElementType assembleScalarValue(const std::vector& bytes) noexcept { if constexpr (std::is_same_v) { assert( bytes.size() == sizeof(float) && @@ -107,7 +125,73 @@ ElementType assembleScalarValue(const std::vector& bytes) { } } -double applySamplerWrapS(const double u, const int32_t wrapS) { +template +ElementType assembleVecNValue(const std::vector& bytes) noexcept { + ElementType result = ElementType(); + + const glm::length_t N = + getDimensionsFromPropertyType(TypeToPropertyType::value); + using typename T = ElementType::value_type; + + assert( + sizeof(T) <= 2 && "Components cannot be larger than two bytes in size."); + + if constexpr (std::is_same_v) { + assert(N == 2 && "Only vec2s can contain two-byte integer components."); + uint16_t x = static_cast(bytes[0]) | + (static_cast(bytes[1]) << 8); + uint16_t y = static_cast(bytes[2]) | + (static_cast(bytes[3]) << 8); + + result[0] = *reinterpret_cast(&x); + result[1] = *reinterpret_cast(&y); + } + + if constexpr (std::is_same_v) { + assert(N == 2 && "Only vec2s can contain two-byte integer components."); + result[0] = static_cast(bytes[0]) | + (static_cast(bytes[1]) << 8); + result[1] = static_cast(bytes[2]) | + (static_cast(bytes[3]) << 8); + } + + if constexpr (std::is_same_v) { + for (size_t i = 0; i < bytes.size(); i++) { + result[i] = *reinterpret_cast(&bytes[i]); + } + } + + if constexpr (std::is_same_v) { + for (size_t i = 0; i < bytes.size(); i++) { + result[i] = bytes[i]; + } + } + + return result; +} + +template +PropertyArrayView +assembleArrayValue(const std::vector& bytes) noexcept { + std::vector result(bytes.size() / sizeof(T)); + + if constexpr (sizeof(T) == 2) { + for (int i = 0, b = 0; i < result.size(); i++, b += 2) { + using UintType = std::make_unsigned_t; + UintType resultAsUint = static_cast(bytes[b]) | + (static_cast(bytes[b + 1]) << 8); + result[i] = *reinterpret_cast(&resultAsUint); + } + } else { + for (size_t i = 0; i < bytes.size(); i++) { + result[i] = *reinterpret_cast(&bytes[i]); + } + } + + return PropertyArrayView(std::move(result)); +} + +inline double applySamplerWrapS(const double u, const int32_t wrapS) { if (wrapS == Sampler::WrapS::REPEAT) { double integral = 0; double fraction = std::modf(u, &integral); @@ -122,10 +206,10 @@ double applySamplerWrapS(const double u, const int32_t wrapS) { return integer % 2 == 1 ? 1.0 - fraction : fraction; } - return std::clamp(u, 0.0, 1.0); + return glm::clamp(u, 0.0, 1.0); } -double applySamplerWrapT(const double v, const int32_t wrapT) { +inline double applySamplerWrapT(const double v, const int32_t wrapT) { if (wrapT == Sampler::WrapT::REPEAT) { double integral = 0; double fraction = std::modf(v, &integral); @@ -140,15 +224,19 @@ double applySamplerWrapT(const double v, const int32_t wrapT) { return integer % 2 == 1 ? 1.0 - fraction : fraction; } - return std::clamp(v, 0.0, 1.0); + return glm::clamp(v, 0.0, 1.0); } -} // namespace /** - * @brief A view of the data specified by a {@link PropertyTextureProperty}. + * @brief A view of the non-normalized data specified by a {@link PropertyTextureProperty}. * * Provides utilities to sample the property texture property using texture * coordinates. + * + * @tparam ElementType The type of the elements represented in the property view + * @tparam Normalized Whether or not the property is normalized. If normalized, + * the elements can be retrieved as normalized floating-point numbers, as + * opposed to their integer values. */ template class PropertyTexturePropertyView; @@ -159,6 +247,8 @@ class PropertyTexturePropertyView; * * Provides utilities to sample the property texture property using texture * coordinates. + * + * @tparam ElementType The type of the elements represented in the property view */ template class PropertyTexturePropertyView @@ -173,7 +263,7 @@ class PropertyTexturePropertyView _pImage(nullptr), _texCoordSetIndex(0), _channels(), - _swizzle("") {} + _swizzle() {} /** * @brief Constructs an invalid instance for an erroneous property. @@ -186,7 +276,7 @@ class PropertyTexturePropertyView _pImage(nullptr), _texCoordSetIndex(0), _channels(), - _swizzle("") { + _swizzle() { assert( _status != PropertyTexturePropertyViewStatus::Valid && "An empty property view should not be constructed with a valid status"); @@ -197,11 +287,9 @@ class PropertyTexturePropertyView * * @param property The {@link PropertyTextureProperty} * @param classProperty The {@link ClassProperty} this property conforms to. - * @param pSampler A pointer to the sampler used by the property. - * @param pImage A pointer to the image used by the property. - * @param texCoordSetIndex The value of {@link PropertyTextureProperty::texcoord}. + * @param sampler The {@link Sampler} used by the property. + * @param image The {@link ImageCesium} used by the property. * @param channels The value of {@link PropertyTextureProperty::channels}. - * @param normalized Whether this property has a normalized integer type. */ PropertyTexturePropertyView( const PropertyTextureProperty& property, @@ -214,11 +302,13 @@ class PropertyTexturePropertyView _pImage(&image), _texCoordSetIndex(property.texCoord), _channels(channels), - _swizzle("") { + _swizzle() { if (this->_status != PropertyTexturePropertyViewStatus::Valid) { return; } + _swizzle.reserve(_channels.size()); + for (size_t i = 0; i < _channels.size(); ++i) { switch (_channels[i]) { case 0: @@ -240,16 +330,15 @@ class PropertyTexturePropertyView } /** - * @brief Gets the raw value of the property for the given texture - * coordinates with all value transforms applied. That is, if the property - * specifies an offset and scale, they will be applied to the value before the - * value is returned. The sampler's wrapping mode will be used when sampling - * the texture. + * @brief Gets the value of the property for the given texture coordinates + * with all value transforms applied. That is, if the property specifies an + * offset and scale, they will be applied to the value before the value is + * returned. The sampler's wrapping mode will be used when sampling the + * texture. * - * If this property has a specified "no data" value, and the retrieved element - * is equal to that value, then this will return the property's specified - * default value. If the property did not provide a default value, this - * returns std::nullopt. + * If this property has a specified "no data" value, this will return the + * property's default value for any elements that equal this "no data" value. + * If the property did not specify a default value, this returns std::nullopt. * * @param u The u-component of the texture coordinates. * @param v The v-component of the texture coordinates. @@ -260,7 +349,7 @@ class PropertyTexturePropertyView std::optional get(double u, double v) const noexcept { ElementType value = getRaw(u, v); - if (this->noData() && value == *(this->noData())) { + if (value == this->noData()) { return this->defaultValue(); } @@ -310,11 +399,11 @@ class PropertyTexturePropertyView double yCoord = std::floor(wrappedV * this->_pImage->height); // Clamp to ensure no out-of-bounds data access - int64_t x = std::clamp( + int64_t x = glm::clamp( static_cast(xCoord), static_cast(0), static_cast(this->_pImage->width) - 1); - int64_t y = std::clamp( + int64_t y = glm::clamp( static_cast(yCoord), static_cast(0), static_cast(this->_pImage->height) - 1); @@ -333,7 +422,7 @@ class PropertyTexturePropertyView channelValues[i] = *(pValue + this->_channels[i]); } - return assembleValueFromChannels(channelValues); + return assembleValueFromChannels(channelValues); } /** @@ -364,103 +453,6 @@ class PropertyTexturePropertyView const std::string& getSwizzle() const noexcept { return this->_swizzle; } private: - ElementType - assembleValueFromChannels(const std::vector& bytes) const noexcept { - assert(bytes.size() > 0 && "Channel input must have at least one value."); - - if constexpr (IsMetadataScalar::value) { - return assembleScalarValue(bytes); - } - - if constexpr (IsMetadataVecN::value) { - return assembleVecNValue(bytes); - } - - if constexpr (IsMetadataArray::value) { - return assembleArrayValue::type>( - bytes); - } - } - - ElementType - assembleVecNValue(const std::vector& bytes) const noexcept { - const glm::length_t N = - getDimensionsFromPropertyType(TypeToPropertyType::value); - switch (N) { - case 2: - return assembleVecNValueImpl<2, typename ElementType::value_type>(bytes); - case 3: - return assembleVecNValueImpl<3, typename ElementType::value_type>(bytes); - case 4: - return assembleVecNValueImpl<4, typename ElementType::value_type>(bytes); - default: - return ElementType(); - } - } - - template - ElementType - assembleVecNValueImpl(const std::vector& bytes) const noexcept { - ElementType result = ElementType(); - assert( - sizeof(T) <= 2 && - "Components cannot be larger than two bytes in size."); - - if constexpr (std::is_same_v) { - assert(N == 2 && "Only vec2s can contain two-byte integer components."); - uint16_t x = static_cast(bytes[0]) | - (static_cast(bytes[1]) << 8); - uint16_t y = static_cast(bytes[2]) | - (static_cast(bytes[3]) << 8); - - result[0] = *reinterpret_cast(&x); - result[1] = *reinterpret_cast(&y); - } - - if constexpr (std::is_same_v) { - assert(N == 2 && "Only vec2s can contain two-byte integer components."); - result[0] = static_cast(bytes[0]) | - (static_cast(bytes[1]) << 8); - result[1] = static_cast(bytes[2]) | - (static_cast(bytes[3]) << 8); - } - - if constexpr (std::is_same_v) { - for (size_t i = 0; i < bytes.size(); i++) { - result[i] = *reinterpret_cast(&bytes[i]); - } - } - - if constexpr (std::is_same_v) { - for (size_t i = 0; i < bytes.size(); i++) { - result[i] = bytes[i]; - } - } - - return result; - } - - template - PropertyArrayView - assembleArrayValue(const std::vector& bytes) const noexcept { - std::vector result(bytes.size() / sizeof(T)); - - if constexpr (sizeof(T) == 2) { - for (int i = 0, b = 0; i < result.size(); i++, b += 2) { - using UintType = std::make_unsigned_t; - UintType resultAsUint = static_cast(bytes[b]) | - (static_cast(bytes[b + 1]) << 8); - result[i] = *reinterpret_cast(&resultAsUint); - } - } else { - for (size_t i = 0; i < bytes.size(); i++) { - result[i] = *reinterpret_cast(&bytes[i]); - } - } - - return PropertyArrayView(std::move(result)); - } - const Sampler* _pSampler; const ImageCesium* _pImage; int64_t _texCoordSetIndex; @@ -491,7 +483,7 @@ class PropertyTexturePropertyView _pImage(nullptr), _texCoordSetIndex(0), _channels(), - _swizzle("") {} + _swizzle() {} /** * @brief Constructs an invalid instance for an erroneous property. @@ -504,7 +496,7 @@ class PropertyTexturePropertyView _pImage(nullptr), _texCoordSetIndex(0), _channels(), - _swizzle("") { + _swizzle() { assert( _status != PropertyTexturePropertyViewStatus::Valid && "An empty property view should not be constructed with a valid status"); @@ -515,11 +507,9 @@ class PropertyTexturePropertyView * * @param property The {@link PropertyTextureProperty} * @param classProperty The {@link ClassProperty} this property conforms to. - * @param pSampler A pointer to the sampler used by the property. - * @param pImage A pointer to the image used by the property. - * @param texCoordSetIndex The value of {@link PropertyTextureProperty::texcoord}. + * @param sampler The {@link Sampler} used by the property. + * @param image The {@link ImageCesium} used by the property. * @param channels The value of {@link PropertyTextureProperty::channels}. - * @param normalized Whether this property has a normalized integer type. */ PropertyTexturePropertyView( const PropertyTextureProperty& property, @@ -532,11 +522,12 @@ class PropertyTexturePropertyView _pImage(&image), _texCoordSetIndex(property.texCoord), _channels(channels), - _swizzle("") { + _swizzle() { if (this->_status != PropertyTexturePropertyViewStatus::Valid) { return; } + _swizzle.reserve(_channels.size()); for (size_t i = 0; i < _channels.size(); ++i) { switch (_channels[i]) { case 0: @@ -654,11 +645,11 @@ class PropertyTexturePropertyView double yCoord = std::floor(wrappedV * this->_pImage->height); // Clamp to ensure no out-of-bounds data access - int64_t x = std::clamp( + int64_t x = glm::clamp( static_cast(xCoord), static_cast(0), static_cast(this->_pImage->width) - 1); - int64_t y = std::clamp( + int64_t y = glm::clamp( static_cast(yCoord), static_cast(0), static_cast(this->_pImage->height) - 1); @@ -677,7 +668,7 @@ class PropertyTexturePropertyView channelValues[i] = *(pValue + this->_channels[i]); } - return assembleValueFromChannels(channelValues); + return assembleValueFromChannels(channelValues); } /** @@ -708,103 +699,6 @@ class PropertyTexturePropertyView const std::string& getSwizzle() const noexcept { return this->_swizzle; } private: - ElementType - assembleValueFromChannels(const std::vector& bytes) const noexcept { - assert(bytes.size() > 0 && "Channel input must have at least one value."); - - if constexpr (IsMetadataScalar::value) { - return assembleScalarValue(bytes); - } - - if constexpr (IsMetadataVecN::value) { - return assembleVecNValue(bytes); - } - - if constexpr (IsMetadataArray::value) { - return assembleArrayValue::type>( - bytes); - } - } - - ElementType - assembleVecNValue(const std::vector& bytes) const noexcept { - const glm::length_t N = - getDimensionsFromPropertyType(TypeToPropertyType::value); - switch (N) { - case 2: - return assembleVecNValueImpl<2, typename ElementType::value_type>(bytes); - case 3: - return assembleVecNValueImpl<3, typename ElementType::value_type>(bytes); - case 4: - return assembleVecNValueImpl<4, typename ElementType::value_type>(bytes); - default: - return ElementType(); - } - } - - template - ElementType - assembleVecNValueImpl(const std::vector& bytes) const noexcept { - ElementType result = ElementType(); - assert( - sizeof(T) <= 2 && - "Components cannot be larger than two bytes in size."); - - if constexpr (std::is_same_v) { - assert(N == 2 && "Only vec2s can contain two-byte integer components."); - uint16_t x = static_cast(bytes[0]) | - (static_cast(bytes[1]) << 8); - uint16_t y = static_cast(bytes[2]) | - (static_cast(bytes[3]) << 8); - - result[0] = *reinterpret_cast(&x); - result[1] = *reinterpret_cast(&y); - } - - if constexpr (std::is_same_v) { - assert(N == 2 && "Only vec2s can contain two-byte integer components."); - result[0] = static_cast(bytes[0]) | - (static_cast(bytes[1]) << 8); - result[1] = static_cast(bytes[2]) | - (static_cast(bytes[3]) << 8); - } - - if constexpr (std::is_same_v) { - for (size_t i = 0; i < bytes.size(); i++) { - result[i] = *reinterpret_cast(&bytes[i]); - } - } - - if constexpr (std::is_same_v) { - for (size_t i = 0; i < bytes.size(); i++) { - result[i] = bytes[i]; - } - } - - return result; - } - - template - PropertyArrayView - assembleArrayValue(const std::vector& bytes) const noexcept { - std::vector result(bytes.size() / sizeof(T)); - - if constexpr (sizeof(T) == 2) { - for (int i = 0, b = 0; i < result.size(); i++, b += 2) { - using UintType = std::make_unsigned_t; - UintType resultAsUint = static_cast(bytes[b]) | - (static_cast(bytes[b + 1]) << 8); - result[i] = *reinterpret_cast(&resultAsUint); - } - } else { - for (size_t i = 0; i < bytes.size(); i++) { - result[i] = *reinterpret_cast(&bytes[i]); - } - } - - return PropertyArrayView(std::move(result)); - } - const Sampler* _pSampler; const ImageCesium* _pImage; int64_t _texCoordSetIndex; diff --git a/CesiumGltf/include/CesiumGltf/PropertyTextureView.h b/CesiumGltf/include/CesiumGltf/PropertyTextureView.h index 1a56fa2ec..5f2d4e98d 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTextureView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTextureView.h @@ -42,9 +42,10 @@ enum class PropertyTextureViewStatus { /** * @brief A view on a {@link PropertyTexture}. * - * This should be used to get a {@link PropertyTexturePropertyView} of a property in the property texture. - * It will validate the EXT_structural_metadata format and ensure {@link PropertyTexturePropertyView} - * does not access out of bounds. + * This should be used to get a {@link PropertyTexturePropertyView} of a property + * in the property texture. It will validate the EXT_structural_metadata format + * and + * ensure {@link PropertyTexturePropertyView} does not access data out of bounds. */ class PropertyTextureView { public: @@ -82,14 +83,24 @@ class PropertyTextureView { * property stored in the {@link PropertyTexture}. * * This method will validate the EXT_structural_metadata format to ensure - * {@link PropertyTexturePropertyView} retrieves the correct data. T must be - * a scalar with a supported component type (uint8_t, uint16_t, uint32_t, + * {@link PropertyTexturePropertyView} retrieves the correct data. T must + * be a scalar with a supported component type (uint8_t, uint16_t, uint32_t, * float), a glm vecN composed of one of the scalar types, or a - * PropertyArrayView containing one of the scalar types + * PropertyArrayView containing one of the scalar types. + * + * If T does not match the type specified by the class property, this returns + * an invalid PropertyTexturePropertyView. Likewise, if the value of + * Normalized + * does not match the value of {@ClassProperty::normalized} for that class property, + * this returns an invalid property view. Only types with integer components + * may be normalized. * + * @tparam T The C++ type corresponding to the type of the data retrieved. + * @tparam Normalized Whether the property is normalized. Only applicable to + * types with integer components. * @param propertyName The name of the property to retrieve data from - * @return A {@link PropertyTexturePropertyView} of the property. If no valid property is - * found, the property view will be invalid. + * @return A {@link PropertyTexturePropertyView} of the property. If no valid + * property is found, the property view will be invalid. */ template PropertyTexturePropertyView @@ -117,10 +128,11 @@ class PropertyTextureView { * {@link PropertyTexturePropertyView} retrieves the correct data. T must be * a scalar with a supported component type (uint8_t, uint16_t, uint32_t, * float), a glm vecN composed of one of the scalar types, or a - * PropertyArrayView containing one of the scalar types. If the property is - * invalid, an empty {@link PropertyTexturePropertyView} with an error status - * will be passed to the callback. Otherwise, a valid property view will be - * passed to the callback. + * PropertyArrayView containing one of the scalar types. + * + * If the property is somehow invalid, an empty {@link PropertyTexturePropertyView} + * with an error status will be passed to the callback. Otherwise, a valid + * property view will be passed to the callback. * * @param propertyName The name of the property to retrieve data from * @tparam callback A callback function that accepts a property name and a @@ -152,7 +164,28 @@ class PropertyTextureView { componentType = convertStringToPropertyComponentType(*pClassProperty->componentType); } + bool normalized = pClassProperty->normalized; + if (normalized) { + switch (componentType) { + case PropertyComponentType::Int8: + case PropertyComponentType::Uint8: + case PropertyComponentType::Int16: + case PropertyComponentType::Uint16: + case PropertyComponentType::Int32: + case PropertyComponentType::Uint32: + case PropertyComponentType::Int64: + case PropertyComponentType::Uint64: + break; + default: + // Only integer components may be normalized. + callback( + propertyName, + PropertyTexturePropertyView( + PropertyTexturePropertyViewStatus::ErrorInvalidNormalization)); + return; + } + } if (pClassProperty->array) { if (normalized) { @@ -225,10 +258,11 @@ class PropertyTextureView { * {@link PropertyTexturePropertyView} retrieves the correct data. T must be * a scalar with a supported component type (uint8_t, uint16_t, uint32_t, * float), a glm vecN composed of one of the scalar types, or a - * PropertyArrayView containing one of the scalar types. If the property is - * invalid, an empty {@link PropertyTexturePropertyView} with an error status - * will be passed to the callback. Otherwise, a valid property view will be - * passed to the callback. + * PropertyArrayView containing one of the scalar types. + * + * If the property is invalid, an empty {@link PropertyTexturePropertyView} with an + * error status will be passed to the callback. Otherwise, a valid property + * view will be passed to the callback. * * @param propertyName The name of the property to retrieve data from * @tparam callback A callback function that accepts property name and @@ -512,7 +546,7 @@ class PropertyTextureView { if (classProperty.normalized != Normalized) { return PropertyTexturePropertyView( - PropertyTexturePropertyViewStatus::ErrorInvalidNormalization); + PropertyTexturePropertyViewStatus::ErrorNormalizationMismatch); } // Only up to four bytes of image data are supported. @@ -552,7 +586,7 @@ class PropertyTextureView { if (classProperty.normalized != Normalized) { return PropertyTexturePropertyView( - PropertyTexturePropertyViewStatus::ErrorInvalidNormalization); + PropertyTexturePropertyViewStatus::ErrorNormalizationMismatch); } // Only up to four bytes of image data are supported. @@ -593,7 +627,7 @@ class PropertyTextureView { if (classProperty.normalized != Normalized) { return PropertyTexturePropertyView, Normalized>( - PropertyTexturePropertyViewStatus::ErrorInvalidNormalization); + PropertyTexturePropertyViewStatus::ErrorNormalizationMismatch); } // Only scalar arrays are supported. The scalar component type must not @@ -685,5 +719,5 @@ class PropertyTextureView { const Class* _pClass; PropertyTextureViewStatus _status; -}; // namespace CesiumGltf +}; } // namespace CesiumGltf diff --git a/CesiumGltf/include/CesiumGltf/PropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyView.h index 804992f64..8a283fa58 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyView.h @@ -59,38 +59,43 @@ class PropertyViewStatus { */ static const PropertyViewStatusType ErrorInvalidNormalization = 5; + /** + * @brief This property view's normalization differs from what + * is specified in {@link ClassProperty::normalized} + */ + static const PropertyViewStatusType ErrorNormalizationMismatch = 6; + /** * @brief The property provided an invalid offset value. */ - static const PropertyViewStatusType ErrorInvalidOffset = 6; + static const PropertyViewStatusType ErrorInvalidOffset = 7; /** * @brief The property provided an invalid scale value. */ - static const PropertyViewStatusType ErrorInvalidScale = 7; + static const PropertyViewStatusType ErrorInvalidScale = 8; /** * @brief The property provided an invalid maximum value. */ - static const PropertyViewStatusType ErrorInvalidMax = 8; + static const PropertyViewStatusType ErrorInvalidMax = 9; /** * @brief The property provided an invalid minimum value. */ - static const PropertyViewStatusType ErrorInvalidMin = 9; + static const PropertyViewStatusType ErrorInvalidMin = 10; /** * @brief The property provided an invalid "no data" value. */ - static const PropertyViewStatusType ErrorInvalidNoDataValue = 10; + static const PropertyViewStatusType ErrorInvalidNoDataValue = 11; /** * @brief The property provided an invalid default value. */ - static const PropertyViewStatusType ErrorInvalidDefaultValue = 11; + static const PropertyViewStatusType ErrorInvalidDefaultValue = 12; }; -namespace { template PropertyViewStatusType validatePropertyType(const ClassProperty& classProperty) { @@ -228,7 +233,6 @@ int64_t getCount(std::optional>& buffer) { return static_cast(buffer->size() / sizeof(ElementType)); } -} // namespace /** * @brief Represents a metadata property in EXT_structural_metadata. diff --git a/CesiumGltf/src/FeatureIdTextureView.cpp b/CesiumGltf/src/FeatureIdTextureView.cpp index 22553e8d5..cf1ecb811 100644 --- a/CesiumGltf/src/FeatureIdTextureView.cpp +++ b/CesiumGltf/src/FeatureIdTextureView.cpp @@ -79,11 +79,11 @@ int64_t FeatureIdTextureView::getFeatureID(double u, double v) const noexcept { double yCoord = std::floor(v * this->_pImage->height); // Clamp to ensure no out-of-bounds data access - int64_t x = std::clamp( + int64_t x = glm::clamp( static_cast(xCoord), static_cast(0), static_cast(this->_pImage->width - 1)); - int64_t y = std::clamp( + int64_t y = glm::clamp( static_cast(yCoord), static_cast(0), static_cast(this->_pImage->height - 1)); diff --git a/CesiumGltf/src/PropertyTableView.cpp b/CesiumGltf/src/PropertyTableView.cpp index f2456cf77..3ba261999 100644 --- a/CesiumGltf/src/PropertyTableView.cpp +++ b/CesiumGltf/src/PropertyTableView.cpp @@ -360,7 +360,7 @@ PropertyTableView::getBooleanArrayPropertyValues( // Handle fixed-length arrays if (fixedLengthArrayCount > 0) { - size_t maxRequiredBytes = maxRequiredBytes = static_cast(glm::ceil( + size_t maxRequiredBytes = static_cast(glm::ceil( static_cast(_pPropertyTable->count * fixedLengthArrayCount) / 8.0)); diff --git a/CesiumGltf/src/PropertyTexturePropertyView.cpp b/CesiumGltf/src/PropertyTexturePropertyView.cpp new file mode 100644 index 000000000..f5bd80aed --- /dev/null +++ b/CesiumGltf/src/PropertyTexturePropertyView.cpp @@ -0,0 +1,23 @@ +#include "CesiumGltf/PropertyTexturePropertyView.h" + +using namespace CesiumGltf; + +// Re-initialize consts here to avoid "undefined reference" errors with GCC / +// Clang. +const PropertyViewStatusType + PropertyTexturePropertyViewStatus::ErrorInvalidPropertyTexture; +const PropertyViewStatusType + PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty; +const PropertyViewStatusType + PropertyTexturePropertyViewStatus::ErrorInvalidTexture; +const PropertyViewStatusType + PropertyTexturePropertyViewStatus::ErrorInvalidSampler; +const PropertyViewStatusType + PropertyTexturePropertyViewStatus::ErrorInvalidImage; +const PropertyViewStatusType PropertyTexturePropertyViewStatus::ErrorEmptyImage; +const PropertyViewStatusType + PropertyTexturePropertyViewStatus::ErrorInvalidBytesPerChannel; +const PropertyViewStatusType + PropertyTexturePropertyViewStatus::ErrorInvalidChannels; +const PropertyViewStatusType + PropertyTexturePropertyViewStatus::ErrorChannelsAndTypeMismatch; diff --git a/CesiumGltf/test/TestPropertyTableView.cpp b/CesiumGltf/test/TestPropertyTableView.cpp index 3bb8ca0e8..378915c13 100644 --- a/CesiumGltf/test/TestPropertyTableView.cpp +++ b/CesiumGltf/test/TestPropertyTableView.cpp @@ -144,8 +144,7 @@ TEST_CASE("Test scalar PropertyTableProperty") { for (int64_t i = 0; i < uint32Property.size(); ++i) { REQUIRE(uint32Property.getRaw(i) == values[static_cast(i)]); - REQUIRE(uint32Property.get(i)); - REQUIRE(*uint32Property.get(i) == uint32Property.getRaw(i)); + REQUIRE(uint32Property.get(i) == uint32Property.getRaw(i)); } } @@ -200,7 +199,7 @@ TEST_CASE("Test scalar PropertyTableProperty") { view.getPropertyView("TestClassProperty"); REQUIRE( uint32NormalizedInvalid.status() == - PropertyTablePropertyViewStatus::ErrorInvalidNormalization); + PropertyTablePropertyViewStatus::ErrorNormalizationMismatch); } SECTION("Wrong buffer index") { @@ -256,7 +255,6 @@ TEST_CASE("Test scalar PropertyTableProperty (normalized)") { std::vector values = {-128, 0, 32, 2340, -1234, 127}; addBufferToModel(model, values); - size_t valueBufferIndex = model.buffers.size() - 1; size_t valueBufferViewIndex = model.bufferViews.size() - 1; ExtensionModelExtStructuralMetadata& metadata = @@ -299,8 +297,7 @@ TEST_CASE("Test scalar PropertyTableProperty (normalized)") { for (int64_t i = 0; i < int16Property.size(); ++i) { auto value = int16Property.getRaw(i); REQUIRE(value == values[static_cast(i)]); - REQUIRE(int16Property.get(i)); - REQUIRE(*int16Property.get(i) == normalize(value)); + REQUIRE(int16Property.get(i) == normalize(value)); } } @@ -346,7 +343,7 @@ TEST_CASE("Test scalar PropertyTableProperty (normalized)") { view.getPropertyView("TestClassProperty"); REQUIRE( nonNormalizedInvalid.status() == - PropertyTablePropertyViewStatus::ErrorInvalidNormalization); + PropertyTablePropertyViewStatus::ErrorNormalizationMismatch); } SECTION("Access incorrectly as double") { @@ -356,52 +353,6 @@ TEST_CASE("Test scalar PropertyTableProperty (normalized)") { doubleInvalid.status() == PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); } - - SECTION("Wrong buffer index") { - model.bufferViews[valueBufferViewIndex].buffer = 2; - PropertyTablePropertyView propertyView = - view.getPropertyView("TestClassProperty"); - REQUIRE( - propertyView.status() == - PropertyTablePropertyViewStatus::ErrorInvalidValueBuffer); - } - - SECTION("Wrong buffer view index") { - propertyTableProperty.values = -1; - PropertyTablePropertyView propertyView = - view.getPropertyView("TestClassProperty"); - REQUIRE( - propertyView.status() == - PropertyTablePropertyViewStatus::ErrorInvalidValueBufferView); - } - - SECTION("Buffer view points outside of the real buffer length") { - model.buffers[valueBufferIndex].cesium.data.resize(4); - PropertyTablePropertyView propertyView = - view.getPropertyView("TestClassProperty"); - REQUIRE( - propertyView.status() == - PropertyTablePropertyViewStatus::ErrorBufferViewOutOfBounds); - } - - SECTION("Buffer view length isn't multiple of sizeof(T)") { - model.bufferViews[valueBufferViewIndex].byteLength = 7; - PropertyTablePropertyView propertyView = - view.getPropertyView("TestClassProperty"); - REQUIRE( - propertyView.status() == PropertyTablePropertyViewStatus:: - ErrorBufferViewSizeNotDivisibleByTypeSize); - } - - SECTION("Buffer view length doesn't match with propertyTableCount") { - model.bufferViews[valueBufferViewIndex].byteLength = 2; - PropertyTablePropertyView propertyView = - view.getPropertyView("TestClassProperty"); - REQUIRE( - propertyView.status() == - PropertyTablePropertyViewStatus:: - ErrorBufferViewSizeDoesNotMatchPropertyTableCount); - } } TEST_CASE("Test vecN PropertyTableProperty") { @@ -525,7 +476,7 @@ TEST_CASE("Test vecN PropertyTableProperty") { view.getPropertyView("TestClassProperty"); REQUIRE( normalizedInvalid.status() == - PropertyTablePropertyViewStatus::ErrorInvalidNormalization); + PropertyTablePropertyViewStatus::ErrorNormalizationMismatch); } SECTION("Wrong buffer index") { @@ -585,7 +536,6 @@ TEST_CASE("Test vecN PropertyTableProperty (normalized)") { glm::ivec3(-4, 8, -13)}; addBufferToModel(model, values); - size_t valueBufferIndex = model.buffers.size() - 1; size_t valueBufferViewIndex = model.bufferViews.size() - 1; ExtensionModelExtStructuralMetadata& metadata = @@ -628,8 +578,7 @@ TEST_CASE("Test vecN PropertyTableProperty (normalized)") { for (int64_t i = 0; i < ivec3Property.size(); ++i) { auto value = ivec3Property.getRaw(i); REQUIRE(value == values[static_cast(i)]); - REQUIRE(ivec3Property.get(i)); - REQUIRE(*ivec3Property.get(i) == normalize(value)); + REQUIRE(ivec3Property.get(i) == normalize(value)); } } @@ -681,7 +630,7 @@ TEST_CASE("Test vecN PropertyTableProperty (normalized)") { view.getPropertyView("TestClassProperty"); REQUIRE( nonNormalizedInvalid.status() == - PropertyTablePropertyViewStatus::ErrorInvalidNormalization); + PropertyTablePropertyViewStatus::ErrorNormalizationMismatch); } SECTION("Access incorrectly as dvec3") { @@ -691,53 +640,6 @@ TEST_CASE("Test vecN PropertyTableProperty (normalized)") { dvec3Invalid.status() == PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); } - - SECTION("Wrong buffer index") { - model.bufferViews[valueBufferViewIndex].buffer = 2; - PropertyTablePropertyView ivec3Property = - view.getPropertyView("TestClassProperty"); - REQUIRE( - ivec3Property.status() == - PropertyTablePropertyViewStatus::ErrorInvalidValueBuffer); - } - - SECTION("Wrong buffer view index") { - propertyTableProperty.values = -1; - PropertyTablePropertyView ivec3Property = - view.getPropertyView("TestClassProperty"); - REQUIRE( - ivec3Property.status() == - PropertyTablePropertyViewStatus::ErrorInvalidValueBufferView); - } - - SECTION("Buffer view points outside of the real buffer length") { - model.buffers[valueBufferIndex].cesium.data.resize(12); - PropertyTablePropertyView ivec3Property = - view.getPropertyView("TestClassProperty"); - REQUIRE( - ivec3Property.status() == - PropertyTablePropertyViewStatus::ErrorBufferViewOutOfBounds); - } - - SECTION("Buffer view length isn't multiple of sizeof(T)") { - model.bufferViews[valueBufferViewIndex].byteLength = 11; - PropertyTablePropertyView ivec3Property = - view.getPropertyView("TestClassProperty"); - REQUIRE( - ivec3Property.status() == - PropertyTablePropertyViewStatus:: - ErrorBufferViewSizeNotDivisibleByTypeSize); - } - - SECTION("Buffer view length doesn't match with propertyTableCount") { - model.bufferViews[valueBufferViewIndex].byteLength = 12; - PropertyTablePropertyView ivec3Property = - view.getPropertyView("TestClassProperty"); - REQUIRE( - ivec3Property.status() == - PropertyTablePropertyViewStatus:: - ErrorBufferViewSizeDoesNotMatchPropertyTableCount); - } } TEST_CASE("Test matN PropertyTableProperty") { @@ -801,8 +703,7 @@ TEST_CASE("Test matN PropertyTableProperty") { for (int64_t i = 0; i < u32mat2x2Property.size(); ++i) { REQUIRE(u32mat2x2Property.getRaw(i) == values[static_cast(i)]); - REQUIRE(u32mat2x2Property.get(i)); - REQUIRE(*u32mat2x2Property.get(i) == u32mat2x2Property.getRaw(i)); + REQUIRE(u32mat2x2Property.get(i) == u32mat2x2Property.getRaw(i)); } } @@ -868,13 +769,11 @@ TEST_CASE("Test matN PropertyTableProperty") { } SECTION("Access incorrectly as normalized") { - PropertyTablePropertyView, true> - normalizedInvalid = - view.getPropertyView, true>( - "TestClassProperty"); + PropertyTablePropertyView normalizedInvalid = + view.getPropertyView("TestClassProperty"); REQUIRE( normalizedInvalid.status() == - PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); + PropertyTablePropertyViewStatus::ErrorNormalizationMismatch); } SECTION("Wrong buffer index") { @@ -946,7 +845,6 @@ TEST_CASE("Test matN PropertyTableProperty (normalized)") { // clang-format on addBufferToModel(model, values); - size_t valueBufferIndex = model.buffers.size() - 1; size_t valueBufferViewIndex = model.bufferViews.size() - 1; ExtensionModelExtStructuralMetadata& metadata = @@ -990,8 +888,7 @@ TEST_CASE("Test matN PropertyTableProperty (normalized)") { for (int64_t i = 0; i < u32mat2x2Property.size(); ++i) { auto value = u32mat2x2Property.getRaw(i); REQUIRE(value == values[static_cast(i)]); - REQUIRE(u32mat2x2Property.get(i)); - REQUIRE(*u32mat2x2Property.get(i) == normalize(value)); + REQUIRE(u32mat2x2Property.get(i) == normalize(value)); } } @@ -1044,7 +941,7 @@ TEST_CASE("Test matN PropertyTableProperty (normalized)") { view.getPropertyView("TestClassProperty"); REQUIRE( nonNormalizedInvalid.status() == - PropertyTablePropertyViewStatus::ErrorInvalidNormalization); + PropertyTablePropertyViewStatus::ErrorNormalizationMismatch); } SECTION("Access incorrectly as dmat2") { @@ -1054,55 +951,6 @@ TEST_CASE("Test matN PropertyTableProperty (normalized)") { dmat2Invalid.status() == PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); } - - SECTION("Wrong buffer index") { - model.bufferViews[valueBufferViewIndex].buffer = 2; - PropertyTablePropertyView u32mat2x2Property = - view.getPropertyView("TestClassProperty"); - REQUIRE( - u32mat2x2Property.status() == - PropertyTablePropertyViewStatus::ErrorInvalidValueBuffer); - } - - SECTION("Wrong buffer view index") { - propertyTableProperty.values = -1; - PropertyTablePropertyView u32mat2x2Property = - view.getPropertyView("TestClassProperty"); - REQUIRE( - u32mat2x2Property.status() == - PropertyTablePropertyViewStatus::ErrorInvalidValueBufferView); - } - - SECTION("Buffer view points outside of the real buffer length") { - model.buffers[valueBufferIndex].cesium.data.resize(sizeof(glm::u32mat2x2)); - PropertyTablePropertyView u32mat2x2Property = - view.getPropertyView("TestClassProperty"); - REQUIRE( - u32mat2x2Property.status() == - PropertyTablePropertyViewStatus::ErrorBufferViewOutOfBounds); - } - - SECTION("Buffer view length isn't multiple of sizeof(T)") { - model.bufferViews[valueBufferViewIndex].byteLength = - sizeof(glm::u32mat2x2) * 4 - 1; - PropertyTablePropertyView u32mat2x2Property = - view.getPropertyView("TestClassProperty"); - REQUIRE( - u32mat2x2Property.status() == - PropertyTablePropertyViewStatus:: - ErrorBufferViewSizeNotDivisibleByTypeSize); - } - - SECTION("Buffer view length doesn't match with propertyTableCount") { - model.bufferViews[valueBufferViewIndex].byteLength = sizeof(glm::u32mat2x2); - - PropertyTablePropertyView u32mat2x2Property = - view.getPropertyView("TestClassProperty"); - REQUIRE( - u32mat2x2Property.status() == - PropertyTablePropertyViewStatus:: - ErrorBufferViewSizeDoesNotMatchPropertyTableCount); - } } TEST_CASE("Test boolean PropertyTableProperty") { @@ -1421,7 +1269,7 @@ TEST_CASE("Test fixed-length scalar array") { "TestClassProperty"); REQUIRE( normalizedInvalid.status() == - PropertyTablePropertyViewStatus::ErrorInvalidNormalization); + PropertyTablePropertyViewStatus::ErrorNormalizationMismatch); } SECTION("Buffer size is not a multiple of type size") { @@ -1460,7 +1308,6 @@ TEST_CASE("Test fixed-length scalar array (normalized)") { {12, 34, 30, 11, 34, 34, 11, 33, 122, 33, 223, 11}; addBufferToModel(model, values); - size_t valueBufferViewIndex = model.bufferViews.size() - 1; ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); @@ -1549,39 +1396,7 @@ TEST_CASE("Test fixed-length scalar array (normalized)") { "TestClassProperty"); REQUIRE( nonNormalizedInvalid.status() == - PropertyTablePropertyViewStatus::ErrorInvalidNormalization); - } - - SECTION("Buffer size is not a multiple of type size") { - model.bufferViews[valueBufferViewIndex].byteLength = 13; - PropertyTablePropertyView, true> arrayProperty = - view.getPropertyView, true>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus:: - ErrorBufferViewSizeNotDivisibleByTypeSize); - } - - SECTION("Negative count") { - testClassProperty.count = -1; - PropertyTablePropertyView, true> arrayProperty = - view.getPropertyView, true>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == PropertyTablePropertyViewStatus:: - ErrorArrayCountAndOffsetBufferDontExist); - } - - SECTION("Value buffer doesn't fit into property table count") { - testClassProperty.count = 55; - PropertyTablePropertyView, true> arrayProperty = - view.getPropertyView, true>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus:: - ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + PropertyTablePropertyViewStatus::ErrorNormalizationMismatch); } } @@ -1663,10 +1478,11 @@ TEST_CASE("Test variable-length scalar array") { for (size_t i = 0; i < expected.size(); ++i) { PropertyArrayView array = property.getRaw(static_cast(i)); + REQUIRE(array.size() == static_cast(expected[i].size())); + auto maybeArray = property.get(static_cast(i)); REQUIRE(maybeArray); REQUIRE(maybeArray->size() == array.size()); - REQUIRE(array.size() == static_cast(expected[i].size())); for (size_t j = 0; j < expected[i].size(); ++j) { REQUIRE(expected[i][j] == array[static_cast(j)]); @@ -1678,9 +1494,12 @@ TEST_CASE("Test variable-length scalar array") { } SECTION("Incorrectly normalized") { - PropertyTablePropertyView> property = - view.getPropertyView>("TestClassProperty"); - REQUIRE(property.status() == PropertyTablePropertyViewStatus::Valid); + PropertyTablePropertyView, true> property = + view.getPropertyView, true>( + "TestClassProperty"); + REQUIRE( + property.status() == + PropertyTablePropertyViewStatus::ErrorNormalizationMismatch); } SECTION("Wrong offset type") { @@ -1783,11 +1602,9 @@ TEST_CASE("Test variable-length scalar array (normalized)") { } addBufferToModel(model, values); - size_t valueBufferIndex = model.buffers.size() - 1; size_t valueBufferViewIndex = model.bufferViews.size() - 1; addBufferToModel(model, offsets); - size_t offsetBufferIndex = model.buffers.size() - 1; size_t offsetBufferViewIndex = model.bufferViews.size() - 1; ExtensionModelExtStructuralMetadata& metadata = @@ -1834,10 +1651,11 @@ TEST_CASE("Test variable-length scalar array (normalized)") { for (size_t i = 0; i < expected.size(); ++i) { PropertyArrayView array = property.getRaw(static_cast(i)); + REQUIRE(array.size() == static_cast(expected[i].size())); + auto maybeArray = property.get(static_cast(i)); REQUIRE(maybeArray); REQUIRE(maybeArray->size() == array.size()); - REQUIRE(array.size() == static_cast(expected[i].size())); for (size_t j = 0; j < expected[i].size(); ++j) { auto value = array[static_cast(j)]; @@ -1852,81 +1670,7 @@ TEST_CASE("Test variable-length scalar array (normalized)") { view.getPropertyView>("TestClassProperty"); REQUIRE( property.status() == - PropertyTablePropertyViewStatus::ErrorInvalidNormalization); - } - - SECTION("Wrong offset type") { - propertyTableProperty.arrayOffsetType = - PropertyTableProperty::ArrayOffsetType::UINT8; - PropertyTablePropertyView, true> arrayProperty = - view.getPropertyView, true>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus:: - ErrorBufferViewSizeDoesNotMatchPropertyTableCount); - - propertyTableProperty.arrayOffsetType = - PropertyTableProperty::ArrayOffsetType::UINT16; - arrayProperty = view.getPropertyView, true>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus:: - ErrorBufferViewSizeDoesNotMatchPropertyTableCount); - - propertyTableProperty.arrayOffsetType = "NONSENSE"; - arrayProperty = view.getPropertyView, true>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); - - propertyTableProperty.arrayOffsetType = ""; - propertyTableProperty.stringOffsetType = - PropertyTableProperty::StringOffsetType::UINT64; - arrayProperty = view.getPropertyView, true>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); - } - - SECTION("Offset values are not sorted ascending") { - propertyTableProperty.arrayOffsetType = - PropertyTableProperty::ArrayOffsetType::UINT64; - uint64_t* offset = reinterpret_cast( - model.buffers[offsetBufferIndex].cesium.data.data()); - offset[propertyTable.count] = 0; - PropertyTablePropertyView, true> arrayProperty = - view.getPropertyView, true>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus::ErrorArrayOffsetsNotSorted); - } - - SECTION("Offset value points outside of value buffer") { - uint64_t* offset = reinterpret_cast( - model.buffers[offsetBufferIndex].cesium.data.data()); - offset[propertyTable.count] = - static_cast(model.buffers[valueBufferIndex].byteLength + 4); - PropertyTablePropertyView, true> arrayProperty = - view.getPropertyView, true>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus::ErrorArrayOffsetOutOfBounds); - } - - SECTION("Count and offset buffer are both present") { - testClassProperty.count = 3; - PropertyTablePropertyView, true> property = - view.getPropertyView, true>( - "TestClassProperty"); - REQUIRE( - property.status() == - PropertyTablePropertyViewStatus::ErrorArrayCountAndOffsetBufferCoexist); + PropertyTablePropertyViewStatus::ErrorNormalizationMismatch); } } @@ -2035,7 +1779,7 @@ TEST_CASE("Test fixed-length vecN array") { "TestClassProperty"); REQUIRE( normalizedInvalid.status() == - PropertyTablePropertyViewStatus::ErrorInvalidNormalization); + PropertyTablePropertyViewStatus::ErrorNormalizationMismatch); } SECTION("Buffer size is not a multiple of type size") { @@ -2083,7 +1827,6 @@ TEST_CASE("Test fixed-length vecN array (normalized)") { }; addBufferToModel(model, values); - size_t valueBufferViewIndex = model.bufferViews.size() - 1; ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); @@ -2176,47 +1919,13 @@ TEST_CASE("Test fixed-length vecN array (normalized)") { } SECTION("Incorrect non-normalization") { - PropertyTablePropertyView> normalizedInvalid = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - normalizedInvalid.status() == - PropertyTablePropertyViewStatus::ErrorInvalidNormalization); - } - - SECTION("Buffer size is not a multiple of type size") { - model.bufferViews[valueBufferViewIndex].byteLength = 13; - PropertyTablePropertyView, true> - arrayProperty = - view.getPropertyView, true>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus:: - ErrorBufferViewSizeNotDivisibleByTypeSize); - } - - SECTION("Negative count") { - testClassProperty.count = -1; - PropertyTablePropertyView, true> - arrayProperty = - view.getPropertyView, true>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == PropertyTablePropertyViewStatus:: - ErrorArrayCountAndOffsetBufferDontExist); - } - - SECTION("Value buffer doesn't fit into property table count") { - testClassProperty.count = 55; - PropertyTablePropertyView, true> - arrayProperty = - view.getPropertyView, true>( + PropertyTablePropertyView> + nonNormalizedInvalid = + view.getPropertyView>( "TestClassProperty"); REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus:: - ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + nonNormalizedInvalid.status() == + PropertyTablePropertyViewStatus::ErrorNormalizationMismatch); } } @@ -2300,9 +2009,10 @@ TEST_CASE("Test variable-length vecN array") { for (size_t i = 0; i < expected.size(); ++i) { PropertyArrayView array = property.getRaw(static_cast(i)); + REQUIRE(array.size() == static_cast(expected[i].size())); + auto maybeArray = property.get(i); REQUIRE(maybeArray); - REQUIRE(array.size() == static_cast(expected[i].size())); for (size_t j = 0; j < expected[i].size(); ++j) { auto value = array[static_cast(j)]; REQUIRE(expected[i][j] == value); @@ -2317,7 +2027,7 @@ TEST_CASE("Test variable-length vecN array") { "TestClassProperty"); REQUIRE( property.status() == - PropertyTablePropertyViewStatus::ErrorInvalidNormalization); + PropertyTablePropertyViewStatus::ErrorNormalizationMismatch); } SECTION("Wrong offset type") { @@ -2425,11 +2135,9 @@ TEST_CASE("Test variable-length vecN array (normalized)") { } addBufferToModel(model, values); - size_t valueBufferIndex = model.buffers.size() - 1; size_t valueBufferViewIndex = model.bufferViews.size() - 1; addBufferToModel(model, offsets); - size_t offsetBufferIndex = model.buffers.size() - 1; size_t offsetBufferViewIndex = model.bufferViews.size() - 1; ExtensionModelExtStructuralMetadata& metadata = @@ -2451,126 +2159,50 @@ TEST_CASE("Test variable-length vecN array (normalized)") { propertyTable.properties["TestClassProperty"]; propertyTableProperty.values = static_cast(valueBufferViewIndex); propertyTableProperty.arrayOffsets = - static_cast(offsetBufferViewIndex); - propertyTableProperty.arrayOffsetType = - PropertyTableProperty::ArrayOffsetType::UINT64; - - PropertyTableView view(model, propertyTable); - REQUIRE(view.status() == PropertyTableViewStatus::Valid); - REQUIRE(view.size() == propertyTable.count); - - const ClassProperty* classProperty = - view.getClassProperty("TestClassProperty"); - REQUIRE(classProperty); - REQUIRE(classProperty->type == ClassProperty::Type::VEC3); - REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT32); - REQUIRE(classProperty->array); - REQUIRE(!classProperty->count); - REQUIRE(classProperty->normalized); - - SECTION("Access the correct type") { - PropertyTablePropertyView, true> property = - view.getPropertyView, true>( - "TestClassProperty"); - REQUIRE(property.status() == PropertyTablePropertyViewStatus::Valid); - for (size_t i = 0; i < expected.size(); ++i) { - PropertyArrayView array = - property.getRaw(static_cast(i)); - auto maybeArray = property.get(i); - REQUIRE(maybeArray); - REQUIRE(array.size() == static_cast(expected[i].size())); - for (size_t j = 0; j < expected[i].size(); ++j) { - auto value = array[static_cast(j)]; - REQUIRE(expected[i][j] == value); - REQUIRE((*maybeArray)[j] == normalize(value)); - } - } - } - - SECTION("Incorrectly non-normalized") { - PropertyTablePropertyView> property = - view.getPropertyView>( - "TestClassProperty"); - REQUIRE( - property.status() == - PropertyTablePropertyViewStatus::ErrorInvalidNormalization); - } - - SECTION("Wrong offset type") { - propertyTableProperty.arrayOffsetType = - PropertyTableProperty::ArrayOffsetType::UINT8; - PropertyTablePropertyView, true> - arrayProperty = - view.getPropertyView, true>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus:: - ErrorBufferViewSizeDoesNotMatchPropertyTableCount); - - propertyTableProperty.arrayOffsetType = - PropertyTableProperty::ArrayOffsetType::UINT16; - arrayProperty = view.getPropertyView, true>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus:: - ErrorBufferViewSizeDoesNotMatchPropertyTableCount); - - propertyTableProperty.arrayOffsetType = "NONSENSE"; - arrayProperty = view.getPropertyView, true>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); - - propertyTableProperty.arrayOffsetType = ""; - propertyTableProperty.stringOffsetType = - PropertyTableProperty::StringOffsetType::UINT64; - arrayProperty = view.getPropertyView, true>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); - } + static_cast(offsetBufferViewIndex); + propertyTableProperty.arrayOffsetType = + PropertyTableProperty::ArrayOffsetType::UINT64; - SECTION("Offset values are not sorted ascending") { - propertyTableProperty.arrayOffsetType = - PropertyTableProperty::ArrayOffsetType::UINT64; - uint64_t* offset = reinterpret_cast( - model.buffers[offsetBufferIndex].cesium.data.data()); - offset[propertyTable.count] = 0; - PropertyTablePropertyView, true> - arrayProperty = - view.getPropertyView, true>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus::ErrorArrayOffsetsNotSorted); - } + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); - SECTION("Offset value points outside of value buffer") { - uint64_t* offset = reinterpret_cast( - model.buffers[offsetBufferIndex].cesium.data.data()); - offset[propertyTable.count] = - static_cast(model.buffers[valueBufferIndex].byteLength + 4); - PropertyTablePropertyView, true> - arrayProperty = - view.getPropertyView, true>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus::ErrorArrayOffsetOutOfBounds); - } + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::VEC3); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT32); + REQUIRE(classProperty->array); + REQUIRE(!classProperty->count); + REQUIRE(classProperty->normalized); - SECTION("Count and offset buffer are both present") { - testClassProperty.count = 3; + SECTION("Access the correct type") { PropertyTablePropertyView, true> property = view.getPropertyView, true>( "TestClassProperty"); + REQUIRE(property.status() == PropertyTablePropertyViewStatus::Valid); + for (size_t i = 0; i < expected.size(); ++i) { + PropertyArrayView array = + property.getRaw(static_cast(i)); + REQUIRE(array.size() == static_cast(expected[i].size())); + + auto maybeArray = property.get(i); + REQUIRE(maybeArray); + for (size_t j = 0; j < expected[i].size(); ++j) { + auto value = array[static_cast(j)]; + REQUIRE(expected[i][j] == value); + REQUIRE((*maybeArray)[j] == normalize(value)); + } + } + } + + SECTION("Incorrectly non-normalized") { + PropertyTablePropertyView> property = + view.getPropertyView>( + "TestClassProperty"); REQUIRE( property.status() == - PropertyTablePropertyViewStatus::ErrorArrayCountAndOffsetBufferCoexist); + PropertyTablePropertyViewStatus::ErrorNormalizationMismatch); } } @@ -2690,7 +2322,7 @@ TEST_CASE("Test fixed-length matN array") { "TestClassProperty"); REQUIRE( normalizedInvalid.status() == - PropertyTablePropertyViewStatus::ErrorInvalidNormalization); + PropertyTablePropertyViewStatus::ErrorNormalizationMismatch); } SECTION("Buffer size is not a multiple of type size") { @@ -2752,7 +2384,6 @@ TEST_CASE("Test fixed-length matN array (normalized)") { // clang-format on addBufferToModel(model, values); - size_t valueBufferViewIndex = model.bufferViews.size() - 1; ExtensionModelExtStructuralMetadata& metadata = model.addExtension(); @@ -2850,42 +2481,7 @@ TEST_CASE("Test fixed-length matN array (normalized)") { "TestClassProperty"); REQUIRE( nonNormalizedInvalid.status() == - PropertyTablePropertyViewStatus::ErrorInvalidNormalization); - } - - SECTION("Buffer size is not a multiple of type size") { - model.bufferViews[valueBufferViewIndex].byteLength = 13; - PropertyTablePropertyView, true> - arrayProperty = - view.getPropertyView, true>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus:: - ErrorBufferViewSizeNotDivisibleByTypeSize); - } - - SECTION("Negative count") { - testClassProperty.count = -1; - PropertyTablePropertyView, true> - arrayProperty = - view.getPropertyView, true>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == PropertyTablePropertyViewStatus:: - ErrorArrayCountAndOffsetBufferDontExist); - } - - SECTION("Value buffer doesn't fit into property table count") { - testClassProperty.count = 55; - PropertyTablePropertyView, true> - arrayProperty = - view.getPropertyView, true>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus:: - ErrorBufferViewSizeDoesNotMatchPropertyTableCount); + PropertyTablePropertyViewStatus::ErrorNormalizationMismatch); } } @@ -2989,9 +2585,10 @@ TEST_CASE("Test variable-length matN array") { for (size_t i = 0; i < expected.size(); ++i) { PropertyArrayView array = property.getRaw(static_cast(i)); + REQUIRE(array.size() == static_cast(expected[i].size())); + auto maybeArray = property.get(i); REQUIRE(maybeArray); - REQUIRE(array.size() == static_cast(expected[i].size())); for (size_t j = 0; j < expected[i].size(); ++j) { auto value = array[static_cast(j)]; REQUIRE(expected[i][j] == value); @@ -3007,7 +2604,7 @@ TEST_CASE("Test variable-length matN array") { "TestClassProperty"); REQUIRE( property.status() == - PropertyTablePropertyViewStatus::ErrorInvalidNormalization); + PropertyTablePropertyViewStatus::ErrorNormalizationMismatch); } SECTION("Wrong offset type") { @@ -3135,11 +2732,9 @@ TEST_CASE("Test variable-length matN array (normalized)") { } addBufferToModel(model, values); - size_t valueBufferIndex = model.buffers.size() - 1; size_t valueBufferViewIndex = model.bufferViews.size() - 1; addBufferToModel(model, offsets); - size_t offsetBufferIndex = model.buffers.size() - 1; size_t offsetBufferViewIndex = model.bufferViews.size() - 1; ExtensionModelExtStructuralMetadata& metadata = @@ -3204,88 +2799,7 @@ TEST_CASE("Test variable-length matN array (normalized)") { "TestClassProperty"); REQUIRE( property.status() == - PropertyTablePropertyViewStatus::ErrorInvalidNormalization); - } - - SECTION("Wrong offset type") { - propertyTableProperty.arrayOffsetType = - PropertyTableProperty::ArrayOffsetType::UINT8; - PropertyTablePropertyView, true> - arrayProperty = - view.getPropertyView, true>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus:: - ErrorBufferViewSizeDoesNotMatchPropertyTableCount); - - propertyTableProperty.arrayOffsetType = - PropertyTableProperty::ArrayOffsetType::UINT16; - arrayProperty = - view.getPropertyView, true>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus:: - ErrorBufferViewSizeDoesNotMatchPropertyTableCount); - - propertyTableProperty.arrayOffsetType = "NONSENSE"; - arrayProperty = - view.getPropertyView, true>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); - - propertyTableProperty.arrayOffsetType = ""; - propertyTableProperty.stringOffsetType = - PropertyTableProperty::StringOffsetType::UINT64; - arrayProperty = - view.getPropertyView, true>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); - } - - SECTION("Offset values are not sorted ascending") { - propertyTableProperty.arrayOffsetType = - PropertyTableProperty::ArrayOffsetType::UINT64; - uint64_t* offset = reinterpret_cast( - model.buffers[offsetBufferIndex].cesium.data.data()); - offset[propertyTable.count] = 0; - PropertyTablePropertyView, true> - arrayProperty = - view.getPropertyView, true>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus::ErrorArrayOffsetsNotSorted); - } - - SECTION("Offset value points outside of value buffer") { - uint64_t* offset = reinterpret_cast( - model.buffers[offsetBufferIndex].cesium.data.data()); - offset[propertyTable.count] = - static_cast(model.buffers[valueBufferIndex].byteLength + 4); - PropertyTablePropertyView, true> - arrayProperty = - view.getPropertyView, true>( - "TestClassProperty"); - REQUIRE( - arrayProperty.status() == - PropertyTablePropertyViewStatus::ErrorArrayOffsetOutOfBounds); - } - - SECTION("Count and offset buffer are both present") { - testClassProperty.count = 3; - PropertyTablePropertyView, true> - property = - view.getPropertyView, true>( - "TestClassProperty"); - REQUIRE( - property.status() == - PropertyTablePropertyViewStatus::ErrorArrayCountAndOffsetBufferCoexist); + PropertyTablePropertyViewStatus::ErrorNormalizationMismatch); } } @@ -3488,9 +3002,10 @@ TEST_CASE("Test variable-length boolean array") { for (size_t i = 0; i < expected.size(); ++i) { PropertyArrayView array = boolArrayProperty.getRaw(static_cast(i)); + REQUIRE(array.size() == static_cast(expected[i].size())); + auto maybeArray = boolArrayProperty.get(i); REQUIRE(maybeArray); - REQUIRE(array.size() == static_cast(expected[i].size())); for (size_t j = 0; j < expected[i].size(); ++j) { auto value = array[static_cast(j)]; REQUIRE(expected[i][j] == value); @@ -3826,6 +3341,7 @@ TEST_CASE("Test variable-length arrays of strings") { view.getPropertyView>( "TestClassProperty"); REQUIRE(stringProperty.status() == PropertyTablePropertyViewStatus::Valid); + for (size_t i = 0; i < expected.size(); ++i) { PropertyArrayView stringArray = stringProperty.getRaw(static_cast(i)); @@ -4025,19 +3541,14 @@ TEST_CASE("Test with PropertyTableProperty offset, scale, min, max") { view.getPropertyView("TestClassProperty"); REQUIRE(propertyView.status() == PropertyTablePropertyViewStatus::Valid); REQUIRE(propertyView.size() > 0); - REQUIRE(propertyView.offset()); - REQUIRE(*propertyView.offset() == offset); - REQUIRE(propertyView.scale()); - REQUIRE(*propertyView.scale() == scale); - REQUIRE(propertyView.min()); - REQUIRE(*propertyView.min() == min); - REQUIRE(propertyView.max()); - REQUIRE(*propertyView.max() == max); + REQUIRE(propertyView.offset() == offset); + REQUIRE(propertyView.scale() == scale); + REQUIRE(propertyView.min() == min); + REQUIRE(propertyView.max() == max); for (int64_t i = 0; i < propertyView.size(); ++i) { REQUIRE(propertyView.getRaw(i) == values[static_cast(i)]); - REQUIRE(propertyView.get(i)); - REQUIRE(*propertyView.get(i) == propertyView.getRaw(i) * scale + offset); + REQUIRE(propertyView.get(i) == propertyView.getRaw(i) * scale + offset); } } @@ -4055,26 +3566,21 @@ TEST_CASE("Test with PropertyTableProperty offset, scale, min, max") { view.getPropertyView("TestClassProperty"); REQUIRE(propertyView.status() == PropertyTablePropertyViewStatus::Valid); REQUIRE(propertyView.size() > 0); - REQUIRE(propertyView.offset()); - REQUIRE(*propertyView.offset() == newOffset); - REQUIRE(propertyView.scale()); - REQUIRE(*propertyView.scale() == newScale); - REQUIRE(propertyView.min()); - REQUIRE(*propertyView.min() == newMin); - REQUIRE(propertyView.max()); - REQUIRE(*propertyView.max() == newMax); + REQUIRE(propertyView.offset() == newOffset); + REQUIRE(propertyView.scale() == newScale); + REQUIRE(propertyView.min() == newMin); + REQUIRE(propertyView.max() == newMax); for (int64_t i = 0; i < propertyView.size(); ++i) { REQUIRE(propertyView.getRaw(i) == values[static_cast(i)]); - REQUIRE(propertyView.get(i)); REQUIRE( - *propertyView.get(i) == - propertyView.getRaw(i) * newScale + newOffset); + propertyView.get(i) == propertyView.getRaw(i) * newScale + newOffset); } } } -TEST_CASE("Test with PropertyTableProperty offset, scale, min, max (normalized)") { +TEST_CASE( + "Test with PropertyTableProperty offset, scale, min, max (normalized)") { Model model; std::vector values = {-128, 0, 32, 127}; const double offset = 0.5; @@ -4129,20 +3635,15 @@ TEST_CASE("Test with PropertyTableProperty offset, scale, min, max (normalized)" view.getPropertyView("TestClassProperty"); REQUIRE(propertyView.status() == PropertyTablePropertyViewStatus::Valid); REQUIRE(propertyView.size() > 0); - REQUIRE(propertyView.offset()); - REQUIRE(*propertyView.offset() == offset); - REQUIRE(propertyView.scale()); - REQUIRE(*propertyView.scale() == scale); - REQUIRE(propertyView.min()); - REQUIRE(*propertyView.min() == min); - REQUIRE(propertyView.max()); - REQUIRE(*propertyView.max() == max); + REQUIRE(propertyView.offset() == offset); + REQUIRE(propertyView.scale() == scale); + REQUIRE(propertyView.min() == min); + REQUIRE(propertyView.max() == max); for (int64_t i = 0; i < propertyView.size(); ++i) { REQUIRE(propertyView.getRaw(i) == values[static_cast(i)]); - REQUIRE(propertyView.get(i)); REQUIRE( - *propertyView.get(i) == + propertyView.get(i) == normalize(propertyView.getRaw(i)) * scale + offset); } } @@ -4161,20 +3662,15 @@ TEST_CASE("Test with PropertyTableProperty offset, scale, min, max (normalized)" view.getPropertyView("TestClassProperty"); REQUIRE(propertyView.status() == PropertyTablePropertyViewStatus::Valid); REQUIRE(propertyView.size() > 0); - REQUIRE(propertyView.offset()); - REQUIRE(*propertyView.offset() == newOffset); - REQUIRE(propertyView.scale()); - REQUIRE(*propertyView.scale() == newScale); - REQUIRE(propertyView.min()); - REQUIRE(*propertyView.min() == newMin); - REQUIRE(propertyView.max()); - REQUIRE(*propertyView.max() == newMax); + REQUIRE(propertyView.offset() == newOffset); + REQUIRE(propertyView.scale() == newScale); + REQUIRE(propertyView.min() == newMin); + REQUIRE(propertyView.max() == newMax); for (int64_t i = 0; i < propertyView.size(); ++i) { REQUIRE(propertyView.getRaw(i) == values[static_cast(i)]); - REQUIRE(propertyView.get(i)); REQUIRE( - *propertyView.get(i) == + propertyView.get(i) == normalize(propertyView.getRaw(i)) * newScale + newOffset); } } @@ -4225,16 +3721,14 @@ TEST_CASE("Test with PropertyTableProperty noData value") { view.getPropertyView("TestClassProperty"); REQUIRE(propertyView.status() == PropertyTablePropertyViewStatus::Valid); REQUIRE(propertyView.size() > 0); - REQUIRE(propertyView.noData()); - REQUIRE(*propertyView.noData() == noData); + REQUIRE(propertyView.noData() == noData); for (int64_t i = 0; i < propertyView.size(); ++i) { REQUIRE(propertyView.getRaw(i) == values[static_cast(i)]); if (propertyView.getRaw(i) == noData) { REQUIRE(!propertyView.get(i)); } else { - REQUIRE(propertyView.get(i)); - REQUIRE(*propertyView.get(i) == propertyView.getRaw(i)); + REQUIRE(propertyView.get(i) == propertyView.getRaw(i)); } } } @@ -4248,18 +3742,15 @@ TEST_CASE("Test with PropertyTableProperty noData value") { view.getPropertyView("TestClassProperty"); REQUIRE(propertyView.status() == PropertyTablePropertyViewStatus::Valid); REQUIRE(propertyView.size() > 0); - REQUIRE(propertyView.noData()); - REQUIRE(*propertyView.noData() == noData); - REQUIRE(propertyView.defaultValue()); - REQUIRE(*propertyView.defaultValue() == defaultValue); + REQUIRE(propertyView.noData() == noData); + REQUIRE(propertyView.defaultValue() == defaultValue); for (int64_t i = 0; i < propertyView.size(); ++i) { REQUIRE(propertyView.getRaw(i) == values[static_cast(i)]); - REQUIRE(propertyView.get(i)); if (propertyView.getRaw(i) == noData) { - REQUIRE(*propertyView.get(i) == defaultValue); + REQUIRE(propertyView.get(i) == defaultValue); } else { - REQUIRE(*propertyView.get(i) == propertyView.getRaw(i)); + REQUIRE(propertyView.get(i) == propertyView.getRaw(i)); } } } @@ -4311,16 +3802,14 @@ TEST_CASE("Test with PropertyTableProperty noData value (normalized)") { view.getPropertyView("TestClassProperty"); REQUIRE(propertyView.status() == PropertyTablePropertyViewStatus::Valid); REQUIRE(propertyView.size() > 0); - REQUIRE(propertyView.noData()); - REQUIRE(*propertyView.noData() == noData); + REQUIRE(propertyView.noData() == noData); for (int64_t i = 0; i < propertyView.size(); ++i) { REQUIRE(propertyView.getRaw(i) == values[static_cast(i)]); if (propertyView.getRaw(i) == noData) { REQUIRE(!propertyView.get(i)); } else { - REQUIRE(propertyView.get(i)); - REQUIRE(*propertyView.get(i) == normalize(propertyView.getRaw(i))); + REQUIRE(propertyView.get(i) == normalize(propertyView.getRaw(i))); } } } @@ -4335,18 +3824,15 @@ TEST_CASE("Test with PropertyTableProperty noData value (normalized)") { view.getPropertyView("TestClassProperty"); REQUIRE(propertyView.status() == PropertyTablePropertyViewStatus::Valid); REQUIRE(propertyView.size() > 0); - REQUIRE(propertyView.noData()); - REQUIRE(*propertyView.noData() == noData); - REQUIRE(propertyView.defaultValue()); - REQUIRE(*propertyView.defaultValue() == defaultValue); + REQUIRE(propertyView.noData() == noData); + REQUIRE(propertyView.defaultValue() == defaultValue); for (int64_t i = 0; i < propertyView.size(); ++i) { REQUIRE(propertyView.getRaw(i) == values[static_cast(i)]); - REQUIRE(propertyView.get(i)); if (propertyView.getRaw(i) == noData) { - REQUIRE(*propertyView.get(i) == defaultValue); + REQUIRE(propertyView.get(i) == defaultValue); } else { - REQUIRE(*propertyView.get(i) == normalize(propertyView.getRaw(i))); + REQUIRE(propertyView.get(i) == normalize(propertyView.getRaw(i))); } } } @@ -4435,6 +3921,56 @@ TEST_CASE("Test callback for invalid PropertyTableProperty") { REQUIRE(invokedCallbackCount == 2); } +TEST_CASE("Test callback for invalid normalized PropertyTableProperty") { + Model model; + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::FLOAT32; + testClassProperty.normalized = true; // This is erroneous. + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = static_cast(5); + + PropertyTableProperty& propertyTableProperty = + propertyTable.properties["TestClassProperty"]; + propertyTableProperty.values = 0; + + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); + REQUIRE( + classProperty->componentType == ClassProperty::ComponentType::FLOAT32); + REQUIRE(!classProperty->array); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(classProperty->normalized); + + uint32_t invokedCallbackCount = 0; + view.getPropertyView( + "TestClassProperty", + [&invokedCallbackCount]( + const std::string& /*propertyName*/, + auto propertyValue) mutable { + invokedCallbackCount++; + REQUIRE( + propertyValue.status() == + PropertyTablePropertyViewStatus::ErrorInvalidNormalization); + REQUIRE(propertyValue.size() == 0); + }); + + REQUIRE(invokedCallbackCount == 1); +} + TEST_CASE("Test callback for scalar PropertyTableProperty") { Model model; std::vector values = {12, 34, 30, 11, 34, 34, 11, 33, 122, 33}; @@ -4491,10 +4027,7 @@ TEST_CASE("Test callback for scalar PropertyTableProperty") { REQUIRE( static_cast(propertyValue.getRaw(i)) == expectedValue); - - auto maybeValue = propertyValue.get(i); - REQUIRE(maybeValue); - REQUIRE(*maybeValue == expectedValue); + REQUIRE(propertyValue.get(i) == expectedValue); } } else { FAIL("getPropertyView returned PropertyTablePropertyView of " @@ -4562,10 +4095,7 @@ TEST_CASE("Test callback for scalar PropertyTableProperty (normalized)") { REQUIRE( static_cast(propertyValue.getRaw(i)) == expectedValue); - - auto maybeValue = propertyValue.get(i); - REQUIRE(maybeValue); - REQUIRE(*maybeValue == normalize(expectedValue)); + REQUIRE(propertyValue.get(i) == normalize(expectedValue)); } } else { FAIL("getPropertyView returned PropertyTablePropertyView of " @@ -4636,10 +4166,7 @@ TEST_CASE("Test callback for vecN PropertyTableProperty") { REQUIRE( static_cast(propertyValue.getRaw(i)) == expectedValue); - - auto maybeValue = propertyValue.get(i); - REQUIRE(maybeValue); - REQUIRE(*maybeValue == expectedValue); + REQUIRE(propertyValue.get(i) == expectedValue); } } else { FAIL("getPropertyView returned PropertyTablePropertyView of " @@ -4711,10 +4238,7 @@ TEST_CASE("Test callback for vecN PropertyTableProperty (normalized)") { REQUIRE( static_cast(propertyValue.getRaw(i)) == expectedValue); - - auto maybeValue = propertyValue.get(i); - REQUIRE(maybeValue); - REQUIRE(*maybeValue == normalize(expectedValue)); + REQUIRE(propertyValue.get(i) == normalize(expectedValue)); } } else { FAIL("getPropertyView returned PropertyTablePropertyView of " @@ -4794,10 +4318,7 @@ TEST_CASE("Test callback for matN PropertyTableProperty") { REQUIRE( static_cast(propertyValue.getRaw(i)) == expectedValue); - - auto maybeValue = propertyValue.get(i); - REQUIRE(maybeValue); - REQUIRE(*maybeValue == expectedValue); + REQUIRE(propertyValue.get(i) == expectedValue); } } else { FAIL("getPropertyView returned PropertyTablePropertyView of " @@ -4879,10 +4400,7 @@ TEST_CASE("Test callback for matN PropertyTableProperty (normalized)") { REQUIRE( static_cast(propertyValue.getRaw(i)) == expectedValue); - - auto maybeValue = propertyValue.get(i); - REQUIRE(maybeValue); - REQUIRE(*maybeValue == normalize(expectedValue)); + REQUIRE(propertyValue.get(i) == normalize(expectedValue)); } } else { FAIL("getPropertyView returned PropertyTablePropertyView of " @@ -4965,10 +4483,7 @@ TEST_CASE("Test callback for boolean PropertyTableProperty") { auto expectedValue = expected[static_cast(i)]; REQUIRE( static_cast(propertyValue.getRaw(i)) == expectedValue); - - auto maybeValue = propertyValue.get(i); - REQUIRE(maybeValue); - REQUIRE(*maybeValue == expectedValue); + REQUIRE(propertyValue.get(i) == expectedValue); } } else { FAIL("getPropertyView returned PropertyTablePropertyView of " @@ -5059,10 +4574,7 @@ TEST_CASE("Test callback for string PropertyTableProperty") { REQUIRE( static_cast(propertyValue.getRaw(i)) == expectedValue); - - auto maybeValue = propertyValue.get(i); - REQUIRE(maybeValue); - REQUIRE(*maybeValue == expectedValue); + REQUIRE(propertyValue.get(i) == expectedValue); } } else { FAIL("getPropertyView returned PropertyTablePropertyView of " diff --git a/CesiumGltf/test/TestPropertyTextureView.cpp b/CesiumGltf/test/TestPropertyTextureView.cpp index af6e61e13..626dd3891 100644 --- a/CesiumGltf/test/TestPropertyTextureView.cpp +++ b/CesiumGltf/test/TestPropertyTextureView.cpp @@ -3,10 +3,7 @@ #include #include -#include -#include #include -#include #include using namespace CesiumGltf; @@ -174,12 +171,8 @@ TEST_CASE("Test scalar PropertyTextureProperty") { for (size_t i = 0; i < texCoords.size(); ++i) { glm::dvec2 uv = texCoords[i]; - auto value = uint8Property.getRaw(uv[0], uv[1]); - REQUIRE(value == data[i]); - - auto maybeValue = uint8Property.get(uv[0], uv[1]); - REQUIRE(maybeValue); - REQUIRE(*maybeValue == data[i]); + REQUIRE(uint8Property.getRaw(uv[0], uv[1]) == data[i]); + REQUIRE(uint8Property.get(uv[0], uv[1]) == data[i]); } } @@ -224,7 +217,7 @@ TEST_CASE("Test scalar PropertyTextureProperty") { view.getPropertyView("TestClassProperty"); REQUIRE( normalizedInvalid.status() == - PropertyTexturePropertyViewStatus::ErrorInvalidNormalization); + PropertyTexturePropertyViewStatus::ErrorNormalizationMismatch); } SECTION("Channel and type mismatch") { @@ -360,12 +353,8 @@ TEST_CASE("Test scalar PropertyTextureProperty (normalized)") { for (size_t i = 0; i < texCoords.size(); ++i) { glm::dvec2 uv = texCoords[i]; - auto value = uint8Property.getRaw(uv[0], uv[1]); - REQUIRE(value == data[i]); - - auto maybeValue = uint8Property.get(uv[0], uv[1]); - REQUIRE(maybeValue); - REQUIRE(*maybeValue == normalize(data[i])); + REQUIRE(uint8Property.getRaw(uv[0], uv[1]) == data[i]); + REQUIRE(uint8Property.get(uv[0], uv[1]) == normalize(data[i])); } } @@ -405,7 +394,7 @@ TEST_CASE("Test scalar PropertyTextureProperty (normalized)") { view.getPropertyView("TestClassProperty"); REQUIRE( normalizedInvalid.status() == - PropertyTexturePropertyViewStatus::ErrorInvalidNormalization); + PropertyTexturePropertyViewStatus::ErrorNormalizationMismatch); } SECTION("Access incorrectly as double") { @@ -425,69 +414,6 @@ TEST_CASE("Test scalar PropertyTextureProperty (normalized)") { uint8Property.status() == PropertyTexturePropertyViewStatus::ErrorChannelsAndTypeMismatch); } - - SECTION("Invalid channel values") { - propertyTextureProperty.channels = {5}; - PropertyTexturePropertyView uint8Property = - view.getPropertyView("TestClassProperty"); - REQUIRE( - uint8Property.status() == - PropertyTexturePropertyViewStatus::ErrorInvalidChannels); - } - - SECTION("Zero channel values") { - propertyTextureProperty.channels.clear(); - PropertyTexturePropertyView uint8Property = - view.getPropertyView("TestClassProperty"); - REQUIRE( - uint8Property.status() == - PropertyTexturePropertyViewStatus::ErrorInvalidChannels); - } - - SECTION("Invalid bytes per channel") { - model.images[imageIndex].cesium.bytesPerChannel = 2; - PropertyTexturePropertyView uint8Property = - view.getPropertyView("TestClassProperty"); - REQUIRE( - uint8Property.status() == - PropertyTexturePropertyViewStatus::ErrorInvalidBytesPerChannel); - } - - SECTION("Empty image") { - model.images[imageIndex].cesium.width = 0; - PropertyTexturePropertyView uint8Property = - view.getPropertyView("TestClassProperty"); - REQUIRE( - uint8Property.status() == - PropertyTexturePropertyViewStatus::ErrorEmptyImage); - } - - SECTION("Wrong image index") { - model.textures[textureIndex].source = 1; - PropertyTexturePropertyView uint8Property = - view.getPropertyView("TestClassProperty"); - REQUIRE( - uint8Property.status() == - PropertyTexturePropertyViewStatus::ErrorInvalidImage); - } - - SECTION("Wrong sampler index") { - model.textures[textureIndex].sampler = 1; - PropertyTexturePropertyView uint8Property = - view.getPropertyView("TestClassProperty"); - REQUIRE( - uint8Property.status() == - PropertyTexturePropertyViewStatus::ErrorInvalidSampler); - } - - SECTION("Wrong texture index") { - propertyTextureProperty.index = 1; - PropertyTexturePropertyView uint8Property = - view.getPropertyView("TestClassProperty"); - REQUIRE( - uint8Property.status() == - PropertyTexturePropertyViewStatus::ErrorInvalidTexture); - } } TEST_CASE("Test vecN PropertyTextureProperty") { @@ -554,12 +480,8 @@ TEST_CASE("Test vecN PropertyTextureProperty") { glm::u8vec2(30, 11)}; for (size_t i = 0; i < texCoords.size(); ++i) { glm::dvec2 uv = texCoords[i]; - auto value = u8vec2Property.getRaw(uv[0], uv[1]); - REQUIRE(value == expected[i]); - - auto maybeValue = u8vec2Property.get(uv[0], uv[1]); - REQUIRE(maybeValue); - REQUIRE(*maybeValue == expected[i]); + REQUIRE(u8vec2Property.getRaw(uv[0], uv[1]) == expected[i]); + REQUIRE(u8vec2Property.get(uv[0], uv[1]) == expected[i]); } } @@ -605,7 +527,7 @@ TEST_CASE("Test vecN PropertyTextureProperty") { view.getPropertyView("TestClassProperty"); REQUIRE( normalizedInvalid.status() == - PropertyTexturePropertyViewStatus::ErrorInvalidNormalization); + PropertyTexturePropertyViewStatus::ErrorNormalizationMismatch); } SECTION("Channel and type mismatch") { @@ -702,12 +624,8 @@ TEST_CASE("Test vecN PropertyTextureProperty (normalized)") { glm::u8vec2(30, 11)}; for (size_t i = 0; i < texCoords.size(); ++i) { glm::dvec2 uv = texCoords[i]; - auto value = u8vec2Property.getRaw(uv[0], uv[1]); - REQUIRE(value == expected[i]); - - auto maybeValue = u8vec2Property.get(uv[0], uv[1]); - REQUIRE(maybeValue); - REQUIRE(*maybeValue == normalize(expected[i])); + REQUIRE(u8vec2Property.getRaw(uv[0], uv[1]) == expected[i]); + REQUIRE(u8vec2Property.get(uv[0], uv[1]) == normalize(expected[i])); } } @@ -754,7 +672,7 @@ TEST_CASE("Test vecN PropertyTextureProperty (normalized)") { view.getPropertyView("TestClassProperty"); REQUIRE( normalizedInvalid.status() == - PropertyTexturePropertyViewStatus::ErrorInvalidNormalization); + PropertyTexturePropertyViewStatus::ErrorNormalizationMismatch); } SECTION("Access incorrectly as dvec2") { @@ -774,24 +692,6 @@ TEST_CASE("Test vecN PropertyTextureProperty (normalized)") { u8vec2Property.status() == PropertyTexturePropertyViewStatus::ErrorChannelsAndTypeMismatch); } - - SECTION("Invalid channel values") { - propertyTextureProperty.channels = {0, 4}; - PropertyTexturePropertyView u8vec2Property = - view.getPropertyView("TestClassProperty"); - REQUIRE( - u8vec2Property.status() == - PropertyTexturePropertyViewStatus::ErrorInvalidChannels); - } - - SECTION("Invalid bytes per channel") { - model.images[imageIndex].cesium.bytesPerChannel = 2; - PropertyTexturePropertyView u8vec2Property = - view.getPropertyView("TestClassProperty"); - REQUIRE( - u8vec2Property.status() == - PropertyTexturePropertyViewStatus::ErrorInvalidBytesPerChannel); - } } TEST_CASE("Test array PropertyTextureProperty") { @@ -919,7 +819,7 @@ TEST_CASE("Test array PropertyTextureProperty") { "TestClassProperty"); REQUIRE( normalizedInvalid.status() == - PropertyTexturePropertyViewStatus::ErrorInvalidNormalization); + PropertyTexturePropertyViewStatus::ErrorNormalizationMismatch); } SECTION("Channel and type mismatch") { @@ -1080,7 +980,7 @@ TEST_CASE("Test array PropertyTextureProperty (normalized)") { view.getPropertyView>("TestClassProperty"); REQUIRE( normalizedInvalid.status() == - PropertyTexturePropertyViewStatus::ErrorInvalidNormalization); + PropertyTexturePropertyViewStatus::ErrorNormalizationMismatch); } SECTION("Channel and type mismatch") { @@ -1094,28 +994,6 @@ TEST_CASE("Test array PropertyTextureProperty (normalized)") { uint8ArrayProperty.status() == PropertyTexturePropertyViewStatus::ErrorChannelsAndTypeMismatch); } - - SECTION("Invalid channel values") { - propertyTextureProperty.channels = {0, 4, 1}; - PropertyTexturePropertyView, true> - uint8ArrayProperty = - view.getPropertyView, true>( - "TestClassProperty"); - REQUIRE( - uint8ArrayProperty.status() == - PropertyTexturePropertyViewStatus::ErrorInvalidChannels); - } - - SECTION("Invalid bytes per channel") { - model.images[imageIndex].cesium.bytesPerChannel = 2; - PropertyTexturePropertyView, true> - uint8ArrayProperty = - view.getPropertyView, true>( - "TestClassProperty"); - REQUIRE( - uint8ArrayProperty.status() == - PropertyTexturePropertyViewStatus::ErrorInvalidBytesPerChannel); - } } TEST_CASE("Test with PropertyTextureProperty offset, scale, min, max") { @@ -1194,17 +1072,13 @@ TEST_CASE("Test with PropertyTextureProperty offset, scale, min, max") { PropertyTexturePropertyView property = view.getPropertyView("TestClassProperty"); REQUIRE(property.status() == PropertyTexturePropertyViewStatus::Valid); - REQUIRE(property.offset()); - REQUIRE(*property.offset() == offset); - REQUIRE(property.scale()); - REQUIRE(*property.scale() == scale); - REQUIRE(property.min()); - REQUIRE(*property.min() == min); - REQUIRE(property.max()); - REQUIRE(*property.max() == max); + REQUIRE(property.offset() == offset); + REQUIRE(property.scale() == scale); + REQUIRE(property.min() == min); + REQUIRE(property.max() == max); std::vector expectedRaw(expectedUint.size()); - std::vector> expectedTransformed(expectedUint.size()); + std::vector expectedTransformed(expectedUint.size()); for (size_t i = 0; i < expectedUint.size(); i++) { float value = *reinterpret_cast(&expectedUint[i]); expectedRaw[i] = value; @@ -1213,11 +1087,8 @@ TEST_CASE("Test with PropertyTextureProperty offset, scale, min, max") { for (size_t i = 0; i < texCoords.size(); ++i) { glm::dvec2 uv = texCoords[i]; - auto value = property.getRaw(uv[0], uv[1]); - REQUIRE(value == expectedRaw[i]); - - auto maybeValue = property.get(uv[0], uv[1]); - REQUIRE(maybeValue == expectedTransformed[i]); + REQUIRE(property.getRaw(uv[0], uv[1]) == expectedRaw[i]); + REQUIRE(property.get(uv[0], uv[1]) == expectedTransformed[i]); } } @@ -1234,17 +1105,13 @@ TEST_CASE("Test with PropertyTextureProperty offset, scale, min, max") { PropertyTexturePropertyView property = view.getPropertyView("TestClassProperty"); REQUIRE(property.status() == PropertyTexturePropertyViewStatus::Valid); - REQUIRE(property.offset()); - REQUIRE(*property.offset() == newOffset); - REQUIRE(property.scale()); - REQUIRE(*property.scale() == newScale); - REQUIRE(property.min()); - REQUIRE(*property.min() == newMin); - REQUIRE(property.max()); - REQUIRE(*property.max() == newMax); + REQUIRE(property.offset() == newOffset); + REQUIRE(property.scale() == newScale); + REQUIRE(property.min() == newMin); + REQUIRE(property.max() == newMax); std::vector expectedRaw(expectedUint.size()); - std::vector> expectedTransformed(expectedUint.size()); + std::vector expectedTransformed(expectedUint.size()); for (size_t i = 0; i < expectedUint.size(); i++) { float value = *reinterpret_cast(&expectedUint[i]); expectedRaw[i] = value; @@ -1253,11 +1120,8 @@ TEST_CASE("Test with PropertyTextureProperty offset, scale, min, max") { for (size_t i = 0; i < texCoords.size(); ++i) { glm::dvec2 uv = texCoords[i]; - auto value = property.getRaw(uv[0], uv[1]); - REQUIRE(value == expectedRaw[i]); - - auto maybeValue = property.get(uv[0], uv[1]); - REQUIRE(maybeValue == expectedTransformed[i]); + REQUIRE(property.getRaw(uv[0], uv[1]) == expectedRaw[i]); + REQUIRE(property.get(uv[0], uv[1]) == expectedTransformed[i]); } } } @@ -1327,23 +1191,16 @@ TEST_CASE( PropertyTexturePropertyView property = view.getPropertyView("TestClassProperty"); REQUIRE(property.status() == PropertyTexturePropertyViewStatus::Valid); - REQUIRE(property.offset()); - REQUIRE(*property.offset() == offset); - REQUIRE(property.scale()); - REQUIRE(*property.scale() == scale); - REQUIRE(property.min()); - REQUIRE(*property.min() == min); - REQUIRE(property.max()); - REQUIRE(*property.max() == max); + REQUIRE(property.offset() == offset); + REQUIRE(property.scale() == scale); + REQUIRE(property.min() == min); + REQUIRE(property.max() == max); for (size_t i = 0; i < texCoords.size(); ++i) { glm::dvec2 uv = texCoords[i]; - auto value = property.getRaw(uv[0], uv[1]); - REQUIRE(value == data[i]); - - auto maybeValue = property.get(uv[0], uv[1]); - REQUIRE(maybeValue); - REQUIRE(*maybeValue == normalize(data[i]) * scale + offset); + REQUIRE(property.getRaw(uv[0], uv[1]) == data[i]); + REQUIRE( + property.get(uv[0], uv[1]) == normalize(data[i]) * scale + offset); } } @@ -1360,23 +1217,17 @@ TEST_CASE( PropertyTexturePropertyView property = view.getPropertyView("TestClassProperty"); REQUIRE(property.status() == PropertyTexturePropertyViewStatus::Valid); - REQUIRE(property.offset()); - REQUIRE(*property.offset() == newOffset); - REQUIRE(property.scale()); - REQUIRE(*property.scale() == newScale); - REQUIRE(property.min()); - REQUIRE(*property.min() == newMin); - REQUIRE(property.max()); - REQUIRE(*property.max() == newMax); + REQUIRE(property.offset() == newOffset); + REQUIRE(property.scale() == newScale); + REQUIRE(property.min() == newMin); + REQUIRE(property.max() == newMax); for (size_t i = 0; i < texCoords.size(); ++i) { glm::dvec2 uv = texCoords[i]; - auto value = property.getRaw(uv[0], uv[1]); - REQUIRE(value == data[i]); - - auto maybeValue = property.get(uv[0], uv[1]); - REQUIRE(maybeValue); - REQUIRE(*maybeValue == normalize(data[i]) * newScale + newOffset); + REQUIRE(property.getRaw(uv[0], uv[1]) == data[i]); + REQUIRE( + property.get(uv[0], uv[1]) == + normalize(data[i]) * newScale + newOffset); } } } @@ -1447,8 +1298,7 @@ TEST_CASE("Test with PropertyTextureProperty noData") { if (value == noData) { REQUIRE(!maybeValue); } else { - REQUIRE(maybeValue); - REQUIRE(*maybeValue == data[i]); + REQUIRE(maybeValue == data[i]); } } } @@ -1467,11 +1317,10 @@ TEST_CASE("Test with PropertyTextureProperty noData") { REQUIRE(value == data[i]); auto maybeValue = property.get(uv[0], uv[1]); - REQUIRE(maybeValue); if (value == noData) { - REQUIRE(*maybeValue == defaultValue); + REQUIRE(maybeValue == defaultValue); } else { - REQUIRE(*maybeValue == data[i]); + REQUIRE(maybeValue == data[i]); } } } @@ -1544,8 +1393,7 @@ TEST_CASE("Test with PropertyTextureProperty noData (normalized)") { if (value == noData) { REQUIRE(!maybeValue); } else { - REQUIRE(maybeValue); - REQUIRE(*maybeValue == normalize(data[i])); + REQUIRE(maybeValue == normalize(data[i])); } } } @@ -1564,11 +1412,10 @@ TEST_CASE("Test with PropertyTextureProperty noData (normalized)") { REQUIRE(value == data[i]); auto maybeValue = property.get(uv[0], uv[1]); - REQUIRE(maybeValue); if (value == noData) { - REQUIRE(*maybeValue == defaultValue); + REQUIRE(maybeValue == defaultValue); } else { - REQUIRE(*maybeValue == normalize(data[i])); + REQUIRE(maybeValue == normalize(data[i])); } } } @@ -1651,6 +1498,49 @@ TEST_CASE("Test callback on invalid PropertyTextureProperty") { REQUIRE(invokedCallbackCount == 2); } +TEST_CASE("Test callback on invalid normalized PropertyTextureProperty") { + Model model; + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::FLOAT32; + testClassProperty.normalized = true; // This is erroneous. + + PropertyTexture& propertyTexture = metadata.propertyTextures.emplace_back(); + propertyTexture.classProperty = "TestClass"; + + PropertyTextureProperty& propertyTextureProperty = + propertyTexture.properties["TestClassProperty"]; + propertyTextureProperty.index = static_cast(0); + propertyTextureProperty.texCoord = 0; + propertyTextureProperty.channels = {0, 1}; + + PropertyTextureView view(model, propertyTexture); + REQUIRE(view.status() == PropertyTextureViewStatus::Valid); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + + uint32_t invokedCallbackCount = 0; + auto testCallback = [&invokedCallbackCount]( + const std::string& /*propertyName*/, + auto propertyValue) mutable { + invokedCallbackCount++; + REQUIRE( + propertyValue.status() == + PropertyTexturePropertyViewStatus::ErrorInvalidNormalization); + }; + + view.getPropertyView("TestClassProperty", testCallback); + + REQUIRE(invokedCallbackCount == 1); +} + TEST_CASE("Test callback for scalar PropertyTextureProperty") { Model model; std::vector data = {255, 255, 12, 1, 30, 2, 0, 255}; @@ -1718,12 +1608,8 @@ TEST_CASE("Test callback for scalar PropertyTextureProperty") { for (size_t i = 0; i < expected.size(); ++i) { glm::dvec2& uv = texCoords[i]; - auto value = propertyValue.getRaw(uv[0], uv[1]); - REQUIRE(value == expected[i]); - - auto maybeValue = propertyValue.get(uv[0], uv[1]); - REQUIRE(maybeValue); - REQUIRE(*maybeValue == expected[i]); + REQUIRE(propertyValue.getRaw(uv[0], uv[1]) == expected[i]); + REQUIRE(propertyValue.get(uv[0], uv[1]) == expected[i]); } } else { FAIL("getPropertyView returned PropertyTexturePropertyView of " @@ -1802,12 +1688,8 @@ TEST_CASE("Test callback for scalar PropertyTextureProperty (normalized)") { for (size_t i = 0; i < expected.size(); ++i) { glm::dvec2& uv = texCoords[i]; - auto value = propertyValue.getRaw(uv[0], uv[1]); - REQUIRE(value == expected[i]); - - auto maybeValue = propertyValue.get(uv[0], uv[1]); - REQUIRE(maybeValue); - REQUIRE(*maybeValue == normalize(expected[i])); + REQUIRE(propertyValue.getRaw(uv[0], uv[1]) == expected[i]); + REQUIRE(propertyValue.get(uv[0], uv[1]) == normalize(expected[i])); } } else { FAIL("getPropertyView returned PropertyTexturePropertyView of " @@ -1896,12 +1778,8 @@ TEST_CASE("Test callback for vecN PropertyTextureProperty") { for (size_t i = 0; i < expected.size(); ++i) { glm::dvec2& uv = texCoords[i]; - auto value = propertyValue.getRaw(uv[0], uv[1]); - REQUIRE(value == expected[i]); - - auto maybeValue = propertyValue.get(uv[0], uv[1]); - REQUIRE(maybeValue); - REQUIRE(*maybeValue == expected[i]); + REQUIRE(propertyValue.getRaw(uv[0], uv[1]) == expected[i]); + REQUIRE(propertyValue.get(uv[0], uv[1]) == expected[i]); } } else { FAIL("getPropertyView returned PropertyTexturePropertyView of " @@ -1991,12 +1869,8 @@ TEST_CASE("Test callback for vecN PropertyTextureProperty (normalized)") { for (size_t i = 0; i < expected.size(); ++i) { glm::dvec2& uv = texCoords[i]; - auto value = propertyValue.getRaw(uv[0], uv[1]); - REQUIRE(value == expected[i]); - - auto maybeValue = propertyValue.get(uv[0], uv[1]); - REQUIRE(maybeValue); - REQUIRE(*maybeValue == normalize(expected[i])); + REQUIRE(propertyValue.getRaw(uv[0], uv[1]) == expected[i]); + REQUIRE(propertyValue.get(uv[0], uv[1]) == normalize(expected[i])); } } else { FAIL("getPropertyView returned PropertyTexturePropertyView of " From 2c97ce24bad5af49a56f5167720afb06d4091cd5 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Fri, 25 Aug 2023 17:15:26 -0400 Subject: [PATCH 225/421] Second unit test / cleanup sweep --- CHANGES.md | 10 +- .../CesiumGltf/PropertyTexturePropertyView.h | 12 +- .../include/CesiumGltf/PropertyTextureView.h | 9 +- .../test/TestPropertyTablePropertyView.cpp | 379 +++++++++--------- .../test/TestPropertyTexturePropertyView.cpp | 89 ++-- 5 files changed, 240 insertions(+), 259 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index e21ada456..e4b438c46 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -10,20 +10,22 @@ - In `CesiumGltf`, all generated classes related to `EXT_structural_metadata` have had their `ExtensionExtStructuralMetadata` prefix removed. For example, `ExtensionExtStructuralMetadataClassProperty` has become `ClassProperty`. This also extends to the glTF reader and writer. - In `CesiumGltf`, `ExtensionExtMeshFeaturesFeatureId` and `ExtensionExtMeshFeaturesFeatureIdTexture` have been renamed to `FeatureId` and `FeatureIdTexture` respectively. - Replaced `FeatureIDTextureView` with `FeatureIdTextureView`, which views a `FeatureIdTexture` in `EXT_mesh_features`. Feature ID textures from `EXT_feature_metadata` are no longer supported. -- Renamed `FeatureIDTextureViewStatus` to `FeatureIdTextureViewStatus` for consistency. - Replaced `MetadataFeatureTableView` with `PropertyTableView`, which views a `PropertyTable` in `EXT_structural_metadata`. - Replaced `MetadataPropertyView` with `PropertyTablePropertyView`, which is a view of a `PropertyTableProperty` in `EXT_structural_metadata`. This takes two template parameters: a typename `T` , and a `bool` indicating whether or not the values are normalized. -- Renamed `MetadataArrayView` to `PropertyArrayView`. +- Replaced `MetadataPropertyViewStatus` with `PropertyTablePropertyViewStatus`. `PropertyTablePropertyViewStatus` is a class that inherits from `PropertyViewStatus`, defining additional error codes in the form of `static const` values. - Replaced `FeatureTextureView` with `PropertyTextureView`, which views a `PropertyTexture` in `EXT_structural_metadata`. -- Renamed `FeatureTextureViewStatus` to `PropertyTextureViewStatus`. - Replaced `FeatureTexturePropertyView` with `PropertyTexturePropertyView`, which is a view of a `PropertyTextureProperty` in `EXT_structural_metadata`. This takes two template parameters: a typename `T` , and a `bool` indicating whether or not the values are normalized. - Removed `FeatureTexturePropertyComponentType`, `FeatureTexturePropertyChannelOffsets`, and `FeatureTexturePropertyValue`. `PropertyTextureProperty` retrieves the values with the type indicated by its class property. -- Renamed `FeatureTexturePropertyViewStatus` to `PropertyTexturePropertyViewStatus`. +- Replaced `FeatureTexturePropertyViewStatus` with `PropertyTexturePropertyViewStatus`. `PropertyTexturePropertyViewStatus` is a class that inherits from `PropertyViewStatus`, defining additional error codes in the form of `static const` values. +- Renamed `FeatureIDTextureViewStatus` to `FeatureIdTextureViewStatus` for consistency. +- Renamed `MetadataArrayView` to `PropertyArrayView`. +- Renamed `FeatureTextureViewStatus` to `PropertyTextureViewStatus`. - Refactored `PropertyType` to reflect the values of `type` in a `ClassProperty` from `EXT_structural_metadata`. ##### Additions :tada: - Added `PropertyView`, which acts as a base class for all metadata property views. This takes two template parameters: a type `T` , and a `bool` indicating whether or not the values are normalized. +- Added `PropertyViewStatus`, which defines public `static const` values for various property errors. - Added `PropertyTableViewStatus` to indicate whether a `PropertyTableView` is valid. - Added `PropertyComponentType` to reflect the values of `componentType` in a `ClassProperty` from `EXT_structural_metadata`. diff --git a/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h index 6b191caff..8d4933068 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h @@ -131,7 +131,7 @@ ElementType assembleVecNValue(const std::vector& bytes) noexcept { const glm::length_t N = getDimensionsFromPropertyType(TypeToPropertyType::value); - using typename T = ElementType::value_type; + using T = typename ElementType::value_type; assert( sizeof(T) <= 2 && "Components cannot be larger than two bytes in size."); @@ -278,7 +278,7 @@ class PropertyTexturePropertyView _channels(), _swizzle() { assert( - _status != PropertyTexturePropertyViewStatus::Valid && + this->_status != PropertyTexturePropertyViewStatus::Valid && "An empty property view should not be constructed with a valid status"); } @@ -380,7 +380,7 @@ class PropertyTexturePropertyView ElementType getRaw(double u, double v) const noexcept { assert( - _status == PropertyTexturePropertyViewStatus::Valid && + this->_status == PropertyTexturePropertyViewStatus::Valid && "Check the status() first to make sure view is valid"); double wrappedU = applySamplerWrapS(u, this->_pSampler->wrapS); @@ -498,7 +498,7 @@ class PropertyTexturePropertyView _channels(), _swizzle() { assert( - _status != PropertyTexturePropertyViewStatus::Valid && + this->_status != PropertyTexturePropertyViewStatus::Valid && "An empty property view should not be constructed with a valid status"); } @@ -569,7 +569,7 @@ class PropertyTexturePropertyView std::optional get(double u, double v) const noexcept { ElementType value = getRaw(u, v); - if (this->noData() && value == *(this->noData())) { + if (value == this->noData()) { return this->defaultValue(); } @@ -626,7 +626,7 @@ class PropertyTexturePropertyView ElementType getRaw(double u, double v) const noexcept { assert( - _status == PropertyTexturePropertyViewStatus::Valid && + this->_status == PropertyTexturePropertyViewStatus::Valid && "Check the status() first to make sure view is valid"); double wrappedU = applySamplerWrapS(u, this->_pSampler->wrapS); diff --git a/CesiumGltf/include/CesiumGltf/PropertyTextureView.h b/CesiumGltf/include/CesiumGltf/PropertyTextureView.h index 5f2d4e98d..8b13e84b2 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTextureView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTextureView.h @@ -524,7 +524,8 @@ class PropertyTextureView { template PropertyTexturePropertyView createScalarPropertyView( const ClassProperty& classProperty, - const PropertyTextureProperty& propertyTextureProperty) const { + [[maybe_unused]] const PropertyTextureProperty& propertyTextureProperty) + const { if (classProperty.array) { return PropertyTexturePropertyView( PropertyTexturePropertyViewStatus::ErrorArrayTypeMismatch); @@ -564,7 +565,8 @@ class PropertyTextureView { template PropertyTexturePropertyView createVecNPropertyView( const ClassProperty& classProperty, - const PropertyTextureProperty& propertyTextureProperty) const { + [[maybe_unused]] const PropertyTextureProperty& propertyTextureProperty) + const { if (classProperty.array) { return PropertyTexturePropertyView( PropertyTexturePropertyViewStatus::ErrorArrayTypeMismatch); @@ -605,7 +607,8 @@ class PropertyTextureView { PropertyTexturePropertyView, Normalized> createArrayPropertyView( const ClassProperty& classProperty, - const PropertyTextureProperty& propertyTextureProperty) const { + [[maybe_unused]] const PropertyTextureProperty& propertyTextureProperty) + const { if (!classProperty.array) { return PropertyTexturePropertyView, Normalized>( PropertyTexturePropertyViewStatus::ErrorArrayTypeMismatch); diff --git a/CesiumGltf/test/TestPropertyTablePropertyView.cpp b/CesiumGltf/test/TestPropertyTablePropertyView.cpp index d6fb63878..5149eb536 100644 --- a/CesiumGltf/test/TestPropertyTablePropertyView.cpp +++ b/CesiumGltf/test/TestPropertyTablePropertyView.cpp @@ -47,8 +47,7 @@ template static void checkNumeric(const std::vector& expected) { for (int64_t i = 0; i < property.size(); ++i) { REQUIRE(property.getRaw(i) == expected[static_cast(i)]); - REQUIRE(property.get(i)); - REQUIRE(*property.get(i) == property.getRaw(i)); + REQUIRE(property.get(i) == property.getRaw(i)); } } @@ -90,8 +89,7 @@ static void checkNumeric( for (int64_t i = 0; i < property.size(); ++i) { REQUIRE(property.getRaw(i) == values[static_cast(i)]); if constexpr (IsMetadataFloating::value) { - REQUIRE(property.get(i)); - REQUIRE(*property.get(i) == Approx(*expected[static_cast(i)])); + REQUIRE(property.get(i) == Approx(*expected[static_cast(i)])); } else { REQUIRE(property.get(i) == expected[static_cast(i)]); } @@ -192,6 +190,12 @@ static void checkVariableLengthArray( REQUIRE(values[j] == data[expectedIdx]); ++expectedIdx; } + + auto maybeValues = property.get(i); + REQUIRE(maybeValues); + for (int64_t j = 0; j < values.size(); ++j) { + REQUIRE((*maybeValues)[j] == values[j]); + } } REQUIRE(expectedIdx == data.size()); @@ -398,10 +402,17 @@ static void checkFixedLengthArray( REQUIRE(values[j] == data[expectedIdx]); ++expectedIdx; } + + auto maybeValues = property.get(i); + REQUIRE(maybeValues); + for (int64_t j = 0; j < values.size(); ++j) { + REQUIRE((*maybeValues)[j] == values[j]); + } } REQUIRE(expectedIdx == data.size()); } + template static void checkFixedLengthArrayWithProperties( const std::vector& data, @@ -597,16 +608,16 @@ TEST_CASE("Check scalar PropertyTablePropertyView") { SECTION("Float with Offset / Scale") { std::vector values{12.5f, -12.5f, -5.0f, 6.75f}; - std::optional offset = 1.0f; - std::optional scale = 2.0f; + const float offset = 1.0f; + const float scale = 2.0f; std::vector> expected{26.0f, -24.0f, -9.0f, 14.5f}; checkNumeric(values, expected, offset, scale); } SECTION("Normalized Uint8 with Offset and Scale") { std::vector values{0, 64, 128, 255}; - std::optional offset = 1.0; - std::optional scale = 2.0; + const double offset = 1.0; + const double scale = 2.0; std::vector> expected{ 1.0, 1 + 2 * (64.0 / 255.0), @@ -617,7 +628,7 @@ TEST_CASE("Check scalar PropertyTablePropertyView") { SECTION("Int16 with NoData") { std::vector values{-1, 3, 7, -1}; - std::optional noData = -1; + const int16_t noData = -1; std::vector> expected{ std::nullopt, static_cast(3), @@ -634,8 +645,8 @@ TEST_CASE("Check scalar PropertyTablePropertyView") { SECTION("Int16 with NoData and Default") { std::vector values{-1, 3, 7, -1}; - std::optional noData = -1; - std::optional defaultValue = 0; + const int16_t noData = -1; + const int16_t defaultValue = 0; std::vector> expected{ static_cast(0), static_cast(3), @@ -652,10 +663,10 @@ TEST_CASE("Check scalar PropertyTablePropertyView") { SECTION("Normalized Uint8 with all properties") { std::vector values{0, 64, 128, 255}; - std::optional offset = 1.0; - std::optional scale = 2.0; - std::optional noData = 0; - std::optional defaultValue = 10.0; + const double offset = 1.0; + const double scale = 2.0; + const uint8_t noData = 0; + const double defaultValue = 10.0; std::vector> expected{ 10.0, 1 + 2 * (64.0 / 255.0), @@ -696,22 +707,15 @@ TEST_CASE("Check scalar PropertyTablePropertyView") { static_cast(values.size()), gsl::span(data.data(), data.size())); - REQUIRE(property.offset()); - REQUIRE(*property.offset() == 1.0f); - REQUIRE(property.scale()); - REQUIRE(*property.scale() == 2.0f); - REQUIRE(property.min()); - REQUIRE(*property.min() == 3.0f); - REQUIRE(property.max()); - REQUIRE(*property.max() == 9.0f); + REQUIRE(property.offset() == 1.0f); + REQUIRE(property.scale() == 2.0f); + REQUIRE(property.min() == 3.0f); + REQUIRE(property.max() == 9.0f); std::vector expected{3.0, 7.0f, 5.0f, 9.0f}; for (int64_t i = 0; i < property.size(); ++i) { REQUIRE(property.getRaw(i) == values[static_cast(i)]); - - std::optional transformedValue = property.get(i); - REQUIRE(transformedValue); - REQUIRE(*transformedValue == Approx(expected[static_cast(i)])); + REQUIRE(property.get(i) == Approx(expected[static_cast(i)])); } } } @@ -775,8 +779,8 @@ TEST_CASE("Check vecN PropertyTablePropertyView") { glm::vec3(6.5f, 2.0f, 4.0f), glm::vec3(8.0f, -3.0f, 1.0f), }; - std::optional offset = JsonValue::Array{1.0f, 2.0f, 3.0f}; - std::optional scale = JsonValue::Array{2.0f, 1.0f, 2.0f}; + JsonValue::Array offset{1.0f, 2.0f, 3.0f}; + JsonValue::Array scale{2.0f, 1.0f, 2.0f}; std::vector> expected{ glm::vec3(1.0f, 0.5f, -7.0f), glm::vec3(14.0f, 4.0f, 11.0f), @@ -790,8 +794,8 @@ TEST_CASE("Check vecN PropertyTablePropertyView") { glm::u8vec2(0, 64), glm::u8vec2(128, 255), glm::u8vec2(255, 0)}; - std::optional offset = JsonValue::Array{0.0, 1.0}; - std::optional scale = JsonValue::Array{2.0, 1.0}; + JsonValue::Array offset{0.0, 1.0}; + JsonValue::Array scale{2.0, 1.0}; std::vector> expected{ glm::dvec2(0.0, 1 + (64.0 / 255.0)), glm::dvec2(2 * (128.0 / 255.0), 2.0), @@ -804,7 +808,7 @@ TEST_CASE("Check vecN PropertyTablePropertyView") { glm::i16vec2(-1, 3), glm::i16vec2(-1, -1), glm::i16vec2(7, 0)}; - std::optional noData = JsonValue::Array{-1, -1}; + JsonValue::Array noData{-1, -1}; std::vector> expected{ glm::i16vec2(-1, 3), std::nullopt, @@ -823,8 +827,8 @@ TEST_CASE("Check vecN PropertyTablePropertyView") { glm::i16vec2(-1, 3), glm::i16vec2(-1, -1), glm::i16vec2(7, 0)}; - std::optional noData = JsonValue::Array{-1, -1}; - std::optional defaultValue = JsonValue::Array{0, 1}; + JsonValue::Array noData{-1, -1}; + JsonValue::Array defaultValue{0, 1}; std::vector> expected{ glm::i16vec2(-1, 3), glm::i16vec2(0, 1), @@ -844,10 +848,10 @@ TEST_CASE("Check vecN PropertyTablePropertyView") { glm::u8vec2(128, 255), glm::u8vec2(255, 0), glm::u8vec2(0, 0)}; - std::optional offset = JsonValue::Array{0.0, 1.0}; - std::optional scale = JsonValue::Array{2.0, 1.0}; - std::optional noData = JsonValue::Array{0, 0}; - std::optional defaultValue = JsonValue::Array{5.0, 15.0}; + JsonValue::Array offset{0.0, 1.0}; + JsonValue::Array scale{2.0, 1.0}; + JsonValue::Array noData{0, 0}; + JsonValue::Array defaultValue{5.0, 15.0}; std::vector> expected{ glm::dvec2(0.0, 1 + (64.0 / 255.0)), glm::dvec2(2 * (128.0 / 255.0), 2.0), @@ -891,14 +895,10 @@ TEST_CASE("Check vecN PropertyTablePropertyView") { static_cast(values.size()), gsl::span(data.data(), data.size())); - REQUIRE(property.offset()); - REQUIRE(*property.offset() == glm::vec2(1.0f, 0.5f)); - REQUIRE(property.scale()); - REQUIRE(*property.scale() == glm::vec2(2.0f, 1.0f)); - REQUIRE(property.min()); - REQUIRE(*property.min() == glm::vec2(3.0f, 3.0f)); - REQUIRE(property.max()); - REQUIRE(*property.max() == glm::vec2(6.0f, 4.5f)); + REQUIRE(property.offset() == glm::vec2(1.0f, 0.5f)); + REQUIRE(property.scale() == glm::vec2(2.0f, 1.0f)); + REQUIRE(property.min() == glm::vec2(3.0f, 3.0f)); + REQUIRE(property.max() == glm::vec2(6.0f, 4.5f)); std::vector expected{ glm::vec2(3.0f, 3.5f), @@ -906,10 +906,7 @@ TEST_CASE("Check vecN PropertyTablePropertyView") { glm::vec2(5.0f, 4.5f)}; for (int64_t i = 0; i < property.size(); ++i) { REQUIRE(property.getRaw(i) == values[static_cast(i)]); - - std::optional transformedValue = property.get(i); - REQUIRE(transformedValue); - REQUIRE(*transformedValue == expected[static_cast(i)]); + REQUIRE(property.get(i) == expected[static_cast(i)]); } } } @@ -1029,10 +1026,10 @@ TEST_CASE("Check matN PropertyTablePropertyView") { 8.0f, -1.0f, -3.0f, 1.0f), }; - std::optional offset = JsonValue::Array{ + JsonValue::Array offset{ 1.0f, 2.0f, 3.0f, 1.0f}; - std::optional scale = JsonValue::Array{ + JsonValue::Array scale { 2.0f, 0.0f, 0.0f, 2.0f}; std::vector> expected{ @@ -1059,10 +1056,10 @@ TEST_CASE("Check matN PropertyTablePropertyView") { glm::u8mat2x2( 255, 0, 128, 0)}; - std::optional offset = JsonValue::Array{ + JsonValue::Array offset{ 0.0, 1.0, 1.0, 0.0}; - std::optional scale = JsonValue::Array{ + JsonValue::Array scale{ 2.0, 1.0, 0.0, 2.0}; std::vector> expected{ @@ -1091,8 +1088,7 @@ TEST_CASE("Check matN PropertyTablePropertyView") { -1, -1, -1, 0, 0, 0, 1, 1, 1)}; - std::optional noData = - JsonValue::Array{ + JsonValue::Array noData{ -1, -1, -1, 0, 0, 0, 1, 1, 1}; @@ -1125,11 +1121,11 @@ TEST_CASE("Check matN PropertyTablePropertyView") { -1, -1, -1, 0, 0, 0, 1, 1, 1)}; - std::optional noData = JsonValue::Array{ + JsonValue::Array noData{ -1, -1, -1, 0, 0, 0, 1, 1, 1}; - std::optional defaultValue = JsonValue::Array{ + JsonValue::Array defaultValue{ 1, 0, 0, 0, 1, 0, 0, 0, 1}; @@ -1157,16 +1153,16 @@ TEST_CASE("Check matN PropertyTablePropertyView") { glm::u8mat2x2( 255, 0, 128, 0)}; - std::optional offset = JsonValue::Array{ + JsonValue::Array offset{ 0.0, 1.0, 1.0, 0.0}; - std::optional scale = JsonValue::Array{ + JsonValue::Array scale{ 2.0, 1.0, 0.0, 2.0}; - std::optional noData = JsonValue::Array{ + JsonValue::Array noData{ 0, 0, 0, 0}; - std::optional defaultValue = JsonValue::Array{ + JsonValue::Array defaultValue{ 1.0, 0.0, 0.0, 1.0}; @@ -1202,20 +1198,36 @@ TEST_CASE("Check matN PropertyTablePropertyView") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::MAT2; classProperty.componentType = ClassProperty::ComponentType::FLOAT32; - // clang-fomrat off - classProperty.offset = {0.0f, 0.0f, 0.0f, 0.0f}; - classProperty.scale = {1.0f, 1.0f, 1.0f, 1.0f}; - classProperty.min = {1.0f, 0.0f, 0.0f, 1.0f}; - classProperty.max = {3.0f, 0.0f, 0.0f, 3.0f}; - // clang-fomrat on + // clang-format off + classProperty.offset = { + 0.0f, 0.0f, + 0.0f, 0.0f}; + classProperty.scale = { + 1.0f, 1.0f, + 1.0f, 1.0f}; + classProperty.min = { + 1.0f, 0.0f, + 0.0f, 1.0f}; + classProperty.max = { + 3.0f, 0.0f, + 0.0f, 3.0f}; + // clang-format on PropertyTableProperty propertyTableProperty; - // clang-fomrat off - propertyTableProperty.offset = {1.0f, 0.5f, 0.5f, 1.0f}; - propertyTableProperty.scale = {2.0f, 1.0f, 0.0f, 1.0f}; - propertyTableProperty.min = {3.0f, 0.5f, 0.5f, 2.0f}; - propertyTableProperty.max = {7.0f, 1.5f, 0.5f, 4.0f}; - // clang-fomrat on + // clang-format off + propertyTableProperty.offset = { + 1.0f, 0.5f, + 0.5f, 1.0f}; + propertyTableProperty.scale = { + 2.0f, 1.0f, + 0.0f, 1.0f}; + propertyTableProperty.min = { + 3.0f, 0.5f, + 0.5f, 2.0f}; + propertyTableProperty.max = { + 7.0f, 1.5f, + 0.5f, 4.0f}; + // clang-format on PropertyTablePropertyView property( propertyTableProperty, @@ -1224,20 +1236,16 @@ TEST_CASE("Check matN PropertyTablePropertyView") { gsl::span(data.data(), data.size())); // clang-format off - REQUIRE(property.offset()); - REQUIRE(*property.offset() == glm::mat2( + REQUIRE(property.offset() == glm::mat2( 1.0f, 0.5f, 0.5f, 1.0f)); - REQUIRE(property.scale()); - REQUIRE(*property.scale() == glm::mat2( + REQUIRE(property.scale() == glm::mat2( 2.0f, 1.0f, 0.0f, 1.0f)); - REQUIRE(property.min()); - REQUIRE(*property.min() == glm::mat2( + REQUIRE(property.min() == glm::mat2( 3.0f, 0.5f, 0.5f, 2.0f)); - REQUIRE(property.max()); - REQUIRE(*property.max() == glm::mat2( + REQUIRE(property.max() == glm::mat2( 7.0f, 1.5f, 0.5f, 4.0f)); @@ -1254,10 +1262,7 @@ TEST_CASE("Check matN PropertyTablePropertyView") { // clang-format on for (int64_t i = 0; i < property.size(); ++i) { REQUIRE(property.getRaw(i) == values[static_cast(i)]); - - std::optional transformedValue = property.get(i); - REQUIRE(transformedValue); - REQUIRE(*transformedValue == expected[static_cast(i)]); + REQUIRE(property.get(i) == expected[static_cast(i)]); } } } @@ -1281,6 +1286,7 @@ TEST_CASE("Check boolean PropertyTablePropertyView") { for (int64_t i = 0; i < property.size(); ++i) { REQUIRE(property.getRaw(i) == bits[static_cast(i)]); + REQUIRE(property.get(i) == property.getRaw(i)); } } @@ -1338,8 +1344,7 @@ TEST_CASE("Check string PropertyTablePropertyView") { for (int64_t i = 0; i < property.size(); ++i) { REQUIRE(property.getRaw(i) == strings[static_cast(i)]); - REQUIRE(property.get(i)); - REQUIRE(*property.get(i) == strings[static_cast(i)]); + REQUIRE(property.get(i) == strings[static_cast(i)]); } } @@ -1516,10 +1521,8 @@ TEST_CASE("Check fixed-length scalar array PropertyTablePropertyView") { }; // clang-format on - std::optional offset = - JsonValue::Array{1.0f, 0.0f, -1.0f, 0.0f}; - std::optional scale = - JsonValue::Array{1.0f, 2.0f, 1.0f, 2.0f}; + JsonValue::Array offset{1.0f, 0.0f, -1.0f, 0.0f}; + JsonValue::Array scale{1.0f, 2.0f, 1.0f, 2.0f}; std::vector>> expected{ std::vector{2.0f, 4.0f, 2.0f, 8.0f}, @@ -1534,7 +1537,7 @@ TEST_CASE("Check fixed-length scalar array PropertyTablePropertyView") { -1, -1, -3, 44}; // clang-format on - std::optional noData = JsonValue::Array{-1, -1}; + JsonValue::Array noData{-1, -1}; std::vector>> expected{ std::vector{122, 12}, std::nullopt, @@ -1556,8 +1559,8 @@ TEST_CASE("Check fixed-length scalar array PropertyTablePropertyView") { -1, -1, -3, 44}; // clang-format on - std::optional noData = JsonValue::Array{-1, -1}; - std::optional defaultValue = JsonValue::Array{0, 1}; + JsonValue::Array noData{-1, -1}; + JsonValue::Array defaultValue{0, 1}; std::vector>> expected{ std::vector{122, 12}, std::vector{0, 1}, @@ -1579,10 +1582,10 @@ TEST_CASE("Check fixed-length scalar array PropertyTablePropertyView") { -64, 127, -128, 0, 0, 0}; // clang-format on - std::optional offset = JsonValue::Array{0, 1, 1}; - std::optional scale = JsonValue::Array{1, -1, 2}; - std::optional noData = JsonValue::Array{0, 0, 0}; - std::optional defaultValue = JsonValue::Array{10, 8, 2}; + JsonValue::Array offset{0, 1, 1}; + JsonValue::Array scale{1, -1, 2}; + JsonValue::Array noData{0, 0, 0}; + JsonValue::Array defaultValue{10, 8, 2}; std::vector>> expected{ std::vector{-1.0, 1.0, 1 + 2 * (64.0 / 127.0)}, @@ -1733,10 +1736,8 @@ TEST_CASE("Check fixed-length vecN array PropertyTablePropertyView") { }; // clang-format on - std::optional offset = - JsonValue::Array{{1.0f, 0.0f}, {-1.0f, 0.0f}}; - std::optional scale = - JsonValue::Array{{1.0f, 2.0f}, {1.0f, 2.0f}}; + JsonValue::Array offset{{1.0f, 0.0f}, {-1.0f, 0.0f}}; + JsonValue::Array scale{{1.0f, 2.0f}, {1.0f, 2.0f}}; std::vector>> expected{ std::vector{glm::vec2(2.0f, 4.0f), glm::vec2(2.0f, 8.0f)}, @@ -1751,7 +1752,7 @@ TEST_CASE("Check fixed-length vecN array PropertyTablePropertyView") { glm::ivec2( -3, 44), glm::ivec2(0, 7), glm::ivec2(-1, -1), glm::ivec2(0, 0)}; // clang-format on - std::optional noData = JsonValue::Array{{-1, -1}, {0, 0}}; + JsonValue::Array noData{{-1, -1}, {0, 0}}; std::vector>> expected{ std::vector{glm::ivec2(122, 12), glm::ivec2(-1, -1)}, std::vector{glm::ivec2(-3, 44), glm::ivec2(0, 7)}, @@ -1773,9 +1774,8 @@ TEST_CASE("Check fixed-length vecN array PropertyTablePropertyView") { glm::ivec2( -3, 44), glm::ivec2(0, 7), glm::ivec2(-1, -1), glm::ivec2(0, 0)}; // clang-format on - std::optional noData = JsonValue::Array{{-1, -1}, {0, 0}}; - std::optional defaultValue = - JsonValue::Array{{1, 1}, {1, 2}}; + JsonValue::Array noData{{-1, -1}, {0, 0}}; + JsonValue::Array defaultValue{{1, 1}, {1, 2}}; std::vector>> expected{ std::vector{glm::ivec2(122, 12), glm::ivec2(-1, -1)}, std::vector{glm::ivec2(-3, 44), glm::ivec2(0, 7)}, @@ -1797,11 +1797,10 @@ TEST_CASE("Check fixed-length vecN array PropertyTablePropertyView") { glm::i8vec2(127, -128), glm::i8vec2(0, 0), glm::i8vec2(0), glm::i8vec2(0)}; // clang-format on - std::optional offset = JsonValue::Array{{0, 1}, {1, 2}}; - std::optional scale = JsonValue::Array{{1, -1}, {2, 1}}; - std::optional noData = JsonValue::Array{{0, 0}, {0, 0}}; - std::optional defaultValue = - JsonValue::Array{{10, 2}, {4, 8}}; + JsonValue::Array offset{{0, 1}, {1, 2}}; + JsonValue::Array scale{{1, -1}, {2, 1}}; + JsonValue::Array noData{{0, 0}, {0, 0}}; + JsonValue::Array defaultValue{{10, 2}, {4, 8}}; std::vector>> expected{ std::vector{ @@ -2039,47 +2038,45 @@ TEST_CASE("Check fixed-length matN array PropertyTablePropertyView") { SECTION("Array of 2 mat2s with offset / scale") { // clang-format off std::vector data{ + glm::mat2( + 1.0f, 2.0f, + 3.0f, 4.0f), + glm::mat2( + 5.0f, -1.0f, + 0.0f, 2.0f), + glm::mat2( + -1.0f, -1.0f, + 0.0f, -2.0f), + glm::mat2( + 0.0f, -2.0f, + 4.0f, 3.0f)}; + + JsonValue::Array offset{ + {1.0f, 0.0f, + 2.0f, 3.0f}, + {-1.0f, 0.0f, + 0.0f, 2.0f}}; + JsonValue::Array scale{ + {1.0f, 2.0f, + 1.0f, 0.0f}, + {1.0f, -1.0f, + -1.0f, 2.0f}}; + + std::vector>> expected{ + std::vector{ glm::mat2( - 1.0f, 2.0f, - 3.0f, 4.0f), - glm::mat2( - 5.0f, -1.0f, - 0.0f, 2.0f), + 2.0f, 4.0f, + 5.0f, 3.0f), glm::mat2( - -1.0f, -1.0f, - 0.0f, -2.0f), + 4.0f, 1.0f, + 0.0f, 6.0f)}, + std::vector{ glm::mat2( 0.0f, -2.0f, - 4.0f, 3.0f)}; - - std::optional offset = - JsonValue::Array{ - {1.0f, 0.0f, - 2.0f, 3.0f}, - {-1.0f, 0.0f, - 0.0f, 2.0f}}; - std::optional scale = - JsonValue::Array{ - {1.0f, 2.0f, - 1.0f, 0.0f}, - {1.0f, -1.0f, - -1.0f, 2.0f}}; - - std::vector>> expected{ - std::vector{ - glm::mat2( - 2.0f, 4.0f, - 5.0f, 3.0f), - glm::mat2( - 4.0f, 1.0f, - 0.0f, 6.0f)}, - std::vector{ - glm::mat2( - 0.0f, -2.0f, - 2.0f, 3.0f), - glm::mat2( - -1.0f, 2.0f, - -4.0f, 8.0f)}}; + 2.0f, 3.0f), + glm::mat2( + -1.0f, 2.0f, + -4.0f, 8.0f)}}; // clang-format on checkFixedLengthArrayWithProperties(data, 2, expected, offset, scale); } @@ -2099,7 +2096,7 @@ TEST_CASE("Check fixed-length matN array PropertyTablePropertyView") { -1, 0), glm::imat2x2(-1), glm::imat2x2(0)}; - std::optional noData = JsonValue::Array{ + JsonValue::Array noData{ {-1, 0, 0, -1}, {0, 0, @@ -2144,12 +2141,12 @@ TEST_CASE("Check fixed-length matN array PropertyTablePropertyView") { -1, 0), glm::imat2x2(-1), glm::imat2x2(0)}; - std::optional noData = JsonValue::Array{ + JsonValue::Array noData{ {-1, 0, 0, -1}, {0, 0, 0, 0}}; - std::optional defaultValue = JsonValue::Array{ + JsonValue::Array defaultValue{ {2, 0, 0, 2}, {1, 0, @@ -2198,22 +2195,22 @@ TEST_CASE("Check fixed-length matN array PropertyTablePropertyView") { glm::i8mat2x2( -128, -128, -128, -128)}; - std::optional offset = JsonValue::Array{ + JsonValue::Array offset{ {0, 1, 2, 3}, {1, 2, 0, -2}}; - std::optional scale = JsonValue::Array{ + JsonValue::Array scale{ {1, -1, 0, 1}, {2, 1, -1, 0}}; - std::optional noData = JsonValue::Array{ + JsonValue::Array noData{ {0, 0, 0, 0}, {-128, -128, -128, -128}}; - std::optional defaultValue = JsonValue::Array{ + JsonValue::Array defaultValue{ {1, 0, 0, 1}, {2, 0, @@ -2491,7 +2488,7 @@ TEST_CASE("Check variable-length scalar array PropertyTablePropertyView") { 11 * sizeof(int32_t), 15 * sizeof(int32_t)}; - std::optional noData = JsonValue::Array{0}; + JsonValue::Array noData{0}; std::vector>> expected{ std::vector{3, 200}, @@ -2526,8 +2523,8 @@ TEST_CASE("Check variable-length scalar array PropertyTablePropertyView") { 11 * sizeof(int32_t), 15 * sizeof(int32_t)}; - std::optional noData = JsonValue::Array{0}; - std::optional defaultValue = JsonValue::Array{1}; + JsonValue::Array noData{0}; + JsonValue::Array defaultValue{1}; std::vector>> expected{ std::vector{3, 200}, @@ -2558,8 +2555,8 @@ TEST_CASE("Check variable-length scalar array PropertyTablePropertyView") { }; // clang-format on - std::optional noData = JsonValue::Array{255, 255}; - std::optional defaultValue = JsonValue::Array{-1.0}; + JsonValue::Array noData{255, 255}; + JsonValue::Array defaultValue{-1.0}; std::vector>> expected{ std::vector{1.0, 0.0}, @@ -2692,8 +2689,8 @@ TEST_CASE("Check variable-length vecN array PropertyTablePropertyView") { 7 * sizeof(glm::ivec3), 9 * sizeof(glm::ivec3)}; - std::optional noData = JsonValue::Array{}; - noData->push_back(JsonValue::Array{-1, -1, -1}); + JsonValue::Array noData{}; + noData.push_back(JsonValue::Array{-1, -1, -1}); std::vector>> expected{ std::vector{glm::ivec3(3, 200, 1), glm::ivec3(2, 4, 6)}, @@ -2731,10 +2728,10 @@ TEST_CASE("Check variable-length vecN array PropertyTablePropertyView") { 7 * sizeof(glm::ivec3), 9 * sizeof(glm::ivec3)}; - std::optional noData = JsonValue::Array{}; - noData->push_back(JsonValue::Array{-1, -1, -1}); - std::optional defaultValue = JsonValue::Array{}; - defaultValue->push_back(JsonValue::Array{0, 0, 0}); + JsonValue::Array noData{}; + noData.push_back(JsonValue::Array{-1, -1, -1}); + JsonValue::Array defaultValue{}; + defaultValue.push_back(JsonValue::Array{0, 0, 0}); std::vector>> expected{ std::vector{glm::ivec3(3, 200, 1), glm::ivec3(2, 4, 6)}, @@ -2769,10 +2766,10 @@ TEST_CASE("Check variable-length vecN array PropertyTablePropertyView") { 3 * sizeof(glm::u8vec2), 6 * sizeof(glm::u8vec2)}; - std::optional noData = JsonValue::Array{}; - noData->push_back(JsonValue::Array{0, 0}); - std::optional defaultValue = JsonValue::Array{}; - defaultValue->push_back(JsonValue::Array{-1.0, -1.0}); + JsonValue::Array noData{}; + noData.push_back(JsonValue::Array{0, 0}); + JsonValue::Array defaultValue{}; + defaultValue.push_back(JsonValue::Array{-1.0, -1.0}); std::vector>> expected{ std::vector{ @@ -2993,8 +2990,8 @@ TEST_CASE("Check variable-length matN array PropertyTablePropertyView") { 9 * sizeof(glm::imat3x3)}; // clang-format off - std::optional noData = JsonValue::Array{}; - noData->push_back(JsonValue::Array{ + JsonValue::Array noData{}; + noData.push_back(JsonValue::Array{ -1, 0, 0, 0, -1, 0, 0, 0, -1}); @@ -3037,13 +3034,13 @@ TEST_CASE("Check variable-length matN array PropertyTablePropertyView") { 9 * sizeof(glm::imat3x3)}; // clang-format off - std::optional noData = JsonValue::Array{}; - noData->push_back(JsonValue::Array{ + JsonValue::Array noData{}; + noData.push_back(JsonValue::Array{ -1, 0, 0, 0, -1, 0, 0, 0, -1}); - std::optional defaultValue = JsonValue::Array{}; - defaultValue->push_back(JsonValue::Array{ + JsonValue::Array defaultValue{}; + defaultValue.push_back(JsonValue::Array{ 99, 0, 0, 0, 99, 0, 0, 0, 99}); @@ -3083,12 +3080,12 @@ TEST_CASE("Check variable-length matN array PropertyTablePropertyView") { 6 * sizeof(glm::u8mat2x2)}; // clang-format off - std::optional noData = JsonValue::Array{}; - noData->push_back(JsonValue::Array{ + JsonValue::Array noData{}; + noData.push_back(JsonValue::Array{ 0, 0, 0, 0}); - std::optional defaultValue = JsonValue::Array{}; - defaultValue->push_back(JsonValue::Array{ + JsonValue::Array defaultValue{}; + defaultValue.push_back(JsonValue::Array{ -1.0, 0.0, 0.0, -1.0}); // clang-format on @@ -3568,6 +3565,16 @@ TEST_CASE("Check fixed-length boolean array PropertyTablePropertyView") { REQUIRE(static_cast(val1[9]) == 1); REQUIRE(static_cast(val1[10]) == 1); REQUIRE(static_cast(val1[11]) == 1); + + for (int64_t i = 0; i < property.size(); i++) { + auto value = property.getRaw(i); + auto maybeValue = property.get(i); + REQUIRE(maybeValue); + REQUIRE(maybeValue->size() == value.size()); + for (int64_t j = 0; j < maybeValue->size(); j++) { + REQUIRE((*maybeValue)[j] == value[j]); + } + } } TEST_CASE("Check variable-length boolean array PropertyTablePropertyView") { @@ -3635,4 +3642,14 @@ TEST_CASE("Check variable-length boolean array PropertyTablePropertyView") { REQUIRE(static_cast(val2[13]) == 1); REQUIRE(static_cast(val2[14]) == 1); REQUIRE(static_cast(val2[15]) == 0); + + for (int64_t i = 0; i < property.size(); i++) { + auto value = property.getRaw(i); + auto maybeValue = property.get(i); + REQUIRE(maybeValue); + REQUIRE(maybeValue->size() == value.size()); + for (int64_t j = 0; j < maybeValue->size(); j++) { + REQUIRE((*maybeValue)[j] == value[j]); + } + } } diff --git a/CesiumGltf/test/TestPropertyTexturePropertyView.cpp b/CesiumGltf/test/TestPropertyTexturePropertyView.cpp index 2b1889a64..2450c926f 100644 --- a/CesiumGltf/test/TestPropertyTexturePropertyView.cpp +++ b/CesiumGltf/test/TestPropertyTexturePropertyView.cpp @@ -70,12 +70,8 @@ void checkTextureValues( for (size_t i = 0; i < texCoords.size(); i++) { glm::dvec2 uv = texCoords[i]; - auto value = view.getRaw(uv[0], uv[1]); - REQUIRE(value == expected[i]); - - auto maybeValue = view.get(uv[0], uv[1]); - REQUIRE(maybeValue); - REQUIRE(*maybeValue == expected[i]); + REQUIRE(view.getRaw(uv[0], uv[1]) == expected[i]); + REQUIRE(view.get(uv[0], uv[1]) == expected[i]); } } @@ -147,11 +143,8 @@ void checkTextureValues( for (size_t i = 0; i < texCoords.size(); i++) { glm::dvec2 uv = texCoords[i]; - auto value = view.getRaw(uv[0], uv[1]); - REQUIRE(value == expectedRaw[i]); - - auto maybeValue = view.get(uv[0], uv[1]); - REQUIRE(maybeValue == expectedTransformed[i]); + REQUIRE(view.getRaw(uv[0], uv[1]) == expectedRaw[i]); + REQUIRE(view.get(uv[0], uv[1]) == expectedTransformed[i]); } } @@ -225,11 +218,8 @@ void checkNormalizedTextureValues( for (size_t i = 0; i < texCoords.size(); i++) { glm::dvec2 uv = texCoords[i]; - auto value = view.getRaw(uv[0], uv[1]); - REQUIRE(value == expectedRaw[i]); - - auto maybeValue = view.get(uv[0], uv[1]); - REQUIRE(maybeValue == expectedTransformed[i]); + REQUIRE(view.getRaw(uv[0], uv[1]) == expectedRaw[i]); + REQUIRE(view.get(uv[0], uv[1]) == expectedTransformed[i]); } } @@ -898,8 +888,8 @@ TEST_CASE("Check vecN PropertyTexturePropertyView") { 0, 0, 182, 1}; // clang-format on - std::optional noData = JsonValue::Array{0, 0}; - std::optional defaultValue = JsonValue::Array{127, 127}; + JsonValue::Array noData{0, 0}; + JsonValue::Array defaultValue{127, 127}; std::vector expectedRaw{ glm::i8vec2(28, -1), glm::i8vec2(-2, 1), @@ -1454,11 +1444,8 @@ TEST_CASE("Check that property values override class property values") { for (size_t i = 0; i < texCoords.size(); i++) { glm::dvec2 uv = texCoords[i]; - auto value = view.getRaw(uv[0], uv[1]); - REQUIRE(value == expectedRaw[i]); - - auto maybeValue = view.get(uv[0], uv[1]); - REQUIRE(maybeValue == expectedTransformed[i]); + REQUIRE(view.getRaw(uv[0], uv[1]) == expectedRaw[i]); + REQUIRE(view.get(uv[0], uv[1]) == expectedTransformed[i]); } } @@ -1503,12 +1490,8 @@ TEST_CASE("Check that non-adjacent channels resolve to expected output") { std::vector expected{3, 4, 0, 1}; for (size_t i = 0; i < expected.size(); i++) { glm::dvec2 uv = texCoords[i]; - auto value = view.getRaw(uv[0], uv[1]); - REQUIRE(value == expected[i]); - - auto maybeValue = view.get(uv[0], uv[1]); - REQUIRE(maybeValue); - REQUIRE(*maybeValue == expected[i]); + REQUIRE(view.getRaw(uv[0], uv[1]) == expected[i]); + REQUIRE(view.get(uv[0], uv[1]) == expected[i]); } } @@ -1545,12 +1528,8 @@ TEST_CASE("Check that non-adjacent channels resolve to expected output") { std::vector expected{2, 259, 257, 520}; for (size_t i = 0; i < expected.size(); i++) { glm::dvec2 uv = texCoords[i]; - auto value = view.getRaw(uv[0], uv[1]); - REQUIRE(value == expected[i]); - - auto maybeValue = view.get(uv[0], uv[1]); - REQUIRE(maybeValue); - REQUIRE(*maybeValue == expected[i]); + REQUIRE(view.getRaw(uv[0], uv[1]) == expected[i]); + REQUIRE(view.get(uv[0], uv[1]) == expected[i]); } } @@ -1591,12 +1570,8 @@ TEST_CASE("Check that non-adjacent channels resolve to expected output") { glm::u8vec3(1, 8, 3)}; for (size_t i = 0; i < expected.size(); i++) { glm::dvec2 uv = texCoords[i]; - auto value = view.getRaw(uv[0], uv[1]); - REQUIRE(value == expected[i]); - - auto maybeValue = view.get(uv[0], uv[1]); - REQUIRE(maybeValue); - REQUIRE(*maybeValue == expected[i]); + REQUIRE(view.getRaw(uv[0], uv[1]) == expected[i]); + REQUIRE(view.get(uv[0], uv[1]) == expected[i]); } } @@ -1691,12 +1666,8 @@ TEST_CASE("Check sampling with different sampler values") { glm::dvec2(1.5, -0.5)}; for (size_t i = 0; i < uvs.size(); i++) { glm::dvec2 uv = uvs[i]; - auto value = view.getRaw(uv[0], uv[1]); - REQUIRE(value == data[i]); - - auto maybeValue = view.get(uv[0], uv[1]); - REQUIRE(maybeValue); - REQUIRE(*maybeValue == data[i]); + REQUIRE(view.getRaw(uv[0], uv[1]) == data[i]); + REQUIRE(view.get(uv[0], uv[1]) == data[i]); } } @@ -1719,12 +1690,8 @@ TEST_CASE("Check sampling with different sampler values") { glm::dvec2(-1.25, 2.75)}; for (size_t i = 0; i < uvs.size(); i++) { glm::dvec2 uv = uvs[i]; - auto value = view.getRaw(uv[0], uv[1]); - REQUIRE(value == data[i]); - - auto maybeValue = view.get(uv[0], uv[1]); - REQUIRE(maybeValue); - REQUIRE(*maybeValue == data[i]); + REQUIRE(view.getRaw(uv[0], uv[1]) == data[i]); + REQUIRE(view.get(uv[0], uv[1]) == data[i]); } } @@ -1744,12 +1711,8 @@ TEST_CASE("Check sampling with different sampler values") { glm::dvec2(1.5, 1.5)}; for (size_t i = 0; i < uvs.size(); i++) { glm::dvec2 uv = uvs[i]; - auto value = view.getRaw(uv[0], uv[1]); - REQUIRE(value == data[i]); - - auto maybeValue = view.get(uv[0], uv[1]); - REQUIRE(maybeValue); - REQUIRE(*maybeValue == data[i]); + REQUIRE(view.getRaw(uv[0], uv[1]) == data[i]); + REQUIRE(view.get(uv[0], uv[1]) == data[i]); } } @@ -1769,12 +1732,8 @@ TEST_CASE("Check sampling with different sampler values") { glm::dvec2(1.5, 1.5)}; for (size_t i = 0; i < uvs.size(); i++) { glm::dvec2 uv = uvs[i]; - auto value = view.getRaw(uv[0], uv[1]); - REQUIRE(value == data[i]); - - auto maybeValue = view.get(uv[0], uv[1]); - REQUIRE(maybeValue); - REQUIRE(*maybeValue == data[i]); + REQUIRE(view.getRaw(uv[0], uv[1]) == data[i]); + REQUIRE(view.get(uv[0], uv[1]) == data[i]); } } } From 79eaa3e6877926b4838db2669347f62884f856e6 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Fri, 25 Aug 2023 17:46:16 -0400 Subject: [PATCH 226/421] Final sweep (hopefully) --- .../CesiumGltf/PropertyTexturePropertyView.h | 38 +-- CesiumGltf/include/CesiumGltf/PropertyView.h | 280 +++++++----------- CesiumGltf/test/TestPropertyView.cpp | 152 ++++------ 3 files changed, 184 insertions(+), 286 deletions(-) diff --git a/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h index 8d4933068..70ff28ce6 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h @@ -79,25 +79,6 @@ class PropertyTexturePropertyViewStatus : public PropertyViewStatus { static const int ErrorChannelsAndTypeMismatch = 21; }; -template -ElementType -assembleValueFromChannels(const std::vector& bytes) noexcept { - assert(bytes.size() > 0 && "Channel input must have at least one value."); - - if constexpr (IsMetadataScalar::value) { - return assembleScalarValue(bytes); - } - - if constexpr (IsMetadataVecN::value) { - return assembleVecNValue(bytes); - } - - if constexpr (IsMetadataArray::value) { - return assembleArrayValue::type>( - bytes); - } -} - template ElementType assembleScalarValue(const std::vector& bytes) noexcept { if constexpr (std::is_same_v) { @@ -191,6 +172,25 @@ assembleArrayValue(const std::vector& bytes) noexcept { return PropertyArrayView(std::move(result)); } +template +ElementType +assembleValueFromChannels(const std::vector& bytes) noexcept { + assert(bytes.size() > 0 && "Channel input must have at least one value."); + + if constexpr (IsMetadataScalar::value) { + return assembleScalarValue(bytes); + } + + if constexpr (IsMetadataVecN::value) { + return assembleVecNValue(bytes); + } + + if constexpr (IsMetadataArray::value) { + return assembleArrayValue::type>( + bytes); + } +} + inline double applySamplerWrapS(const double u, const int32_t wrapS) { if (wrapS == Sampler::WrapS::REPEAT) { double integral = 0; diff --git a/CesiumGltf/include/CesiumGltf/PropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyView.h index 8a283fa58..4bc809cf4 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyView.h @@ -3,7 +3,6 @@ #include "CesiumGltf/PropertyTextureProperty.h" #include "CesiumGltf/PropertyTypeTraits.h" -#include #include #include @@ -54,7 +53,7 @@ class PropertyViewStatus { static const PropertyViewStatusType ErrorArrayTypeMismatch = 4; /** - * @brief This property says it is normalized, but is not of an integer + * @brief This property says it is normalized, but it does not have an integer * component type. */ static const PropertyViewStatusType ErrorInvalidNormalization = 5; @@ -283,7 +282,7 @@ template class PropertyView { } if (classProperty.normalized) { - _status = PropertyViewStatus::ErrorInvalidNormalization; + _status = PropertyViewStatus::ErrorNormalizationMismatch; return; } @@ -294,7 +293,7 @@ template class PropertyView { if (classProperty.noData) { if (!_required) { - // "noData" can only be defined if the property is required. + // "noData" can only be defined if the property is not required. _noData = getValue(*classProperty.noData); } @@ -307,7 +306,7 @@ template class PropertyView { if (classProperty.defaultProperty) { if (!_required) { - // "default" can only be defined if the property is required. + // "default" can only be defined if the property is not required. _defaultValue = getValue(*classProperty.defaultProperty); } @@ -323,7 +322,8 @@ template class PropertyView { /** * @brief Constructs an invalid instance for an erroneous property. * - * @param status The value of {@link PropertyViewStatus} indicating the error with the property. + * @param status The value of {@link PropertyViewStatus} indicating the error + * with the property. */ PropertyView(PropertyViewStatusType status) : _status(status), @@ -458,8 +458,49 @@ template class PropertyView { protected: PropertyViewStatusType _status; +private: + bool _required; + + std::optional _offset; + std::optional _scale; + std::optional _max; + std::optional _min; + + std::optional _noData; + std::optional _defaultValue; + + /** + * @brief Attempts to parse an ElementType from the given json value. + * + * If ElementType is a type with multiple components, e.g. a VECN or MATN + * type, this will return std::nullopt if one or more components could not be + * parsed. + * + * @return The value as an instance of ElementType, or std::nullopt if it + * could not be parsed. + */ + static std::optional + getValue(const CesiumUtility::JsonValue& jsonValue) { + if constexpr (IsMetadataScalar::value) { + return getScalar(jsonValue); + } + + if constexpr (IsMetadataVecN::value) { + return getVecN(jsonValue); + } + + if constexpr (IsMetadataMatN::value) { + return getMatN(jsonValue); + } + } + using PropertyDefinitionType = std:: variant; + + /** + * @brief Attempts to parse offset, scale, min, and max properties from the + * given property type. + */ void getNumericPropertyValues(const PropertyDefinitionType& inProperty) { std::visit( [this](auto property) { @@ -517,42 +558,6 @@ template class PropertyView { }, inProperty); } - -private: - bool _required; - - std::optional _offset; - std::optional _scale; - std::optional _max; - std::optional _min; - - std::optional _noData; - std::optional _defaultValue; - - /** - * @brief Attempts to parse an ElementType from the given json value. - * - * If ElementType is a type with multiple components, e.g. a VECN or MATN - * type, this will return std::nullopt if one or more components could not be - * parsed. - * - * @return The value as an instance of ElementType, or std::nullopt if it - * could not be parsed. - */ - static std::optional - getValue(const CesiumUtility::JsonValue& jsonValue) { - if constexpr (IsMetadataScalar::value) { - return getScalar(jsonValue); - } - - if constexpr (IsMetadataVecN::value) { - return getVecN(jsonValue); - } - - if constexpr (IsMetadataMatN::value) { - return getMatN(jsonValue); - } - } }; /** @@ -603,61 +608,19 @@ template class PropertyView { } if (!classProperty.normalized) { - _status = PropertyViewStatus::ErrorInvalidNormalization; - } - - if (classProperty.offset) { - _offset = getValue(*classProperty.offset); - if (!_offset) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidOffset; - return; - } - } - - if (classProperty.scale) { - _scale = getValue(*classProperty.scale); - if (!_scale) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidScale; - return; - } - } - - if (classProperty.max) { - _max = getValue(*classProperty.max); - if (!_scale) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidMax; - return; - } + _status = PropertyViewStatus::ErrorNormalizationMismatch; } - if (classProperty.min) { - _min = getValue(*classProperty.min); - if (!_scale) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidMin; - return; - } - } - - if (_required) { - // "noData" should not be defined if the property is required. - if (classProperty.noData) { - _status = PropertyViewStatus::ErrorInvalidNoDataValue; - return; - } - - // default value should not be defined if the property is required. - if (classProperty.defaultProperty) { - _status = PropertyViewStatus::ErrorInvalidDefaultValue; - return; - } + getNumericPropertyValues(classProperty); + if (_status != PropertyViewStatus::Valid) { + return; } if (classProperty.noData) { - _noData = getValue(*classProperty.noData); + if (!_required) { + // "noData" should not be defined if the property is required. + _noData = getValue(*classProperty.noData); + } if (!_noData) { // The value was specified but something went wrong. _status = PropertyViewStatus::ErrorInvalidNoDataValue; @@ -666,7 +629,11 @@ template class PropertyView { } if (classProperty.defaultProperty) { - _defaultValue = getValue(*classProperty.defaultProperty); + // default value should not be defined if the property is required. + if (!_required) { + _defaultValue = + getValue(*classProperty.defaultProperty); + } if (!_defaultValue) { // The value was specified but something went wrong. _status = PropertyViewStatus::ErrorInvalidDefaultValue; @@ -704,42 +671,7 @@ template class PropertyView { } // If the property has its own values, override the class-provided values. - - if (property.offset) { - _offset = getValue(*property.offset); - if (!_offset) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidOffset; - return; - } - } - - if (property.scale) { - _scale = getValue(*property.scale); - if (!_scale) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidScale; - return; - } - } - - if (property.max) { - _max = getValue(*property.max); - if (!_scale) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidMax; - return; - } - } - - if (property.min) { - _min = getValue(*property.min); - if (!_scale) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidMin; - return; - } - } + getNumericPropertyValues(property); } /** @@ -755,42 +687,7 @@ template class PropertyView { } // If the property has its own values, override the class-provided values. - - if (property.offset) { - _offset = getValue(*property.offset); - if (!_offset) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidOffset; - return; - } - } - - if (property.scale) { - _scale = getValue(*property.scale); - if (!_scale) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidScale; - return; - } - } - - if (property.max) { - _max = getValue(*property.max); - if (!_scale) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidMax; - return; - } - } - - if (property.min) { - _min = getValue(*property.min); - if (!_scale) { - // The value was specified but something went wrong. - _status = PropertyViewStatus::ErrorInvalidMin; - return; - } - } + getNumericPropertyValues(property); } public: @@ -886,6 +783,55 @@ template class PropertyView { return getMatN(jsonValue); } } + + using PropertyDefinitionType = std:: + variant; + + /** + * @brief Attempts to parse offset, scale, min, and max properties from the + * given property type. + */ + void getNumericPropertyValues(const PropertyDefinitionType& inProperty) { + std::visit( + [this](auto property) { + if (property.offset) { + _offset = getValue(*property.offset); + if (!_offset) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidOffset; + return; + } + } + + if (property.scale) { + _scale = getValue(*property.scale); + if (!_scale) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidScale; + return; + } + } + + if (property.max) { + _max = getValue(*property.max); + if (!_scale) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidMax; + return; + } + } + + if (property.min) { + _min = getValue(*property.min); + if (!_scale) { + // The value was specified but something went wrong. + _status = PropertyViewStatus::ErrorInvalidMin; + return; + } + } + }, + inProperty); + } }; /** @@ -1235,7 +1181,7 @@ class PropertyView, false> { } if (classProperty.normalized) { - _status = PropertyViewStatus::ErrorInvalidNormalization; + _status = PropertyViewStatus::ErrorNormalizationMismatch; return; } @@ -1599,7 +1545,7 @@ class PropertyView, true> { } if (!classProperty.normalized) { - _status = PropertyViewStatus::ErrorInvalidNormalization; + _status = PropertyViewStatus::ErrorNormalizationMismatch; return; } diff --git a/CesiumGltf/test/TestPropertyView.cpp b/CesiumGltf/test/TestPropertyView.cpp index 8aa81a9a9..833902790 100644 --- a/CesiumGltf/test/TestPropertyView.cpp +++ b/CesiumGltf/test/TestPropertyView.cpp @@ -46,8 +46,8 @@ TEST_CASE("Boolean PropertyView") { PropertyView view(classProperty); REQUIRE(view.status() == PropertyViewStatus::Valid); REQUIRE(!view.required()); - REQUIRE(view.defaultValue()); - REQUIRE(!*view.defaultValue()); + REQUIRE(view.defaultValue() == false); + ; } SECTION("Reports errors for incorrectly defined properties") { @@ -119,7 +119,7 @@ TEST_CASE("Scalar PropertyView") { classProperty.normalized = true; PropertyView view(classProperty); - REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNormalization); + REQUIRE(view.status() == PropertyViewStatus::ErrorNormalizationMismatch); } SECTION("Constructs with offset, scale, max, and min") { @@ -133,15 +133,10 @@ TEST_CASE("Scalar PropertyView") { PropertyView view(classProperty); REQUIRE(view.status() == PropertyViewStatus::Valid); - REQUIRE(view.offset()); - REQUIRE(view.scale()); - REQUIRE(view.max()); - REQUIRE(view.min()); - - REQUIRE(*view.offset() == 5.04f); - REQUIRE(*view.scale() == 2.2f); - REQUIRE(*view.max() == 10.5f); - REQUIRE(*view.min() == -10.5f); + REQUIRE(view.offset() == 5.04f); + REQUIRE(view.scale() == 2.2f); + REQUIRE(view.max() == 10.5f); + REQUIRE(view.min() == -10.5f); } SECTION("Constructs with noData and defaultProperty") { @@ -155,11 +150,8 @@ TEST_CASE("Scalar PropertyView") { PropertyView view(classProperty); REQUIRE(view.status() == PropertyViewStatus::Valid); REQUIRE(!view.required()); - REQUIRE(view.noData()); - REQUIRE(view.defaultValue()); - - REQUIRE(*view.noData() == 0); - REQUIRE(*view.defaultValue() == 1); + REQUIRE(view.noData() == 0); + REQUIRE(view.defaultValue() == 1); } SECTION("Reports errors for incorrectly defined properties") { @@ -279,7 +271,7 @@ TEST_CASE("Scalar PropertyView (normalized)") { classProperty.normalized = false; PropertyView view(classProperty); - REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNormalization); + REQUIRE(view.status() == PropertyViewStatus::ErrorNormalizationMismatch); } SECTION("Constructs with offset, scale, max, and min") { @@ -294,15 +286,10 @@ TEST_CASE("Scalar PropertyView (normalized)") { PropertyView view(classProperty); REQUIRE(view.status() == PropertyViewStatus::Valid); - REQUIRE(view.offset()); - REQUIRE(view.scale()); - REQUIRE(view.max()); - REQUIRE(view.min()); - - REQUIRE(*view.offset() == 5.04f); - REQUIRE(*view.scale() == 2.2f); - REQUIRE(*view.max() == 10.5f); - REQUIRE(*view.min() == -10.5f); + REQUIRE(view.offset() == 5.04f); + REQUIRE(view.scale() == 2.2f); + REQUIRE(view.max() == 10.5f); + REQUIRE(view.min() == -10.5f); } SECTION("Constructs with noData and defaultProperty") { @@ -317,11 +304,8 @@ TEST_CASE("Scalar PropertyView (normalized)") { PropertyView view(classProperty); REQUIRE(view.status() == PropertyViewStatus::Valid); REQUIRE(!view.required()); - REQUIRE(view.noData()); - REQUIRE(view.defaultValue()); - - REQUIRE(*view.noData() == 0); - REQUIRE(*view.defaultValue() == 1.5); + REQUIRE(view.noData() == 0); + REQUIRE(view.defaultValue() == 1.5); } SECTION("Reports errors for incorrectly defined properties") { @@ -424,7 +408,7 @@ TEST_CASE("VecN PropertyView") { classProperty.normalized = true; PropertyView view(classProperty); - REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNormalization); + REQUIRE(view.status() == PropertyViewStatus::ErrorNormalizationMismatch); } SECTION("Constructs with offset, scale, max, and min") { @@ -438,15 +422,10 @@ TEST_CASE("VecN PropertyView") { PropertyView view(classProperty); REQUIRE(view.status() == PropertyViewStatus::Valid); - REQUIRE(view.offset()); - REQUIRE(view.scale()); - REQUIRE(view.max()); - REQUIRE(view.min()); - - REQUIRE(*view.offset() == glm::vec3(-1, 1, 2)); - REQUIRE(*view.scale() == glm::vec3(2, 1, 3)); - REQUIRE(*view.max() == glm::vec3(10, 5, 6)); - REQUIRE(*view.min() == glm::vec3(-11, -12, -13)); + REQUIRE(view.offset() == glm::vec3(-1, 1, 2)); + REQUIRE(view.scale() == glm::vec3(2, 1, 3)); + REQUIRE(view.max() == glm::vec3(10, 5, 6)); + REQUIRE(view.min() == glm::vec3(-11, -12, -13)); } SECTION("Constructs with noData and defaultProperty") { @@ -459,12 +438,8 @@ TEST_CASE("VecN PropertyView") { PropertyView view(classProperty); REQUIRE(view.status() == PropertyViewStatus::Valid); - REQUIRE(!view.required()); - REQUIRE(view.noData()); - REQUIRE(view.defaultValue()); - - REQUIRE(*view.noData() == glm::vec4(0.0f)); - REQUIRE(*view.defaultValue() == glm::vec4(1.0f, 2.0f, 3.0f, 4.0f)); + REQUIRE(view.noData() == glm::vec4(0.0f)); + REQUIRE(view.defaultValue() == glm::vec4(1.0f, 2.0f, 3.0f, 4.0f)); } SECTION("Reports errors for incorrectly defined properties") { @@ -592,7 +567,7 @@ TEST_CASE("VecN PropertyView (normalized)") { classProperty.normalized = false; PropertyView view(classProperty); - REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNormalization); + REQUIRE(view.status() == PropertyViewStatus::ErrorNormalizationMismatch); } SECTION("Constructs with offset, scale, max, and min") { @@ -607,15 +582,10 @@ TEST_CASE("VecN PropertyView (normalized)") { PropertyView view(classProperty); REQUIRE(view.status() == PropertyViewStatus::Valid); - REQUIRE(view.offset()); - REQUIRE(view.scale()); - REQUIRE(view.max()); - REQUIRE(view.min()); - - REQUIRE(*view.offset() == glm::dvec3(-1, 1, 2)); - REQUIRE(*view.scale() == glm::dvec3(2, 1, 3)); - REQUIRE(*view.max() == glm::dvec3(10, 5, 6)); - REQUIRE(*view.min() == glm::dvec3(-11, -12, -13)); + REQUIRE(view.offset() == glm::dvec3(-1, 1, 2)); + REQUIRE(view.scale() == glm::dvec3(2, 1, 3)); + REQUIRE(view.max() == glm::dvec3(10, 5, 6)); + REQUIRE(view.min() == glm::dvec3(-11, -12, -13)); } SECTION("Constructs with noData and defaultProperty") { @@ -630,11 +600,8 @@ TEST_CASE("VecN PropertyView (normalized)") { PropertyView view(classProperty); REQUIRE(view.status() == PropertyViewStatus::Valid); REQUIRE(!view.required()); - REQUIRE(view.noData()); - REQUIRE(view.defaultValue()); - - REQUIRE(*view.noData() == glm::i8vec4(0, 0, -1, -1)); - REQUIRE(*view.defaultValue() == glm::dvec4(1.0, 2.0, 3.0, 4.5)); + REQUIRE(view.noData() == glm::i8vec4(0, 0, -1, -1)); + REQUIRE(view.defaultValue() == glm::dvec4(1.0, 2.0, 3.0, 4.5)); } SECTION("Reports errors for incorrectly defined properties") { @@ -746,7 +713,7 @@ TEST_CASE("MatN PropertyView") { classProperty.normalized = true; PropertyView view(classProperty); - REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNormalization); + REQUIRE(view.status() == PropertyViewStatus::ErrorNormalizationMismatch); } SECTION("Constructs with offset, scale, max, and min") { @@ -774,35 +741,31 @@ TEST_CASE("MatN PropertyView") { PropertyView view(classProperty); REQUIRE(view.status() == PropertyViewStatus::Valid); - REQUIRE(view.offset()); - REQUIRE(view.scale()); - REQUIRE(view.max()); - REQUIRE(view.min()); // clang-format off glm::mat3 expectedOffset( -1, 1, 2, 3, -1, 4, -5, -5, 0); - REQUIRE(*view.offset() == expectedOffset); + REQUIRE(view.offset() == expectedOffset); glm::mat3 expectedScale( 1, 1, 1, 2, 2, 3, 3, 4, 5); - REQUIRE(*view.scale() == expectedScale); + REQUIRE(view.scale() == expectedScale); glm::mat3 expectedMax( 20, 5, 20, 30, 22, 43, 37, 1, 8); - REQUIRE(*view.max() == expectedMax); + REQUIRE(view.max() == expectedMax); glm::mat3 expectedMin( -10, -2, -3, 0, 20, 4, 9, 4, 5); - REQUIRE(*view.min() == expectedMin); + REQUIRE(view.min() == expectedMin); // clang-format on } @@ -823,19 +786,17 @@ TEST_CASE("MatN PropertyView") { PropertyView view(classProperty); REQUIRE(view.status() == PropertyViewStatus::Valid); REQUIRE(!view.required()); - REQUIRE(view.noData()); - REQUIRE(view.defaultValue()); // clang-format off glm::mat2 expectedNoData( 0.0f, 0.0f, 0.0f, 0.0f); - REQUIRE(*view.noData() == expectedNoData); + REQUIRE(view.noData() == expectedNoData); glm::mat2 expectedDefaultValue( 1.0f, 2.0f, 3.0f, 4.5f); - REQUIRE(*view.defaultValue() == expectedDefaultValue); + REQUIRE(view.defaultValue() == expectedDefaultValue); } SECTION("Reports errors for incorrectly defined properties") { @@ -1018,7 +979,7 @@ TEST_CASE("MatN PropertyView (normalized)") { classProperty.normalized = false; PropertyView view(classProperty); - REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNormalization); + REQUIRE(view.status() == PropertyViewStatus::ErrorNormalizationMismatch); } SECTION("Constructs with offset, scale, max, and min") { @@ -1047,35 +1008,30 @@ TEST_CASE("MatN PropertyView (normalized)") { PropertyView view(classProperty); REQUIRE(view.status() == PropertyViewStatus::Valid); - REQUIRE(view.offset()); - REQUIRE(view.scale()); - REQUIRE(view.max()); - REQUIRE(view.min()); - // clang-format off glm::dmat3 expectedOffset( -1, 1, 2, 3, -1, 4, -5, -5, 0); - REQUIRE(*view.offset() == expectedOffset); + REQUIRE(view.offset() == expectedOffset); glm::dmat3 expectedScale( 1, 1, 1, 2, 2, 3, 3, 4, 5); - REQUIRE(*view.scale() == expectedScale); + REQUIRE(view.scale() == expectedScale); glm::dmat3 expectedMax( 20, 5, 20, 30, 22, 43, 37, 1, 8); - REQUIRE(*view.max() == expectedMax); + REQUIRE(view.max() == expectedMax); glm::dmat3 expectedMin( -10, -2, -3, 0, 20, 4, 9, 4, 5); - REQUIRE(*view.min() == expectedMin); + REQUIRE(view.min() == expectedMin); // clang-format on } @@ -1097,17 +1053,15 @@ TEST_CASE("MatN PropertyView (normalized)") { PropertyView view(classProperty); REQUIRE(view.status() == PropertyViewStatus::Valid); REQUIRE(!view.required()); - REQUIRE(view.noData()); - REQUIRE(view.defaultValue()); glm::imat2x2 expectedNoData(0); - REQUIRE(*view.noData() == expectedNoData); + REQUIRE(view.noData() == expectedNoData); // clang-format off glm::dmat2 expectedDefaultValue( 1.0, 2.0, 3.0, 4.5); - REQUIRE(*view.defaultValue() == expectedDefaultValue); + REQUIRE(view.defaultValue() == expectedDefaultValue); } SECTION("Reports errors for incorrectly defined properties") { @@ -1233,11 +1187,9 @@ TEST_CASE("String PropertyView") { PropertyView view(classProperty); REQUIRE(view.status() == PropertyViewStatus::Valid); REQUIRE(!view.required()); - REQUIRE(view.noData()); - REQUIRE(view.defaultValue()); - REQUIRE(*view.noData() == "null"); - REQUIRE(*view.defaultValue() == "default"); + REQUIRE(view.noData() == "null"); + REQUIRE(view.defaultValue() == "default"); } SECTION("Reports errors for incorrectly defined properties") { @@ -1404,7 +1356,7 @@ TEST_CASE("Scalar Array PropertyView") { classProperty.normalized = true; PropertyView> view(classProperty); - REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNormalization); + REQUIRE(view.status() == PropertyViewStatus::ErrorNormalizationMismatch); } SECTION("Constructs with count") { @@ -1651,7 +1603,7 @@ TEST_CASE("Scalar Array PropertyView (normalized)") { classProperty.normalized = false; PropertyView, true> view(classProperty); - REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNormalization); + REQUIRE(view.status() == PropertyViewStatus::ErrorNormalizationMismatch); } SECTION("Constructs with count") { @@ -1878,7 +1830,7 @@ TEST_CASE("VecN Array PropertyView") { classProperty.normalized = true; PropertyView> view(classProperty); - REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNormalization); + REQUIRE(view.status() == PropertyViewStatus::ErrorNormalizationMismatch); } SECTION("Constructs with count") { @@ -2121,7 +2073,7 @@ TEST_CASE("VecN Array PropertyView (normalized)") { classProperty.normalized = false; PropertyView, true> view(classProperty); - REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNormalization); + REQUIRE(view.status() == PropertyViewStatus::ErrorNormalizationMismatch); } SECTION("Constructs with count") { @@ -2344,7 +2296,7 @@ TEST_CASE("MatN Array PropertyView") { classProperty.normalized = true; PropertyView> view(classProperty); - REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNormalization); + REQUIRE(view.status() == PropertyViewStatus::ErrorNormalizationMismatch); } SECTION("Constructs with count") { @@ -2725,7 +2677,7 @@ TEST_CASE("MatN Array PropertyView (normalized)") { classProperty.normalized = false; PropertyView, true> view(classProperty); - REQUIRE(view.status() == PropertyViewStatus::ErrorInvalidNormalization); + REQUIRE(view.status() == PropertyViewStatus::ErrorNormalizationMismatch); } SECTION("Constructs with count") { From 153890dbbfaac2bb483edde69a5074a1d52bd251 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Mon, 28 Aug 2023 17:47:48 +1000 Subject: [PATCH 227/421] Add `Tileset::loadMetadata`. --- .../include/Cesium3DTilesSelection/Tileset.h | 55 +++++++++-- .../Cesium3DTilesSelection/TilesetMetadata.h | 39 +++++++- Cesium3DTilesSelection/src/Tileset.cpp | 62 ++++++++++++- .../src/TilesetJsonLoader.cpp | 11 ++- .../src/TilesetMetadata.cpp | 93 +++++++++++++++++++ .../test/TestTilesetSelectionAlgorithm.cpp | 2 +- 6 files changed, 242 insertions(+), 20 deletions(-) create mode 100644 Cesium3DTilesSelection/src/TilesetMetadata.cpp diff --git a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/Tileset.h b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/Tileset.h index 3a1aee0c4..335ec1bcc 100644 --- a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/Tileset.h +++ b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/Tileset.h @@ -75,13 +75,6 @@ class CESIUM3DTILESSELECTION_API Tileset final { const TilesetOptions& options = TilesetOptions(), const std::string& ionAssetEndpointUrl = "https://api.cesium.com/"); - /** - * @brief A future that resolves when this Tileset has been destroyed (i.e. - * its destructor has been called) and all async operations that it was - * executing have completed. - */ - CesiumAsync::SharedFuture& getAsyncDestructionCompleteEvent(); - /** * @brief Destroys this tileset. * @@ -94,6 +87,20 @@ class CESIUM3DTILESSELECTION_API Tileset final { */ ~Tileset() noexcept; + /** + * @brief A future that resolves when this Tileset has been destroyed (i.e. + * its destructor has been called) and all async operations that it was + * executing have completed. + */ + CesiumAsync::SharedFuture& getAsyncDestructionCompleteEvent(); + + /** + * @brief A future that resolves when the details of the root tile of this + * tileset are available. The root tile's content (e.g., 3D model), however, + * will not necessarily be loaded yet. + */ + CesiumAsync::SharedFuture& getRootTileAvailableEvent(); + /** * @brief Get tileset credits. */ @@ -209,22 +216,47 @@ class CESIUM3DTILESSELECTION_API Tileset final { int64_t getTotalDataBytes() const noexcept; /** - * @brief Finds the {@link TilesetMetadata} associated with the main or - * external tileset.json that contains a given tile. + * @brief Gets the {@link TilesetMetadata} associated with the main or + * external tileset.json that contains a given tile. If the metadata is not + * yet loaded, this method returns nullptr. * * If this tileset's root tile is not yet available, this method returns * nullptr. * + * If the tileset has a {@link TilesetMetadata::schemaUri}, it will not + * necessarily have been loaded yet. + * * If the provided tile is not the root tile of a tileset.json, this method * walks up the {@link Tile::getParent} chain until it finds the closest * root and then returns the metadata associated with the corresponding * tileset.json. * + * Consider calling {@link loadMetadata} instead, which will return a future + * that only resolves after the root tile is loaded and the `schemaUri`, if + * any, has been resolved. + * * @param pTile The tile. If this parameter is nullptr, the metadata for the * main tileset.json is returned. * @return The found metadata, or nullptr if the root tile is not yet loaded. */ - const TilesetMetadata* findMetadata(const Tile* pTile = nullptr) const; + const TilesetMetadata* getMetadata(const Tile* pTile = nullptr) const; + + /** + * @brief Asynchronously loads the metadata associated with the main + * tileset.json. + * + * Before the returned future resolves, the root tile of this tileset will be + * loaded and the {@link TilesetMetadata::schemaUri} will be loaded if one + * has been specified. + * + * If the tileset or `schemaUri` fail to load, the returned future will + * reject. + * + * @return A shared future that resolves to the loaded metadata. Once this + * future resolves, {@link findMetadata} can be used to synchronously obtain + * the same metadata instance. + */ + CesiumAsync::Future loadMetadata(); private: /** @@ -462,6 +494,9 @@ class CESIUM3DTILESSELECTION_API Tileset final { // scratch variable so that it can allocate only when growing bigger. std::vector _childOcclusionProxies; + CesiumAsync::Promise _rootTileAvailablePromise; + CesiumAsync::SharedFuture _rootTileAvailableFuture; + CesiumUtility::IntrusivePointer _pTilesetContentManager; diff --git a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/TilesetMetadata.h b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/TilesetMetadata.h index ce42940d7..db5b811d7 100644 --- a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/TilesetMetadata.h +++ b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/TilesetMetadata.h @@ -4,14 +4,27 @@ #include #include +#include #include #include #include +namespace CesiumAsync { +class AsyncSystem; +class IAssetAccessor; +} // namespace CesiumAsync + namespace Cesium3DTilesSelection { -struct CESIUM3DTILESSELECTION_API TilesetMetadata { +/** + * @brief Holds the metadata associated with a {@link Tileset} or an external + * tileset. + */ +class CESIUM3DTILESSELECTION_API TilesetMetadata { +public: + ~TilesetMetadata() noexcept; + /** * @brief An object defining the structure of metadata classes and enums. When * this is defined, then `schemaUri` shall be undefined. @@ -35,6 +48,30 @@ struct CESIUM3DTILESSELECTION_API TilesetMetadata { * @brief A metadata entity that is associated with this tileset. */ std::optional metadata; + + /** + * @brief Asynchronously loads the {@link schema} from the {@link schemaUri}. + * If the {@link schemaUri} does not contain a value, this method does + * nothing and returns an already-resolved future. + * + * Calling this method multiple times will return the same shared future each + * time, unless the {@link schemaUri} is changed. In that case, when this + * method is called, the previous load is canceled and the new one begins. + * + * @param asyncSystem The async system used to do work in threads. + * @param pAssetAccessor The asset accessor used to request the schema from + * the schemaUri. + * @return A future that resolves when the schema has been loaded from the + * schemaUri. + */ + CesiumAsync::SharedFuture& loadSchemaUri( + const CesiumAsync::AsyncSystem& asyncSystem, + const std::shared_ptr& pAssetAccessor); + +private: + std::optional> _loadingFuture; + std::optional _loadingSchemaUri; + std::shared_ptr _pLoadingCanceled; }; } // namespace Cesium3DTilesSelection diff --git a/Cesium3DTilesSelection/src/Tileset.cpp b/Cesium3DTilesSelection/src/Tileset.cpp index 95814932e..6666f222d 100644 --- a/Cesium3DTilesSelection/src/Tileset.cpp +++ b/Cesium3DTilesSelection/src/Tileset.cpp @@ -43,6 +43,9 @@ Tileset::Tileset( _previousFrameNumber(0), _distances(), _childOcclusionProxies(), + _rootTileAvailablePromise{externals.asyncSystem.createPromise()}, + _rootTileAvailableFuture{ + this->_rootTileAvailablePromise.getFuture().share()}, _pTilesetContentManager{new TilesetContentManager( _externals, _options, @@ -61,6 +64,9 @@ Tileset::Tileset( _previousFrameNumber(0), _distances(), _childOcclusionProxies(), + _rootTileAvailablePromise{externals.asyncSystem.createPromise()}, + _rootTileAvailableFuture{ + this->_rootTileAvailablePromise.getFuture().share()}, _pTilesetContentManager{new TilesetContentManager( _externals, _options, @@ -79,6 +85,9 @@ Tileset::Tileset( _previousFrameNumber(0), _distances(), _childOcclusionProxies(), + _rootTileAvailablePromise{externals.asyncSystem.createPromise()}, + _rootTileAvailableFuture{ + this->_rootTileAvailablePromise.getFuture().share()}, _pTilesetContentManager{new TilesetContentManager( _externals, _options, @@ -87,10 +96,6 @@ Tileset::Tileset( ionAccessToken, ionAssetEndpointUrl)} {} -CesiumAsync::SharedFuture& Tileset::getAsyncDestructionCompleteEvent() { - return this->_pTilesetContentManager->getAsyncDestructionCompleteEvent(); -} - Tileset::~Tileset() noexcept { this->_pTilesetContentManager->unloadAll(); if (this->_externals.pTileOcclusionProxyPool) { @@ -98,6 +103,14 @@ Tileset::~Tileset() noexcept { } } +CesiumAsync::SharedFuture& Tileset::getAsyncDestructionCompleteEvent() { + return this->_pTilesetContentManager->getAsyncDestructionCompleteEvent(); +} + +CesiumAsync::SharedFuture& Tileset::getRootTileAvailableEvent() { + return this->_rootTileAvailableFuture; +} + const std::vector& Tileset::getTilesetCredits() const noexcept { return this->_pTilesetContentManager->getTilesetCredits(); } @@ -454,7 +467,7 @@ int64_t Tileset::getTotalDataBytes() const noexcept { return this->_pTilesetContentManager->getTotalDataUsed(); } -const TilesetMetadata* Tileset::findMetadata(const Tile* pTile) const { +const TilesetMetadata* Tileset::getMetadata(const Tile* pTile) const { if (pTile == nullptr) { pTile = this->getRootTile(); } @@ -470,6 +483,45 @@ const TilesetMetadata* Tileset::findMetadata(const Tile* pTile) const { return nullptr; } +CesiumAsync::Future Tileset::loadMetadata() { + return this->getRootTileAvailableEvent().thenInMainThread( + [pManager = this->_pTilesetContentManager, + pAssetAccessor = this->_externals.pAssetAccessor, + asyncSystem = + this->getAsyncSystem()]() -> Future { + Tile* pRoot = pManager->getRootTile(); + assert(pRoot); + + TileExternalContent* pExternal = + pRoot->getContent().getExternalContent(); + if (!pExternal) { + return asyncSystem.createResolvedFuture( + nullptr); + } + + TilesetMetadata& metadata = pExternal->metadata; + if (!metadata.schemaUri) { + // No schema URI, so the metadata is ready to go. + return asyncSystem.createResolvedFuture( + &metadata); + } + + return metadata.loadSchemaUri(asyncSystem, pAssetAccessor) + .thenInMainThread( + [pManager, pAssetAccessor]() -> const TilesetMetadata* { + Tile* pRoot = pManager->getRootTile(); + assert(pRoot); + + TileExternalContent* pExternal = + pRoot->getContent().getExternalContent(); + if (!pExternal) { + return nullptr; + } + return &pExternal->metadata; + }); + }); +} + static void markTileNonRendered( TileSelectionState::Result lastResult, Tile& tile, diff --git a/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp b/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp index ddc563455..e6433575d 100644 --- a/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp +++ b/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp @@ -625,6 +625,7 @@ TilesetContentLoaderResult parseTilesetJson( } void parseTilesetMetadata( + const std::string& baseUrl, const rapidjson::Document& tilesetJson, TileExternalContent& externalContent) { auto schemaIt = tilesetJson.FindMember("schema"); @@ -638,7 +639,8 @@ void parseTilesetMetadata( auto schemaUriIt = tilesetJson.FindMember("schemaUri"); if (schemaUriIt != tilesetJson.MemberEnd() && schemaUriIt->value.IsString()) { - externalContent.metadata.schemaUri = schemaUriIt->value.GetString(); + externalContent.metadata.schemaUri = + CesiumUtility::Uri::resolve(baseUrl, schemaUriIt->value.GetString()); } const auto metadataIt = tilesetJson.FindMember("metadata"); @@ -697,7 +699,10 @@ TileLoadResult parseExternalTilesetInWorkerThread( tileRefine); // Populate the root tile with metadata - parseTilesetMetadata(tilesetJson, externalContentInitializer.externalContent); + parseTilesetMetadata( + tileUrl, + tilesetJson, + externalContentInitializer.externalContent); // check and log any errors const auto& errors = externalTilesetLoader.errors; @@ -817,7 +822,7 @@ TilesetContentLoaderResult TilesetJsonLoader::createLoader( result.pRootTile->getContent().getExternalContent(); assert(pExternal); if (pExternal) { - parseTilesetMetadata(tilesetJson, *pExternal); + parseTilesetMetadata(tilesetJsonUrl, tilesetJson, *pExternal); } return result; diff --git a/Cesium3DTilesSelection/src/TilesetMetadata.cpp b/Cesium3DTilesSelection/src/TilesetMetadata.cpp new file mode 100644 index 000000000..a42c76eea --- /dev/null +++ b/Cesium3DTilesSelection/src/TilesetMetadata.cpp @@ -0,0 +1,93 @@ +#include +#include +#include +#include +#include +#include + +using namespace CesiumAsync; +using namespace Cesium3DTilesReader; +using namespace CesiumUtility; + +namespace Cesium3DTilesSelection { + +TilesetMetadata::~TilesetMetadata() noexcept { + if (this->_pLoadingCanceled) { + *this->_pLoadingCanceled = true; + } +} + +SharedFuture& TilesetMetadata::loadSchemaUri( + const AsyncSystem& asyncSystem, + const std::shared_ptr& pAssetAccessor) { + if (!this->_loadingFuture || this->_loadingSchemaUri != this->schemaUri) { + this->_loadingSchemaUri = this->schemaUri; + + if (this->_pLoadingCanceled) { + *this->_pLoadingCanceled = true; + this->_pLoadingCanceled.reset(); + } + + if (!this->schemaUri) { + this->_loadingFuture = asyncSystem.createResolvedFuture().share(); + } else { + std::shared_ptr pLoadingCanceled = std::make_shared(false); + this->_pLoadingCanceled = pLoadingCanceled; + this->_loadingFuture = + pAssetAccessor->get(asyncSystem, *this->schemaUri) + .thenInMainThread([pLoadingCanceled, this, asyncSystem]( + std::shared_ptr&& pRequest) { + Promise promise = asyncSystem.createPromise(); + + if (*pLoadingCanceled) { + promise.reject(std::runtime_error(fmt::format( + "Loading of schema URI {} was canceled.", + pRequest->url()))); + return promise.getFuture(); + } + + const IAssetResponse* pResponse = pRequest->response(); + if (!pResponse) { + promise.reject(std::runtime_error(fmt::format( + "Did not receive a valid response for schema URI {}", + pRequest->url()))); + return promise.getFuture(); + } + + uint16_t statusCode = pResponse->statusCode(); + if (statusCode != 0 && + (statusCode < 200 || statusCode >= 300)) { + promise.reject(std::runtime_error(fmt::format( + "Received status code {} for schema URI {}.", + statusCode, + pRequest->url()))); + return promise.getFuture(); + } + + SchemaReader reader; + auto result = reader.readFromJson(pResponse->data()); + if (!result.value) { + std::string errors = + CesiumUtility::joinToString(result.errors, "\n - "); + if (!errors.empty()) { + errors = " Errors:\n - " + errors; + } + promise.reject(std::runtime_error(fmt::format( + "Error reading Schema from {}.{}", + pRequest->url(), + errors))); + } + + this->schema = std::move(*result.value); + + promise.resolve(); + return promise.getFuture(); + }) + .share(); + } + } + + return *this->_loadingFuture; +} + +} // namespace Cesium3DTilesSelection diff --git a/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp b/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp index 51138bbea..5c87495e2 100644 --- a/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp +++ b/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp @@ -1253,7 +1253,7 @@ TEST_CASE("Allows access to material variants") { Tileset tileset(tilesetExternals, "tileset.json"); initializeTileset(tileset); - const TilesetMetadata* pMetadata = tileset.findMetadata(); + const TilesetMetadata* pMetadata = tileset.getMetadata(); REQUIRE(pMetadata); REQUIRE(pMetadata->schema); REQUIRE(pMetadata->metadata); From 6145b547a69d37a1d0c4049a1588c47a949a9e77 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Mon, 28 Aug 2023 17:48:56 +1000 Subject: [PATCH 228/421] Update CHANGES.md. --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 40100dbb4..c7b71fb7a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -18,7 +18,7 @@ - Added `*Reader` classes to `CesiumGltfReader` and `Cesium3DTilesReader` to allow each of the classes to be individually read from JSON. - Added `getExternalContent` method to the `TileContent` class. - `TileExternalContent` now holds the metadata (`schema`, `schemaUri`, `metadata`, and `groups`) stored in the tileset.json. -- Added `findMetadata` method to `Cesium3DTilesSelection::Tileset`. It returns a `TilesetMetadata` instance representing the metadata associated with a tileset.json. +- Added `loadMetadata` and `getMetadata` methods to `Cesium3DTilesSelection::Tileset`. They provide access to `TilesetMetadata` instance representing the metadata associated with a tileset.json. - Added `MetadataQuery` class to make it easier to find properties with specific semantics in `TilesetMetadata`. ### v0.27.0 - 2023-09-01 From 51e798e4ea2076507474e6020cdede006ee4416a Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Mon, 28 Aug 2023 18:24:08 +1000 Subject: [PATCH 229/421] Add test for async schema loading. --- .../include/Cesium3DTilesSelection/Tileset.h | 3 - Cesium3DTilesSelection/src/Tileset.cpp | 11 +- .../src/TilesetContentManager.cpp | 32 +++- .../src/TilesetContentManager.h | 10 + .../test/TestTilesetSelectionAlgorithm.cpp | 180 ++++++++++++++++++ .../test/data/MaterialVariants/schema.json | 14 ++ .../tileset-external-schema.json | 76 ++++++++ 7 files changed, 308 insertions(+), 18 deletions(-) create mode 100644 Cesium3DTilesSelection/test/data/MaterialVariants/schema.json create mode 100644 Cesium3DTilesSelection/test/data/MaterialVariants/tileset-external-schema.json diff --git a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/Tileset.h b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/Tileset.h index 335ec1bcc..0b5573d83 100644 --- a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/Tileset.h +++ b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/Tileset.h @@ -494,9 +494,6 @@ class CESIUM3DTILESSELECTION_API Tileset final { // scratch variable so that it can allocate only when growing bigger. std::vector _childOcclusionProxies; - CesiumAsync::Promise _rootTileAvailablePromise; - CesiumAsync::SharedFuture _rootTileAvailableFuture; - CesiumUtility::IntrusivePointer _pTilesetContentManager; diff --git a/Cesium3DTilesSelection/src/Tileset.cpp b/Cesium3DTilesSelection/src/Tileset.cpp index 6666f222d..138849c6f 100644 --- a/Cesium3DTilesSelection/src/Tileset.cpp +++ b/Cesium3DTilesSelection/src/Tileset.cpp @@ -43,9 +43,6 @@ Tileset::Tileset( _previousFrameNumber(0), _distances(), _childOcclusionProxies(), - _rootTileAvailablePromise{externals.asyncSystem.createPromise()}, - _rootTileAvailableFuture{ - this->_rootTileAvailablePromise.getFuture().share()}, _pTilesetContentManager{new TilesetContentManager( _externals, _options, @@ -64,9 +61,6 @@ Tileset::Tileset( _previousFrameNumber(0), _distances(), _childOcclusionProxies(), - _rootTileAvailablePromise{externals.asyncSystem.createPromise()}, - _rootTileAvailableFuture{ - this->_rootTileAvailablePromise.getFuture().share()}, _pTilesetContentManager{new TilesetContentManager( _externals, _options, @@ -85,9 +79,6 @@ Tileset::Tileset( _previousFrameNumber(0), _distances(), _childOcclusionProxies(), - _rootTileAvailablePromise{externals.asyncSystem.createPromise()}, - _rootTileAvailableFuture{ - this->_rootTileAvailablePromise.getFuture().share()}, _pTilesetContentManager{new TilesetContentManager( _externals, _options, @@ -108,7 +99,7 @@ CesiumAsync::SharedFuture& Tileset::getAsyncDestructionCompleteEvent() { } CesiumAsync::SharedFuture& Tileset::getRootTileAvailableEvent() { - return this->_rootTileAvailableFuture; + return this->_pTilesetContentManager->getRootTileAvailableEvent(); } const std::vector& Tileset::getTilesetCredits() const noexcept { diff --git a/Cesium3DTilesSelection/src/TilesetContentManager.cpp b/Cesium3DTilesSelection/src/TilesetContentManager.cpp index 699aef4eb..ee315ddfe 100644 --- a/Cesium3DTilesSelection/src/TilesetContentManager.cpp +++ b/Cesium3DTilesSelection/src/TilesetContentManager.cpp @@ -599,7 +599,12 @@ TilesetContentManager::TilesetContentManager( _tilesDataUsed{0}, _destructionCompletePromise{externals.asyncSystem.createPromise()}, _destructionCompleteFuture{ - this->_destructionCompletePromise.getFuture().share()} {} + this->_destructionCompletePromise.getFuture().share()}, + _rootTileAvailablePromise{externals.asyncSystem.createPromise()}, + _rootTileAvailableFuture{ + this->_rootTileAvailablePromise.getFuture().share()} { + this->_rootTileAvailablePromise.resolve(); +} TilesetContentManager::TilesetContentManager( const TilesetExternals& externals, @@ -623,7 +628,10 @@ TilesetContentManager::TilesetContentManager( _tilesDataUsed{0}, _destructionCompletePromise{externals.asyncSystem.createPromise()}, _destructionCompleteFuture{ - this->_destructionCompletePromise.getFuture().share()} { + this->_destructionCompletePromise.getFuture().share()}, + _rootTileAvailablePromise{externals.asyncSystem.createPromise()}, + _rootTileAvailableFuture{ + this->_rootTileAvailablePromise.getFuture().share()} { if (!url.empty()) { this->notifyTileStartLoading(nullptr); @@ -721,13 +729,16 @@ TilesetContentManager::TilesetContentManager( TilesetLoadType::TilesetJson, errorCallback, std::move(result)); + thiz->_rootTileAvailablePromise.resolve(); }) .catchInMainThread([thiz](std::exception&& e) { thiz->notifyTileDoneLoading(nullptr); SPDLOG_LOGGER_ERROR( thiz->_externals.pLogger, - "An unexpected error occurs when loading tile: {}", + "An unexpected error occurred when loading tile: {}", e.what()); + thiz->_rootTileAvailablePromise.reject( + std::runtime_error("Root tile failed to load.")); }); } } @@ -756,7 +767,10 @@ TilesetContentManager::TilesetContentManager( _tilesDataUsed{0}, _destructionCompletePromise{externals.asyncSystem.createPromise()}, _destructionCompleteFuture{ - this->_destructionCompletePromise.getFuture().share()} { + this->_destructionCompletePromise.getFuture().share()}, + _rootTileAvailablePromise{externals.asyncSystem.createPromise()}, + _rootTileAvailableFuture{ + this->_rootTileAvailablePromise.getFuture().share()} { if (ionAssetID > 0) { auto authorizationChangeListener = [this]( const std::string& header, @@ -793,13 +807,16 @@ TilesetContentManager::TilesetContentManager( TilesetLoadType::CesiumIon, errorCallback, std::move(result)); + thiz->_rootTileAvailablePromise.resolve(); }) .catchInMainThread([thiz](std::exception&& e) { thiz->notifyTileDoneLoading(nullptr); SPDLOG_LOGGER_ERROR( thiz->_externals.pLogger, - "An unexpected error occurs when loading tile: {}", + "An unexpected error occurred when loading tile: {}", e.what()); + thiz->_rootTileAvailablePromise.reject( + std::runtime_error("Root tile failed to load.")); }); } } @@ -809,6 +826,11 @@ TilesetContentManager::getAsyncDestructionCompleteEvent() { return this->_destructionCompleteFuture; } +CesiumAsync::SharedFuture& +TilesetContentManager::getRootTileAvailableEvent() { + return this->_rootTileAvailableFuture; +} + TilesetContentManager::~TilesetContentManager() noexcept { assert(this->_tileLoadsInProgress == 0); this->unloadAll(); diff --git a/Cesium3DTilesSelection/src/TilesetContentManager.h b/Cesium3DTilesSelection/src/TilesetContentManager.h index 355fd82a2..273db7cb5 100644 --- a/Cesium3DTilesSelection/src/TilesetContentManager.h +++ b/Cesium3DTilesSelection/src/TilesetContentManager.h @@ -51,6 +51,13 @@ class TilesetContentManager */ CesiumAsync::SharedFuture& getAsyncDestructionCompleteEvent(); + /** + * @brief A future that resolves when the details of the root tile of this + * tileset are available. The root tile's content (e.g., 3D model), however, + * will not necessarily be loaded yet. + */ + CesiumAsync::SharedFuture& getRootTileAvailableEvent(); + ~TilesetContentManager() noexcept; void loadTileContent(Tile& tile, const TilesetOptions& tilesetOptions); @@ -146,5 +153,8 @@ class TilesetContentManager CesiumAsync::Promise _destructionCompletePromise; CesiumAsync::SharedFuture _destructionCompleteFuture; + + CesiumAsync::Promise _rootTileAvailablePromise; + CesiumAsync::SharedFuture _rootTileAvailableFuture; }; } // namespace Cesium3DTilesSelection diff --git a/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp b/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp index 5c87495e2..1f00eef8f 100644 --- a/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp +++ b/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp @@ -1312,6 +1312,186 @@ TEST_CASE("Allows access to material variants") { CHECK(variantsByGroup == expected); } +TEST_CASE("Allows access to material variants in an external schema") { + Cesium3DTilesSelection::registerAllTileContentTypes(); + + std::filesystem::path testDataPath = Cesium3DTilesSelection_TEST_DATA_DIR; + testDataPath = testDataPath / "MaterialVariants"; + std::vector files{ + "tileset-external-schema.json", + "schema.json", + "parent.b3dm"}; + + std::map> + mockCompletedRequests; + for (const auto& file : files) { + std::unique_ptr mockCompletedResponse = + std::make_unique( + static_cast(200), + "doesn't matter", + CesiumAsync::HttpHeaders{}, + readFile(testDataPath / file)); + mockCompletedRequests.insert( + {file, + std::make_shared( + "GET", + file, + CesiumAsync::HttpHeaders{}, + std::move(mockCompletedResponse))}); + } + + std::shared_ptr mockAssetAccessor = + std::make_shared(std::move(mockCompletedRequests)); + TilesetExternals tilesetExternals{ + mockAssetAccessor, + std::make_shared(), + AsyncSystem(std::make_shared()), + nullptr}; + + Tileset tileset(tilesetExternals, "tileset-external-schema.json"); + + // getMetadata returns nullptr before the root tile is loaded. + CHECK(tileset.getMetadata() == nullptr); + + bool wasCalled = false; + tileset.loadMetadata().thenInMainThread( + [&wasCalled](const TilesetMetadata* pMetadata) { + wasCalled = true; + REQUIRE(pMetadata); + REQUIRE(pMetadata->schema); + REQUIRE(pMetadata->metadata); + + std::optional found1 = + Cesium3DTiles::MetadataQuery::findFirstPropertyWithSemantic( + *pMetadata->schema, + *pMetadata->metadata, + "MATERIAL_VARIANTS"); + REQUIRE(found1); + CHECK(found1->classIdentifier == "MaterialVariants"); + CHECK(found1->classDefinition.properties.size() == 1); + CHECK(found1->propertyIdentifier == "material_variants"); + CHECK( + found1->propertyDefinition.description == + "Names of material variants to be expected in the glTF assets"); + REQUIRE(found1->propertyValue.isArray()); + + const JsonValue::Array& variantsJson = found1->propertyValue.getArray(); + std::vector variants(variantsJson.size()); + std::transform( + variantsJson.begin(), + variantsJson.end(), + variants.begin(), + [](const JsonValue& value) { + return value.getStringOrDefault(""); + }); + REQUIRE(variants.size() == 4); + CHECK(variants[0] == "RGB"); + CHECK(variants[1] == "RRR"); + CHECK(variants[2] == "GGG"); + CHECK(variants[3] == "BBB"); + + std::vector> variantsByGroup; + for (const Cesium3DTiles::GroupMetadata& group : pMetadata->groups) { + std::vector& groupVariants = + variantsByGroup.emplace_back(); + + std::optional found2 = + Cesium3DTiles::MetadataQuery::findFirstPropertyWithSemantic( + *pMetadata->schema, + group, + "MATERIAL_VARIANTS"); + REQUIRE(found2); + REQUIRE(found2->propertyValue.isArray()); + + const JsonValue::Array& groupVariantsJson = + found2->propertyValue.getArray(); + groupVariants.reserve(groupVariantsJson.size()); + for (size_t i = 0; i < groupVariantsJson.size(); ++i) { + groupVariants.emplace_back( + groupVariantsJson[i].getStringOrDefault("")); + } + } + + std::vector> expected = { + {"RGB", "RRR"}, + {"GGG", "BBB"}}; + + CHECK(variantsByGroup == expected); + }); + + CHECK(!wasCalled); + initializeTileset(tileset); + CHECK(wasCalled); +} + +TEST_CASE("Future from loadSchema rejects if schemaUri can't be loaded") { + Cesium3DTilesSelection::registerAllTileContentTypes(); + + std::filesystem::path testDataPath = Cesium3DTilesSelection_TEST_DATA_DIR; + testDataPath = testDataPath / "MaterialVariants"; + std::vector files{"tileset-external-schema.json", "parent.b3dm"}; + + std::map> + mockCompletedRequests; + for (const auto& file : files) { + std::unique_ptr mockCompletedResponse = + std::make_unique( + static_cast(200), + "doesn't matter", + CesiumAsync::HttpHeaders{}, + readFile(testDataPath / file)); + mockCompletedRequests.insert( + {file, + std::make_shared( + "GET", + file, + CesiumAsync::HttpHeaders{}, + std::move(mockCompletedResponse))}); + } + + mockCompletedRequests.insert( + {"schema.json", + std::make_shared( + "GET", + "schema.json", + CesiumAsync::HttpHeaders{}, + std::make_unique( + 404, + "doesn't matter", + CesiumAsync::HttpHeaders{}, + std::vector()))}); + + std::shared_ptr mockAssetAccessor = + std::make_shared(std::move(mockCompletedRequests)); + TilesetExternals tilesetExternals{ + mockAssetAccessor, + std::make_shared(), + AsyncSystem(std::make_shared()), + nullptr}; + + Tileset tileset(tilesetExternals, "tileset-external-schema.json"); + + // getMetadata returns nullptr before the root tile is loaded. + CHECK(tileset.getMetadata() == nullptr); + + bool wasResolved = false; + bool wasRejected = false; + tileset.loadMetadata() + .thenInMainThread( + [&wasResolved](const TilesetMetadata*) { wasResolved = true; }) + .catchInMainThread([&wasRejected](const std::exception& exception) { + CHECK(std::string(exception.what()).find("") != std::string::npos); + wasRejected = true; + }); + + CHECK(!wasResolved); + CHECK(!wasRejected); + + initializeTileset(tileset); + CHECK(!wasResolved); + CHECK(wasRejected); +} + namespace { void runUnconditionallyRefinedTestCase(const TilesetOptions& options) { diff --git a/Cesium3DTilesSelection/test/data/MaterialVariants/schema.json b/Cesium3DTilesSelection/test/data/MaterialVariants/schema.json new file mode 100644 index 000000000..3a7004d29 --- /dev/null +++ b/Cesium3DTilesSelection/test/data/MaterialVariants/schema.json @@ -0,0 +1,14 @@ +{ + "classes": { + "MaterialVariants": { + "properties": { + "material_variants": { + "type": "STRING", + "array": true, + "description": "Names of material variants to be expected in the glTF assets", + "semantic": "MATERIAL_VARIANTS" + } + } + } + } +} diff --git a/Cesium3DTilesSelection/test/data/MaterialVariants/tileset-external-schema.json b/Cesium3DTilesSelection/test/data/MaterialVariants/tileset-external-schema.json new file mode 100644 index 000000000..6fcbf8ff0 --- /dev/null +++ b/Cesium3DTilesSelection/test/data/MaterialVariants/tileset-external-schema.json @@ -0,0 +1,76 @@ +{ + "asset": { + "version": "1.0", + "tilesetVersion": "1.2.3" + }, + "extras": { + "name": "Sample Tileset" + }, + "properties": { + "id": { + "minimum": 0, + "maximum": 9 + }, + "Longitude": { + "minimum": -1.3197192952275933, + "maximum": -1.319644104024109 + }, + "Latitude": { + "minimum": 0.698848878034009, + "maximum": 0.6989046192460953 + }, + "Height": { + "minimum": 6.161747192963958, + "maximum": 85.41026367992163 + } + }, + "schemaUri": "schema.json", + "metadata": { + "class": "MaterialVariants", + "properties": { + "material_variants": ["RGB", "RRR", "GGG", "BBB"] + } + }, + "groups": [ + { + "class": "MaterialVariants", + "properties": { + "material_variants": ["RGB", "RRR"] + } + }, + { + "class": "MaterialVariants", + "properties": { + "material_variants": ["GGG", "BBB"] + } + } + ], + "geometricError": 240, + "root": { + "boundingVolume": { + "region": [ + -1.3197209591796106, + 0.6988424218, + -1.3196390408203893, + 0.6989055782, + 0, + 88 + ] + }, + "geometricError": 70, + "refine": "ADD", + "content": { + "uri": "parent.b3dm", + "boundingVolume": { + "region": [ + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0, + 88 + ] + } + } + } +} From 946887abe7dea03f4b0e3fd83b100fd946a19bae Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Mon, 28 Aug 2023 18:41:25 +1000 Subject: [PATCH 230/421] Add getArrayOfStrings helper to JsonValue. --- .../test/TestTilesetSelectionAlgorithm.cpp | 42 ++++--------------- .../include/CesiumUtility/JsonValue.h | 11 +++++ CesiumUtility/src/JsonValue.cpp | 18 ++++++++ 3 files changed, 36 insertions(+), 35 deletions(-) diff --git a/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp b/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp index 1f00eef8f..b15d113ac 100644 --- a/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp +++ b/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp @@ -1272,13 +1272,8 @@ TEST_CASE("Allows access to material variants") { "Names of material variants to be expected in the glTF assets"); REQUIRE(found1->propertyValue.isArray()); - const JsonValue::Array& variantsJson = found1->propertyValue.getArray(); - std::vector variants(variantsJson.size()); - std::transform( - variantsJson.begin(), - variantsJson.end(), - variants.begin(), - [](const JsonValue& value) { return value.getStringOrDefault(""); }); + std::vector variants = + found1->propertyValue.getArrayOfStrings(""); REQUIRE(variants.size() == 4); CHECK(variants[0] == "RGB"); CHECK(variants[1] == "RRR"); @@ -1287,8 +1282,6 @@ TEST_CASE("Allows access to material variants") { std::vector> variantsByGroup; for (const Cesium3DTiles::GroupMetadata& group : pMetadata->groups) { - std::vector& groupVariants = variantsByGroup.emplace_back(); - std::optional found2 = Cesium3DTiles::MetadataQuery::findFirstPropertyWithSemantic( *pMetadata->schema, @@ -1297,12 +1290,7 @@ TEST_CASE("Allows access to material variants") { REQUIRE(found2); REQUIRE(found2->propertyValue.isArray()); - const JsonValue::Array& groupVariantsJson = - found2->propertyValue.getArray(); - groupVariants.reserve(groupVariantsJson.size()); - for (size_t i = 0; i < groupVariantsJson.size(); ++i) { - groupVariants.emplace_back(groupVariantsJson[i].getStringOrDefault("")); - } + variantsByGroup.emplace_back(found2->propertyValue.getArrayOfStrings("")); } std::vector> expected = { @@ -1375,15 +1363,8 @@ TEST_CASE("Allows access to material variants in an external schema") { "Names of material variants to be expected in the glTF assets"); REQUIRE(found1->propertyValue.isArray()); - const JsonValue::Array& variantsJson = found1->propertyValue.getArray(); - std::vector variants(variantsJson.size()); - std::transform( - variantsJson.begin(), - variantsJson.end(), - variants.begin(), - [](const JsonValue& value) { - return value.getStringOrDefault(""); - }); + std::vector variants = + found1->propertyValue.getArrayOfStrings(""); REQUIRE(variants.size() == 4); CHECK(variants[0] == "RGB"); CHECK(variants[1] == "RRR"); @@ -1392,9 +1373,6 @@ TEST_CASE("Allows access to material variants in an external schema") { std::vector> variantsByGroup; for (const Cesium3DTiles::GroupMetadata& group : pMetadata->groups) { - std::vector& groupVariants = - variantsByGroup.emplace_back(); - std::optional found2 = Cesium3DTiles::MetadataQuery::findFirstPropertyWithSemantic( *pMetadata->schema, @@ -1402,14 +1380,8 @@ TEST_CASE("Allows access to material variants in an external schema") { "MATERIAL_VARIANTS"); REQUIRE(found2); REQUIRE(found2->propertyValue.isArray()); - - const JsonValue::Array& groupVariantsJson = - found2->propertyValue.getArray(); - groupVariants.reserve(groupVariantsJson.size()); - for (size_t i = 0; i < groupVariantsJson.size(); ++i) { - groupVariants.emplace_back( - groupVariantsJson[i].getStringOrDefault("")); - } + variantsByGroup.emplace_back( + found2->propertyValue.getArrayOfStrings("")); } std::vector> expected = { diff --git a/CesiumUtility/include/CesiumUtility/JsonValue.h b/CesiumUtility/include/CesiumUtility/JsonValue.h index 47773ed14..26c305e51 100644 --- a/CesiumUtility/include/CesiumUtility/JsonValue.h +++ b/CesiumUtility/include/CesiumUtility/JsonValue.h @@ -421,6 +421,17 @@ class CESIUMUTILITY_API JsonValue final { return std::get(this->value); } + /** + * @brief Gets an array of strings from the value. + * + * @param defaultString The default string to include in the array for an + * element that is not a string. + * @return The array of strings, or an empty array if this value is not an + * array at all. + */ + [[nodiscard]] std::vector + getArrayOfStrings(const std::string& defaultString) const; + /** * @brief Gets the bool from the value. * @return The bool. diff --git a/CesiumUtility/src/JsonValue.cpp b/CesiumUtility/src/JsonValue.cpp index 6c0c8515c..96e1f86cd 100644 --- a/CesiumUtility/src/JsonValue.cpp +++ b/CesiumUtility/src/JsonValue.cpp @@ -1,5 +1,7 @@ #include "CesiumUtility/JsonValue.h" +#include + namespace CesiumUtility { const JsonValue* JsonValue::getValuePtrForKey(const std::string& key) const { @@ -31,3 +33,19 @@ JsonValue* JsonValue::getValuePtrForKey(const std::string& key) { } } // namespace CesiumUtility +std::vector CesiumUtility::JsonValue::getArrayOfStrings( + const std::string& defaultString) const { + if (!this->isArray()) + return std::vector(); + + const JsonValue::Array& array = this->getArray(); + std::vector result(array.size()); + std::transform( + array.begin(), + array.end(), + result.begin(), + [&defaultString](const JsonValue& value) { + return value.getStringOrDefault(defaultString); + }); + return result; +} From 5bf8a7b10e4c424eb2bb95cc7afab3a596f20e6f Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Mon, 28 Aug 2023 19:13:46 +1000 Subject: [PATCH 231/421] struct -> class --- Cesium3DTilesSelection/include/Cesium3DTilesSelection/Tileset.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/Tileset.h b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/Tileset.h index 0b5573d83..27fca4d01 100644 --- a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/Tileset.h +++ b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/Tileset.h @@ -22,7 +22,7 @@ namespace Cesium3DTilesSelection { class TilesetContentManager; -struct TilesetMetadata; +class TilesetMetadata; /** * @brief A Date: Mon, 28 Aug 2023 19:22:41 +1000 Subject: [PATCH 232/421] Fix clang warning. --- CesiumUtility/src/JsonValue.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CesiumUtility/src/JsonValue.cpp b/CesiumUtility/src/JsonValue.cpp index 96e1f86cd..2323e632c 100644 --- a/CesiumUtility/src/JsonValue.cpp +++ b/CesiumUtility/src/JsonValue.cpp @@ -44,8 +44,8 @@ std::vector CesiumUtility::JsonValue::getArrayOfStrings( array.begin(), array.end(), result.begin(), - [&defaultString](const JsonValue& value) { - return value.getStringOrDefault(defaultString); + [&defaultString](const JsonValue& arrayValue) { + return arrayValue.getStringOrDefault(defaultString); }); return result; } From 3f4034101baa8ef7f5c9b09a08debd8de97f1fa4 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Mon, 28 Aug 2023 21:07:29 +1000 Subject: [PATCH 233/421] Fix VS2017/2019 warning. --- Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp b/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp index b15d113ac..b3a67f5e8 100644 --- a/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp +++ b/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp @@ -1428,7 +1428,7 @@ TEST_CASE("Future from loadSchema rejects if schemaUri can't be loaded") { "schema.json", CesiumAsync::HttpHeaders{}, std::make_unique( - 404, + static_cast(404), "doesn't matter", CesiumAsync::HttpHeaders{}, std::vector()))}); From 5b33dd72ad7d43a7a128d0c13c6f6d21f0233356 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Mon, 28 Aug 2023 10:07:08 -0400 Subject: [PATCH 234/421] Fix signed conversions --- CesiumGltf/test/TestPropertyTableView.cpp | 35 +++++++++++++---------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/CesiumGltf/test/TestPropertyTableView.cpp b/CesiumGltf/test/TestPropertyTableView.cpp index 378915c13..00ed7f48b 100644 --- a/CesiumGltf/test/TestPropertyTableView.cpp +++ b/CesiumGltf/test/TestPropertyTableView.cpp @@ -405,8 +405,7 @@ TEST_CASE("Test vecN PropertyTableProperty") { for (int64_t i = 0; i < ivec3Property.size(); ++i) { REQUIRE(ivec3Property.getRaw(i) == values[static_cast(i)]); - REQUIRE(ivec3Property.get(i)); - REQUIRE(*ivec3Property.get(i) == ivec3Property.getRaw(i)); + REQUIRE(ivec3Property.get(i) == ivec3Property.getRaw(i)); } } @@ -1012,8 +1011,7 @@ TEST_CASE("Test boolean PropertyTableProperty") { for (int64_t i = 0; i < boolProperty.size(); ++i) { bool expectedValue = expected[static_cast(i)]; REQUIRE(boolProperty.getRaw(i) == expectedValue); - REQUIRE(boolProperty.get(i)); - REQUIRE(*boolProperty.get(i) == expectedValue); + REQUIRE(boolProperty.get(i) == expectedValue); } } @@ -1097,8 +1095,7 @@ TEST_CASE("Test string PropertyTableProperty") { REQUIRE(stringProperty.status() == PropertyTablePropertyViewStatus::Valid); for (size_t i = 0; i < expected.size(); ++i) { REQUIRE(stringProperty.getRaw(static_cast(i)) == expected[i]); - REQUIRE(stringProperty.get(i)); - REQUIRE(*stringProperty.get(i) == expected[i]); + REQUIRE(stringProperty.get(static_cast(i)) == expected[i]); } } @@ -1224,6 +1221,7 @@ TEST_CASE("Test fixed-length scalar array") { PropertyArrayView array = arrayProperty.getRaw(i); auto maybeArray = arrayProperty.get(i); REQUIRE(maybeArray); + for (int64_t j = 0; j < array.size(); ++j) { REQUIRE(array[j] == values[static_cast(i * 3 + j)]); REQUIRE((*maybeArray)[j] == array[j]); @@ -1354,6 +1352,7 @@ TEST_CASE("Test fixed-length scalar array (normalized)") { PropertyArrayView array = arrayProperty.getRaw(i); auto maybeArray = arrayProperty.get(i); REQUIRE(maybeArray); + for (int64_t j = 0; j < array.size(); ++j) { REQUIRE(array[j] == values[static_cast(i * 3 + j)]); REQUIRE((*maybeArray)[j] == normalize(array[j])); @@ -2011,12 +2010,12 @@ TEST_CASE("Test variable-length vecN array") { property.getRaw(static_cast(i)); REQUIRE(array.size() == static_cast(expected[i].size())); - auto maybeArray = property.get(i); + auto maybeArray = property.get(static_cast(i)); REQUIRE(maybeArray); for (size_t j = 0; j < expected[i].size(); ++j) { auto value = array[static_cast(j)]; REQUIRE(expected[i][j] == value); - REQUIRE((*maybeArray)[j] == value); + REQUIRE((*maybeArray)[static_cast(j)] == value); } } } @@ -2186,12 +2185,12 @@ TEST_CASE("Test variable-length vecN array (normalized)") { property.getRaw(static_cast(i)); REQUIRE(array.size() == static_cast(expected[i].size())); - auto maybeArray = property.get(i); + auto maybeArray = property.get(static_cast(i)); REQUIRE(maybeArray); for (size_t j = 0; j < expected[i].size(); ++j) { auto value = array[static_cast(j)]; REQUIRE(expected[i][j] == value); - REQUIRE((*maybeArray)[j] == normalize(value)); + REQUIRE((*maybeArray)[static_cast(j)] == normalize(value)); } } } @@ -2431,6 +2430,7 @@ TEST_CASE("Test fixed-length matN array (normalized)") { PropertyArrayView array = arrayProperty.getRaw(i); auto maybeArray = arrayProperty.get(i); REQUIRE(maybeArray); + for (int64_t j = 0; j < array.size(); ++j) { REQUIRE(array[j] == values[static_cast(i * 2 + j)]); REQUIRE((*maybeArray)[j] == normalize(array[j])); @@ -2587,12 +2587,12 @@ TEST_CASE("Test variable-length matN array") { property.getRaw(static_cast(i)); REQUIRE(array.size() == static_cast(expected[i].size())); - auto maybeArray = property.get(i); + auto maybeArray = property.get(static_cast(i)); REQUIRE(maybeArray); for (size_t j = 0; j < expected[i].size(); ++j) { auto value = array[static_cast(j)]; REQUIRE(expected[i][j] == value); - REQUIRE((*maybeArray)[j] == value); + REQUIRE((*maybeArray)[static_cast(j)] == value); } } } @@ -2782,9 +2782,10 @@ TEST_CASE("Test variable-length matN array (normalized)") { for (size_t i = 0; i < expected.size(); ++i) { PropertyArrayView array = property.getRaw(static_cast(i)); + REQUIRE(array.size() == static_cast(expected[i].size())); + auto maybeArray = property.get(i); REQUIRE(maybeArray); - REQUIRE(array.size() == static_cast(expected[i].size())); for (size_t j = 0; j < expected[i].size(); ++j) { auto value = array[static_cast(j)]; REQUIRE(expected[i][j] == value); @@ -2875,6 +2876,7 @@ TEST_CASE("Test fixed-length boolean array") { PropertyArrayView array = boolArrayProperty.getRaw(i); auto maybeArray = boolArrayProperty.get(i); REQUIRE(maybeArray); + for (int64_t j = 0; j < array.size(); ++j) { REQUIRE(array[j] == expected[static_cast(i * 3 + j)]); REQUIRE((*maybeArray)[j] == array[j]); @@ -3004,12 +3006,12 @@ TEST_CASE("Test variable-length boolean array") { boolArrayProperty.getRaw(static_cast(i)); REQUIRE(array.size() == static_cast(expected[i].size())); - auto maybeArray = boolArrayProperty.get(i); + auto maybeArray = boolArrayProperty.get(static_cast(i)); REQUIRE(maybeArray); for (size_t j = 0; j < expected[i].size(); ++j) { auto value = array[static_cast(j)]; REQUIRE(expected[i][j] == value); - REQUIRE((*maybeArray)[j] == value); + REQUIRE((*maybeArray)[static_cast(j)] == value); } } } @@ -4645,6 +4647,7 @@ TEST_CASE("Test callback for scalar array PropertyTableProperty") { PropertyArrayView array = propertyValue.getRaw(i); auto maybeArray = propertyValue.get(i); REQUIRE(maybeArray); + for (int64_t j = 0; j < array.size(); ++j) { REQUIRE(array[j] == values[static_cast(i * 3 + j)]); REQUIRE((*maybeArray)[j] == array[j]); @@ -4720,6 +4723,7 @@ TEST_CASE("Test callback for scalar array PropertyTableProperty (normalized)") { PropertyArrayView array = propertyValue.getRaw(i); auto maybeArray = propertyValue.get(i); REQUIRE(maybeArray); + for (int64_t j = 0; j < array.size(); ++j) { REQUIRE(array[j] == values[static_cast(i * 3 + j)]); REQUIRE((*maybeArray)[j] == normalize(array[j])); @@ -5066,6 +5070,7 @@ TEST_CASE("Test callback for boolean array PropertyTableProperty") { PropertyArrayView array = propertyValue.getRaw(i); auto maybeArray = propertyValue.get(i); REQUIRE(maybeArray); + for (int64_t j = 0; j < array.size(); ++j) { REQUIRE(array[j] == expected[static_cast(i * 3 + j)]); REQUIRE((*maybeArray)[j] == array[j]); From 36337303b1d7d60b490a4ff78ebe20ef48246596 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Mon, 28 Aug 2023 10:49:18 -0400 Subject: [PATCH 235/421] Fix sign conversion --- CesiumGltf/test/TestPropertyTableView.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CesiumGltf/test/TestPropertyTableView.cpp b/CesiumGltf/test/TestPropertyTableView.cpp index 00ed7f48b..b3c4e33ef 100644 --- a/CesiumGltf/test/TestPropertyTableView.cpp +++ b/CesiumGltf/test/TestPropertyTableView.cpp @@ -2784,12 +2784,12 @@ TEST_CASE("Test variable-length matN array (normalized)") { property.getRaw(static_cast(i)); REQUIRE(array.size() == static_cast(expected[i].size())); - auto maybeArray = property.get(i); + auto maybeArray = property.get(static_cast(i)); REQUIRE(maybeArray); for (size_t j = 0; j < expected[i].size(); ++j) { auto value = array[static_cast(j)]; REQUIRE(expected[i][j] == value); - REQUIRE((*maybeArray)[j] == normalize(value)); + REQUIRE((*maybeArray)[static_cast(j)] == normalize(value)); } } } From 7f13e1c3f4f1adae84fbd5d7e827cb810edd47cf Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Mon, 28 Aug 2023 11:21:10 -0400 Subject: [PATCH 236/421] Another CI fix --- CesiumGltf/test/TestPropertyTexturePropertyView.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CesiumGltf/test/TestPropertyTexturePropertyView.cpp b/CesiumGltf/test/TestPropertyTexturePropertyView.cpp index 2450c926f..87dea27b2 100644 --- a/CesiumGltf/test/TestPropertyTexturePropertyView.cpp +++ b/CesiumGltf/test/TestPropertyTexturePropertyView.cpp @@ -485,7 +485,7 @@ void checkNormalizedTextureArrayValues( auto expectedValue = *(expectedTransformed[i]); REQUIRE(maybeValue->size() == static_cast(expectedValue.size())); for (int64_t j = 0; j < maybeValue->size(); j++) { - REQUIRE((*maybeValue)[j] == expectedValue[j]); + REQUIRE((*maybeValue)[j] == expectedValue[static_cast(j)]); } } } From f51012e6023be5b54421412c73bf2f18b9ebe95f Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Mon, 28 Aug 2023 13:18:51 -0400 Subject: [PATCH 237/421] Fix documentation and test --- .../CesiumGltf/PropertyTexturePropertyView.h | 3 +-- .../include/CesiumGltf/PropertyTextureView.h | 21 +++++++++---------- .../test/TestPropertyTexturePropertyView.cpp | 2 +- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h index 70ff28ce6..821e3932d 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h @@ -7,7 +7,6 @@ #include "CesiumGltf/PropertyView.h" #include "CesiumGltf/Sampler.h" -#include #include #include #include @@ -228,7 +227,7 @@ inline double applySamplerWrapT(const double v, const int32_t wrapT) { } /** - * @brief A view of the non-normalized data specified by a {@link PropertyTextureProperty}. + * @brief A view of the data specified by a {@link PropertyTextureProperty}. * * Provides utilities to sample the property texture property using texture * coordinates. diff --git a/CesiumGltf/include/CesiumGltf/PropertyTextureView.h b/CesiumGltf/include/CesiumGltf/PropertyTextureView.h index 8b13e84b2..39e65b077 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTextureView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTextureView.h @@ -84,16 +84,15 @@ class PropertyTextureView { * * This method will validate the EXT_structural_metadata format to ensure * {@link PropertyTexturePropertyView} retrieves the correct data. T must - * be a scalar with a supported component type (uint8_t, uint16_t, uint32_t, - * float), a glm vecN composed of one of the scalar types, or a - * PropertyArrayView containing one of the scalar types. + * be a scalar with a supported component type (int8_t, uint8_t, int16_t, + * uint16_t, int32_t, uint32_t, float), a glm vecN composed of one of the + * scalar types, or a PropertyArrayView containing one of the scalar types. * * If T does not match the type specified by the class property, this returns * an invalid PropertyTexturePropertyView. Likewise, if the value of - * Normalized - * does not match the value of {@ClassProperty::normalized} for that class property, - * this returns an invalid property view. Only types with integer components - * may be normalized. + * Normalized does not match the value of {@ClassProperty::normalized} for that + * class property, this returns an invalid property view. Only types with + * integer components may be normalized. * * @tparam T The C++ type corresponding to the type of the data retrieved. * @tparam Normalized Whether the property is normalized. Only applicable to @@ -125,10 +124,10 @@ class PropertyTextureView { * of the property with the specified name. * * This method will validate the EXT_structural_metadata format to ensure - * {@link PropertyTexturePropertyView} retrieves the correct data. T must be - * a scalar with a supported component type (uint8_t, uint16_t, uint32_t, - * float), a glm vecN composed of one of the scalar types, or a - * PropertyArrayView containing one of the scalar types. + * {@link PropertyTexturePropertyView} retrieves the correct data. . T must + * be a scalar with a supported component type (int8_t, uint8_t, int16_t, + * uint16_t, int32_t, uint32_t, float), a glm vecN composed of one of the + * scalar types, or a PropertyArrayView containing one of the scalar types. * * If the property is somehow invalid, an empty {@link PropertyTexturePropertyView} * with an error status will be passed to the callback. Otherwise, a valid diff --git a/CesiumGltf/test/TestPropertyTexturePropertyView.cpp b/CesiumGltf/test/TestPropertyTexturePropertyView.cpp index 87dea27b2..638c721a4 100644 --- a/CesiumGltf/test/TestPropertyTexturePropertyView.cpp +++ b/CesiumGltf/test/TestPropertyTexturePropertyView.cpp @@ -485,7 +485,7 @@ void checkNormalizedTextureArrayValues( auto expectedValue = *(expectedTransformed[i]); REQUIRE(maybeValue->size() == static_cast(expectedValue.size())); for (int64_t j = 0; j < maybeValue->size(); j++) { - REQUIRE((*maybeValue)[j] == expectedValue[static_cast(j)]); + REQUIRE((*maybeValue)[j] == expectedValue[static_cast(j)]); } } } From 763271ac7468c749383399c07a1842022d6305a0 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Mon, 28 Aug 2023 13:55:55 -0400 Subject: [PATCH 238/421] Add missing const int to source file --- CesiumGltf/src/PropertyView.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/CesiumGltf/src/PropertyView.cpp b/CesiumGltf/src/PropertyView.cpp index 030469450..0f62eeed7 100644 --- a/CesiumGltf/src/PropertyView.cpp +++ b/CesiumGltf/src/PropertyView.cpp @@ -10,6 +10,7 @@ const PropertyViewStatusType PropertyViewStatus::ErrorTypeMismatch; const PropertyViewStatusType PropertyViewStatus::ErrorComponentTypeMismatch; const PropertyViewStatusType PropertyViewStatus::ErrorArrayTypeMismatch; const PropertyViewStatusType PropertyViewStatus::ErrorInvalidNormalization; +const PropertyViewStatusType PropertyViewStatus::ErrorNormalizationMismatch; const PropertyViewStatusType PropertyViewStatus::ErrorInvalidOffset; const PropertyViewStatusType PropertyViewStatus::ErrorInvalidScale; const PropertyViewStatusType PropertyViewStatus::ErrorInvalidMax; From a8f46ab0c5cb630227a457af5400130391d612fc Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Mon, 28 Aug 2023 17:49:51 -0400 Subject: [PATCH 239/421] Add PropertyAttributeView and PropertyAttributePropertyView --- CesiumGltf/include/CesiumGltf/AccessorView.h | 2 +- .../PropertyAttributePropertyView.h | 364 ++++++++++ .../CesiumGltf/PropertyAttributeView.h | 686 ++++++++++++++++++ .../include/CesiumGltf/PropertyTableView.h | 26 +- .../CesiumGltf/PropertyTexturePropertyView.h | 4 +- .../include/CesiumGltf/PropertyTextureView.h | 26 +- CesiumGltf/include/CesiumGltf/PropertyType.h | 2 + CesiumGltf/include/CesiumGltf/PropertyView.h | 84 +-- CesiumGltf/src/PropertyAttributeView.cpp | 94 +++ CesiumGltf/src/PropertyType.cpp | 11 + CesiumGltf/test/TestPropertyType.cpp | 44 ++ 11 files changed, 1261 insertions(+), 82 deletions(-) create mode 100644 CesiumGltf/include/CesiumGltf/PropertyAttributePropertyView.h create mode 100644 CesiumGltf/include/CesiumGltf/PropertyAttributeView.h create mode 100644 CesiumGltf/src/PropertyAttributeView.cpp diff --git a/CesiumGltf/include/CesiumGltf/AccessorView.h b/CesiumGltf/include/CesiumGltf/AccessorView.h index b693a4df4..3d73bf2e7 100644 --- a/CesiumGltf/include/CesiumGltf/AccessorView.h +++ b/CesiumGltf/include/CesiumGltf/AccessorView.h @@ -403,7 +403,7 @@ createAccessorView( } // TODO Print a warning here??? return callback(AccessorView>( - AccessorViewStatus::InvalidComponentType)); + AccessorViewStatus::InvalidType)); } } // namespace CesiumImpl diff --git a/CesiumGltf/include/CesiumGltf/PropertyAttributePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyAttributePropertyView.h new file mode 100644 index 000000000..7439758da --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/PropertyAttributePropertyView.h @@ -0,0 +1,364 @@ +#pragma once + +#include "CesiumGltf/AccessorView.h" +#include "CesiumGltf/PropertyAttributeProperty.h" +#include "CesiumGltf/PropertyTransformations.h" +#include "CesiumGltf/PropertyTypeTraits.h" +#include "CesiumGltf/PropertyView.h" + +#include +#include +#include + +namespace CesiumGltf { +/** + * @brief Indicates the status of a property attribute property view. + * + * The {@link PropertyAttributePropertyView} constructor always completes + * successfully. However it may not always reflect the actual content of the + * corresponding property texture property. This enumeration provides the + * reason. + */ +class PropertyAttributePropertyViewStatus : public PropertyViewStatus { +public: + /** + * @brief This property view was initialized from an invalid + * {@link PropertyAttribute}. + */ + static const int ErrorInvalidPropertyAttribute = 13; + + /** + * @brief This property view is associated with a {@link ClassProperty} of an + * unsupported type. + */ + static const int ErrorUnsupportedProperty = 14; + + /** + * @brief This property view was initialized with a primitive that does not + * contain the specified attribute. + */ + static const int ErrorMissingAttribute = 15; + + /** + * @brief This property view's attribute does not have a valid accessor index. + */ + static const int ErrorInvalidAccessor = 16; + + /** + * @brief This property view's type does not match the type of the accessor it + * uses. + */ + static const int ErrorAccessorTypeMismatch = 17; + + /** + * @brief This property view's component type does not match the type of the + * accessor it uses. + */ + static const int ErrorAccessorComponentTypeMismatch = 18; + + /** + * @brief This property view's normalization does not match the normalization + * of the accessor it uses. + */ + static const int ErrorAccessorNormalizationMismatch = 19; + + /** + * @brief This property view uses an accessor that does not have a valid + * buffer view index. + */ + static const int ErrorInvalidBufferView = 20; + + /** + * @brief This property view uses a buffer view that does not have a valid + * buffer index. + */ + static const int ErrorInvalidBuffer = 21; + + /** + * @brief This property view uses an accessor that points outside the bounds + * of its target buffer view. + */ + static const PropertyViewStatusType ErrorAccessorOutOfBounds = 22; + + /** + * @brief This property view uses a buffer view that points outside the bounds + * of its target buffer. + */ + static const PropertyViewStatusType ErrorBufferViewOutOfBounds = 23; +}; + +/** + * @brief A view of the data specified by a {@link PropertyAttributeProperty}. + * + * Ideally, property attribute properties can be initialized as vertex + * attributes in the target rendering context. However, some runtime engines do + * not allow custom vertex attributes. To compensate, this view can be used to + * sample the property attributes property via vertex index. + * + * @tparam ElementType The type of the elements represented in the property view + * @tparam Normalized Whether or not the property is normalized. If normalized, + * the elements can be retrieved as normalized floating-point numbers, as + * opposed to their integer values. + */ +template +class PropertyAttributePropertyView; + +/** + * @brief A view of the non-normalized data specified by a + * {@link PropertyAttributeProperty}. + * + * Ideally, property attribute properties can be initialized as vertex + * attributes in the target rendering context. However, some runtime engines do + * not allow custom vertex attributes. This view can be used instead to sample + * the property attributes property via vertex index. + * + * @tparam ElementType The type of the elements represented in the property view + */ +template +class PropertyAttributePropertyView + : public PropertyView { +public: + /** + * @brief Constructs an invalid instance for a non-existent property. + */ + PropertyAttributePropertyView() noexcept + : PropertyView(), _accessor{} {} + + /** + * @brief Constructs an invalid instance for an erroneous property. + * + * @param status The code from {@link PropertyAttributePropertyViewStatus} indicating the error with the property. + */ + PropertyAttributePropertyView(PropertyViewStatusType status) noexcept + : PropertyView(status), _accessor{} { + assert( + this->_status != PropertyAttributePropertyViewStatus::Valid && + "An empty property view should not be constructed with a valid status"); + } + + /** + * @brief Construct a view of the data specified by a {@link PropertyAttributeProperty}. + * + * @param property The {@link PropertyAttributeProperty} + * @param classProperty The {@link ClassProperty} this property conforms to. + * @param accsesorView The {@link AccessorView} for the data that this property is + * associated with. + */ + PropertyAttributePropertyView( + const PropertyAttributeProperty& property, + const ClassProperty& classProperty, + const AccessorView& accessorView) noexcept + : PropertyView(classProperty, property), + _accessor{accessorView} {} + + /** + * @brief Gets the value of the property for the given texture coordinates + * with all value transforms applied. That is, if the property specifies an + * offset and scale, they will be applied to the value before the value is + * returned. + * + * If this property has a specified "no data" value, this will return the + * property's default value for any elements that equal this "no data" value. + * If the property did not specify a default value, this returns std::nullopt. + * + * @param index The vertex index. + * + * @return The value of the property for the given vertex, or std::nullopt if + * it matches the "no data" value + */ + std::optional get(int64_t index) const noexcept { + ElementType value = getRaw(index); + + if (value == this->noData()) { + return this->defaultValue(); + } + + return transformValue(value, this->offset(), this->scale()); + } + + /** + * @brief Gets the raw value of the property for the given vertex index. + * + * If this property has a specified "no data" value, the raw value will still + * be returned, even if it equals the "no data" value. + * + * @param index The vertex index. + * + * @return The value of the property for the given vertex. + */ + ElementType getRaw(int64_t index) const noexcept { + assert( + this->_status == PropertyAttributePropertyViewStatus::Valid && + "Check the status() first to make sure view is valid"); + assert( + size() > 0 && + "Check the size() of the view to make sure it's not empty"); + assert(index >= 0 && "index must be non-negative"); + assert(index < size() && "index must be less than size"); + + return _accessor[index]; + } + + /** + * @brief Get the number of elements in this PropertyAttributePropertyView. + * If the view is valid, this returns the count of the elements in the + * attribute's accessor. Otherwise, this returns 0. + * + * @return The number of elements in this PropertyAttributePropertyView. + */ + int64_t size() const noexcept { + if (this->_status != PropertyAttributePropertyViewStatus::Valid) { + return 0; + } + + return _accessor.size(); + } + +private: + AccessorView _accessor; +}; + +/** + * @brief A view of the normalized data specified by a + * {@link PropertyAttributeProperty}. + * + * Ideally, property attribute properties can be initialized as vertex + * attributes in the target rendering context. However, some runtime engines do + * not allow custom vertex attributes. This view can be used instead to sample + * the property attributes property via vertex index. + * + * @tparam ElementType The type of the elements represented in the property view + */ +template +class PropertyAttributePropertyView + : public PropertyView { +private: + using NormalizedType = typename TypeToNormalizedType::type; + +public: + /** + * @brief Constructs an invalid instance for a non-existent property. + */ + PropertyAttributePropertyView() noexcept + : PropertyView(), _accessor{} {} + + /** + * @brief Constructs an invalid instance for an erroneous property. + * + * @param status The code from {@link PropertyAttributePropertyViewStatus} indicating the error with the property. + */ + PropertyAttributePropertyView(PropertyViewStatusType status) noexcept + : PropertyView(status), _accessor{} { + assert( + this->_status != PropertyAttributePropertyViewStatus::Valid && + "An empty property view should not be constructed with a valid status"); + } + + /** + * @brief Construct a view of the data specified by a {@link PropertyAttributeProperty}. + * + * @param property The {@link PropertyAttributeProperty} + * @param classProperty The {@link ClassProperty} this property conforms to. + * @param accsesorView The {@link AccessorView} for the data that this property is + * associated with. + */ + PropertyAttributePropertyView( + const PropertyAttributeProperty& property, + const ClassProperty& classProperty, + const AccessorView& accessorView) noexcept + : PropertyView(classProperty, property), + _accessor{accessorView} {} + + /** + * @brief Gets the value of the property for the given texture coordinates + * with all value transforms applied. That is, if the property specifies an + * offset and scale, they will be applied to the value before the value is + * returned. + * + * If this property has a specified "no data" value, this will return the + * property's default value for any elements that equal this "no data" value. + * If the property did not specify a default value, this returns std::nullopt. + * + * @param index The vertex index. + * + * @return The value of the property for the given vertex, or std::nullopt if + * it matches the "no data" value + */ + std::optional get(int64_t index) const noexcept { + ElementType value = getRaw(index); + + if (value == this->noData()) { + return this->defaultValue(); + } + + if constexpr (IsMetadataScalar::value) { + return transformValue( + normalize(value), + this->offset(), + this->scale()); + } + + if constexpr (IsMetadataVecN::value) { + constexpr glm::length_t N = ElementType::length(); + using T = typename ElementType::value_type; + using NormalizedT = typename NormalizedType::value_type; + return transformValue>( + normalize(value), + this->offset(), + this->scale()); + } + + if constexpr (IsMetadataMatN::value) { + constexpr glm::length_t N = ElementType::length(); + using T = typename ElementType::value_type; + using NormalizedT = typename NormalizedType::value_type; + return transformValue>( + normalize(value), + this->offset(), + this->scale()); + } + } + + /** + * @brief Gets the raw value of the property for the given vertex index. + * + * If this property has a specified "no data" value, the raw value will still + * be returned, even if it equals the "no data" value. + * + * @param index The vertex index. + * + * @return The value of the property for the given vertex. + */ + ElementType getRaw(int64_t index) const noexcept { + assert( + this->_status == PropertyAttributePropertyViewStatus::Valid && + "Check the status() first to make sure view is valid"); + assert( + size() > 0 && + "Check the size() of the view to make sure it's not empty"); + assert(index >= 0 && "index must be non-negative"); + assert(index < size() && "index must be less than size"); + + return _accessor[index]; + } + + /** + * @brief Get the number of elements in this PropertyAttributePropertyView. + * If the view is valid, this returns the count of the elements in the + * attribute's accessor. Otherwise, this returns 0. + * + * @return The number of elements in this PropertyAttributePropertyView. + */ + int64_t size() const noexcept { + if (this->_status != PropertyAttributePropertyViewStatus::Valid) { + return 0; + } + + return _accessor.size(); + } + +private: + AccessorView _accessor; +}; + +} // namespace CesiumGltf diff --git a/CesiumGltf/include/CesiumGltf/PropertyAttributeView.h b/CesiumGltf/include/CesiumGltf/PropertyAttributeView.h new file mode 100644 index 000000000..eb76620c5 --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/PropertyAttributeView.h @@ -0,0 +1,686 @@ +#pragma once + +#include "CesiumGltf/Class.h" +#include "CesiumGltf/ClassProperty.h" +#include "CesiumGltf/ExtensionModelExtStructuralMetadata.h" +#include "CesiumGltf/PropertyAttribute.h" +#include "CesiumGltf/PropertyAttributePropertyView.h" +#include "Model.h" + +namespace CesiumGltf { +/** + * @brief Indicates the status of a property attribute view. + * + * The {@link PropertyAttributeView} constructor always completes successfully. + * However it may not always reflect the actual content of the + * {@link PropertyAttribute}. This enumeration provides the reason. + */ +enum class PropertyAttributeViewStatus { + /** + * @brief This property attribute view is valid and ready to use. + */ + Valid, + + /** + * @brief The glTF is missing the EXT_structural_metadata extension. + */ + ErrorMissingMetadataExtension, + + /** + * @brief The glTF EXT_structural_metadata extension doesn't contain a schema. + */ + ErrorMissingSchema, + + /** + * @brief The property attribute's specified class could not be found in the + * extension. + */ + ErrorClassNotFound +}; + +PropertyType getAccessorTypeAsPropertyType(const Accessor& accessor); + +PropertyComponentType +getAccessorComponentTypeAsPropertyComponentType(const Accessor& accessor); + +/** + * @brief A view on a {@link PropertyAttribute}. + * + * This should be used to get a {@link PropertyAttributePropertyView} of a property + * in the property attribute. It will validate the EXT_structural_metadata + * format and ensure {@link PropertyAttributePropertyView} does not access data out + * of bounds. + */ +class PropertyAttributeView { +public: + /** + * @brief Construct a PropertyAttributeView. + * + * @param model The glTF that contains the property attribute's data. + * @param propertyAttribute The {@link PropertyAttribute} from which + * the view will retrieve data. + */ + PropertyAttributeView( + const Model& model, + const PropertyAttribute& propertyAttribute) noexcept; + + /** + * @brief Gets the status of this property attribute view. + * + * Indicates whether the view accurately reflects the property attribute's + * data, or whether an error occurred. + */ + PropertyAttributeViewStatus status() const noexcept { return this->_status; } + + /** + * @brief Finds the {@link ClassProperty} that + * describes the type information of the property with the specified name. + * @param propertyName The name of the property to retrieve the class for. + * @return A pointer to the {@link ClassProperty}. + * Return nullptr if the PropertyAttributeView is invalid or if no class + * property was found. + */ + const ClassProperty* getClassProperty(const std::string& propertyName) const; + + /** + * @brief Gets a {@link PropertyAttributePropertyView} that views the data of a + * property stored in the {@link PropertyAttribute}. + * + * This method will validate the EXT_structural_metadata format to ensure + * {@link PropertyAttributePropertyView} retrieves the correct data. T must + * be a scalar with a supported component type (int8_t, uint8_t, int16_t, + * uint16_t, uint32_t, float), a glm vecN composed of one of the scalar types, + * or a glm matN containing one of the scalar types. + * + * If T does not match the type specified by the class property, this returns + * an invalid PropertyAttributePropertyView. Likewise, if the value of + * Normalized does not match the value of {@ClassProperty::normalized} for that + * class property, this returns an invalid property view. Only types with + * integer components may be normalized. + * + * @tparam T The C++ type corresponding to the type of the data retrieved. + * @tparam Normalized Whether the property is normalized. Only applicable to + * types with integer components. + * @param primitive The target primitive + * @param propertyName The name of the property to retrieve data from + * @return A {@link PropertyAttributePropertyView} of the property. If no valid + * property is found, the property view will be invalid. + */ + template + PropertyAttributePropertyView getPropertyView( + const std::string& propertyName, + const MeshPrimitive& primitive) const { + if (this->_status != PropertyAttributeViewStatus::Valid) { + return PropertyAttributePropertyView( + PropertyAttributePropertyViewStatus::ErrorInvalidPropertyAttribute); + } + const ClassProperty* pClassProperty = getClassProperty(propertyName); + if (!pClassProperty) { + return PropertyAttributePropertyView( + PropertyAttributePropertyViewStatus::ErrorNonexistentProperty); + } + + if constexpr ( + IsMetadataArray::value || IsMetadataBoolean::value || + IsMetadataString::value) { + return PropertyAttributePropertyView( + PropertyAttributePropertyViewStatus::ErrorUnsupportedProperty); + } + + return getPropertyViewImpl( + propertyName, + *pClassProperty, + primitive); + } + + /** + * @brief Gets a {@link PropertyAttributePropertyView} through a callback that accepts a + * property name and a {@link PropertyAttributePropertyView} that views the data + * of the property with the specified name. + * + * This method will validate the EXT_structural_metadata format to ensure + * {@link PropertyAttributePropertyView} retrieves the correct data. T must + * be a scalar with a supported component type (int8_t, uint8_t, int16_t, + * uint16_t, uint32_t, float), a glm vecN composed of one of the scalar types, + * or a glm matN containing one of the scalar types. + * + * If the property is somehow invalid, an empty {@link PropertyAttributePropertyView} + * with an error status will be passed to the callback. Otherwise, a valid + * property view will be passed to the callback. + * + * @param propertyName The name of the property to retrieve data from + * @tparam callback A callback function that accepts a property name and a + * {@link PropertyAttributePropertyView} + */ + template + void getPropertyView( + const std::string& propertyName, + const MeshPrimitive& primitive, + Callback&& callback) const { + if (this->_status != PropertyAttributeViewStatus::Valid) { + callback( + propertyName, + PropertyAttributePropertyView( + PropertyAttributePropertyViewStatus:: + ErrorInvalidPropertyAttribute)); + return; + } + + const ClassProperty* pClassProperty = getClassProperty(propertyName); + if (!pClassProperty) { + callback( + propertyName, + PropertyAttributePropertyView( + PropertyAttributePropertyViewStatus::ErrorNonexistentProperty)); + return; + } + + PropertyType type = convertStringToPropertyType(pClassProperty->type); + PropertyComponentType componentType = PropertyComponentType::None; + if (pClassProperty->componentType) { + componentType = + convertStringToPropertyComponentType(*pClassProperty->componentType); + } + + bool normalized = pClassProperty->normalized; + if (normalized && !isPropertyComponentTypeInteger(componentType)) { + callback( + propertyName, + PropertyAttributePropertyView( + PropertyAttributePropertyViewStatus::ErrorInvalidNormalization)); + return; + } + + if (type == PropertyType::Scalar) { + if (normalized) { + getScalarPropertyViewImpl( + propertyName, + *pClassProperty, + primitive, + componentType, + std::forward(callback)); + } else { + getScalarPropertyViewImpl( + propertyName, + *pClassProperty, + primitive, + componentType, + std::forward(callback)); + } + return; + } + + if (isPropertyTypeVecN(type)) { + if (normalized) { + getVecNPropertyViewImpl( + propertyName, + *pClassProperty, + primitive, + type, + componentType, + std::forward(callback)); + } else { + getVecNPropertyViewImpl( + propertyName, + *pClassProperty, + primitive, + type, + componentType, + std::forward(callback)); + } + return; + } + + if (isPropertyTypeMatN(type)) { + if (normalized) { + getMatNPropertyViewImpl( + propertyName, + *pClassProperty, + primitive, + type, + componentType, + std::forward(callback)); + } else { + getMatNPropertyViewImpl( + propertyName, + *pClassProperty, + primitive, + type, + componentType, + std::forward(callback)); + } + return; + } + + callback( + propertyName, + PropertyAttributePropertyView( + PropertyAttributePropertyViewStatus::ErrorUnsupportedProperty)); + return; + } + + /** + * @brief Iterates over each property in the {@link PropertyAttribute} with a callback + * that accepts a property name and a {@link PropertyAttributePropertyView} to view + * the data stored in the {@link PropertyAttributeProperty}. + * + * This method will validate the EXT_structural_metadata format to ensure + * {@link PropertyAttributePropertyView} retrieves the correct data. T must be + * a scalar with a supported component type (uint8_t, uint16_t, uint32_t, + * float), a glm vecN composed of one of the scalar types, or a + * PropertyArrayView containing one of the scalar types. + * + * If the property is invalid, an empty {@link PropertyAttributePropertyView} with an + * error status will be passed to the callback. Otherwise, a valid property + * view will be passed to the callback. + * + * @param propertyName The name of the property to retrieve data from + * @tparam callback A callback function that accepts property name and + * {@link PropertyAttributePropertyView} + */ + template + void + forEachProperty(const MeshPrimitive& primitive, Callback&& callback) const { + for (const auto& property : this->_pClass->properties) { + getPropertyView( + property.first, + primitive, + std::forward(callback)); + } + } + +private: + template + PropertyAttributePropertyView getPropertyViewImpl( + const std::string& propertyName, + const ClassProperty& classProperty, + const MeshPrimitive& primitive) const { + auto propertyAttributePropertyIter = + _pPropertyAttribute->properties.find(propertyName); + if (propertyAttributePropertyIter == + _pPropertyAttribute->properties.end()) { + return PropertyAttributePropertyView( + PropertyAttributePropertyViewStatus::ErrorNonexistentProperty); + } + + const PropertyAttributeProperty& propertyAttributeProperty = + propertyAttributePropertyIter->second; + + return createPropertyView( + classProperty, + propertyAttributeProperty, + primitive); + } + + template + void getScalarPropertyViewImpl( + const std::string& propertyName, + const ClassProperty& classProperty, + const MeshPrimitive& primitive, + PropertyComponentType componentType, + Callback&& callback) const { + switch (componentType) { + case PropertyComponentType::Int8: + callback( + propertyName, + getPropertyViewImpl( + propertyName, + classProperty, + primitive)); + return; + case PropertyComponentType::Uint8: + callback( + propertyName, + getPropertyViewImpl( + propertyName, + classProperty, + primitive)); + return; + case PropertyComponentType::Int16: + callback( + propertyName, + getPropertyViewImpl( + propertyName, + classProperty, + primitive)); + return; + case PropertyComponentType::Uint16: + callback( + propertyName, + getPropertyViewImpl( + propertyName, + classProperty, + primitive)); + break; + case PropertyComponentType::Uint32: + callback( + propertyName, + getPropertyViewImpl( + propertyName, + classProperty, + primitive)); + break; + case PropertyComponentType::Float32: + callback( + propertyName, + getPropertyViewImpl( + propertyName, + classProperty, + primitive)); + break; + default: + callback( + propertyName, + PropertyAttributePropertyView( + PropertyAttributePropertyViewStatus::ErrorUnsupportedProperty)); + break; + } + } + + template + void getVecNPropertyViewImpl( + const std::string& propertyName, + const ClassProperty& classProperty, + const MeshPrimitive& primitive, + PropertyComponentType componentType, + Callback&& callback) const { + switch (componentType) { + case PropertyComponentType::Int8: + callback( + propertyName, + getPropertyViewImpl, Normalized>( + propertyName, + classProperty, + primitive)); + break; + case PropertyComponentType::Uint8: + callback( + propertyName, + getPropertyViewImpl, Normalized>( + propertyName, + classProperty, + primitive)); + break; + case PropertyComponentType::Int16: + callback( + propertyName, + getPropertyViewImpl, Normalized>( + propertyName, + classProperty, + primitive)); + break; + case PropertyComponentType::Uint16: + callback( + propertyName, + getPropertyViewImpl, Normalized>( + propertyName, + classProperty, + primitive)); + break; + default: + case PropertyComponentType::Uint32: + callback( + propertyName, + getPropertyViewImpl, Normalized>( + propertyName, + classProperty, + primitive)); + break; + case PropertyComponentType::Float32: + callback( + propertyName, + getPropertyViewImpl, false>( + propertyName, + classProperty, + primitive)); + break; + default: + callback( + propertyName, + PropertyAttributePropertyView( + PropertyAttributePropertyViewStatus::ErrorUnsupportedProperty)); + break; + } + } + + template + void getVecNPropertyViewImpl( + const std::string& propertyName, + const ClassProperty& classProperty, + const MeshPrimitive& primitive, + PropertyType type, + PropertyComponentType componentType, + Callback&& callback) const { + const glm::length_t N = getDimensionsFromPropertyType(type); + switch (N) { + case 2: + getVecNPropertyViewImpl( + propertyName, + classProperty, + primitive, + componentType, + std::forward(callback)); + break; + case 3: + getVecNPropertyViewImpl( + propertyName, + classProperty, + primitive, + componentType, + std::forward(callback)); + break; + case 4: + getVecNPropertyViewImpl( + propertyName, + classProperty, + primitive, + componentType, + std::forward(callback)); + break; + default: + callback( + propertyName, + PropertyAttributePropertyView( + PropertyAttributePropertyViewStatus::ErrorTypeMismatch)); + break; + } + } + + template + void getMatNPropertyViewImpl( + const std::string& propertyName, + const ClassProperty& classProperty, + const MeshPrimitive& primitive, + PropertyComponentType componentType, + Callback&& callback) const { + switch (componentType) { + case PropertyComponentType::Int8: + callback( + propertyName, + getPropertyViewImpl, Normalized>( + propertyName, + classProperty, + primitive)); + break; + case PropertyComponentType::Uint8: + callback( + propertyName, + getPropertyViewImpl, Normalized>( + propertyName, + classProperty, + primitive)); + break; + case PropertyComponentType::Int16: + callback( + propertyName, + getPropertyViewImpl, Normalized>( + propertyName, + classProperty, + primitive)); + break; + case PropertyComponentType::Uint16: + callback( + propertyName, + getPropertyViewImpl, Normalized>( + propertyName, + classProperty, + primitive)); + break; + case PropertyComponentType::Uint32: + callback( + propertyName, + getPropertyViewImpl, Normalized>( + propertyName, + classProperty, + primitive)); + break; + case PropertyComponentType::Float32: + callback( + propertyName, + getPropertyViewImpl, false>( + propertyName, + classProperty, + primitive)); + break; + default: + callback( + propertyName, + PropertyAttributePropertyView( + PropertyAttributePropertyViewStatus::ErrorUnsupportedProperty)); + break; + } + } + + template + void getMatNPropertyViewImpl( + const std::string& propertyName, + const ClassProperty& classProperty, + const MeshPrimitive& primitive, + PropertyType type, + PropertyComponentType componentType, + Callback&& callback) const { + glm::length_t N = getDimensionsFromPropertyType(type); + switch (N) { + case 2: + getMatNPropertyViewImpl( + propertyName, + classProperty, + primitive, + componentType, + std::forward(callback)); + break; + case 3: + getMatNPropertyViewImpl( + propertyName, + classProperty, + primitive, + componentType, + std::forward(callback)); + break; + case 4: + getMatNPropertyViewImpl( + propertyName, + classProperty, + primitive, + componentType, + std::forward(callback)); + break; + default: + callback( + propertyName, + PropertyAttributePropertyView( + PropertyAttributePropertyViewStatus::ErrorTypeMismatch)); + break; + } + } + + template + PropertyAttributePropertyView createPropertyView( + const ClassProperty& classProperty, + const PropertyAttributeProperty& propertyAttributeProperty, + const MeshPrimitive& primitive) const { + const PropertyType type = convertStringToPropertyType(classProperty.type); + if (TypeToPropertyType::value != type) { + return PropertyAttributePropertyView( + PropertyAttributePropertyViewStatus::ErrorTypeMismatch); + } + + const PropertyComponentType componentType = + convertStringToPropertyComponentType( + classProperty.componentType.value_or("")); + if (TypeToPropertyType::component != componentType) { + return PropertyAttributePropertyView( + PropertyAttributePropertyViewStatus::ErrorComponentTypeMismatch); + } + + if (classProperty.normalized != Normalized) { + return PropertyAttributePropertyView( + PropertyAttributePropertyViewStatus::ErrorNormalizationMismatch); + } + + if (primitive.attributes.find(propertyAttributeProperty.attribute) == + primitive.attributes.end()) { + return PropertyAttributePropertyView( + PropertyAttributePropertyViewStatus::ErrorMissingAttribute); + } + + const Accessor* pAccessor = _pModel->getSafe( + &_pModel->accessors, + primitive.attributes.at(propertyAttributeProperty.attribute)); + if (!pAccessor) { + return PropertyAttributePropertyView( + PropertyAttributePropertyViewStatus::ErrorInvalidAccessor); + } + + if (getAccessorTypeAsPropertyType(*pAccessor) != type) { + return PropertyAttributePropertyView( + PropertyAttributePropertyViewStatus::ErrorAccessorTypeMismatch); + } + + if (getAccessorComponentTypeAsPropertyComponentType(*pAccessor) != + componentType) { + return PropertyAttributePropertyView( + PropertyAttributePropertyViewStatus:: + ErrorAccessorComponentTypeMismatch); + } + + if (pAccessor->normalized != Normalized) { + return PropertyAttributePropertyView( + PropertyAttributePropertyViewStatus:: + ErrorAccessorNormalizationMismatch); + } + + AccessorView accessorView = AccessorView(*_pModel, *pAccessor); + if (accessorView.status() != AccessorViewStatus::Valid) { + switch (accessorView.status()) { + case AccessorViewStatus::InvalidBufferViewIndex: + return PropertyAttributePropertyView( + PropertyAttributePropertyViewStatus::ErrorInvalidBufferView); + case AccessorViewStatus::InvalidBufferIndex: + return PropertyAttributePropertyView( + PropertyAttributePropertyViewStatus::ErrorInvalidBuffer); + case AccessorViewStatus::BufferViewTooSmall: + return PropertyAttributePropertyView( + PropertyAttributePropertyViewStatus::ErrorAccessorOutOfBounds); + case AccessorViewStatus::BufferTooSmall: + return PropertyAttributePropertyView( + PropertyAttributePropertyViewStatus::ErrorBufferViewOutOfBounds); + default: + return PropertyAttributePropertyView( + PropertyAttributePropertyViewStatus::ErrorInvalidAccessor); + } + } + + return PropertyAttributePropertyView( + propertyAttributeProperty, + classProperty, + accessorView); + } + + const Model* _pModel; + const PropertyAttribute* _pPropertyAttribute; + const Class* _pClass; + + PropertyAttributeViewStatus _status; +}; +} // namespace CesiumGltf diff --git a/CesiumGltf/include/CesiumGltf/PropertyTableView.h b/CesiumGltf/include/CesiumGltf/PropertyTableView.h index bd18c705e..b92e991ba 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTableView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTableView.h @@ -184,25 +184,13 @@ class PropertyTableView { } bool normalized = pClassProperty->normalized; - if (normalized) { - switch (componentType) { - case PropertyComponentType::Int8: - case PropertyComponentType::Uint8: - case PropertyComponentType::Int16: - case PropertyComponentType::Uint16: - case PropertyComponentType::Int32: - case PropertyComponentType::Uint32: - case PropertyComponentType::Int64: - case PropertyComponentType::Uint64: - break; - default: - // Only integer components may be normalized. - callback( - propertyName, - PropertyTablePropertyView( - PropertyTablePropertyViewStatus::ErrorInvalidNormalization)); - return; - } + if (normalized && !isPropertyComponentTypeInteger(componentType)) { + // Only integer components may be normalized. + callback( + propertyName, + PropertyTablePropertyView( + PropertyTablePropertyViewStatus::ErrorInvalidNormalization)); + return; } if (pClassProperty->array) { diff --git a/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h index 821e3932d..4c0e856b0 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h @@ -190,7 +190,7 @@ assembleValueFromChannels(const std::vector& bytes) noexcept { } } -inline double applySamplerWrapS(const double u, const int32_t wrapS) { +inline double applySamplerWrapS(const double u, const int32_t wrapS) noexcept { if (wrapS == Sampler::WrapS::REPEAT) { double integral = 0; double fraction = std::modf(u, &integral); @@ -208,7 +208,7 @@ inline double applySamplerWrapS(const double u, const int32_t wrapS) { return glm::clamp(u, 0.0, 1.0); } -inline double applySamplerWrapT(const double v, const int32_t wrapT) { +inline double applySamplerWrapT(const double v, const int32_t wrapT) noexcept { if (wrapT == Sampler::WrapT::REPEAT) { double integral = 0; double fraction = std::modf(v, &integral); diff --git a/CesiumGltf/include/CesiumGltf/PropertyTextureView.h b/CesiumGltf/include/CesiumGltf/PropertyTextureView.h index 39e65b077..ed2546110 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTextureView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTextureView.h @@ -165,25 +165,13 @@ class PropertyTextureView { } bool normalized = pClassProperty->normalized; - if (normalized) { - switch (componentType) { - case PropertyComponentType::Int8: - case PropertyComponentType::Uint8: - case PropertyComponentType::Int16: - case PropertyComponentType::Uint16: - case PropertyComponentType::Int32: - case PropertyComponentType::Uint32: - case PropertyComponentType::Int64: - case PropertyComponentType::Uint64: - break; - default: - // Only integer components may be normalized. - callback( - propertyName, - PropertyTexturePropertyView( - PropertyTexturePropertyViewStatus::ErrorInvalidNormalization)); - return; - } + if (normalized && !isPropertyComponentTypeInteger(componentType)) { + // Only integer components may be normalized. + callback( + propertyName, + PropertyTexturePropertyView( + PropertyTexturePropertyViewStatus::ErrorInvalidNormalization)); + return; } if (pClassProperty->array) { diff --git a/CesiumGltf/include/CesiumGltf/PropertyType.h b/CesiumGltf/include/CesiumGltf/PropertyType.h index 8f1c8deff..6739ed620 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyType.h +++ b/CesiumGltf/include/CesiumGltf/PropertyType.h @@ -55,6 +55,8 @@ bool isPropertyTypeVecN(PropertyType type); bool isPropertyTypeMatN(PropertyType type); +bool isPropertyComponentTypeInteger(PropertyComponentType componentType); + glm::length_t getDimensionsFromPropertyType(PropertyType type); size_t getSizeOfComponentType(PropertyComponentType componentType); diff --git a/CesiumGltf/include/CesiumGltf/PropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyView.h index 4bc809cf4..5257de32b 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyView.h @@ -1,4 +1,5 @@ #include "CesiumGltf/ClassProperty.h" +#include "CesiumGltf/PropertyAttributeProperty.h" #include "CesiumGltf/PropertyTableProperty.h" #include "CesiumGltf/PropertyTextureProperty.h" #include "CesiumGltf/PropertyTypeTraits.h" @@ -367,6 +368,22 @@ template class PropertyView { getNumericPropertyValues(property); } + /** + * @brief Constructs a property instance from a property attribute property + * and its class definition. + */ + PropertyView( + const ClassProperty& classProperty, + const PropertyAttributeProperty& property) + : PropertyView(classProperty) { + if (_status != PropertyViewStatus::Valid) { + return; + } + + // If the property has its own values, override the class-provided values. + getNumericPropertyValues(property); + } + public: /** * @brief Gets the status of this property view, indicating whether an error @@ -494,8 +511,11 @@ template class PropertyView { } } - using PropertyDefinitionType = std:: - variant; + using PropertyDefinitionType = std::variant< + ClassProperty, + PropertyTableProperty, + PropertyTextureProperty, + PropertyAttributeProperty>; /** * @brief Attempts to parse offset, scale, min, and max properties from the @@ -690,6 +710,22 @@ template class PropertyView { getNumericPropertyValues(property); } + /** + * @brief Constructs a property instance from a property attribute property + * and its class definition. + */ + PropertyView( + const ClassProperty& classProperty, + const PropertyAttributeProperty& property) + : PropertyView(classProperty) { + if (_status != PropertyViewStatus::Valid) { + return; + } + + // If the property has its own values, override the class-provided values. + getNumericPropertyValues(property); + } + public: /** * @brief Gets the status of this property view, indicating whether an error @@ -784,8 +820,11 @@ template class PropertyView { } } - using PropertyDefinitionType = std:: - variant; + using PropertyDefinitionType = std::variant< + ClassProperty, + PropertyTableProperty, + PropertyTextureProperty, + PropertyAttributeProperty>; /** * @brief Attempts to parse offset, scale, min, and max properties from the @@ -890,15 +929,6 @@ template <> class PropertyView { const PropertyTableProperty& /*property*/) : PropertyView(classProperty) {} - /** - * @brief Constructs a property instance from a property texture property and - * its class definition. - */ - PropertyView( - const ClassProperty& classProperty, - const PropertyTextureProperty& /*property*/) - : PropertyView(classProperty) {} - public: /** * @copydoc PropertyView::status @@ -1040,15 +1070,6 @@ template <> class PropertyView { const PropertyTableProperty& /*property*/) : PropertyView(classProperty) {} - /** - * @brief Constructs a property instance from a property texture property and - * its class definition. - */ - PropertyView( - const ClassProperty& classProperty, - const PropertyTextureProperty& /*property*/) - : PropertyView(classProperty) {} - public: /** * @copydoc PropertyView::status @@ -1624,7 +1645,6 @@ class PropertyView, true> { } // If the property has its own values, override the class-provided values. - getNumericPropertyValues(property); } @@ -1906,15 +1926,6 @@ template <> class PropertyView> { const PropertyTableProperty& /*property*/) : PropertyView(classProperty) {} - /** - * @brief Constructs a property instance from a property texture property - * and its class definition. - */ - PropertyView( - const ClassProperty& classProperty, - const PropertyTextureProperty& /*property*/) - : PropertyView(classProperty) {} - public: /** * @copydoc PropertyView::status @@ -2139,15 +2150,6 @@ template <> class PropertyView> { const PropertyTableProperty& /*property*/) : PropertyView(classProperty) {} - /** - * @brief Constructs a property instance from a property texture property - * and its class definition. - */ - PropertyView( - const ClassProperty& classProperty, - const PropertyTextureProperty& /*property*/) - : PropertyView(classProperty) {} - public: /** * @copydoc PropertyView::status diff --git a/CesiumGltf/src/PropertyAttributeView.cpp b/CesiumGltf/src/PropertyAttributeView.cpp new file mode 100644 index 000000000..331ddfe2f --- /dev/null +++ b/CesiumGltf/src/PropertyAttributeView.cpp @@ -0,0 +1,94 @@ +#include "CesiumGltf/PropertyAttributeView.h" + +namespace CesiumGltf { +PropertyType getAccessorTypeAsPropertyType(const Accessor& accessor) { + if (accessor.type == Accessor::Type::SCALAR) { + return PropertyType::Scalar; + } + if (accessor.type == Accessor::Type::VEC2) { + return PropertyType::Vec2; + } + if (accessor.type == Accessor::Type::VEC3) { + return PropertyType::Vec3; + } + if (accessor.type == Accessor::Type::VEC4) { + return PropertyType::Vec4; + } + if (accessor.type == Accessor::Type::MAT2) { + return PropertyType::Mat2; + } + if (accessor.type == Accessor::Type::MAT3) { + return PropertyType::Mat3; + } + if (accessor.type == Accessor::Type::MAT4) { + return PropertyType::Mat4; + } + + return PropertyType::Invalid; +} + +PropertyComponentType +getAccessorComponentTypeAsPropertyComponentType(const Accessor& accessor) { + switch (accessor.componentType) { + case Accessor::ComponentType::BYTE: + return PropertyComponentType::Int8; + case Accessor::ComponentType::UNSIGNED_BYTE: + return PropertyComponentType::Uint8; + case Accessor::ComponentType::SHORT: + return PropertyComponentType::Int16; + case Accessor::ComponentType::UNSIGNED_SHORT: + return PropertyComponentType::Uint16; + case Accessor::ComponentType::UNSIGNED_INT: + return PropertyComponentType::Uint32; + case Accessor::ComponentType::FLOAT: + return PropertyComponentType::Float32; + default: + return PropertyComponentType::None; + } +} + +PropertyAttributeView::PropertyAttributeView( + const Model& model, + const PropertyAttribute& propertyAttribute) noexcept + : _pModel(&model), + _pPropertyAttribute(&propertyAttribute), + _pClass(nullptr), + _status() { + const ExtensionModelExtStructuralMetadata* pMetadata = + model.getExtension(); + + if (!pMetadata) { + this->_status = PropertyAttributeViewStatus::ErrorMissingMetadataExtension; + return; + } + + if (!pMetadata->schema) { + this->_status = PropertyAttributeViewStatus::ErrorMissingSchema; + return; + } + + const auto& classIt = + pMetadata->schema->classes.find(propertyAttribute.classProperty); + if (classIt == pMetadata->schema->classes.end()) { + this->_status = PropertyAttributeViewStatus::ErrorClassNotFound; + return; + } + + this->_pClass = &classIt->second; +} + +const ClassProperty* +PropertyAttributeView::getClassProperty(const std::string& propertyName) const { + if (_status != PropertyAttributeViewStatus::Valid) { + return nullptr; + } + + auto propertyIter = _pClass->properties.find(propertyName); + if (propertyIter == _pClass->properties.end()) { + return nullptr; + } + + return &propertyIter->second; +} + +} // namespace CesiumGltf diff --git a/CesiumGltf/src/PropertyType.cpp b/CesiumGltf/src/PropertyType.cpp index f30be7931..4e5b5402e 100644 --- a/CesiumGltf/src/PropertyType.cpp +++ b/CesiumGltf/src/PropertyType.cpp @@ -201,6 +201,17 @@ bool isPropertyTypeMatN(PropertyType type) { type == PropertyType::Mat4; } +bool isPropertyComponentTypeInteger(PropertyComponentType componentType) { + return componentType == PropertyComponentType::Int8 || + componentType == PropertyComponentType::Uint8 || + componentType == PropertyComponentType::Int16 || + componentType == PropertyComponentType::Uint16 || + componentType == PropertyComponentType::Int32 || + componentType == PropertyComponentType::Uint32 || + componentType == PropertyComponentType::Int64 || + componentType == PropertyComponentType::Uint64; +} + glm::length_t getDimensionsFromPropertyType(PropertyType type) { switch (type) { case PropertyType::Scalar: diff --git a/CesiumGltf/test/TestPropertyType.cpp b/CesiumGltf/test/TestPropertyType.cpp index acca55dd6..fffa7579b 100644 --- a/CesiumGltf/test/TestPropertyType.cpp +++ b/CesiumGltf/test/TestPropertyType.cpp @@ -234,6 +234,50 @@ TEST_CASE("Test convertStringOffsetTypeStringToPropertyComponentType") { PropertyComponentType::None); } +TEST_CASE("Test isPropertyTypeVecN") { + REQUIRE(isPropertyTypeVecN(PropertyType::Vec2)); + REQUIRE(isPropertyTypeVecN(PropertyType::Vec3)); + REQUIRE(isPropertyTypeVecN(PropertyType::Vec4)); + + REQUIRE(!isPropertyTypeVecN(PropertyType::Scalar)); + REQUIRE(!isPropertyTypeVecN(PropertyType::Mat2)); + REQUIRE(!isPropertyTypeVecN(PropertyType::Mat3)); + REQUIRE(!isPropertyTypeVecN(PropertyType::Mat4)); + REQUIRE(!isPropertyTypeVecN(PropertyType::Boolean)); + REQUIRE(!isPropertyTypeVecN(PropertyType::Enum)); + REQUIRE(!isPropertyTypeVecN(PropertyType::String)); + REQUIRE(!isPropertyTypeVecN(PropertyType::Invalid)); +} + +TEST_CASE("Test isPropertyTypeMatN") { + REQUIRE(isPropertyTypeMatN(PropertyType::Mat2)); + REQUIRE(isPropertyTypeMatN(PropertyType::Mat3)); + REQUIRE(isPropertyTypeMatN(PropertyType::Mat4)); + + REQUIRE(!isPropertyTypeVecN(PropertyType::Scalar)); + REQUIRE(!isPropertyTypeMatN(PropertyType::Vec2)); + REQUIRE(!isPropertyTypeMatN(PropertyType::Vec3)); + REQUIRE(!isPropertyTypeMatN(PropertyType::Vec4)); + REQUIRE(!isPropertyTypeMatN(PropertyType::Boolean)); + REQUIRE(!isPropertyTypeMatN(PropertyType::Enum)); + REQUIRE(!isPropertyTypeMatN(PropertyType::String)); +} + +TEST_CASE("Test isPropertyComponentTypeInteger") { + REQUIRE(isPropertyComponentTypeInteger(PropertyComponentType::Int8)); + REQUIRE(isPropertyComponentTypeInteger(PropertyComponentType::Uint8)); + REQUIRE(isPropertyComponentTypeInteger(PropertyComponentType::Int16)); + REQUIRE(isPropertyComponentTypeInteger(PropertyComponentType::Uint16)); + REQUIRE(isPropertyComponentTypeInteger(PropertyComponentType::Int32)); + REQUIRE(isPropertyComponentTypeInteger(PropertyComponentType::Uint32)); + REQUIRE(isPropertyComponentTypeInteger(PropertyComponentType::Int64)); + REQUIRE(isPropertyComponentTypeInteger(PropertyComponentType::Uint64)); + + REQUIRE(!isPropertyComponentTypeInteger(PropertyComponentType::None)); + REQUIRE(!isPropertyComponentTypeInteger(PropertyComponentType::Float32)); + REQUIRE(!isPropertyComponentTypeInteger(PropertyComponentType::Float64)); +} + TEST_CASE("Test getDimensionsFromPropertyType") { REQUIRE(getDimensionsFromPropertyType(PropertyType::Scalar) == 1); From 85ebd3ed7f7e684626f3bee87044ae5d3c90133e Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Tue, 29 Aug 2023 12:10:47 -0400 Subject: [PATCH 240/421] Add unit tests for new classes, reorganize old tests --- .../TestPropertyAttributePropertyView.cpp | 859 +++++++ CesiumGltf/test/TestPropertyAttributeView.cpp | 2282 +++++++++++++++++ .../test/TestPropertyTablePropertyView.cpp | 1141 +++++---- .../test/TestPropertyTexturePropertyView.cpp | 34 +- 4 files changed, 3804 insertions(+), 512 deletions(-) create mode 100644 CesiumGltf/test/TestPropertyAttributePropertyView.cpp create mode 100644 CesiumGltf/test/TestPropertyAttributeView.cpp diff --git a/CesiumGltf/test/TestPropertyAttributePropertyView.cpp b/CesiumGltf/test/TestPropertyAttributePropertyView.cpp new file mode 100644 index 000000000..c87139ab1 --- /dev/null +++ b/CesiumGltf/test/TestPropertyAttributePropertyView.cpp @@ -0,0 +1,859 @@ +#include "CesiumGltf/PropertyAttributePropertyView.h" + +#include +#include + +#include +#include + +using namespace CesiumGltf; +using namespace CesiumUtility; + +template +const Accessor& addValuesToModel(Model& model, const std::vector& values) { + Buffer& buffer = model.buffers.emplace_back(); + buffer.cesium.data.resize(values.size() * sizeof(T)); + buffer.byteLength = static_cast(buffer.cesium.data.size()); + std::memcpy( + buffer.cesium.data.data(), + values.data(), + buffer.cesium.data.size()); + + BufferView& bufferView = model.bufferViews.emplace_back(); + bufferView.buffer = static_cast(model.buffers.size() - 1); + bufferView.byteOffset = 0; + bufferView.byteLength = buffer.byteLength; + + Accessor& accessor = model.accessors.emplace_back(); + accessor.bufferView = static_cast(model.bufferViews.size() - 1); + accessor.count = values.size(); + accessor.byteOffset = 0; + + PropertyType type = TypeToPropertyType::value; + switch (type) { + case PropertyType::Scalar: + accessor.type = Accessor::Type::SCALAR; + break; + case PropertyType::Vec2: + accessor.type = Accessor::Type::VEC2; + break; + case PropertyType::Vec3: + accessor.type = Accessor::Type::VEC3; + break; + case PropertyType::Vec4: + accessor.type = Accessor::Type::VEC4; + break; + case PropertyType::Mat2: + accessor.type = Accessor::Type::MAT2; + break; + case PropertyType::Mat3: + accessor.type = Accessor::Type::MAT3; + break; + case PropertyType::Mat4: + accessor.type = Accessor::Type::MAT4; + break; + default: + assert(false && "Input type is not supported as an accessor type"); + break; + } + + PropertyComponentType componentType = TypeToPropertyType::component; + switch (componentType) { + case PropertyComponentType::Int8: + accessor.componentType = Accessor::ComponentType::BYTE; + break; + case PropertyComponentType::Uint8: + accessor.componentType = Accessor::ComponentType::UNSIGNED_BYTE; + break; + case PropertyComponentType::Int16: + accessor.componentType = Accessor::ComponentType::SHORT; + break; + case PropertyComponentType::Uint16: + accessor.componentType = Accessor::ComponentType::UNSIGNED_SHORT; + break; + case PropertyComponentType::Uint32: + accessor.componentType = Accessor::ComponentType::UNSIGNED_INT; + break; + case PropertyComponentType::Float32: + accessor.componentType = Accessor::ComponentType::FLOAT; + break; + default: + assert( + false && + "Input component type is not supported as an accessor component type"); + break; + } + + accessor.normalized = Normalized; + + return accessor; +} + +template void checkAttributeValues(const std::vector& values) { + Model model; + const Accessor& accessor = addValuesToModel(model, values); + AccessorView accessorView = AccessorView(model, accessor); + + PropertyAttributeProperty property; + ClassProperty classProperty; + classProperty.type = + convertPropertyTypeToString(TypeToPropertyType::value); + + PropertyComponentType componentType = TypeToPropertyType::component; + classProperty.componentType = + convertPropertyComponentTypeToString(componentType); + + PropertyAttributePropertyView view(property, classProperty, accessorView); + + REQUIRE(view.size() == static_cast(values.size())); + REQUIRE(!view.normalized()); + + for (int64_t i = 0; i < view.size(); i++) { + REQUIRE(view.getRaw(i) == values[static_cast(i)]); + REQUIRE(view.get(i) == view.getRaw(i)); + } +} + +template +void checkAttributeValues( + const std::vector& values, + const std::vector>& expected, + const std::optional offset = std::nullopt, + const std::optional scale = std::nullopt, + const std::optional noData = std::nullopt, + const std::optional defaultValue = std::nullopt) { + Model model; + const Accessor& accessor = addValuesToModel(model, values); + AccessorView accessorView = AccessorView(model, accessor); + + PropertyAttributeProperty property; + ClassProperty classProperty; + classProperty.type = + convertPropertyTypeToString(TypeToPropertyType::value); + + PropertyComponentType componentType = TypeToPropertyType::component; + classProperty.componentType = + convertPropertyComponentTypeToString(componentType); + + classProperty.offset = offset; + classProperty.scale = scale; + classProperty.noData = noData; + classProperty.defaultProperty = defaultValue; + + PropertyAttributePropertyView view(property, classProperty, accessorView); + + REQUIRE(view.size() == static_cast(values.size())); + REQUIRE(!view.normalized()); + + for (int64_t i = 0; i < view.size(); i++) { + REQUIRE(view.getRaw(i) == values[static_cast(i)]); + REQUIRE(view.get(i) == expected[static_cast(i)]); + } +} + +template ::type> +void checkNormalizedAttributeValues( + const std::vector& values, + const std::vector>& expected, + const std::optional offset = std::nullopt, + const std::optional scale = std::nullopt, + const std::optional noData = std::nullopt, + const std::optional defaultValue = std::nullopt) { + Model model; + const Accessor& accessor = addValuesToModel(model, values); + AccessorView accessorView = AccessorView(model, accessor); + + PropertyAttributeProperty property; + ClassProperty classProperty; + classProperty.type = + convertPropertyTypeToString(TypeToPropertyType::value); + + PropertyComponentType componentType = TypeToPropertyType::component; + classProperty.componentType = + convertPropertyComponentTypeToString(componentType); + + classProperty.normalized = true; + + classProperty.offset = offset; + classProperty.scale = scale; + classProperty.noData = noData; + classProperty.defaultProperty = defaultValue; + + PropertyAttributePropertyView view( + property, + classProperty, + accessorView); + + REQUIRE(view.size() == static_cast(values.size())); + REQUIRE(view.normalized()); + + for (int64_t i = 0; i < view.size(); i++) { + REQUIRE(view.getRaw(i) == values[static_cast(i)]); + REQUIRE(view.get(i) == expected[static_cast(i)]); + } +} + +TEST_CASE("Check scalar PropertyAttributePropertyView") { + SECTION("Uint8") { + std::vector data{12, 33, 56, 67}; + checkAttributeValues(data); + } + + SECTION("Int16") { + std::vector data{-1, -32511, 768, 438}; + checkAttributeValues(data); + } + + SECTION("uint32_t") { + std::vector data{16777216, 65545, 131604, 16777480}; + checkAttributeValues(data); + } + + SECTION("float") { + std::vector data{12.3333f, -12.44555f, -5.6111f, 6.7421f}; + checkAttributeValues(data); + } + + SECTION("float with offset / scale") { + std::vector data{1.0f, 2.0f, 3.0f, 4.0f}; + + const float offset = 1.0f; + const float scale = 2.0f; + + std::vector> expected{3.0f, 5.0f, 7.0f, 9.0f}; + checkAttributeValues(data, expected, offset, scale); + } + + SECTION("uint8_t with noData") { + std::vector data{12, 33, 0, 128, 0, 56, 67}; + const uint8_t noData = 0; + std::vector> expected{ + data[0], + data[1], + std::nullopt, + data[3], + std::nullopt, + data[5], + data[6]}; + checkAttributeValues(data, expected, std::nullopt, std::nullopt, noData); + } + + SECTION("uint8_t with noData and defaultValue") { + std::vector data{12, 33, 0, 128, 0, 56, 67}; + const uint8_t noData = 0; + const uint8_t defaultValue = 255; + std::vector> expected{ + data[0], + data[1], + defaultValue, + data[3], + defaultValue, + data[5], + data[6]}; + checkAttributeValues( + data, + expected, + std::nullopt, + std::nullopt, + noData, + defaultValue); + } +} + +TEST_CASE("Check scalar PropertyAttributePropertyView (normalized)") { + SECTION("Uint8") { + std::vector data{12, 33, 56, 67}; + std::vector> expected{ + 12.0 / 255.0, + 33 / 255.0, + 56 / 255.0, + 67 / 255.0}; + checkNormalizedAttributeValues(data, expected); + } + + SECTION("Int16") { + std::vector data{-1, -32511, 768, 438}; + std::vector> expected{ + normalize(data[0]), + normalize(data[1]), + normalize(data[2]), + normalize(data[3])}; + checkNormalizedAttributeValues(data, expected); + } + + SECTION("Uint32") { + std::vector data{16777216, 65545, 131604, 16777480}; + std::vector> expected{ + normalize(data[0]), + normalize(data[1]), + normalize(data[2]), + normalize(data[3])}; + checkNormalizedAttributeValues(data, expected); + } + + SECTION("Uint8 with offset / scale") { + std::vector data{12, 33, 56, 67}; + const double offset = 1.0; + const double scale = 2.0; + std::vector> expected{ + normalize(data[0]) * scale + offset, + normalize(data[1]) * scale + offset, + normalize(data[2]) * scale + offset, + normalize(data[3]) * scale + offset, + }; + checkNormalizedAttributeValues(data, expected, offset, scale); + } + + SECTION("Uint8 with all properties") { + std::vector data{12, 33, 56, 0, 67}; + const double offset = 1.0; + const double scale = 2.0; + const uint8_t noData = 0; + const double defaultValue = 10.0; + std::vector> expected{ + normalize(data[0]) * scale + offset, + normalize(data[1]) * scale + offset, + normalize(data[2]) * scale + offset, + 10.0, + normalize(data[4]) * scale + offset, + }; + checkNormalizedAttributeValues( + data, + expected, + offset, + scale, + noData, + defaultValue); + } +} + +TEST_CASE("Check vecN PropertyAttributePropertyView") { + SECTION("glm::i8vec2") { + std::vector data{ + glm::i8vec2(28, -1), + glm::i8vec2(-2, 1), + glm::i8vec2(0, 3), + glm::i8vec2(-74, 1)}; + checkAttributeValues(data); + } + + SECTION("glm::vec3") { + std::vector data{ + glm::vec3(1.5f, 2.0f, -3.3f), + glm::vec3(4.12f, -5.008f, 6.0f), + glm::vec3(7.0f, 8.0f, 9.01f), + glm::vec3(-0.28f, 5.0f, 1.2f)}; + checkAttributeValues(data); + } + + SECTION("glm::uvec4") { + std::vector data{ + glm::uvec4(0, 12, 324, 256), + glm::uvec4(9234, 12, 7, 1), + glm::uvec4(532, 2, 88, 16), + glm::uvec4(264, 256, 22, 101)}; + checkAttributeValues(data); + } + + SECTION("glm::vec3 with offset / scale") { + std::vector data{ + glm::vec3(1.0f, 2.0f, 3.0f), + glm::vec3(-1.0f, -2.0f, -3.0f), + glm::vec3(0.0f), + glm::vec3(1.0f)}; + + JsonValue::Array offset{1.0f, 0.0f, -1.0f}; + JsonValue::Array scale{2.0f, 2.0f, 2.0f}; + + std::vector> expected{ + glm::vec3(3.0f, 4.0f, 5.0f), + glm::vec3(-1.0f, -4.0f, -7.0f), + glm::vec3(1.0f, 0.0f, -1.0f), + glm::vec3(3.0f, 2.0f, 1.0f)}; + + checkAttributeValues(data, expected, offset, scale); + } + + SECTION("glm::i8vec2 with noData") { + std::vector data{ + glm::i8vec2(28, -1), + glm::i8vec2(-2, 1), + glm::i8vec2(0, 3), + glm::i8vec2(0), + glm::i8vec2(-74, 1)}; + + JsonValue::Array noData{0, 0}; + + std::vector> expected{ + glm::i8vec2(28, -1), + glm::i8vec2(-2, 1), + glm::i8vec2(0, 3), + std::nullopt, + glm::i8vec2(-74, 1)}; + checkAttributeValues(data, expected, std::nullopt, std::nullopt, noData); + } + + SECTION("glm::i8vec2 with defaultValue") { + std::vector data{ + glm::i8vec2(28, -1), + glm::i8vec2(-2, 1), + glm::i8vec2(0, 3), + glm::i8vec2(0, 0), + glm::i8vec2(-74, 1)}; + + JsonValue::Array noData{0, 0}; + JsonValue::Array defaultValue{127, 127}; + + std::vector> expected{ + glm::i8vec2(28, -1), + glm::i8vec2(-2, 1), + glm::i8vec2(0, 3), + glm::i8vec2(127, 127), + glm::i8vec2(-74, 1)}; + checkAttributeValues( + data, + expected, + std::nullopt, + std::nullopt, + noData, + defaultValue); + } +} + +TEST_CASE("Check vecN PropertyAttributePropertyView (normalized)") { + SECTION("glm::i8vec2") { + std::vector data{ + glm::i8vec2(28, -1), + glm::i8vec2(-2, 1), + glm::i8vec2(0, 3), + glm::i8vec2(-74, 1)}; + std::vector> expected{ + normalize(data[0]), + normalize(data[1]), + normalize(data[2]), + normalize(data[3])}; + checkNormalizedAttributeValues(data, expected); + } + + SECTION("glm::u8vec3") { + std::vector data{ + glm::u8vec3(1, 2, 3), + glm::u8vec3(4, 5, 6), + glm::u8vec3(7, 8, 9), + glm::u8vec3(0, 5, 2)}; + std::vector> expected{ + normalize(data[0]), + normalize(data[1]), + normalize(data[2]), + normalize(data[3])}; + checkNormalizedAttributeValues(data, expected); + } + + SECTION("glm::uvec4") { + std::vector data{ + glm::uvec4(123, 20, 13, 0), + glm::uvec4(43, 5, 86, 11), + glm::uvec4(7345, 448, 3219, 83), + glm::uvec4(0, 775, 12, 27)}; + std::vector> expected{ + normalize(data[0]), + normalize(data[1]), + normalize(data[2]), + normalize(data[3])}; + checkNormalizedAttributeValues(data, expected); + } + + SECTION("glm::i8vec2 with offset / scale") { + std::vector data{ + glm::i8vec2(28, -1), + glm::i8vec2(-2, 1), + glm::i8vec2(0, 3), + glm::i8vec2(0), + glm::i8vec2(-74, 1)}; + + const glm::dvec2 offset(-1.0, 4.0); + const glm::dvec2 scale(2.0, 1.0); + + std::vector> expected{ + normalize(data[0]) * scale + offset, + normalize(data[1]) * scale + offset, + normalize(data[2]) * scale + offset, + normalize(data[3]) * scale + offset, + normalize(data[4]) * scale + offset, + }; + checkNormalizedAttributeValues( + data, + expected, + JsonValue::Array{offset[0], offset[1]}, + JsonValue::Array{scale[0], scale[1]}); + } + + SECTION("glm::i8vec2 with all properties") { + const glm::dvec2 offset(-1.0, 4.0); + const glm::dvec2 scale(2.0, 1.0); + const glm::i8vec2 noData(0); + const glm::dvec2 defaultValue(100.0, 5.5); + + std::vector data{ + glm::i8vec2(28, -1), + glm::i8vec2(-2, 1), + glm::i8vec2(0, 3), + glm::i8vec2(0), + glm::i8vec2(-74, 1)}; + std::vector> expected{ + normalize(data[0]) * scale + offset, + normalize(data[1]) * scale + offset, + normalize(data[2]) * scale + offset, + defaultValue, + normalize(data[4]) * scale + offset, + }; + checkNormalizedAttributeValues( + data, + expected, + JsonValue::Array{offset[0], offset[1]}, + JsonValue::Array{scale[0], scale[1]}, + JsonValue::Array{noData[0], noData[1]}, + JsonValue::Array{defaultValue[0], defaultValue[1]}); + } +} + +TEST_CASE("Check matN PropertyAttributePropertyView") { + SECTION("Float Mat2") { + // clang-format off + std::vector data{ + glm::mat2( + 1.0f, 2.0f, + 3.0f, 4.0f), + glm::mat2( + -10.0f, 40.0f, + 0.08f, 5.4f), + glm::mat2( + 9.99f, -2.0f, + -0.4f, 0.23f) + }; + // clang-format on + checkAttributeValues(data); + } + + SECTION("Int16 Mat3") { + // clang-format off + std::vector data{ + glm::i16mat3x3( + 1, 2, 3, + 4, 5, 6, + 7, 8, 9), + glm::i16mat3x3( + 10, 0, 5, + -14, 35, 16, + -2, 3, 4), + glm::i16mat3x3( + -6, 5, 2, + 14, 4, -33, + 2, 1, 0) + }; + // clang-format on + checkAttributeValues(data); + } + + SECTION("Float Mat4") { + // clang-format off + std::vector data{ + glm::mat4( + 1.0f, 2.0f, 3.0f, 4.0f, + 5.0f, 6.0f, 7.0f, 8.0f, + 9.0f, 10.0f, 11.0f, 12.0f, + 13.0f, 14.0f, 15.0f, 16.0f), + glm::mat4( + 0.1f, 0.2f, 0.3f, 0.4f, + 0.5f, 0.6f, 0.7f, 0.8f, + -9.0f, -10.0f, -11.0f, -12.0f, + 13.0f, 14.0f, 15.0f, 16.0f), + glm::mat4( + 1.0f, 0.0f, 0.0f, 10.0f, + 0.0f, 0.0f, -1.0f, -3.5f, + 0.0f, 1.0f, 0.0f, 20.4f, + 0.0f, 0.0f, 0.0f, 1.0f) + }; + // clang-format on + checkAttributeValues(data); + } + + SECTION("Float Mat2 with Offset / Scale") { + // clang-format off + std::vector data{ + glm::mat2( + 1.0f, 3.0f, + 4.0f, 2.0f), + glm::mat2( + 6.5f, 2.0f, + -2.0f, 0.0f), + glm::mat2( + 8.0f, -1.0f, + -3.0f, 1.0f), + }; + const JsonValue::Array offset{ + 1.0f, 2.0f, + 3.0f, 1.0f}; + const JsonValue::Array scale { + 2.0f, 0.0f, + 0.0f, 2.0f}; + + std::vector> expected{ + glm::mat2( + 3.0f, 2.0f, + 3.0f, 5.0f), + glm::mat2( + 14.0f, 2.0f, + 3.0f, 1.0f), + glm::mat2( + 17.0f, 2.0f, + 3.0f, 3.0f), + }; + // clang-format on + checkAttributeValues(data, expected, offset, scale); + } + + SECTION("Int16 Mat3 with NoData") { + // clang-format off + std::vector data{ + glm::i16mat3x3( + 1, 2, 3, + -1, -2, -3, + 0, 1, 0), + glm::i16mat3x3( + 1, -1, 0, + 0, 1, 2, + 0, 4, 5), + glm::i16mat3x3( + -1, -1, -1, + 0, 0, 0, + 1, 1, 1)}; + JsonValue::Array noData{ + -1, -1, -1, + 0, 0, 0, + 1, 1, 1}; + // clang-format on + std::vector> expected{ + data[0], + data[1], + std::nullopt}; + checkAttributeValues( + data, + expected, + std::nullopt, + std::nullopt, + noData, + std::nullopt); + }; + + SECTION("Int16 Mat3 with NoData and DefaultValue") { + // clang-format off + std::vector data{ + glm::i16mat3x3( + 1, 2, 3, + -1, -2, -3, + 0, 1, 0), + glm::i16mat3x3( + 1, -1, 0, + 0, 1, 2, + 0, 4, 5), + glm::i16mat3x3( + -1, -1, -1, + 0, 0, 0, + 1, 1, 1)}; + JsonValue::Array noData{ + -1, -1, -1, + 0, 0, 0, + 1, 1, 1}; + JsonValue::Array defaultValue{ + 1, 0, 0, + 0, 1, 0, + 0, 0, 1}; + // clang-format on + std::vector> expected{ + data[0], + data[1], + glm::i16mat3x3(1)}; + checkAttributeValues( + data, + expected, + std::nullopt, + std::nullopt, + noData, + defaultValue); + }; +} + +TEST_CASE("Check matN PropertyAttributePropertyView (normalized)") { + SECTION("Normalized Uint8 Mat2") { + // clang-format off + std::vector data{ + glm::u8mat2x2( + 0, 64, + 255, 255), + glm::u8mat2x2( + 255, 0, + 128, 0)}; + std::vector> expected{ + glm::dmat2( + 0.0, 64.0 / 255.0, + 1.0, 1.0), + glm::dmat2( + 1.0, 0.0, + 128.0 / 255.0, 0.0)}; + // clang-format on + checkNormalizedAttributeValues(data, expected); + } + + SECTION("Normalized Int16 Mat2") { + // clang-format off + std::vector data{ + glm::i16mat2x2( + -32768, 0, + 16384, 32767), + glm::i16mat2x2( + 0, 32767, + 32767, -32768)}; + std::vector> expected{ + glm::dmat2( + -1.0, 0.0, + 16384.0 / 32767.0, 1.0), + glm::dmat2( + 0.0, 1.0, + 1.0, -1.0), + }; + // clang-format on + checkNormalizedAttributeValues(data, expected); + } + + SECTION("Normalized Uint8 Mat2 with Offset and Scale") { + // clang-format off + std::vector values{ + glm::u8mat2x2( + 0, 64, + 255, 255), + glm::u8mat2x2( + 255, 0, + 128, 0)}; + JsonValue::Array offset{ + 0.0, 1.0, + 1.0, 0.0}; + JsonValue::Array scale{ + 2.0, 1.0, + 0.0, 2.0}; + std::vector> expected{ + glm::dmat2( + 0.0, 1 + 64.0 / 255.0, + 1.0, 2.0), + glm::dmat2( + 2.0, 1.0, + 1.0, 0.0)}; + // clang-format on + checkNormalizedAttributeValues(values, expected, offset, scale); + } + + SECTION("Normalized Uint8 Mat2 with all properties") { + // clang-format off + std::vector values{ + glm::u8mat2x2( + 0, 64, + 255, 255), + glm::u8mat2x2(0), + glm::u8mat2x2( + 255, 0, + 128, 0)}; + JsonValue::Array offset{ + 0.0, 1.0, + 1.0, 0.0}; + JsonValue::Array scale{ + 2.0, 1.0, + 0.0, 2.0}; + JsonValue::Array noData{ + 0, 0, + 0, 0}; + JsonValue::Array defaultValue{ + 1.0, 0.0, + 0.0, 1.0}; + + std::vector> expected{ + glm::dmat2( + 0.0, 1 + 64.0 / 255.0, + 1.0, 2.0), + glm::dmat2(1.0), + glm::dmat2( + 2.0, 1.0, + 1.0, 0.0)}; + // clang-format on + checkNormalizedAttributeValues( + values, + expected, + offset, + scale, + noData, + defaultValue); + } +} + +TEST_CASE("Check that PropertyAttributeProperty values override class property values") { + Model model; + std::vector data{1.0f, 2.0f, 3.0f, 4.0f}; + + Buffer& buffer = model.buffers.emplace_back(); + buffer.cesium.data.resize(data.size() * sizeof(float)); + buffer.byteLength = static_cast(buffer.cesium.data.size()); + std::memcpy( + buffer.cesium.data.data(), + data.data(), + buffer.cesium.data.size()); + + BufferView& bufferView = model.bufferViews.emplace_back(); + bufferView.buffer = static_cast(model.buffers.size() - 1); + bufferView.byteOffset = 0; + bufferView.byteLength = buffer.byteLength; + + Accessor& accessor = model.accessors.emplace_back(); + accessor.bufferView = static_cast(model.bufferViews.size() - 1); + accessor.count = data.size(); + accessor.byteOffset = 0; + accessor.type = Accessor::Type::SCALAR; + accessor.componentType = Accessor::ComponentType::FLOAT; + + AccessorView accessorView = AccessorView(model, accessor); + + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::SCALAR; + classProperty.componentType = ClassProperty::ComponentType::FLOAT32; + + classProperty.offset = 0.0f; + classProperty.scale = 1.0f; + classProperty.min = -10.0f; + classProperty.max = 10.0f; + + const float offset = 1.0f; + const float scale = 2.0f; + const float min = 3.0f; + const float max = 9.0f; + + std::vector> expected{3.0f, 5.0f, 7.0f, 9.0f}; + + PropertyAttributeProperty property; + property.offset = offset; + property.scale = scale; + property.min = min; + property.max = max; + + PropertyAttributePropertyView view( + property, + classProperty, + accessorView); + REQUIRE(view.offset() == offset); + REQUIRE(view.scale() == scale); + REQUIRE(view.min() == min); + REQUIRE(view.max() == max); + + REQUIRE(view.size() == static_cast(data.size())); + for (int64_t i = 0; i < view.size(); i++) { + REQUIRE(view.getRaw(i) == data[static_cast(i)]); + REQUIRE(view.get(i) == expected[static_cast(i)]); + } +} diff --git a/CesiumGltf/test/TestPropertyAttributeView.cpp b/CesiumGltf/test/TestPropertyAttributeView.cpp new file mode 100644 index 000000000..a242bb6df --- /dev/null +++ b/CesiumGltf/test/TestPropertyAttributeView.cpp @@ -0,0 +1,2282 @@ +#include "CesiumGltf/PropertyAttributeView.h" + +#include +#include + +#include +#include + +using namespace CesiumGltf; + +namespace { +template +void addAttributeToModel( + Model& model, + MeshPrimitive& primitive, + const std::string& name, + const std::vector& values) { + Buffer& buffer = model.buffers.emplace_back(); + buffer.cesium.data.resize(values.size() * sizeof(T)); + buffer.byteLength = static_cast(buffer.cesium.data.size()); + std::memcpy( + buffer.cesium.data.data(), + values.data(), + buffer.cesium.data.size()); + + BufferView& bufferView = model.bufferViews.emplace_back(); + bufferView.buffer = static_cast(model.buffers.size() - 1); + bufferView.byteOffset = 0; + bufferView.byteLength = buffer.byteLength; + + Accessor& accessor = model.accessors.emplace_back(); + accessor.bufferView = static_cast(model.bufferViews.size() - 1); + accessor.count = values.size(); + accessor.byteOffset = 0; + + PropertyType type = TypeToPropertyType::value; + switch (type) { + case PropertyType::Scalar: + accessor.type = Accessor::Type::SCALAR; + break; + case PropertyType::Vec2: + accessor.type = Accessor::Type::VEC2; + break; + case PropertyType::Vec3: + accessor.type = Accessor::Type::VEC3; + break; + case PropertyType::Vec4: + accessor.type = Accessor::Type::VEC4; + break; + case PropertyType::Mat2: + accessor.type = Accessor::Type::MAT2; + break; + case PropertyType::Mat3: + accessor.type = Accessor::Type::MAT3; + break; + case PropertyType::Mat4: + accessor.type = Accessor::Type::MAT4; + break; + default: + assert(false && "Input type is not supported as an accessor type"); + break; + } + + PropertyComponentType componentType = TypeToPropertyType::component; + switch (componentType) { + case PropertyComponentType::Int8: + accessor.componentType = Accessor::ComponentType::BYTE; + break; + case PropertyComponentType::Uint8: + accessor.componentType = Accessor::ComponentType::UNSIGNED_BYTE; + break; + case PropertyComponentType::Int16: + accessor.componentType = Accessor::ComponentType::SHORT; + break; + case PropertyComponentType::Uint16: + accessor.componentType = Accessor::ComponentType::UNSIGNED_SHORT; + break; + case PropertyComponentType::Uint32: + accessor.componentType = Accessor::ComponentType::UNSIGNED_INT; + break; + case PropertyComponentType::Float32: + accessor.componentType = Accessor::ComponentType::FLOAT; + break; + default: + assert( + false && + "Input component type is not supported as an accessor component type"); + break; + } + + accessor.normalized = Normalized; + + primitive.attributes[name] = static_cast(model.accessors.size() - 1); +} +} // namespace + +TEST_CASE("Test PropertyAttributeView on model without EXT_structural_metadata " + "extension") { + Model model; + + // Create an erroneously isolated property Attribute. + PropertyAttribute propertyAttribute; + propertyAttribute.classProperty = "TestClass"; + + PropertyAttributeProperty& propertyAttributeProperty = + propertyAttribute.properties["TestClassProperty"]; + propertyAttributeProperty.attribute = "_ATTRIBUTE"; + + PropertyAttributeView view(model, propertyAttribute); + REQUIRE( + view.status() == + PropertyAttributeViewStatus::ErrorMissingMetadataExtension); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(!classProperty); +} + +TEST_CASE("Test PropertyAttributeView on model without metadata schema") { + Model model; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + PropertyAttribute& propertyAttribute = + metadata.propertyAttributes.emplace_back(); + propertyAttribute.classProperty = "TestClass"; + + PropertyAttributeProperty& propertyAttributeProperty = + propertyAttribute.properties["TestClassProperty"]; + propertyAttributeProperty.attribute = "_ATTRIBUTE"; + + PropertyAttributeView view(model, propertyAttribute); + REQUIRE(view.status() == PropertyAttributeViewStatus::ErrorMissingSchema); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(!classProperty); +} + +TEST_CASE("Test property attribute with nonexistent class") { + Model model; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::UINT8; + + PropertyAttribute& propertyAttribute = + metadata.propertyAttributes.emplace_back(); + propertyAttribute.classProperty = "I Don't Exist"; + + PropertyAttributeProperty& propertyAttributeProperty = + propertyAttribute.properties["TestClassProperty"]; + propertyAttributeProperty.attribute = "_ATTRIBUTE"; + + PropertyAttributeView view(model, propertyAttribute); + REQUIRE(view.status() == PropertyAttributeViewStatus::ErrorClassNotFound); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(!classProperty); +} + +TEST_CASE("Test scalar PropertyAttributeProperty") { + Model model; + Mesh& mesh = model.meshes.emplace_back(); + MeshPrimitive& primitive = mesh.primitives.emplace_back(); + + const std::string attributeName = "_ATTRIBUTE"; + std::vector data = {12, 34, 30, 11}; + + addAttributeToModel(model, primitive, attributeName, data); + size_t accessorIndex = model.accessors.size() - 1; + size_t bufferIndex = model.buffers.size() - 1; + size_t bufferViewIndex = model.bufferViews.size() - 1; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::UINT16; + + PropertyAttribute& propertyAttribute = + metadata.propertyAttributes.emplace_back(); + propertyAttribute.classProperty = "TestClass"; + + PropertyAttributeProperty& propertyAttributeProperty = + propertyAttribute.properties["TestClassProperty"]; + propertyAttributeProperty.attribute = attributeName; + + PropertyAttributeView view(model, propertyAttribute); + REQUIRE(view.status() == PropertyAttributeViewStatus::Valid); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT16); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->array); + REQUIRE(!classProperty->normalized); + + SECTION("Access correct type") { + PropertyAttributePropertyView uint16Property = + view.getPropertyView("TestClassProperty", primitive); + REQUIRE( + uint16Property.status() == PropertyAttributePropertyViewStatus::Valid); + for (size_t i = 0; i < data.size(); ++i) { + REQUIRE(uint16Property.getRaw(static_cast(i)) == data[i]); + REQUIRE(uint16Property.get(static_cast(i)) == data[i]); + } + } + + SECTION("Access wrong type") { + PropertyAttributePropertyView u16vec2Invalid = + view.getPropertyView("TestClassProperty", primitive); + REQUIRE( + u16vec2Invalid.status() == + PropertyAttributePropertyViewStatus::ErrorTypeMismatch); + } + + SECTION("Access wrong component type") { + PropertyAttributePropertyView uint8Invalid = + view.getPropertyView("TestClassProperty", primitive); + REQUIRE( + uint8Invalid.status() == + PropertyAttributePropertyViewStatus::ErrorComponentTypeMismatch); + + PropertyAttributePropertyView int32Invalid = + view.getPropertyView("TestClassProperty", primitive); + REQUIRE( + int32Invalid.status() == + PropertyAttributePropertyViewStatus::ErrorComponentTypeMismatch); + + PropertyAttributePropertyView uint64Invalid = + view.getPropertyView("TestClassProperty", primitive); + REQUIRE( + uint64Invalid.status() == + PropertyAttributePropertyViewStatus::ErrorComponentTypeMismatch); + } + + SECTION("Access incorrectly as normalized") { + PropertyAttributePropertyView normalizedInvalid = + view.getPropertyView("TestClassProperty", primitive); + REQUIRE( + normalizedInvalid.status() == + PropertyAttributePropertyViewStatus::ErrorNormalizationMismatch); + } + + SECTION("Buffer view points outside of the real buffer length") { + model.buffers[bufferIndex].cesium.data.resize(4); + PropertyAttributePropertyView property = + view.getPropertyView("TestClassProperty", primitive); + REQUIRE( + property.status() == + PropertyAttributePropertyViewStatus::ErrorBufferViewOutOfBounds); + } + + SECTION("Wrong buffer index") { + model.bufferViews[bufferViewIndex].buffer = 2; + PropertyAttributePropertyView property = + view.getPropertyView("TestClassProperty", primitive); + REQUIRE( + property.status() == + PropertyAttributePropertyViewStatus::ErrorInvalidBuffer); + } + + SECTION("Wrong buffer view index") { + model.accessors[accessorIndex].bufferView = -1; + PropertyAttributePropertyView property = + view.getPropertyView("TestClassProperty", primitive); + REQUIRE( + property.status() == + PropertyAttributePropertyViewStatus::ErrorInvalidBufferView); + } + + SECTION("Wrong accessor normalization") { + model.accessors[accessorIndex].normalized = true; + PropertyAttributePropertyView property = + view.getPropertyView("TestClassProperty", primitive); + REQUIRE( + property.status() == PropertyAttributePropertyViewStatus:: + ErrorAccessorNormalizationMismatch); + } + + SECTION("Wrong accessor component type") { + model.accessors[accessorIndex].componentType = + Accessor::ComponentType::SHORT; + PropertyAttributePropertyView property = + view.getPropertyView("TestClassProperty", primitive); + REQUIRE( + property.status() == PropertyAttributePropertyViewStatus:: + ErrorAccessorComponentTypeMismatch); + } + + SECTION("Wrong accessor type") { + model.accessors[accessorIndex].type = Accessor::Type::VEC2; + PropertyAttributePropertyView property = + view.getPropertyView("TestClassProperty", primitive); + REQUIRE( + property.status() == + PropertyAttributePropertyViewStatus::ErrorAccessorTypeMismatch); + } + + SECTION("Wrong accessor index") { + primitive.attributes[attributeName] = -1; + PropertyAttributePropertyView property = + view.getPropertyView("TestClassProperty", primitive); + REQUIRE( + property.status() == + PropertyAttributePropertyViewStatus::ErrorInvalidAccessor); + } + + SECTION("Missing attribute") { + primitive.attributes.clear(); + PropertyAttributePropertyView property = + view.getPropertyView("TestClassProperty", primitive); + REQUIRE( + property.status() == + PropertyAttributePropertyViewStatus::ErrorMissingAttribute); + } +} + +// TEST_CASE("Test scalar PropertyAttributeProperty (normalized)") { +// Model model; +// std::vector data = {12, 34, 30, 11}; +// +// addAttributeToModel( +// model, +// Sampler::WrapS::CLAMP_TO_EDGE, +// Sampler::WrapS::CLAMP_TO_EDGE, +// 2, +// 2, +// 1, +// data); +// size_t AttributeIndex = model.Attributes.size() - 1; +// size_t imageIndex = model.images.size() - 1; +// +// ExtensionModelExtStructuralMetadata& metadata = +// model.addExtension(); +// +// Schema& schema = metadata.schema.emplace(); +// Class& testClass = schema.classes["TestClass"]; +// ClassProperty& testClassProperty = +// testClass.properties["TestClassProperty"]; testClassProperty.type = +// ClassProperty::Type::SCALAR; testClassProperty.componentType = +// ClassProperty::ComponentType::UINT8; testClassProperty.normalized = true; +// +// PropertyAttribute& propertyAttribute = +// metadata.propertyAttributes.emplace_back(); propertyAttribute.classProperty +// = "TestClass"; +// +// PropertyAttributeProperty& propertyAttributeProperty = +// propertyAttribute.properties["TestClassProperty"]; +// propertyAttributeProperty.index = static_cast(AttributeIndex); +// propertyAttributeProperty.texCoord = 0; +// propertyAttributeProperty.channels = {0}; +// +// PropertyAttributeView view(model, propertyAttribute); +// REQUIRE(view.status() == PropertyAttributeViewStatus::Valid); +// +// const ClassProperty* classProperty = +// view.getClassProperty("TestClassProperty"); +// REQUIRE(classProperty); +// REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); +// REQUIRE(classProperty->componentType == +// ClassProperty::ComponentType::UINT8); REQUIRE(classProperty->count == +// std::nullopt); REQUIRE(!classProperty->array); +// REQUIRE(classProperty->normalized); +// +// SECTION("Access correct type") { +// PropertyAttributePropertyView uint8Property = +// view.getPropertyView("TestClassProperty"); +// REQUIRE(uint8Property.status() == +// PropertyAttributePropertyViewStatus::Valid); +// +// std::vector texCoords{ +// glm::dvec2(0, 0), +// glm::dvec2(0.5, 0), +// glm::dvec2(0, 0.5), +// glm::dvec2(0.5, 0.5)}; +// +// for (size_t i = 0; i < texCoords.size(); ++i) { +// glm::dvec2 uv = texCoords[i]; +// REQUIRE(uint8Property.getRaw(uv[0], uv[1]) == data[i]); +// REQUIRE(uint8Property.get(uv[0], uv[1]) == normalize(data[i])); +// } +// } +// +// SECTION("Access wrong type") { +// PropertyAttributePropertyView u8vec2Invalid = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// u8vec2Invalid.status() == +// PropertyAttributePropertyViewStatus::ErrorTypeMismatch); +// } +// +// SECTION("Access wrong component type") { +// PropertyAttributePropertyView uint16Invalid = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// uint16Invalid.status() == +// PropertyAttributePropertyViewStatus::ErrorComponentTypeMismatch); +// +// PropertyAttributePropertyView int32Invalid = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// int32Invalid.status() == +// PropertyAttributePropertyViewStatus::ErrorComponentTypeMismatch); +// } +// +// SECTION("Access incorrectly as array") { +// PropertyAttributePropertyView, true> +// arrayInvalid +// = +// view.getPropertyView, true>( +// "TestClassProperty"); +// REQUIRE( +// arrayInvalid.status() == +// PropertyAttributePropertyViewStatus::ErrorArrayTypeMismatch); +// } +// +// SECTION("Access incorrectly as non-normalized") { +// PropertyAttributePropertyView normalizedInvalid = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// normalizedInvalid.status() == +// PropertyAttributePropertyViewStatus::ErrorNormalizationMismatch); +// } +// +// SECTION("Access incorrectly as double") { +// PropertyAttributePropertyView doubleInvalid = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// doubleInvalid.status() == +// PropertyAttributePropertyViewStatus::ErrorComponentTypeMismatch); +// } +// +// SECTION("Channel and type mismatch") { +// model.images[imageIndex].cesium.channels = 2; +// propertyAttributeProperty.channels = {0, 1}; +// PropertyAttributePropertyView uint8Property = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// uint8Property.status() == +// PropertyAttributePropertyViewStatus::ErrorChannelsAndTypeMismatch); +// } +//} +// +// TEST_CASE("Test vecN PropertyAttributeProperty") { +// Model model; +// std::vector data = {12, 34, 10, 3, 40, 0, 30, 11}; +// +// addAttributeToModel( +// model, +// Sampler::WrapS::CLAMP_TO_EDGE, +// Sampler::WrapS::CLAMP_TO_EDGE, +// 2, +// 2, +// 2, +// data); +// size_t AttributeIndex = model.Attributes.size() - 1; +// size_t imageIndex = model.images.size() - 1; +// +// ExtensionModelExtStructuralMetadata& metadata = +// model.addExtension(); +// +// Schema& schema = metadata.schema.emplace(); +// Class& testClass = schema.classes["TestClass"]; +// ClassProperty& testClassProperty = +// testClass.properties["TestClassProperty"]; testClassProperty.type = +// ClassProperty::Type::VEC2; testClassProperty.componentType = +// ClassProperty::ComponentType::UINT8; +// +// PropertyAttribute& propertyAttribute = +// metadata.propertyAttributes.emplace_back(); propertyAttribute.classProperty +// = "TestClass"; +// +// PropertyAttributeProperty& propertyAttributeProperty = +// propertyAttribute.properties["TestClassProperty"]; +// propertyAttributeProperty.index = static_cast(AttributeIndex); +// propertyAttributeProperty.texCoord = 0; +// propertyAttributeProperty.channels = {0, 1}; +// +// PropertyAttributeView view(model, propertyAttribute); +// REQUIRE(view.status() == PropertyAttributeViewStatus::Valid); +// +// const ClassProperty* classProperty = +// view.getClassProperty("TestClassProperty"); +// REQUIRE(classProperty); +// REQUIRE(classProperty->type == ClassProperty::Type::VEC2); +// REQUIRE(classProperty->componentType == +// ClassProperty::ComponentType::UINT8); REQUIRE(classProperty->count == +// std::nullopt); REQUIRE(!classProperty->array); +// REQUIRE(!classProperty->normalized); +// +// SECTION("Access correct type") { +// PropertyAttributePropertyView u8vec2Property = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// u8vec2Property.status() == +// PropertyAttributePropertyViewStatus::Valid); +// +// std::vector texCoords{ +// glm::dvec2(0, 0), +// glm::dvec2(0.5, 0), +// glm::dvec2(0, 0.5), +// glm::dvec2(0.5, 0.5)}; +// +// std::vector expected{ +// glm::u8vec2(12, 34), +// glm::u8vec2(10, 3), +// glm::u8vec2(40, 0), +// glm::u8vec2(30, 11)}; +// for (size_t i = 0; i < texCoords.size(); ++i) { +// glm::dvec2 uv = texCoords[i]; +// REQUIRE(u8vec2Property.getRaw(uv[0], uv[1]) == expected[i]); +// REQUIRE(u8vec2Property.get(uv[0], uv[1]) == expected[i]); +// } +// } +// +// SECTION("Access wrong type") { +// PropertyAttributePropertyView uint8Invalid = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// uint8Invalid.status() == +// PropertyAttributePropertyViewStatus::ErrorTypeMismatch); +// +// PropertyAttributePropertyView u8vec3Invalid = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// u8vec3Invalid.status() == +// PropertyAttributePropertyViewStatus::ErrorTypeMismatch); +// } +// +// SECTION("Access wrong component type") { +// PropertyAttributePropertyView u16vec2Invalid = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// u16vec2Invalid.status() == +// PropertyAttributePropertyViewStatus::ErrorComponentTypeMismatch); +// +// PropertyAttributePropertyView i8vec2Invalid = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// i8vec2Invalid.status() == +// PropertyAttributePropertyViewStatus::ErrorComponentTypeMismatch); +// } +// +// SECTION("Access incorrectly as array") { +// PropertyAttributePropertyView> arrayInvalid +// = +// view.getPropertyView>( +// "TestClassProperty"); +// REQUIRE( +// arrayInvalid.status() == +// PropertyAttributePropertyViewStatus::ErrorArrayTypeMismatch); +// } +// +// SECTION("Access incorrectly as normalized") { +// PropertyAttributePropertyView normalizedInvalid = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// normalizedInvalid.status() == +// PropertyAttributePropertyViewStatus::ErrorNormalizationMismatch); +// } +// +// SECTION("Channel and type mismatch") { +// model.images[imageIndex].cesium.channels = 4; +// propertyAttributeProperty.channels = {0, 1, 2, 3}; +// PropertyAttributePropertyView u8vec2Property = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// u8vec2Property.status() == +// PropertyAttributePropertyViewStatus::ErrorChannelsAndTypeMismatch); +// } +// +// SECTION("Invalid channel values") { +// propertyAttributeProperty.channels = {0, 4}; +// PropertyAttributePropertyView u8vec2Property = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// u8vec2Property.status() == +// PropertyAttributePropertyViewStatus::ErrorInvalidChannels); +// } +// +// SECTION("Invalid bytes per channel") { +// model.images[imageIndex].cesium.bytesPerChannel = 2; +// PropertyAttributePropertyView u8vec2Property = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// u8vec2Property.status() == +// PropertyAttributePropertyViewStatus::ErrorInvalidBytesPerChannel); +// } +//} +// +// TEST_CASE("Test vecN PropertyAttributeProperty (normalized)") { +// Model model; +// std::vector data = {12, 34, 10, 3, 40, 0, 30, 11}; +// +// addAttributeToModel( +// model, +// Sampler::WrapS::CLAMP_TO_EDGE, +// Sampler::WrapS::CLAMP_TO_EDGE, +// 2, +// 2, +// 2, +// data); +// size_t AttributeIndex = model.Attributes.size() - 1; +// size_t imageIndex = model.images.size() - 1; +// +// ExtensionModelExtStructuralMetadata& metadata = +// model.addExtension(); +// +// Schema& schema = metadata.schema.emplace(); +// Class& testClass = schema.classes["TestClass"]; +// ClassProperty& testClassProperty = +// testClass.properties["TestClassProperty"]; testClassProperty.type = +// ClassProperty::Type::VEC2; testClassProperty.componentType = +// ClassProperty::ComponentType::UINT8; testClassProperty.normalized = true; +// +// PropertyAttribute& propertyAttribute = +// metadata.propertyAttributes.emplace_back(); propertyAttribute.classProperty +// = "TestClass"; +// +// PropertyAttributeProperty& propertyAttributeProperty = +// propertyAttribute.properties["TestClassProperty"]; +// propertyAttributeProperty.index = static_cast(AttributeIndex); +// propertyAttributeProperty.texCoord = 0; +// propertyAttributeProperty.channels = {0, 1}; +// +// PropertyAttributeView view(model, propertyAttribute); +// REQUIRE(view.status() == PropertyAttributeViewStatus::Valid); +// +// const ClassProperty* classProperty = +// view.getClassProperty("TestClassProperty"); +// REQUIRE(classProperty); +// REQUIRE(classProperty->type == ClassProperty::Type::VEC2); +// REQUIRE(classProperty->componentType == +// ClassProperty::ComponentType::UINT8); REQUIRE(classProperty->count == +// std::nullopt); REQUIRE(!classProperty->array); +// REQUIRE(classProperty->normalized); +// +// SECTION("Access correct type") { +// PropertyAttributePropertyView u8vec2Property = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// u8vec2Property.status() == +// PropertyAttributePropertyViewStatus::Valid); +// +// std::vector texCoords{ +// glm::dvec2(0, 0), +// glm::dvec2(0.5, 0), +// glm::dvec2(0, 0.5), +// glm::dvec2(0.5, 0.5)}; +// +// std::vector expected{ +// glm::u8vec2(12, 34), +// glm::u8vec2(10, 3), +// glm::u8vec2(40, 0), +// glm::u8vec2(30, 11)}; +// for (size_t i = 0; i < texCoords.size(); ++i) { +// glm::dvec2 uv = texCoords[i]; +// REQUIRE(u8vec2Property.getRaw(uv[0], uv[1]) == expected[i]); +// REQUIRE(u8vec2Property.get(uv[0], uv[1]) == normalize(expected[i])); +// } +// } +// +// SECTION("Access wrong type") { +// PropertyAttributePropertyView uint8Invalid = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// uint8Invalid.status() == +// PropertyAttributePropertyViewStatus::ErrorTypeMismatch); +// +// PropertyAttributePropertyView u8vec3Invalid = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// u8vec3Invalid.status() == +// PropertyAttributePropertyViewStatus::ErrorTypeMismatch); +// } +// +// SECTION("Access wrong component type") { +// PropertyAttributePropertyView u16vec2Invalid = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// u16vec2Invalid.status() == +// PropertyAttributePropertyViewStatus::ErrorComponentTypeMismatch); +// +// PropertyAttributePropertyView i8vec2Invalid = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// i8vec2Invalid.status() == +// PropertyAttributePropertyViewStatus::ErrorComponentTypeMismatch); +// } +// +// SECTION("Access incorrectly as array") { +// PropertyAttributePropertyView, true> +// arrayInvalid = +// view.getPropertyView, true>( +// "TestClassProperty"); +// REQUIRE( +// arrayInvalid.status() == +// PropertyAttributePropertyViewStatus::ErrorArrayTypeMismatch); +// } +// +// SECTION("Access incorrectly as non-normalized") { +// PropertyAttributePropertyView normalizedInvalid = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// normalizedInvalid.status() == +// PropertyAttributePropertyViewStatus::ErrorNormalizationMismatch); +// } +// +// SECTION("Access incorrectly as dvec2") { +// PropertyAttributePropertyView normalizedInvalid = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// normalizedInvalid.status() == +// PropertyAttributePropertyViewStatus::ErrorComponentTypeMismatch); +// } +// +// SECTION("Channel and type mismatch") { +// model.images[imageIndex].cesium.channels = 4; +// propertyAttributeProperty.channels = {0, 1, 2, 3}; +// PropertyAttributePropertyView u8vec2Property = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// u8vec2Property.status() == +// PropertyAttributePropertyViewStatus::ErrorChannelsAndTypeMismatch); +// } +//} +// +// TEST_CASE("Test array PropertyAttributeProperty") { +// Model model; +// // clang-format off +// std::vector data = { +// 12, 34, 10, +// 40, 0, 30, +// 80, 4, 2, +// 6, 3, 4, +// }; +// // clang-format on +// +// addAttributeToModel( +// model, +// Sampler::WrapS::CLAMP_TO_EDGE, +// Sampler::WrapS::CLAMP_TO_EDGE, +// 2, +// 2, +// 3, +// data); +// size_t AttributeIndex = model.Attributes.size() - 1; +// size_t imageIndex = model.images.size() - 1; +// +// ExtensionModelExtStructuralMetadata& metadata = +// model.addExtension(); +// +// Schema& schema = metadata.schema.emplace(); +// Class& testClass = schema.classes["TestClass"]; +// ClassProperty& testClassProperty = +// testClass.properties["TestClassProperty"]; testClassProperty.type = +// ClassProperty::Type::SCALAR; testClassProperty.componentType = +// ClassProperty::ComponentType::UINT8; testClassProperty.array = true; +// testClassProperty.count = 3; +// +// PropertyAttribute& propertyAttribute = +// metadata.propertyAttributes.emplace_back(); propertyAttribute.classProperty +// = "TestClass"; +// +// PropertyAttributeProperty& propertyAttributeProperty = +// propertyAttribute.properties["TestClassProperty"]; +// propertyAttributeProperty.index = static_cast(AttributeIndex); +// propertyAttributeProperty.texCoord = 0; +// propertyAttributeProperty.channels = {0, 1, 2}; +// +// PropertyAttributeView view(model, propertyAttribute); +// REQUIRE(view.status() == PropertyAttributeViewStatus::Valid); +// +// const ClassProperty* classProperty = +// view.getClassProperty("TestClassProperty"); +// REQUIRE(classProperty); +// REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); +// REQUIRE(classProperty->componentType == +// ClassProperty::ComponentType::UINT8); REQUIRE(classProperty->array); +// REQUIRE(classProperty->count == 3); +// REQUIRE(!classProperty->normalized); +// +// SECTION("Access correct type") { +// PropertyAttributePropertyView> +// uint8ArrayProperty +// = +// view.getPropertyView>("TestClassProperty"); +// REQUIRE( +// uint8ArrayProperty.status() == +// PropertyAttributePropertyViewStatus::Valid); +// +// std::vector texCoords{ +// glm::dvec2(0, 0), +// glm::dvec2(0.5, 0), +// glm::dvec2(0, 0.5), +// glm::dvec2(0.5, 0.5)}; +// +// int64_t size = static_cast(texCoords.size()); +// for (int64_t i = 0; i < size; ++i) { +// glm::dvec2 uv = texCoords[static_cast(i)]; +// +// auto dataStart = data.begin() + i * 3; +// std::vector expected(dataStart, dataStart + 3); +// +// const PropertyArrayView& value = +// uint8ArrayProperty.getRaw(uv[0], uv[1]); +// REQUIRE(static_cast(value.size()) == expected.size()); +// for (int64_t j = 0; j < value.size(); j++) { +// REQUIRE(value[j] == expected[static_cast(j)]); +// } +// +// auto maybeValue = uint8ArrayProperty.get(uv[0], uv[1]); +// REQUIRE(maybeValue); +// for (int64_t j = 0; j < maybeValue->size(); j++) { +// REQUIRE((*maybeValue)[j] == value[j]); +// } +// } +// } +// +// SECTION("Access wrong component type") { +// PropertyAttributePropertyView> int8ArrayInvalid +// = +// view.getPropertyView>("TestClassProperty"); +// REQUIRE( +// int8ArrayInvalid.status() == +// PropertyAttributePropertyViewStatus::ErrorComponentTypeMismatch); +// +// PropertyAttributePropertyView> +// uint16ArrayInvalid = +// view.getPropertyView>( +// "TestClassProperty"); +// REQUIRE( +// uint16ArrayInvalid.status() == +// PropertyAttributePropertyViewStatus::ErrorComponentTypeMismatch); +// } +// +// SECTION("Access incorrectly as non-array") { +// PropertyAttributePropertyView uint8Invalid = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// uint8Invalid.status() == +// PropertyAttributePropertyViewStatus::ErrorArrayTypeMismatch); +// +// PropertyAttributePropertyView u8vec3Invalid = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// u8vec3Invalid.status() == +// PropertyAttributePropertyViewStatus::ErrorArrayTypeMismatch); +// } +// +// SECTION("Access incorrectly as normalized") { +// PropertyAttributePropertyView, true> +// normalizedInvalid = +// view.getPropertyView, true>( +// "TestClassProperty"); +// REQUIRE( +// normalizedInvalid.status() == +// PropertyAttributePropertyViewStatus::ErrorNormalizationMismatch); +// } +// +// SECTION("Channel and type mismatch") { +// model.images[imageIndex].cesium.channels = 4; +// propertyAttributeProperty.channels = {0, 1, 2, 3}; +// PropertyAttributePropertyView> +// uint8ArrayProperty +// = +// view.getPropertyView>("TestClassProperty"); +// REQUIRE( +// uint8ArrayProperty.status() == +// PropertyAttributePropertyViewStatus::ErrorChannelsAndTypeMismatch); +// } +// +// SECTION("Invalid channel values") { +// propertyAttributeProperty.channels = {0, 4, 1}; +// PropertyAttributePropertyView> +// uint8ArrayProperty +// = +// view.getPropertyView>("TestClassProperty"); +// REQUIRE( +// uint8ArrayProperty.status() == +// PropertyAttributePropertyViewStatus::ErrorInvalidChannels); +// } +// +// SECTION("Invalid bytes per channel") { +// model.images[imageIndex].cesium.bytesPerChannel = 2; +// PropertyAttributePropertyView> +// uint8ArrayProperty +// = +// view.getPropertyView>("TestClassProperty"); +// REQUIRE( +// uint8ArrayProperty.status() == +// PropertyAttributePropertyViewStatus::ErrorInvalidBytesPerChannel); +// } +//} +// +// TEST_CASE("Test array PropertyAttributeProperty (normalized)") { +// Model model; +// // clang-format off +// std::vector data = { +// 12, 34, 10, +// 40, 0, 30, +// 80, 4, 2, +// 6, 3, 4, +// }; +// // clang-format on +// +// addAttributeToModel( +// model, +// Sampler::WrapS::CLAMP_TO_EDGE, +// Sampler::WrapS::CLAMP_TO_EDGE, +// 2, +// 2, +// 3, +// data); +// size_t AttributeIndex = model.Attributes.size() - 1; +// size_t imageIndex = model.images.size() - 1; +// +// ExtensionModelExtStructuralMetadata& metadata = +// model.addExtension(); +// +// Schema& schema = metadata.schema.emplace(); +// Class& testClass = schema.classes["TestClass"]; +// ClassProperty& testClassProperty = +// testClass.properties["TestClassProperty"]; testClassProperty.type = +// ClassProperty::Type::SCALAR; testClassProperty.componentType = +// ClassProperty::ComponentType::UINT8; testClassProperty.array = true; +// testClassProperty.count = 3; +// testClassProperty.normalized = true; +// +// PropertyAttribute& propertyAttribute = +// metadata.propertyAttributes.emplace_back(); propertyAttribute.classProperty +// = "TestClass"; +// +// PropertyAttributeProperty& propertyAttributeProperty = +// propertyAttribute.properties["TestClassProperty"]; +// propertyAttributeProperty.index = static_cast(AttributeIndex); +// propertyAttributeProperty.texCoord = 0; +// propertyAttributeProperty.channels = {0, 1, 2}; +// +// PropertyAttributeView view(model, propertyAttribute); +// REQUIRE(view.status() == PropertyAttributeViewStatus::Valid); +// +// const ClassProperty* classProperty = +// view.getClassProperty("TestClassProperty"); +// REQUIRE(classProperty); +// REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); +// REQUIRE(classProperty->componentType == +// ClassProperty::ComponentType::UINT8); REQUIRE(classProperty->array); +// REQUIRE(classProperty->count == 3); +// REQUIRE(classProperty->normalized); +// +// SECTION("Access correct type") { +// PropertyAttributePropertyView, true> +// uint8ArrayProperty = +// view.getPropertyView, true>( +// "TestClassProperty"); +// REQUIRE( +// uint8ArrayProperty.status() == +// PropertyAttributePropertyViewStatus::Valid); +// +// std::vector texCoords{ +// glm::dvec2(0, 0), +// glm::dvec2(0.5, 0), +// glm::dvec2(0, 0.5), +// glm::dvec2(0.5, 0.5)}; +// +// int64_t size = static_cast(texCoords.size()); +// for (int64_t i = 0; i < size; ++i) { +// glm::dvec2 uv = texCoords[static_cast(i)]; +// +// auto dataStart = data.begin() + i * 3; +// std::vector expected(dataStart, dataStart + 3); +// +// const PropertyArrayView& value = +// uint8ArrayProperty.getRaw(uv[0], uv[1]); +// REQUIRE(static_cast(value.size()) == expected.size()); +// for (int64_t j = 0; j < value.size(); j++) { +// REQUIRE(value[j] == expected[static_cast(j)]); +// } +// +// auto maybeValue = uint8ArrayProperty.get(uv[0], uv[1]); +// REQUIRE(maybeValue); +// for (int64_t j = 0; j < maybeValue->size(); j++) { +// REQUIRE((*maybeValue)[j] == normalize(value[j])); +// } +// } +// } +// +// SECTION("Access wrong component type") { +// PropertyAttributePropertyView, true> +// int8ArrayInvalid = +// view.getPropertyView, true>( +// "TestClassProperty"); +// REQUIRE( +// int8ArrayInvalid.status() == +// PropertyAttributePropertyViewStatus::ErrorComponentTypeMismatch); +// +// PropertyAttributePropertyView, true> +// uint16ArrayInvalid = +// view.getPropertyView, true>( +// "TestClassProperty"); +// REQUIRE( +// uint16ArrayInvalid.status() == +// PropertyAttributePropertyViewStatus::ErrorComponentTypeMismatch); +// } +// +// SECTION("Access incorrectly as non-array") { +// PropertyAttributePropertyView uint8Invalid = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// uint8Invalid.status() == +// PropertyAttributePropertyViewStatus::ErrorArrayTypeMismatch); +// +// PropertyAttributePropertyView u8vec3Invalid = +// view.getPropertyView("TestClassProperty"); +// REQUIRE( +// u8vec3Invalid.status() == +// PropertyAttributePropertyViewStatus::ErrorArrayTypeMismatch); +// } +// +// SECTION("Access incorrectly as normalized") { +// PropertyAttributePropertyView> +// normalizedInvalid +// = +// view.getPropertyView>("TestClassProperty"); +// REQUIRE( +// normalizedInvalid.status() == +// PropertyAttributePropertyViewStatus::ErrorNormalizationMismatch); +// } +// +// SECTION("Channel and type mismatch") { +// model.images[imageIndex].cesium.channels = 4; +// propertyAttributeProperty.channels = {0, 1, 2, 3}; +// PropertyAttributePropertyView, true> +// uint8ArrayProperty = +// view.getPropertyView, true>( +// "TestClassProperty"); +// REQUIRE( +// uint8ArrayProperty.status() == +// PropertyAttributePropertyViewStatus::ErrorChannelsAndTypeMismatch); +// } +//} +// +// TEST_CASE("Test with PropertyAttributeProperty offset, scale, min, max") { +// Model model; +// // clang-format off +// std::vector data{ +// 0, 0, 0, 1, +// 9, 0, 1, 0, +// 20, 2, 2, 0, +// 8, 1, 0, 1}; +// // clang-format on +// +// std::vector expectedUint{16777216, 65545, 131604, 16777480}; +// +// const float offset = 1.0f; +// const float scale = 2.0f; +// const float min = -10.0f; +// const float max = 10.0f; +// +// addAttributeToModel( +// model, +// Sampler::WrapS::CLAMP_TO_EDGE, +// Sampler::WrapS::CLAMP_TO_EDGE, +// 2, +// 2, +// 4, +// data); +// size_t AttributeIndex = model.Attributes.size() - 1; +// +// ExtensionModelExtStructuralMetadata& metadata = +// model.addExtension(); +// +// Schema& schema = metadata.schema.emplace(); +// Class& testClass = schema.classes["TestClass"]; +// ClassProperty& testClassProperty = +// testClass.properties["TestClassProperty"]; testClassProperty.type = +// ClassProperty::Type::SCALAR; testClassProperty.componentType = +// ClassProperty::ComponentType::FLOAT32; testClassProperty.offset = offset; +// testClassProperty.scale = scale; +// testClassProperty.min = min; +// testClassProperty.max = max; +// +// PropertyAttribute& propertyAttribute = +// metadata.propertyAttributes.emplace_back(); propertyAttribute.classProperty +// = "TestClass"; +// +// PropertyAttributeProperty& propertyAttributeProperty = +// propertyAttribute.properties["TestClassProperty"]; +// propertyAttributeProperty.index = static_cast(AttributeIndex); +// propertyAttributeProperty.texCoord = 0; +// propertyAttributeProperty.channels = {0, 1, 2, 3}; +// +// PropertyAttributeView view(model, propertyAttribute); +// REQUIRE(view.status() == PropertyAttributeViewStatus::Valid); +// +// const ClassProperty* classProperty = +// view.getClassProperty("TestClassProperty"); +// REQUIRE(classProperty); +// REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); +// REQUIRE( +// classProperty->componentType == ClassProperty::ComponentType::FLOAT32); +// REQUIRE(classProperty->count == std::nullopt); +// REQUIRE(!classProperty->array); +// REQUIRE(!classProperty->normalized); +// REQUIRE(classProperty->offset); +// REQUIRE(classProperty->scale); +// REQUIRE(classProperty->min); +// REQUIRE(classProperty->max); +// +// std::vector texCoords{ +// glm::dvec2(0, 0), +// glm::dvec2(0.5, 0), +// glm::dvec2(0, 0.5), +// glm::dvec2(0.5, 0.5)}; +// +// SECTION("Use class property values") { +// PropertyAttributePropertyView property = +// view.getPropertyView("TestClassProperty"); +// REQUIRE(property.status() == PropertyAttributePropertyViewStatus::Valid); +// REQUIRE(property.offset() == offset); +// REQUIRE(property.scale() == scale); +// REQUIRE(property.min() == min); +// REQUIRE(property.max() == max); +// +// std::vector expectedRaw(expectedUint.size()); +// std::vector expectedTransformed(expectedUint.size()); +// for (size_t i = 0; i < expectedUint.size(); i++) { +// float value = *reinterpret_cast(&expectedUint[i]); +// expectedRaw[i] = value; +// expectedTransformed[i] = value * scale + offset; +// } +// +// for (size_t i = 0; i < texCoords.size(); ++i) { +// glm::dvec2 uv = texCoords[i]; +// REQUIRE(property.getRaw(uv[0], uv[1]) == expectedRaw[i]); +// REQUIRE(property.get(uv[0], uv[1]) == expectedTransformed[i]); +// } +// } +// +// SECTION("Use own property values") { +// const float newOffset = 1.0f; +// const float newScale = -1.0f; +// const float newMin = -3.0f; +// const float newMax = 0.0f; +// propertyAttributeProperty.offset = newOffset; +// propertyAttributeProperty.scale = newScale; +// propertyAttributeProperty.min = newMin; +// propertyAttributeProperty.max = newMax; +// +// PropertyAttributePropertyView property = +// view.getPropertyView("TestClassProperty"); +// REQUIRE(property.status() == PropertyAttributePropertyViewStatus::Valid); +// REQUIRE(property.offset() == newOffset); +// REQUIRE(property.scale() == newScale); +// REQUIRE(property.min() == newMin); +// REQUIRE(property.max() == newMax); +// +// std::vector expectedRaw(expectedUint.size()); +// std::vector expectedTransformed(expectedUint.size()); +// for (size_t i = 0; i < expectedUint.size(); i++) { +// float value = *reinterpret_cast(&expectedUint[i]); +// expectedRaw[i] = value; +// expectedTransformed[i] = value * newScale + newOffset; +// } +// +// for (size_t i = 0; i < texCoords.size(); ++i) { +// glm::dvec2 uv = texCoords[i]; +// REQUIRE(property.getRaw(uv[0], uv[1]) == expectedRaw[i]); +// REQUIRE(property.get(uv[0], uv[1]) == expectedTransformed[i]); +// } +// } +//} +// +// TEST_CASE( +// "Test with PropertyAttributeProperty offset, scale, min, max +// (normalized)") +// { +// Model model; +// std::vector data = {12, 34, 30, 11}; +// +// const double offset = 1.0; +// const double scale = 2.0; +// const double min = 1.0; +// const double max = 3.0; +// +// addAttributeToModel( +// model, +// Sampler::WrapS::CLAMP_TO_EDGE, +// Sampler::WrapS::CLAMP_TO_EDGE, +// 2, +// 2, +// 1, +// data); +// size_t AttributeIndex = model.Attributes.size() - 1; +// +// ExtensionModelExtStructuralMetadata& metadata = +// model.addExtension(); +// +// Schema& schema = metadata.schema.emplace(); +// Class& testClass = schema.classes["TestClass"]; +// ClassProperty& testClassProperty = +// testClass.properties["TestClassProperty"]; testClassProperty.type = +// ClassProperty::Type::SCALAR; testClassProperty.componentType = +// ClassProperty::ComponentType::UINT8; testClassProperty.normalized = true; +// testClassProperty.offset = offset; +// testClassProperty.scale = scale; +// testClassProperty.min = min; +// testClassProperty.max = max; +// +// PropertyAttribute& propertyAttribute = +// metadata.propertyAttributes.emplace_back(); propertyAttribute.classProperty +// = "TestClass"; +// +// PropertyAttributeProperty& propertyAttributeProperty = +// propertyAttribute.properties["TestClassProperty"]; +// propertyAttributeProperty.index = static_cast(AttributeIndex); +// propertyAttributeProperty.texCoord = 0; +// propertyAttributeProperty.channels = {0}; +// +// PropertyAttributeView view(model, propertyAttribute); +// REQUIRE(view.status() == PropertyAttributeViewStatus::Valid); +// +// const ClassProperty* classProperty = +// view.getClassProperty("TestClassProperty"); +// REQUIRE(classProperty); +// REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); +// REQUIRE(classProperty->componentType == +// ClassProperty::ComponentType::UINT8); REQUIRE(classProperty->count == +// std::nullopt); REQUIRE(!classProperty->array); +// REQUIRE(classProperty->normalized); +// +// std::vector texCoords{ +// glm::dvec2(0, 0), +// glm::dvec2(0.5, 0), +// glm::dvec2(0, 0.5), +// glm::dvec2(0.5, 0.5)}; +// +// SECTION("Use class property values") { +// PropertyAttributePropertyView property = +// view.getPropertyView("TestClassProperty"); +// REQUIRE(property.status() == PropertyAttributePropertyViewStatus::Valid); +// REQUIRE(property.offset() == offset); +// REQUIRE(property.scale() == scale); +// REQUIRE(property.min() == min); +// REQUIRE(property.max() == max); +// +// for (size_t i = 0; i < texCoords.size(); ++i) { +// glm::dvec2 uv = texCoords[i]; +// REQUIRE(property.getRaw(uv[0], uv[1]) == data[i]); +// REQUIRE( +// property.get(uv[0], uv[1]) == normalize(data[i]) * scale + offset); +// } +// } +// +// SECTION("Use own property values") { +// const double newOffset = 2.0; +// const double newScale = 5.0; +// const double newMin = 10.0; +// const double newMax = 11.0; +// propertyAttributeProperty.offset = newOffset; +// propertyAttributeProperty.scale = newScale; +// propertyAttributeProperty.min = newMin; +// propertyAttributeProperty.max = newMax; +// +// PropertyAttributePropertyView property = +// view.getPropertyView("TestClassProperty"); +// REQUIRE(property.status() == PropertyAttributePropertyViewStatus::Valid); +// REQUIRE(property.offset() == newOffset); +// REQUIRE(property.scale() == newScale); +// REQUIRE(property.min() == newMin); +// REQUIRE(property.max() == newMax); +// +// for (size_t i = 0; i < texCoords.size(); ++i) { +// glm::dvec2 uv = texCoords[i]; +// REQUIRE(property.getRaw(uv[0], uv[1]) == data[i]); +// REQUIRE( +// property.get(uv[0], uv[1]) == +// normalize(data[i]) * newScale + newOffset); +// } +// } +//} +// +// TEST_CASE("Test with PropertyAttributeProperty noData") { +// Model model; +// std::vector data = {12, 34, 30, 11}; +// const uint8_t noData = 34; +// +// addAttributeToModel( +// model, +// Sampler::WrapS::CLAMP_TO_EDGE, +// Sampler::WrapS::CLAMP_TO_EDGE, +// 2, +// 2, +// 1, +// data); +// size_t AttributeIndex = model.Attributes.size() - 1; +// +// ExtensionModelExtStructuralMetadata& metadata = +// model.addExtension(); +// +// Schema& schema = metadata.schema.emplace(); +// Class& testClass = schema.classes["TestClass"]; +// ClassProperty& testClassProperty = +// testClass.properties["TestClassProperty"]; testClassProperty.type = +// ClassProperty::Type::SCALAR; testClassProperty.componentType = +// ClassProperty::ComponentType::UINT8; testClassProperty.noData = noData; +// +// PropertyAttribute& propertyAttribute = +// metadata.propertyAttributes.emplace_back(); propertyAttribute.classProperty +// = "TestClass"; +// +// PropertyAttributeProperty& propertyAttributeProperty = +// propertyAttribute.properties["TestClassProperty"]; +// propertyAttributeProperty.index = static_cast(AttributeIndex); +// propertyAttributeProperty.texCoord = 0; +// propertyAttributeProperty.channels = {0}; +// +// PropertyAttributeView view(model, propertyAttribute); +// REQUIRE(view.status() == PropertyAttributeViewStatus::Valid); +// +// const ClassProperty* classProperty = +// view.getClassProperty("TestClassProperty"); +// REQUIRE(classProperty); +// REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); +// REQUIRE(classProperty->componentType == +// ClassProperty::ComponentType::UINT8); REQUIRE(classProperty->count == +// std::nullopt); REQUIRE(!classProperty->array); +// REQUIRE(!classProperty->normalized); +// +// std::vector texCoords{ +// glm::dvec2(0, 0), +// glm::dvec2(0.5, 0), +// glm::dvec2(0, 0.5), +// glm::dvec2(0.5, 0.5)}; +// +// SECTION("Without default value") { +// PropertyAttributePropertyView property = +// view.getPropertyView("TestClassProperty"); +// REQUIRE(property.status() == PropertyAttributePropertyViewStatus::Valid); +// +// for (size_t i = 0; i < texCoords.size(); ++i) { +// glm::dvec2 uv = texCoords[i]; +// auto value = property.getRaw(uv[0], uv[1]); +// REQUIRE(value == data[i]); +// +// auto maybeValue = property.get(uv[0], uv[1]); +// if (value == noData) { +// REQUIRE(!maybeValue); +// } else { +// REQUIRE(maybeValue == data[i]); +// } +// } +// } +// +// SECTION("With default value") { +// const uint8_t defaultValue = 255; +// testClassProperty.defaultProperty = defaultValue; +// +// PropertyAttributePropertyView property = +// view.getPropertyView("TestClassProperty"); +// REQUIRE(property.status() == PropertyAttributePropertyViewStatus::Valid); +// +// for (size_t i = 0; i < texCoords.size(); ++i) { +// glm::dvec2 uv = texCoords[i]; +// auto value = property.getRaw(uv[0], uv[1]); +// REQUIRE(value == data[i]); +// +// auto maybeValue = property.get(uv[0], uv[1]); +// if (value == noData) { +// REQUIRE(maybeValue == defaultValue); +// } else { +// REQUIRE(maybeValue == data[i]); +// } +// } +// } +//} +// +// TEST_CASE("Test with PropertyAttributeProperty noData (normalized)") { +// Model model; +// std::vector data = {12, 34, 30, 11}; +// const uint8_t noData = 34; +// +// addAttributeToModel( +// model, +// Sampler::WrapS::CLAMP_TO_EDGE, +// Sampler::WrapS::CLAMP_TO_EDGE, +// 2, +// 2, +// 1, +// data); +// size_t AttributeIndex = model.Attributes.size() - 1; +// +// ExtensionModelExtStructuralMetadata& metadata = +// model.addExtension(); +// +// Schema& schema = metadata.schema.emplace(); +// Class& testClass = schema.classes["TestClass"]; +// ClassProperty& testClassProperty = +// testClass.properties["TestClassProperty"]; testClassProperty.type = +// ClassProperty::Type::SCALAR; testClassProperty.componentType = +// ClassProperty::ComponentType::UINT8; testClassProperty.normalized = true; +// testClassProperty.noData = noData; +// +// PropertyAttribute& propertyAttribute = +// metadata.propertyAttributes.emplace_back(); propertyAttribute.classProperty +// = "TestClass"; +// +// PropertyAttributeProperty& propertyAttributeProperty = +// propertyAttribute.properties["TestClassProperty"]; +// propertyAttributeProperty.index = static_cast(AttributeIndex); +// propertyAttributeProperty.texCoord = 0; +// propertyAttributeProperty.channels = {0}; +// +// PropertyAttributeView view(model, propertyAttribute); +// REQUIRE(view.status() == PropertyAttributeViewStatus::Valid); +// +// const ClassProperty* classProperty = +// view.getClassProperty("TestClassProperty"); +// REQUIRE(classProperty); +// REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); +// REQUIRE(classProperty->componentType == +// ClassProperty::ComponentType::UINT8); REQUIRE(classProperty->count == +// std::nullopt); REQUIRE(!classProperty->array); +// REQUIRE(classProperty->normalized); +// +// std::vector texCoords{ +// glm::dvec2(0, 0), +// glm::dvec2(0.5, 0), +// glm::dvec2(0, 0.5), +// glm::dvec2(0.5, 0.5)}; +// +// SECTION("Without default value") { +// PropertyAttributePropertyView property = +// view.getPropertyView("TestClassProperty"); +// REQUIRE(property.status() == PropertyAttributePropertyViewStatus::Valid); +// +// for (size_t i = 0; i < texCoords.size(); ++i) { +// glm::dvec2 uv = texCoords[i]; +// auto value = property.getRaw(uv[0], uv[1]); +// REQUIRE(value == data[i]); +// +// auto maybeValue = property.get(uv[0], uv[1]); +// if (value == noData) { +// REQUIRE(!maybeValue); +// } else { +// REQUIRE(maybeValue == normalize(data[i])); +// } +// } +// } +// +// SECTION("With default value") { +// const double defaultValue = -1.0; +// testClassProperty.defaultProperty = defaultValue; +// +// PropertyAttributePropertyView property = +// view.getPropertyView("TestClassProperty"); +// REQUIRE(property.status() == PropertyAttributePropertyViewStatus::Valid); +// +// for (size_t i = 0; i < texCoords.size(); ++i) { +// glm::dvec2 uv = texCoords[i]; +// auto value = property.getRaw(uv[0], uv[1]); +// REQUIRE(value == data[i]); +// +// auto maybeValue = property.get(uv[0], uv[1]); +// if (value == noData) { +// REQUIRE(maybeValue == defaultValue); +// } else { +// REQUIRE(maybeValue == normalize(data[i])); +// } +// } +// } +//} +// +// TEST_CASE("Test callback on invalid property Attribute view") { +// Model model; +// ExtensionModelExtStructuralMetadata& metadata = +// model.addExtension(); +// metadata.schema.emplace(); +// +// // Property Attribute has a nonexistent class. +// PropertyAttribute& propertyAttribute = +// metadata.propertyAttributes.emplace_back(); propertyAttribute.classProperty +// = "TestClass"; +// +// PropertyAttributeProperty& propertyAttributeProperty = +// propertyAttribute.properties["TestClassProperty"]; +// propertyAttributeProperty.index = -1; +// +// PropertyAttributeView view(model, propertyAttribute); +// REQUIRE(view.status() == PropertyAttributeViewStatus::ErrorClassNotFound); +// +// const ClassProperty* classProperty = +// view.getClassProperty("TestClassProperty"); +// REQUIRE(!classProperty); +// +// uint32_t invokedCallbackCount = 0; +// view.getPropertyView( +// "TestClassProperty", +// [&invokedCallbackCount]( +// const std::string& /*propertyName*/, +// auto propertyValue) mutable { +// invokedCallbackCount++; +// REQUIRE( +// propertyValue.status() == +// PropertyAttributePropertyViewStatus::ErrorInvalidPropertyAttribute); +// }); +// +// REQUIRE(invokedCallbackCount == 1); +//} +// +// TEST_CASE("Test callback on invalid PropertyAttributeProperty") { +// Model model; +// ExtensionModelExtStructuralMetadata& metadata = +// model.addExtension(); +// +// Schema& schema = metadata.schema.emplace(); +// Class& testClass = schema.classes["TestClass"]; +// ClassProperty& testClassProperty = testClass.properties["InvalidProperty"]; +// testClassProperty.type = ClassProperty::Type::SCALAR; +// testClassProperty.componentType = ClassProperty::ComponentType::UINT8; +// +// PropertyAttribute& propertyAttribute = +// metadata.propertyAttributes.emplace_back(); propertyAttribute.classProperty +// = "TestClass"; +// +// PropertyAttributeProperty& propertyAttributeProperty = +// propertyAttribute.properties["InvalidProperty"]; +// propertyAttributeProperty.index = -1; +// +// PropertyAttributeView view(model, propertyAttribute); +// REQUIRE(view.status() == PropertyAttributeViewStatus::Valid); +// +// const ClassProperty* classProperty = +// view.getClassProperty("InvalidProperty"); REQUIRE(classProperty); +// +// classProperty = view.getClassProperty("NonexistentProperty"); +// REQUIRE(!classProperty); +// +// uint32_t invokedCallbackCount = 0; +// auto testCallback = [&invokedCallbackCount]( +// const std::string& /*propertyName*/, +// auto propertyValue) mutable { +// invokedCallbackCount++; +// REQUIRE(propertyValue.status() != +// PropertyAttributePropertyViewStatus::Valid); +// }; +// +// view.getPropertyView("InvalidProperty", testCallback); +// view.getPropertyView("NonexistentProperty", testCallback); +// +// REQUIRE(invokedCallbackCount == 2); +//} +// +// TEST_CASE("Test callback on invalid normalized PropertyAttributeProperty") { +// Model model; +// ExtensionModelExtStructuralMetadata& metadata = +// model.addExtension(); +// +// Schema& schema = metadata.schema.emplace(); +// Class& testClass = schema.classes["TestClass"]; +// ClassProperty& testClassProperty = +// testClass.properties["TestClassProperty"]; testClassProperty.type = +// ClassProperty::Type::SCALAR; testClassProperty.componentType = +// ClassProperty::ComponentType::FLOAT32; testClassProperty.normalized = true; +// // This is erroneous. +// +// PropertyAttribute& propertyAttribute = +// metadata.propertyAttributes.emplace_back(); propertyAttribute.classProperty +// = "TestClass"; +// +// PropertyAttributeProperty& propertyAttributeProperty = +// propertyAttribute.properties["TestClassProperty"]; +// propertyAttributeProperty.index = static_cast(0); +// propertyAttributeProperty.texCoord = 0; +// propertyAttributeProperty.channels = {0, 1}; +// +// PropertyAttributeView view(model, propertyAttribute); +// REQUIRE(view.status() == PropertyAttributeViewStatus::Valid); +// +// const ClassProperty* classProperty = +// view.getClassProperty("TestClassProperty"); +// REQUIRE(classProperty); +// +// uint32_t invokedCallbackCount = 0; +// auto testCallback = [&invokedCallbackCount]( +// const std::string& /*propertyName*/, +// auto propertyValue) mutable { +// invokedCallbackCount++; +// REQUIRE( +// propertyValue.status() == +// PropertyAttributePropertyViewStatus::ErrorInvalidNormalization); +// }; +// +// view.getPropertyView("TestClassProperty", testCallback); +// +// REQUIRE(invokedCallbackCount == 1); +//} +// +// TEST_CASE("Test callback for scalar PropertyAttributeProperty") { +// Model model; +// std::vector data = {255, 255, 12, 1, 30, 2, 0, 255}; +// +// addAttributeToModel( +// model, +// Sampler::WrapS::CLAMP_TO_EDGE, +// Sampler::WrapS::CLAMP_TO_EDGE, +// 2, +// 2, +// 2, +// data); +// size_t AttributeIndex = model.Attributes.size() - 1; +// +// ExtensionModelExtStructuralMetadata& metadata = +// model.addExtension(); +// +// Schema& schema = metadata.schema.emplace(); +// Class& testClass = schema.classes["TestClass"]; +// ClassProperty& testClassProperty = +// testClass.properties["TestClassProperty"]; testClassProperty.type = +// ClassProperty::Type::SCALAR; testClassProperty.componentType = +// ClassProperty::ComponentType::INT16; +// +// PropertyAttribute& propertyAttribute = +// metadata.propertyAttributes.emplace_back(); propertyAttribute.classProperty +// = "TestClass"; +// +// PropertyAttributeProperty& propertyAttributeProperty = +// propertyAttribute.properties["TestClassProperty"]; +// propertyAttributeProperty.index = static_cast(AttributeIndex); +// propertyAttributeProperty.texCoord = 0; +// propertyAttributeProperty.channels = {0, 1}; +// +// PropertyAttributeView view(model, propertyAttribute); +// REQUIRE(view.status() == PropertyAttributeViewStatus::Valid); +// +// const ClassProperty* classProperty = +// view.getClassProperty("TestClassProperty"); +// REQUIRE(classProperty); +// REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); +// REQUIRE(classProperty->componentType == +// ClassProperty::ComponentType::INT16); REQUIRE(classProperty->count == +// std::nullopt); REQUIRE(!classProperty->array); +// REQUIRE(!classProperty->normalized); +// +// std::vector texCoords{ +// glm::dvec2(0, 0), +// glm::dvec2(0.5, 0), +// glm::dvec2(0, 0.5), +// glm::dvec2(0.5, 0.5)}; +// +// std::vector expected{-1, 268, 542, -256}; +// uint32_t invokedCallbackCount = 0; +// view.getPropertyView( +// "TestClassProperty", +// [&expected, &texCoords, &invokedCallbackCount]( +// const std::string& /*propertyName*/, +// auto propertyValue) mutable { +// invokedCallbackCount++; +// if constexpr (std::is_same_v< +// PropertyAttributePropertyView, +// decltype(propertyValue)>) { +// REQUIRE( +// propertyValue.status() == +// PropertyAttributePropertyViewStatus::Valid); +// +// for (size_t i = 0; i < expected.size(); ++i) { +// glm::dvec2& uv = texCoords[i]; +// REQUIRE(propertyValue.getRaw(uv[0], uv[1]) == expected[i]); +// REQUIRE(propertyValue.get(uv[0], uv[1]) == expected[i]); +// } +// } else { +// FAIL("getPropertyView returned PropertyAttributePropertyView of " +// "incorrect type for TestClassProperty."); +// } +// }); +// +// REQUIRE(invokedCallbackCount == 1); +//} +// +// TEST_CASE("Test callback for scalar PropertyAttributeProperty (normalized)") +// { +// Model model; +// std::vector data = {255, 255, 12, 1, 30, 2, 0, 255}; +// +// addAttributeToModel( +// model, +// Sampler::WrapS::CLAMP_TO_EDGE, +// Sampler::WrapS::CLAMP_TO_EDGE, +// 2, +// 2, +// 2, +// data); +// size_t AttributeIndex = model.Attributes.size() - 1; +// +// ExtensionModelExtStructuralMetadata& metadata = +// model.addExtension(); +// +// Schema& schema = metadata.schema.emplace(); +// Class& testClass = schema.classes["TestClass"]; +// ClassProperty& testClassProperty = +// testClass.properties["TestClassProperty"]; testClassProperty.type = +// ClassProperty::Type::SCALAR; testClassProperty.componentType = +// ClassProperty::ComponentType::INT16; testClassProperty.normalized = true; +// +// PropertyAttribute& propertyAttribute = +// metadata.propertyAttributes.emplace_back(); propertyAttribute.classProperty +// = "TestClass"; +// +// PropertyAttributeProperty& propertyAttributeProperty = +// propertyAttribute.properties["TestClassProperty"]; +// propertyAttributeProperty.index = static_cast(AttributeIndex); +// propertyAttributeProperty.texCoord = 0; +// propertyAttributeProperty.channels = {0, 1}; +// +// PropertyAttributeView view(model, propertyAttribute); +// REQUIRE(view.status() == PropertyAttributeViewStatus::Valid); +// +// const ClassProperty* classProperty = +// view.getClassProperty("TestClassProperty"); +// REQUIRE(classProperty); +// REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); +// REQUIRE(classProperty->componentType == +// ClassProperty::ComponentType::INT16); REQUIRE(classProperty->count == +// std::nullopt); REQUIRE(!classProperty->array); +// REQUIRE(classProperty->normalized); +// +// std::vector texCoords{ +// glm::dvec2(0, 0), +// glm::dvec2(0.5, 0), +// glm::dvec2(0, 0.5), +// glm::dvec2(0.5, 0.5)}; +// +// std::vector expected{-1, 268, 542, -256}; +// uint32_t invokedCallbackCount = 0; +// view.getPropertyView( +// "TestClassProperty", +// [&expected, &texCoords, &invokedCallbackCount]( +// const std::string& /*propertyName*/, +// auto propertyValue) mutable { +// invokedCallbackCount++; +// if constexpr (std::is_same_v< +// PropertyAttributePropertyView, +// decltype(propertyValue)>) { +// REQUIRE( +// propertyValue.status() == +// PropertyAttributePropertyViewStatus::Valid); +// +// for (size_t i = 0; i < expected.size(); ++i) { +// glm::dvec2& uv = texCoords[i]; +// REQUIRE(propertyValue.getRaw(uv[0], uv[1]) == expected[i]); +// REQUIRE(propertyValue.get(uv[0], uv[1]) == +// normalize(expected[i])); +// } +// } else { +// FAIL("getPropertyView returned PropertyAttributePropertyView of " +// "incorrect type for TestClassProperty."); +// } +// }); +// +// REQUIRE(invokedCallbackCount == 1); +//} +// +// TEST_CASE("Test callback for vecN PropertyAttributeProperty") { +// Model model; +// // clang-format off +// std::vector data = { +// 255, 255, +// 12, 1, +// 30, 2, +// 0, 255}; +// // clang-format on +// +// addAttributeToModel( +// model, +// Sampler::WrapS::CLAMP_TO_EDGE, +// Sampler::WrapS::CLAMP_TO_EDGE, +// 2, +// 2, +// 2, +// data); +// size_t AttributeIndex = model.Attributes.size() - 1; +// +// ExtensionModelExtStructuralMetadata& metadata = +// model.addExtension(); +// +// Schema& schema = metadata.schema.emplace(); +// Class& testClass = schema.classes["TestClass"]; +// ClassProperty& testClassProperty = +// testClass.properties["TestClassProperty"]; testClassProperty.type = +// ClassProperty::Type::VEC2; testClassProperty.componentType = +// ClassProperty::ComponentType::INT8; +// +// PropertyAttribute& propertyAttribute = +// metadata.propertyAttributes.emplace_back(); propertyAttribute.classProperty +// = "TestClass"; +// +// PropertyAttributeProperty& propertyAttributeProperty = +// propertyAttribute.properties["TestClassProperty"]; +// propertyAttributeProperty.index = static_cast(AttributeIndex); +// propertyAttributeProperty.texCoord = 0; +// propertyAttributeProperty.channels = {0, 1}; +// +// PropertyAttributeView view(model, propertyAttribute); +// REQUIRE(view.status() == PropertyAttributeViewStatus::Valid); +// +// const ClassProperty* classProperty = +// view.getClassProperty("TestClassProperty"); +// REQUIRE(classProperty); +// REQUIRE(classProperty->type == ClassProperty::Type::VEC2); +// REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT8); +// REQUIRE(classProperty->count == std::nullopt); +// REQUIRE(!classProperty->array); +// REQUIRE(!classProperty->normalized); +// +// std::vector expected{ +// glm::i8vec2(-1, -1), +// glm::i8vec2(12, 1), +// glm::i8vec2(30, 2), +// glm::i8vec2(0, -1)}; +// +// std::vector texCoords{ +// glm::dvec2(0, 0), +// glm::dvec2(0.5, 0), +// glm::dvec2(0, 0.5), +// glm::dvec2(0.5, 0.5)}; +// +// uint32_t invokedCallbackCount = 0; +// view.getPropertyView( +// "TestClassProperty", +// [&expected, &texCoords, &invokedCallbackCount]( +// const std::string& /*propertyName*/, +// auto propertyValue) mutable { +// invokedCallbackCount++; +// if constexpr (std::is_same_v< +// PropertyAttributePropertyView, +// decltype(propertyValue)>) { +// REQUIRE( +// propertyValue.status() == +// PropertyAttributePropertyViewStatus::Valid); +// +// for (size_t i = 0; i < expected.size(); ++i) { +// glm::dvec2& uv = texCoords[i]; +// REQUIRE(propertyValue.getRaw(uv[0], uv[1]) == expected[i]); +// REQUIRE(propertyValue.get(uv[0], uv[1]) == expected[i]); +// } +// } else { +// FAIL("getPropertyView returned PropertyAttributePropertyView of " +// "incorrect type for TestClassProperty."); +// } +// }); +// +// REQUIRE(invokedCallbackCount == 1); +//} +// +// TEST_CASE("Test callback for vecN PropertyAttributeProperty (normalized)") { +// Model model; +// // clang-format off +// std::vector data = { +// 255, 255, +// 12, 1, +// 30, 2, +// 0, 255}; +// // clang-format on +// +// addAttributeToModel( +// model, +// Sampler::WrapS::CLAMP_TO_EDGE, +// Sampler::WrapS::CLAMP_TO_EDGE, +// 2, +// 2, +// 2, +// data); +// size_t AttributeIndex = model.Attributes.size() - 1; +// +// ExtensionModelExtStructuralMetadata& metadata = +// model.addExtension(); +// +// Schema& schema = metadata.schema.emplace(); +// Class& testClass = schema.classes["TestClass"]; +// ClassProperty& testClassProperty = +// testClass.properties["TestClassProperty"]; testClassProperty.type = +// ClassProperty::Type::VEC2; testClassProperty.componentType = +// ClassProperty::ComponentType::INT8; testClassProperty.normalized = true; +// +// PropertyAttribute& propertyAttribute = +// metadata.propertyAttributes.emplace_back(); propertyAttribute.classProperty +// = "TestClass"; +// +// PropertyAttributeProperty& propertyAttributeProperty = +// propertyAttribute.properties["TestClassProperty"]; +// propertyAttributeProperty.index = static_cast(AttributeIndex); +// propertyAttributeProperty.texCoord = 0; +// propertyAttributeProperty.channels = {0, 1}; +// +// PropertyAttributeView view(model, propertyAttribute); +// REQUIRE(view.status() == PropertyAttributeViewStatus::Valid); +// +// const ClassProperty* classProperty = +// view.getClassProperty("TestClassProperty"); +// REQUIRE(classProperty); +// REQUIRE(classProperty->type == ClassProperty::Type::VEC2); +// REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT8); +// REQUIRE(classProperty->count == std::nullopt); +// REQUIRE(!classProperty->array); +// REQUIRE(classProperty->normalized); +// +// std::vector expected{ +// glm::i8vec2(-1, -1), +// glm::i8vec2(12, 1), +// glm::i8vec2(30, 2), +// glm::i8vec2(0, -1)}; +// +// std::vector texCoords{ +// glm::dvec2(0, 0), +// glm::dvec2(0.5, 0), +// glm::dvec2(0, 0.5), +// glm::dvec2(0.5, 0.5)}; +// +// uint32_t invokedCallbackCount = 0; +// view.getPropertyView( +// "TestClassProperty", +// [&expected, &texCoords, &invokedCallbackCount]( +// const std::string& /*propertyName*/, +// auto propertyValue) mutable { +// invokedCallbackCount++; +// if constexpr (std::is_same_v< +// PropertyAttributePropertyView, +// decltype(propertyValue)>) { +// REQUIRE( +// propertyValue.status() == +// PropertyAttributePropertyViewStatus::Valid); +// +// for (size_t i = 0; i < expected.size(); ++i) { +// glm::dvec2& uv = texCoords[i]; +// REQUIRE(propertyValue.getRaw(uv[0], uv[1]) == expected[i]); +// REQUIRE(propertyValue.get(uv[0], uv[1]) == +// normalize(expected[i])); +// } +// } else { +// FAIL("getPropertyView returned PropertyAttributePropertyView of " +// "incorrect type for TestClassProperty."); +// } +// }); +// +// REQUIRE(invokedCallbackCount == 1); +//} +// +// TEST_CASE("Test callback for array PropertyAttributeProperty") { +// Model model; +// // clang-format off +// std::vector data = { +// 254, 0, 253, 1, +// 10, 2, 40, 3, +// 30, 0, 0, 2, +// 10, 2, 255, 4}; +// // clang-format on +// +// addAttributeToModel( +// model, +// Sampler::WrapS::CLAMP_TO_EDGE, +// Sampler::WrapS::CLAMP_TO_EDGE, +// 2, +// 2, +// 4, +// data); +// size_t AttributeIndex = model.Attributes.size() - 1; +// +// ExtensionModelExtStructuralMetadata& metadata = +// model.addExtension(); +// +// Schema& schema = metadata.schema.emplace(); +// Class& testClass = schema.classes["TestClass"]; +// ClassProperty& testClassProperty = +// testClass.properties["TestClassProperty"]; testClassProperty.type = +// ClassProperty::Type::SCALAR; testClassProperty.componentType = +// ClassProperty::ComponentType::UINT16; testClassProperty.array = true; +// testClassProperty.count = 2; +// +// PropertyAttribute& propertyAttribute = +// metadata.propertyAttributes.emplace_back(); propertyAttribute.classProperty +// = "TestClass"; +// +// PropertyAttributeProperty& propertyAttributeProperty = +// propertyAttribute.properties["TestClassProperty"]; +// propertyAttributeProperty.index = static_cast(AttributeIndex); +// propertyAttributeProperty.texCoord = 0; +// propertyAttributeProperty.channels = {0, 1, 2, 3}; +// +// PropertyAttributeView view(model, propertyAttribute); +// REQUIRE(view.status() == PropertyAttributeViewStatus::Valid); +// +// const ClassProperty* classProperty = +// view.getClassProperty("TestClassProperty"); +// REQUIRE(classProperty); +// REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); +// REQUIRE(classProperty->componentType == +// ClassProperty::ComponentType::UINT16); REQUIRE(classProperty->array); +// REQUIRE(classProperty->count == 2); +// REQUIRE(!classProperty->normalized); +// +// std::vector> expected{ +// {254, 509}, +// {522, 808}, +// {30, 512}, +// {522, 1279}}; +// +// std::vector texCoords{ +// glm::dvec2(0, 0), +// glm::dvec2(0.5, 0), +// glm::dvec2(0, 0.5), +// glm::dvec2(0.5, 0.5)}; +// +// uint32_t invokedCallbackCount = 0; +// view.getPropertyView( +// "TestClassProperty", +// [&expected, &texCoords, &invokedCallbackCount]( +// const std::string& /*propertyName*/, +// auto propertyValue) mutable { +// invokedCallbackCount++; +// if constexpr (std::is_same_v< +// PropertyAttributePropertyView< +// PropertyArrayView>, +// decltype(propertyValue)>) { +// REQUIRE( +// propertyValue.status() == +// PropertyAttributePropertyViewStatus::Valid); +// +// for (size_t i = 0; i < expected.size(); ++i) { +// std::vector& expectedArray = expected[i]; +// glm::dvec2& uv = texCoords[i]; +// PropertyArrayView array = +// propertyValue.getRaw(uv[0], uv[1]); +// +// REQUIRE(static_cast(array.size()) == +// expectedArray.size()); for (int64_t j = 0; j < array.size(); j++) +// { +// REQUIRE(array[j] == expectedArray[static_cast(j)]); +// } +// +// auto maybeArray = propertyValue.get(uv[0], uv[1]); +// REQUIRE(maybeArray); +// REQUIRE( +// static_cast(maybeArray->size()) == +// expectedArray.size()); +// for (int64_t j = 0; j < array.size(); j++) { +// REQUIRE( +// (*maybeArray)[j] == expectedArray[static_cast(j)]); +// } +// } +// } else { +// FAIL("getPropertyView returned PropertyAttributePropertyView of " +// "incorrect type for TestClassProperty."); +// } +// }); +// +// REQUIRE(invokedCallbackCount == 1); +//} +// +// TEST_CASE("Test callback for array PropertyAttributeProperty (normalized)") { +// Model model; +// // clang-format off +// std::vector data = { +// 254, 0, 253, 1, +// 10, 2, 40, 3, +// 30, 0, 0, 2, +// 10, 2, 255, 4}; +// // clang-format on +// +// addAttributeToModel( +// model, +// Sampler::WrapS::CLAMP_TO_EDGE, +// Sampler::WrapS::CLAMP_TO_EDGE, +// 2, +// 2, +// 4, +// data); +// size_t AttributeIndex = model.Attributes.size() - 1; +// +// ExtensionModelExtStructuralMetadata& metadata = +// model.addExtension(); +// +// Schema& schema = metadata.schema.emplace(); +// Class& testClass = schema.classes["TestClass"]; +// ClassProperty& testClassProperty = +// testClass.properties["TestClassProperty"]; testClassProperty.type = +// ClassProperty::Type::SCALAR; testClassProperty.componentType = +// ClassProperty::ComponentType::UINT16; testClassProperty.array = true; +// testClassProperty.count = 2; +// testClassProperty.normalized = true; +// +// PropertyAttribute& propertyAttribute = +// metadata.propertyAttributes.emplace_back(); propertyAttribute.classProperty +// = "TestClass"; +// +// PropertyAttributeProperty& propertyAttributeProperty = +// propertyAttribute.properties["TestClassProperty"]; +// propertyAttributeProperty.index = static_cast(AttributeIndex); +// propertyAttributeProperty.texCoord = 0; +// propertyAttributeProperty.channels = {0, 1, 2, 3}; +// +// PropertyAttributeView view(model, propertyAttribute); +// REQUIRE(view.status() == PropertyAttributeViewStatus::Valid); +// +// const ClassProperty* classProperty = +// view.getClassProperty("TestClassProperty"); +// REQUIRE(classProperty); +// REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); +// REQUIRE(classProperty->componentType == +// ClassProperty::ComponentType::UINT16); REQUIRE(classProperty->array); +// REQUIRE(classProperty->count == 2); +// REQUIRE(classProperty->normalized); +// +// std::vector> expected{ +// {254, 509}, +// {522, 808}, +// {30, 512}, +// {522, 1279}}; +// +// std::vector texCoords{ +// glm::dvec2(0, 0), +// glm::dvec2(0.5, 0), +// glm::dvec2(0, 0.5), +// glm::dvec2(0.5, 0.5)}; +// +// uint32_t invokedCallbackCount = 0; +// view.getPropertyView( +// "TestClassProperty", +// [&expected, &texCoords, &invokedCallbackCount]( +// const std::string& /*propertyName*/, +// auto propertyValue) mutable { +// invokedCallbackCount++; +// if constexpr ( +// std::is_same_v< +// PropertyAttributePropertyView, +// true>, decltype(propertyValue)>) { +// REQUIRE( +// propertyValue.status() == +// PropertyAttributePropertyViewStatus::Valid); +// +// for (size_t i = 0; i < expected.size(); ++i) { +// std::vector& expectedArray = expected[i]; +// glm::dvec2& uv = texCoords[i]; +// PropertyArrayView array = +// propertyValue.getRaw(uv[0], uv[1]); +// +// REQUIRE(static_cast(array.size()) == +// expectedArray.size()); for (int64_t j = 0; j < array.size(); j++) +// { +// REQUIRE(array[j] == expectedArray[static_cast(j)]); +// } +// +// auto maybeArray = propertyValue.get(uv[0], uv[1]); +// REQUIRE(maybeArray); +// REQUIRE( +// static_cast(maybeArray->size()) == +// expectedArray.size()); +// for (int64_t j = 0; j < array.size(); j++) { +// auto rawValue = expectedArray[static_cast(j)]; +// REQUIRE((*maybeArray)[j] == normalize(rawValue)); +// } +// } +// } else { +// FAIL("getPropertyView returned PropertyAttributePropertyView of " +// "incorrect type for TestClassProperty."); +// } +// }); +// +// REQUIRE(invokedCallbackCount == 1); +//} +// +// TEST_CASE("Test callback on unsupported PropertyAttributeProperty") { +// Model model; +// // clang-format off +// std::vector data = { +// 254, 0, 253, 1, +// 10, 2, 40, 3, +// 30, 0, 0, 2, +// 10, 2, 255, 4}; +// // clang-format on +// +// addAttributeToModel( +// model, +// Sampler::WrapS::CLAMP_TO_EDGE, +// Sampler::WrapS::CLAMP_TO_EDGE, +// 2, +// 1, +// 8, +// data); +// size_t AttributeIndex = model.Attributes.size() - 1; +// +// ExtensionModelExtStructuralMetadata& metadata = +// model.addExtension(); +// +// Schema& schema = metadata.schema.emplace(); +// Class& testClass = schema.classes["TestClass"]; +// +// ClassProperty& doubleClassProperty = +// testClass.properties["DoubleClassProperty"]; +// doubleClassProperty.type = ClassProperty::Type::SCALAR; +// doubleClassProperty.componentType = ClassProperty::ComponentType::FLOAT64; +// +// ClassProperty& arrayClassProperty = +// testClass.properties["ArrayClassProperty"]; +// arrayClassProperty.type = ClassProperty::Type::VEC4; +// arrayClassProperty.componentType = ClassProperty::ComponentType::UINT8; +// arrayClassProperty.array = true; +// arrayClassProperty.count = 2; +// +// PropertyAttribute& propertyAttribute = +// metadata.propertyAttributes.emplace_back(); propertyAttribute.classProperty +// = "TestClass"; +// +// PropertyAttributeProperty& doubleProperty = +// propertyAttribute.properties["DoubleClassProperty"]; +// doubleProperty.index = static_cast(AttributeIndex); +// doubleProperty.texCoord = 0; +// doubleProperty.channels = {0, 1, 2, 3, 4, 5, 6, 7}; +// +// PropertyAttributeProperty& arrayProperty = +// propertyAttribute.properties["ArrayClassProperty"]; +// arrayProperty.index = static_cast(AttributeIndex); +// arrayProperty.texCoord = 0; +// arrayProperty.channels = {0, 1, 2, 3, 4, 5, 6, 7}; +// +// PropertyAttributeView view(model, propertyAttribute); +// REQUIRE(view.status() == PropertyAttributeViewStatus::Valid); +// +// const ClassProperty* classProperty = +// view.getClassProperty("DoubleClassProperty"); +// REQUIRE(classProperty); +// REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); +// REQUIRE( +// classProperty->componentType == ClassProperty::ComponentType::FLOAT64); +// REQUIRE(!classProperty->array); +// +// classProperty = view.getClassProperty("ArrayClassProperty"); +// REQUIRE(classProperty); +// REQUIRE(classProperty->type == ClassProperty::Type::VEC4); +// REQUIRE(classProperty->componentType == +// ClassProperty::ComponentType::UINT8); REQUIRE(classProperty->array); +// REQUIRE(classProperty->count == 2); +// +// uint32_t invokedCallbackCount = 0; +// view.getPropertyView( +// "DoubleClassProperty", +// [&invokedCallbackCount]( +// const std::string& /*propertyName*/, +// auto propertyValue) mutable { +// invokedCallbackCount++; +// REQUIRE( +// propertyValue.status() == +// PropertyAttributePropertyViewStatus::ErrorUnsupportedProperty); +// }); +// REQUIRE(invokedCallbackCount == 1); +// +// view.getPropertyView( +// "ArrayClassProperty", +// [&invokedCallbackCount]( +// const std::string& /*propertyName*/, +// auto propertyValue) mutable { +// invokedCallbackCount++; +// REQUIRE( +// propertyValue.status() == +// PropertyAttributePropertyViewStatus::ErrorUnsupportedProperty); +// }); +// REQUIRE(invokedCallbackCount == 2); +//} diff --git a/CesiumGltf/test/TestPropertyTablePropertyView.cpp b/CesiumGltf/test/TestPropertyTablePropertyView.cpp index 5149eb536..12011cabb 100644 --- a/CesiumGltf/test/TestPropertyTablePropertyView.cpp +++ b/CesiumGltf/test/TestPropertyTablePropertyView.cpp @@ -202,7 +202,7 @@ static void checkVariableLengthArray( } template -static void checkVariableLengthArrayWithProperties( +static void checkVariableLengthArray( const std::vector& data, const std::vector& offsets, PropertyComponentType offsetType, @@ -414,7 +414,7 @@ static void checkFixedLengthArray( } template -static void checkFixedLengthArrayWithProperties( +static void checkFixedLengthArray( const std::vector& data, int64_t fixedLengthArrayCount, const std::vector>>& expected, @@ -586,26 +586,6 @@ TEST_CASE("Check scalar PropertyTablePropertyView") { checkNumeric(data); } - SECTION("Normalized Uint8") { - std::vector values{0, 64, 128, 255}; - std::vector> expected{ - 0.0, - 64.0 / 255.0, - 128.0 / 255.0, - 1.0}; - checkNormalizedNumeric(values, expected); - } - - SECTION("Normalized Int16") { - std::vector values{-32768, 0, 16384, 32767}; - std::vector> expected{ - -1.0, - 0.0, - 16384.0 / 32767.0, - 1.0}; - checkNormalizedNumeric(values, expected); - } - SECTION("Float with Offset / Scale") { std::vector values{12.5f, -12.5f, -5.0f, 6.75f}; const float offset = 1.0f; @@ -614,18 +594,6 @@ TEST_CASE("Check scalar PropertyTablePropertyView") { checkNumeric(values, expected, offset, scale); } - SECTION("Normalized Uint8 with Offset and Scale") { - std::vector values{0, 64, 128, 255}; - const double offset = 1.0; - const double scale = 2.0; - std::vector> expected{ - 1.0, - 1 + 2 * (64.0 / 255.0), - 1 + 2 * (128.0 / 255.0), - 3.0}; - checkNormalizedNumeric(values, expected, offset, scale); - } - SECTION("Int16 with NoData") { std::vector values{-1, 3, 7, -1}; const int16_t noData = -1; @@ -661,26 +629,6 @@ TEST_CASE("Check scalar PropertyTablePropertyView") { defaultValue); } - SECTION("Normalized Uint8 with all properties") { - std::vector values{0, 64, 128, 255}; - const double offset = 1.0; - const double scale = 2.0; - const uint8_t noData = 0; - const double defaultValue = 10.0; - std::vector> expected{ - 10.0, - 1 + 2 * (64.0 / 255.0), - 1 + 2 * (128.0 / 255.0), - 3.0}; - checkNormalizedNumeric( - values, - expected, - offset, - scale, - noData, - defaultValue); - } - SECTION("Overrides class property values") { std::vector values{1.0f, 3.0f, 2.0f, 4.0f}; std::vector data; @@ -720,6 +668,60 @@ TEST_CASE("Check scalar PropertyTablePropertyView") { } } +TEST_CASE("Check scalar PropertyTablePropertyView (normalized)") { + SECTION("Uint8") { + std::vector values{0, 64, 128, 255}; + std::vector> expected{ + 0.0, + 64.0 / 255.0, + 128.0 / 255.0, + 1.0}; + checkNormalizedNumeric(values, expected); + } + + SECTION("Int16") { + std::vector values{-32768, 0, 16384, 32767}; + std::vector> expected{ + -1.0, + 0.0, + 16384.0 / 32767.0, + 1.0}; + checkNormalizedNumeric(values, expected); + } + + SECTION("Uint8 with Offset and Scale") { + std::vector values{0, 64, 128, 255}; + const double offset = 1.0; + const double scale = 2.0; + std::vector> expected{ + 1.0, + 1 + 2 * (64.0 / 255.0), + 1 + 2 * (128.0 / 255.0), + 3.0}; + checkNormalizedNumeric(values, expected, offset, scale); + } + + SECTION("Uint8 with all properties") { + std::vector values{0, 64, 128, 255}; + const double offset = 1.0; + const double scale = 2.0; + const uint8_t noData = 0; + const double defaultValue = 10.0; + std::vector> expected{ + 10.0, + 1 + 2 * (64.0 / 255.0), + 1 + 2 * (128.0 / 255.0), + 3.0}; + checkNormalizedNumeric( + values, + expected, + offset, + scale, + noData, + defaultValue); + } +} + TEST_CASE("Check vecN PropertyTablePropertyView") { SECTION("Float Vec2") { std::vector data{ @@ -748,31 +750,6 @@ TEST_CASE("Check vecN PropertyTablePropertyView") { checkNumeric(data); } - SECTION("Normalized Uint8 Vec2") { - std::vector values{ - glm::u8vec2(0, 64), - glm::u8vec2(128, 255), - glm::u8vec2(255, 0)}; - std::vector> expected{ - glm::dvec2(0.0, 64.0 / 255.0), - glm::dvec2(128.0 / 255.0, 1.0), - glm::dvec2(1.0, 0.0)}; - checkNormalizedNumeric(values, expected); - } - - SECTION("Normalized Int16 Vec2") { - std::vector values{ - glm::i16vec2(-32768, 0), - glm::i16vec2(16384, 32767), - glm::i16vec2(32767, -32768)}; - std::vector> expected{ - glm::dvec2(-1.0, 0.0), - glm::dvec2(16384.0 / 32767.0, 1.0), - glm::dvec2(1.0, -1.0), - }; - checkNormalizedNumeric(values, expected); - } - SECTION("Float Vec3 with Offset / Scale") { std::vector values{ glm::vec3(0.0f, -1.5f, -5.0f), @@ -789,20 +766,6 @@ TEST_CASE("Check vecN PropertyTablePropertyView") { checkNumeric(values, expected, offset, scale); } - SECTION("Normalized Uint8 Vec2 with Offset and Scale") { - std::vector values{ - glm::u8vec2(0, 64), - glm::u8vec2(128, 255), - glm::u8vec2(255, 0)}; - JsonValue::Array offset{0.0, 1.0}; - JsonValue::Array scale{2.0, 1.0}; - std::vector> expected{ - glm::dvec2(0.0, 1 + (64.0 / 255.0)), - glm::dvec2(2 * (128.0 / 255.0), 2.0), - glm::dvec2(2.0, 1.0)}; - checkNormalizedNumeric(values, expected, offset, scale); - } - SECTION("Int16 Vec2 with NoData") { std::vector values{ glm::i16vec2(-1, 3), @@ -842,30 +805,6 @@ TEST_CASE("Check vecN PropertyTablePropertyView") { defaultValue); }; - SECTION("Normalized Uint8 Vec2 with all properties") { - std::vector values{ - glm::u8vec2(0, 64), - glm::u8vec2(128, 255), - glm::u8vec2(255, 0), - glm::u8vec2(0, 0)}; - JsonValue::Array offset{0.0, 1.0}; - JsonValue::Array scale{2.0, 1.0}; - JsonValue::Array noData{0, 0}; - JsonValue::Array defaultValue{5.0, 15.0}; - std::vector> expected{ - glm::dvec2(0.0, 1 + (64.0 / 255.0)), - glm::dvec2(2 * (128.0 / 255.0), 2.0), - glm::dvec2(2.0, 1.0), - glm::dvec2(5.0, 15.0)}; - checkNormalizedNumeric( - values, - expected, - offset, - scale, - noData, - defaultValue); - } - SECTION("Overrides class property values") { std::vector values{ glm::vec2(1.0f, 3.0f), @@ -911,6 +850,71 @@ TEST_CASE("Check vecN PropertyTablePropertyView") { } } +TEST_CASE("Check vecN PropertyTablePropertyView (normalized)") { + SECTION("Uint8 Vec2") { + std::vector values{ + glm::u8vec2(0, 64), + glm::u8vec2(128, 255), + glm::u8vec2(255, 0)}; + std::vector> expected{ + glm::dvec2(0.0, 64.0 / 255.0), + glm::dvec2(128.0 / 255.0, 1.0), + glm::dvec2(1.0, 0.0)}; + checkNormalizedNumeric(values, expected); + } + + SECTION("Int16 Vec2") { + std::vector values{ + glm::i16vec2(-32768, 0), + glm::i16vec2(16384, 32767), + glm::i16vec2(32767, -32768)}; + std::vector> expected{ + glm::dvec2(-1.0, 0.0), + glm::dvec2(16384.0 / 32767.0, 1.0), + glm::dvec2(1.0, -1.0), + }; + checkNormalizedNumeric(values, expected); + } + + SECTION("Uint8 Vec2 with Offset and Scale") { + std::vector values{ + glm::u8vec2(0, 64), + glm::u8vec2(128, 255), + glm::u8vec2(255, 0)}; + JsonValue::Array offset{0.0, 1.0}; + JsonValue::Array scale{2.0, 1.0}; + std::vector> expected{ + glm::dvec2(0.0, 1 + (64.0 / 255.0)), + glm::dvec2(2 * (128.0 / 255.0), 2.0), + glm::dvec2(2.0, 1.0)}; + checkNormalizedNumeric(values, expected, offset, scale); + } + + SECTION("Uint8 Vec2 with all properties") { + std::vector values{ + glm::u8vec2(0, 64), + glm::u8vec2(128, 255), + glm::u8vec2(255, 0), + glm::u8vec2(0, 0)}; + JsonValue::Array offset{0.0, 1.0}; + JsonValue::Array scale{2.0, 1.0}; + JsonValue::Array noData{0, 0}; + JsonValue::Array defaultValue{5.0, 15.0}; + std::vector> expected{ + glm::dvec2(0.0, 1 + (64.0 / 255.0)), + glm::dvec2(2 * (128.0 / 255.0), 2.0), + glm::dvec2(2.0, 1.0), + glm::dvec2(5.0, 15.0)}; + checkNormalizedNumeric( + values, + expected, + offset, + scale, + noData, + defaultValue); + } +} + TEST_CASE("Check matN PropertyTablePropertyView") { SECTION("Float Mat2") { // clang-format off @@ -972,48 +976,7 @@ TEST_CASE("Check matN PropertyTablePropertyView") { checkNumeric(data); } - SECTION("Normalized Uint8 Mat2") { - // clang-format off - std::vector values{ - glm::u8mat2x2( - 0, 64, - 255, 255), - glm::u8mat2x2( - 255, 0, - 128, 0)}; - std::vector> expected{ - glm::dmat2( - 0.0, 64.0 / 255.0, - 1.0, 1.0), - glm::dmat2( - 1.0, 0.0, - 128.0 / 255.0, 0.0)}; - // clang-format on - checkNormalizedNumeric(values, expected); - } - - SECTION("Normalized Int16 Mat2") { - // clang-format off - std::vector values{ - glm::i16mat2x2( - -32768, 0, - 16384, 32767), - glm::i16mat2x2( - 0, 32767, - 32767, -32768)}; - std::vector> expected{ - glm::dmat2( - -1.0, 0.0, - 16384.0 / 32767.0, 1.0), - glm::dmat2( - 0.0, 1.0, - 1.0, -1.0), - }; - // clang-format on - checkNormalizedNumeric(values, expected); - } - - SECTION("Float Mat2 with Offset / Scale") { + SECTION("Float Mat2 with Offset / Scale") { // clang-format off std::vector values{ glm::mat2( @@ -1047,32 +1010,6 @@ TEST_CASE("Check matN PropertyTablePropertyView") { checkNumeric(values, expected, offset, scale); } - SECTION("Normalized Uint8 Mat2 with Offset and Scale") { - // clang-format off - std::vector values{ - glm::u8mat2x2( - 0, 64, - 255, 255), - glm::u8mat2x2( - 255, 0, - 128, 0)}; - JsonValue::Array offset{ - 0.0, 1.0, - 1.0, 0.0}; - JsonValue::Array scale{ - 2.0, 1.0, - 0.0, 2.0}; - std::vector> expected{ - glm::dmat2( - 0.0, 1 + 64.0 / 255.0, - 1.0, 2.0), - glm::dmat2( - 2.0, 1.0, - 1.0, 0.0)}; - // clang-format on - checkNormalizedNumeric(values, expected, offset, scale); - } - SECTION("Int16 Mat3 with NoData") { // clang-format off std::vector values{ @@ -1106,7 +1043,7 @@ TEST_CASE("Check matN PropertyTablePropertyView") { std::nullopt); }; - SECTION("Int16 Mat3 with NoData") { + SECTION("Int16 Mat3 with NoData and DefaultValue") { // clang-format off std::vector values{ glm::i16mat3x3( @@ -1143,47 +1080,6 @@ TEST_CASE("Check matN PropertyTablePropertyView") { defaultValue); }; - SECTION("Normalized Uint8 Mat2 with all properties") { - // clang-format off - std::vector values{ - glm::u8mat2x2( - 0, 64, - 255, 255), - glm::u8mat2x2(0), - glm::u8mat2x2( - 255, 0, - 128, 0)}; - JsonValue::Array offset{ - 0.0, 1.0, - 1.0, 0.0}; - JsonValue::Array scale{ - 2.0, 1.0, - 0.0, 2.0}; - JsonValue::Array noData{ - 0, 0, - 0, 0}; - JsonValue::Array defaultValue{ - 1.0, 0.0, - 0.0, 1.0}; - - std::vector> expected{ - glm::dmat2( - 0.0, 1 + 64.0 / 255.0, - 1.0, 2.0), - glm::dmat2(1.0), - glm::dmat2( - 2.0, 1.0, - 1.0, 0.0)}; - // clang-format on - checkNormalizedNumeric( - values, - expected, - offset, - scale, - noData, - defaultValue); - } - SECTION("Overrides class property values") { // clang-fomrat off std::vector values{ @@ -1267,6 +1163,116 @@ TEST_CASE("Check matN PropertyTablePropertyView") { } } +TEST_CASE("Check matN PropertyTablePropertyView (normalized)") { + SECTION("Uint8 Mat2") { + // clang-format off + std::vector values{ + glm::u8mat2x2( + 0, 64, + 255, 255), + glm::u8mat2x2( + 255, 0, + 128, 0)}; + std::vector> expected{ + glm::dmat2( + 0.0, 64.0 / 255.0, + 1.0, 1.0), + glm::dmat2( + 1.0, 0.0, + 128.0 / 255.0, 0.0)}; + // clang-format on + checkNormalizedNumeric(values, expected); + } + + SECTION("Int16 Mat2") { + // clang-format off + std::vector values{ + glm::i16mat2x2( + -32768, 0, + 16384, 32767), + glm::i16mat2x2( + 0, 32767, + 32767, -32768)}; + std::vector> expected{ + glm::dmat2( + -1.0, 0.0, + 16384.0 / 32767.0, 1.0), + glm::dmat2( + 0.0, 1.0, + 1.0, -1.0), + }; + // clang-format on + checkNormalizedNumeric(values, expected); + } + + SECTION("Uint8 Mat2 with Offset and Scale") { + // clang-format off + std::vector values{ + glm::u8mat2x2( + 0, 64, + 255, 255), + glm::u8mat2x2( + 255, 0, + 128, 0)}; + JsonValue::Array offset{ + 0.0, 1.0, + 1.0, 0.0}; + JsonValue::Array scale{ + 2.0, 1.0, + 0.0, 2.0}; + std::vector> expected{ + glm::dmat2( + 0.0, 1 + 64.0 / 255.0, + 1.0, 2.0), + glm::dmat2( + 2.0, 1.0, + 1.0, 0.0)}; + // clang-format on + checkNormalizedNumeric(values, expected, offset, scale); + } + + SECTION("Normalized Uint8 Mat2 with all properties") { + // clang-format off + std::vector values{ + glm::u8mat2x2( + 0, 64, + 255, 255), + glm::u8mat2x2(0), + glm::u8mat2x2( + 255, 0, + 128, 0)}; + JsonValue::Array offset{ + 0.0, 1.0, + 1.0, 0.0}; + JsonValue::Array scale{ + 2.0, 1.0, + 0.0, 2.0}; + JsonValue::Array noData{ + 0, 0, + 0, 0}; + JsonValue::Array defaultValue{ + 1.0, 0.0, + 0.0, 1.0}; + + std::vector> expected{ + glm::dmat2( + 0.0, 1 + 64.0 / 255.0, + 1.0, 2.0), + glm::dmat2(1.0), + glm::dmat2( + 2.0, 1.0, + 1.0, 0.0)}; + // clang-format on + checkNormalizedNumeric( + values, + expected, + offset, + scale, + noData, + defaultValue); + } +} + TEST_CASE("Check boolean PropertyTablePropertyView") { std::bitset bits = 0b11110101; unsigned long val = bits.to_ulong(); @@ -1501,18 +1507,6 @@ TEST_CASE("Check fixed-length scalar array PropertyTablePropertyView") { checkFixedLengthArray(data, 4); } - SECTION("Array of 4 normalized uint8_ts") { - // clang-format off - std::vector data{ - 255, 64, 0, 255, - 128, 0, 255, 0}; - // clang-format on - std::vector>> expected{ - std::vector{1.0, 64.0 / 255.0, 0.0, 1.0}, - std::vector{128.0 / 255.0, 0.0, 1.0, 0.0}}; - checkNormalizedFixedLengthArray(data, 4, expected); - } - SECTION("Array of 4 floats with offset / scale") { // clang-format off std::vector data{ @@ -1527,7 +1521,7 @@ TEST_CASE("Check fixed-length scalar array PropertyTablePropertyView") { std::vector>> expected{ std::vector{2.0f, 4.0f, 2.0f, 8.0f}, std::vector{6.0f, -2.0f, -1.0f, 4.0f}}; - checkFixedLengthArrayWithProperties(data, 4, expected, offset, scale); + checkFixedLengthArray(data, 4, expected, offset, scale); } SECTION("Array of 2 int32_ts with noData value") { @@ -1542,7 +1536,7 @@ TEST_CASE("Check fixed-length scalar array PropertyTablePropertyView") { std::vector{122, 12}, std::nullopt, std::vector{-3, 44}}; - checkFixedLengthArrayWithProperties( + checkFixedLengthArray( data, 2, expected, @@ -1565,7 +1559,7 @@ TEST_CASE("Check fixed-length scalar array PropertyTablePropertyView") { std::vector{122, 12}, std::vector{0, 1}, std::vector{-3, 44}}; - checkFixedLengthArrayWithProperties( + checkFixedLengthArray( data, 2, expected, @@ -1575,33 +1569,6 @@ TEST_CASE("Check fixed-length scalar array PropertyTablePropertyView") { defaultValue); } - SECTION("Array of 3 normalized int8_ts with all properties") { - // clang-format off - std::vector data{ - -128, 0, 64, - -64, 127, -128, - 0, 0, 0}; - // clang-format on - JsonValue::Array offset{0, 1, 1}; - JsonValue::Array scale{1, -1, 2}; - JsonValue::Array noData{0, 0, 0}; - JsonValue::Array defaultValue{10, 8, 2}; - - std::vector>> expected{ - std::vector{-1.0, 1.0, 1 + 2 * (64.0 / 127.0)}, - std::vector{-64.0 / 127.0, 0.0, -1.0}, - std::vector{10.0, 8.0, 2.0}, - }; - checkNormalizedFixedLengthArray( - data, - 3, - expected, - offset, - scale, - noData, - defaultValue); - } - SECTION("Overrides class property values") { // clang-format off std::vector data{ @@ -1676,6 +1643,48 @@ TEST_CASE("Check fixed-length scalar array PropertyTablePropertyView") { } } +TEST_CASE( + "Check fixed-length scalar array PropertyTablePropertyView (normalized)") { + SECTION("Array of 4 uint8_ts") { + // clang-format off + std::vector data{ + 255, 64, 0, 255, + 128, 0, 255, 0}; + // clang-format on + std::vector>> expected{ + std::vector{1.0, 64.0 / 255.0, 0.0, 1.0}, + std::vector{128.0 / 255.0, 0.0, 1.0, 0.0}}; + checkNormalizedFixedLengthArray(data, 4, expected); + } + + SECTION("Array of 3 normalized int8_ts with all properties") { + // clang-format off + std::vector data{ + -128, 0, 64, + -64, 127, -128, + 0, 0, 0}; + // clang-format on + JsonValue::Array offset{0, 1, 1}; + JsonValue::Array scale{1, -1, 2}; + JsonValue::Array noData{0, 0, 0}; + JsonValue::Array defaultValue{10, 8, 2}; + + std::vector>> expected{ + std::vector{-1.0, 1.0, 1 + 2 * (64.0 / 127.0)}, + std::vector{-64.0 / 127.0, 0.0, -1.0}, + std::vector{10.0, 8.0, 2.0}, + }; + checkNormalizedFixedLengthArray( + data, + 3, + expected, + offset, + scale, + noData, + defaultValue); + } +} + TEST_CASE("Check fixed-length vecN array PropertyTablePropertyView") { SECTION("Array of 4 u8vec2s") { std::vector data{ @@ -1712,22 +1721,6 @@ TEST_CASE("Check fixed-length vecN array PropertyTablePropertyView") { checkFixedLengthArray(data, 3); } - SECTION("Array of 2 normalized u8vec2s") { - std::vector data{ - glm::u8vec2(255, 64), - glm::u8vec2(0, 255), - glm::u8vec2(128, 0), - glm::u8vec2(255, 0)}; - std::vector>> expected{ - std::vector{ - glm::dvec2(1.0, 64.0 / 255.0), - glm::dvec2(0.0, 1.0)}, - std::vector{ - glm::dvec2(128.0 / 255.0, 0.0), - glm::dvec2(1.0, 0.0)}}; - checkNormalizedFixedLengthArray(data, 2, expected); - } - SECTION("Array of 2 vec2s with offset / scale") { // clang-format off std::vector data{ @@ -1742,7 +1735,7 @@ TEST_CASE("Check fixed-length vecN array PropertyTablePropertyView") { std::vector>> expected{ std::vector{glm::vec2(2.0f, 4.0f), glm::vec2(2.0f, 8.0f)}, std::vector{glm::vec2(6.0f, -2.0f), glm::vec2(-1.0f, 4.0f)}}; - checkFixedLengthArrayWithProperties(data, 2, expected, offset, scale); + checkFixedLengthArray(data, 2, expected, offset, scale); } SECTION("Array of 2 ivec2 with noData value") { @@ -1757,7 +1750,7 @@ TEST_CASE("Check fixed-length vecN array PropertyTablePropertyView") { std::vector{glm::ivec2(122, 12), glm::ivec2(-1, -1)}, std::vector{glm::ivec2(-3, 44), glm::ivec2(0, 7)}, std::nullopt}; - checkFixedLengthArrayWithProperties( + checkFixedLengthArray( data, 2, expected, @@ -1780,7 +1773,7 @@ TEST_CASE("Check fixed-length vecN array PropertyTablePropertyView") { std::vector{glm::ivec2(122, 12), glm::ivec2(-1, -1)}, std::vector{glm::ivec2(-3, 44), glm::ivec2(0, 7)}, std::vector{glm::ivec2(1, 1), glm::ivec2(1, 2)}}; - checkFixedLengthArrayWithProperties( + checkFixedLengthArray( data, 2, expected, @@ -1790,7 +1783,103 @@ TEST_CASE("Check fixed-length vecN array PropertyTablePropertyView") { defaultValue); } - SECTION("Array of 2 normalized i8vec2 with all properties") { + SECTION("Overrides class property values") { + // clang-format off + std::vector data{ + glm::vec2(1.0f, 2.0f), glm::vec2(3.0f, 4.0f), + glm::vec2(0.0f, -1.0f), glm::vec2(1.0f, -2.0f)}; + // clang-format on + const int64_t count = 2; + const int64_t instanceCount = 2; + + std::vector buffer; + buffer.resize(data.size() * sizeof(glm::vec2)); + std::memcpy(buffer.data(), data.data(), buffer.size()); + + ClassProperty classProperty; + classProperty.type = ClassProperty::Type::VEC2; + classProperty.componentType = ClassProperty::ComponentType::FLOAT32; + + classProperty.array = true; + classProperty.count = count; + classProperty.offset = JsonValue::Array{{0, 0}, {0, 0}}; + classProperty.scale = JsonValue::Array{{1, 1}, {1, 1}}; + classProperty.min = JsonValue::Array{{0.0f, -1.0f}, {1.0f, -2.0f}}; + classProperty.max = JsonValue::Array{{1.0f, 2.0f}, {3.0f, 4.0f}}; + + PropertyTableProperty propertyTableProperty; + propertyTableProperty.offset = JsonValue::Array{{2, 1}, {0, -1}}; + propertyTableProperty.scale = JsonValue::Array{{1, 0}, {1, -1}}; + propertyTableProperty.min = JsonValue::Array{{0.0f, 1.0f}, {1.0f, -2.0f}}; + propertyTableProperty.max = JsonValue::Array{{1.0f, 1.0f}, {3.0f, 4.0f}}; + + PropertyTablePropertyView> property( + propertyTableProperty, + classProperty, + instanceCount, + gsl::span(buffer.data(), buffer.size()), + gsl::span(), + gsl::span(), + PropertyComponentType::None, + PropertyComponentType::None); + + REQUIRE(property.arrayCount() == count); + REQUIRE(property.size() == instanceCount); + + REQUIRE(property.offset()); + checkArrayEqual(*property.offset(), {glm::vec2{2, 1}, glm::vec2{0, -1}}); + + REQUIRE(property.scale()); + checkArrayEqual(*property.scale(), {glm::vec2{1, 0}, glm::vec2{1, -1}}); + + REQUIRE(property.min()); + checkArrayEqual( + *property.min(), + {glm::vec2{0.0f, 1.0f}, glm::vec2{1.0f, -2.0f}}); + + REQUIRE(property.max()); + checkArrayEqual( + *property.max(), + {glm::vec2(1.0f, 1.0f), glm::vec2(3.0f, 4.0f)}); + + std::vector> expected{ + {glm::vec2(3.0f, 1.0f), glm::vec2(3.0f, -5.0f)}, + {glm::vec2(2.0f, 1.0f), glm::vec2(1.0f, 1.0f)}}; + size_t expectedIdx = 0; + for (int64_t i = 0; i < property.size(); ++i) { + PropertyArrayView rawValues = property.getRaw(i); + auto values = property.get(i); + REQUIRE(values); + for (int64_t j = 0; j < rawValues.size(); ++j) { + REQUIRE(rawValues[j] == data[expectedIdx]); + REQUIRE( + (*values)[j] == + expected[static_cast(i)][static_cast(j)]); + ++expectedIdx; + } + } + } +} + +TEST_CASE( + "Check fixed-length vecN array PropertyTablePropertyView (normalized)") { + SECTION("Array of 2 u8vec2s") { + std::vector data{ + glm::u8vec2(255, 64), + glm::u8vec2(0, 255), + glm::u8vec2(128, 0), + glm::u8vec2(255, 0)}; + std::vector>> expected{ + std::vector{ + glm::dvec2(1.0, 64.0 / 255.0), + glm::dvec2(0.0, 1.0)}, + std::vector{ + glm::dvec2(128.0 / 255.0, 0.0), + glm::dvec2(1.0, 0.0)}}; + checkNormalizedFixedLengthArray(data, 2, expected); + } + + SECTION("Array of 2 i8vec2 with all properties") { // clang-format off std::vector data{ glm::i8vec2(-128, 0), glm::i8vec2(64, -64), @@ -1997,44 +2086,6 @@ TEST_CASE("Check fixed-length matN array PropertyTablePropertyView") { checkFixedLengthArray(data, 3); } - SECTION("Array of 2 normalized u8mat2x2s") { - // clang-format off - std::vector data{ - glm::u8mat2x2( - 255, 64, - 0, 255), - glm::u8mat2x2( - 0, 255, - 64, 128), - glm::u8mat2x2( - 128, 0, - 0, 255), - glm::u8mat2x2( - 255, 32, - 255, 0)}; - - std::vector>> expected{ - std::vector{ - glm::dmat2( - 1.0, 64.0 / 255.0, - 0.0, 1.0), - glm::dmat2( - 0.0, 1.0, - 64.0 / 255.0, 128.0 / 255.0), - }, - std::vector{ - glm::dmat2( - 128.0 / 255.0, 0.0, - 0.0, 1.0), - glm::dmat2( - 1.0, 32.0 / 255.0, - 1.0, 0.0), - }}; - // clang-format on - - checkNormalizedFixedLengthArray(data, 2, expected); - } - SECTION("Array of 2 mat2s with offset / scale") { // clang-format off std::vector data{ @@ -2078,7 +2129,7 @@ TEST_CASE("Check fixed-length matN array PropertyTablePropertyView") { -1.0f, 2.0f, -4.0f, 8.0f)}}; // clang-format on - checkFixedLengthArrayWithProperties(data, 2, expected, offset, scale); + checkFixedLengthArray(data, 2, expected, offset, scale); } SECTION("Array of 2 imat2x2 with noData value") { @@ -2116,7 +2167,7 @@ TEST_CASE("Check fixed-length matN array PropertyTablePropertyView") { -1, 0)}, std::nullopt}; // clang-format on - checkFixedLengthArrayWithProperties( + checkFixedLengthArray( data, 2, expected, @@ -2170,7 +2221,7 @@ TEST_CASE("Check fixed-length matN array PropertyTablePropertyView") { glm::imat2x2(1) }}; // clang-format on - checkFixedLengthArrayWithProperties( + checkFixedLengthArray( data, 2, expected, @@ -2180,72 +2231,6 @@ TEST_CASE("Check fixed-length matN array PropertyTablePropertyView") { defaultValue); } - SECTION("Array of 2 normalized i8mat2x2 with all properties") { - // clang-format off - std::vector data{ - glm::i8mat2x2(-128), - glm::i8mat2x2( - 64, -64, - 0, 255), - glm::i8mat2x2( - 127, -128, - -128, 0), - glm::i8mat2x2(0), - glm::i8mat2x2(0), - glm::i8mat2x2( - -128, -128, - -128, -128)}; - JsonValue::Array offset{ - {0, 1, - 2, 3}, - {1, 2, - 0, -2}}; - JsonValue::Array scale{ - {1, -1, - 0, 1}, - {2, 1, - -1, 0}}; - JsonValue::Array noData{ - {0, 0, - 0, 0}, - {-128, -128, - -128, -128}}; - JsonValue::Array defaultValue{ - {1, 0, - 0, 1}, - {2, 0, - 0, 2}}; - - std::vector>> expected{ - std::vector{ - glm::dmat2( - -1.0, 1.0, - 2.0, 2.0), - glm::dmat2( - 1 + 2 * (64.0 / 127.0), 2 - (64.0 / 127.0), - 0, -2)}, - std::vector{ - glm::dmat2( - 1.0, 2.0, - 2.0, 3.0), - glm::dmat2( - 1.0, 2.0, - 0.0, -2.0)}, - std::vector{ - glm::dmat2(1), - glm::dmat2(2)}, - }; - // clang-format on - checkNormalizedFixedLengthArray( - data, - 2, - expected, - offset, - scale, - noData, - defaultValue); - } - SECTION("Overrides class property values") { // clang-format off std::vector data{ @@ -2364,33 +2349,140 @@ TEST_CASE("Check fixed-length matN array PropertyTablePropertyView") { 4.0f, -1.0f, 4.0f, 8.0f)}); - std::vector> expected{ - {glm::mat2( - 3.0f, 1.0f, - -1.0f, 0.0f), - glm::mat2( - 2.0f, -1.0f, - 4.0f, 4.0f)}, - {glm::mat2( - 5.0f, 1.0f, - -1.0f, 4.0f), - glm::mat2( - 4.0f, -1.0f, - 4.0f, 8.0f)}}; + std::vector> expected{ + {glm::mat2( + 3.0f, 1.0f, + -1.0f, 0.0f), + glm::mat2( + 2.0f, -1.0f, + 4.0f, 4.0f)}, + {glm::mat2( + 5.0f, 1.0f, + -1.0f, 4.0f), + glm::mat2( + 4.0f, -1.0f, + 4.0f, 8.0f)}}; + // clang-format on + size_t expectedIdx = 0; + for (int64_t i = 0; i < property.size(); ++i) { + PropertyArrayView rawValues = property.getRaw(i); + auto values = property.get(i); + REQUIRE(values); + for (int64_t j = 0; j < rawValues.size(); ++j) { + REQUIRE(rawValues[j] == data[expectedIdx]); + REQUIRE( + (*values)[j] == + expected[static_cast(i)][static_cast(j)]); + ++expectedIdx; + } + } + } +} + +TEST_CASE( + "Check fixed-length matN array PropertyTablePropertyView (normalized)") { + SECTION("Array of 2 u8mat2x2s") { + // clang-format off + std::vector data{ + glm::u8mat2x2( + 255, 64, + 0, 255), + glm::u8mat2x2( + 0, 255, + 64, 128), + glm::u8mat2x2( + 128, 0, + 0, 255), + glm::u8mat2x2( + 255, 32, + 255, 0)}; + + std::vector>> expected{ + std::vector{ + glm::dmat2( + 1.0, 64.0 / 255.0, + 0.0, 1.0), + glm::dmat2( + 0.0, 1.0, + 64.0 / 255.0, 128.0 / 255.0), + }, + std::vector{ + glm::dmat2( + 128.0 / 255.0, 0.0, + 0.0, 1.0), + glm::dmat2( + 1.0, 32.0 / 255.0, + 1.0, 0.0), + }}; + // clang-format on + + checkNormalizedFixedLengthArray(data, 2, expected); + } + + SECTION("Array of 2 i8mat2x2 with all properties") { + // clang-format off + std::vector data{ + glm::i8mat2x2(-128), + glm::i8mat2x2( + 64, -64, + 0, 255), + glm::i8mat2x2( + 127, -128, + -128, 0), + glm::i8mat2x2(0), + glm::i8mat2x2(0), + glm::i8mat2x2( + -128, -128, + -128, -128)}; + JsonValue::Array offset{ + {0, 1, + 2, 3}, + {1, 2, + 0, -2}}; + JsonValue::Array scale{ + {1, -1, + 0, 1}, + {2, 1, + -1, 0}}; + JsonValue::Array noData{ + {0, 0, + 0, 0}, + {-128, -128, + -128, -128}}; + JsonValue::Array defaultValue{ + {1, 0, + 0, 1}, + {2, 0, + 0, 2}}; + + std::vector>> expected{ + std::vector{ + glm::dmat2( + -1.0, 1.0, + 2.0, 2.0), + glm::dmat2( + 1 + 2 * (64.0 / 127.0), 2 - (64.0 / 127.0), + 0, -2)}, + std::vector{ + glm::dmat2( + 1.0, 2.0, + 2.0, 3.0), + glm::dmat2( + 1.0, 2.0, + 0.0, -2.0)}, + std::vector{ + glm::dmat2(1), + glm::dmat2(2)}, + }; // clang-format on - size_t expectedIdx = 0; - for (int64_t i = 0; i < property.size(); ++i) { - PropertyArrayView rawValues = property.getRaw(i); - auto values = property.get(i); - REQUIRE(values); - for (int64_t j = 0; j < rawValues.size(); ++j) { - REQUIRE(rawValues[j] == data[expectedIdx]); - REQUIRE( - (*values)[j] == - expected[static_cast(i)][static_cast(j)]); - ++expectedIdx; - } - } + checkNormalizedFixedLengthArray( + data, + 2, + expected, + offset, + scale, + noData, + defaultValue); } } @@ -2496,7 +2588,7 @@ TEST_CASE("Check variable-length scalar array PropertyTablePropertyView") { std::vector{1, 3, 2}, std::nullopt, std::vector{1, 3, 4, 1}}; - checkVariableLengthArrayWithProperties( + checkVariableLengthArray( data, offsets, PropertyComponentType::Uint32, @@ -2532,7 +2624,7 @@ TEST_CASE("Check variable-length scalar array PropertyTablePropertyView") { std::vector{1, 3, 2}, std::vector{1}, std::vector{1, 3, 4, 1}}; - checkVariableLengthArrayWithProperties( + checkVariableLengthArray( data, offsets, PropertyComponentType::Uint32, @@ -2576,6 +2668,67 @@ TEST_CASE("Check variable-length scalar array PropertyTablePropertyView") { } } +TEST_CASE("Check variable-length scalar array PropertyTablePropertyView " + "(normalized)") { + SECTION("Array of uint8_t") { + // clang-format off + std::vector data{ + 255, 0, + 0, 255, 128, + 64, + }; + std::vector offsets{ + 0, 2, 5, 6 + }; + // clang-format on + + std::vector>> expected{ + std::vector{1.0, 0.0}, + std::vector{0.0, 1.0, 128.0 / 255.0}, + std::vector{64.0 / 255.0}, + }; + checkNormalizedVariableLengthArray( + data, + offsets, + PropertyComponentType::Uint32, + 3, + expected); + } + + SECTION("Array of uint8_t with NoData and DefaultValue") { + // clang-format off + std::vector data{ + 255, 0, + 0, 255, 128, + 64, + 255, 255 + }; + std::vector offsets{ + 0, 2, 5, 6, 8 + }; + // clang-format on + + JsonValue::Array noData{255, 255}; + JsonValue::Array defaultValue{-1.0}; + + std::vector>> expected{ + std::vector{1.0, 0.0}, + std::vector{0.0, 1.0, 128.0 / 255.0}, + std::vector{64.0 / 255}, + std::vector{-1.0}, + }; + + checkNormalizedVariableLengthArray( + data, + offsets, + PropertyComponentType::Uint32, + 4, + expected, + noData, + defaultValue); + } +} + TEST_CASE("Check variable-length vecN array PropertyTablePropertyView") { SECTION("Array of ivec2") { // clang-format off @@ -2639,38 +2792,6 @@ TEST_CASE("Check variable-length vecN array PropertyTablePropertyView") { checkVariableLengthArray(data, offsets, PropertyComponentType::Uint32, 5); } - SECTION("Array of normalized u8vec2") { - // clang-format off - std::vector data{ - glm::u8vec2(255, 0), glm::u8vec2(0, 64), - glm::u8vec2(0, 0), - glm::u8vec2(128, 255), glm::u8vec2(255, 255), glm::u8vec2(32, 64) - }; - // clang-format on - std::vector offsets{ - 0 * sizeof(glm::u8vec2), - 2 * sizeof(glm::u8vec2), - 3 * sizeof(glm::u8vec2), - 6 * sizeof(glm::u8vec2)}; - - std::vector>> expected{ - std::vector{ - glm::dvec2(1.0, 0.0), - glm::dvec2(0.0, 64.0 / 255.0)}, - std::vector{glm::dvec2(0.0, 0.0)}, - std::vector{ - glm::dvec2(128.0 / 255.0, 1.0), - glm::dvec2(1.0, 1.0), - glm::dvec2(32.0 / 255.0, 64.0 / 255.0)}, - }; - checkNormalizedVariableLengthArray( - data, - offsets, - PropertyComponentType::Uint32, - 3, - expected); - } - SECTION("Array of ivec3 with NoData") { // clang-format off std::vector data{ @@ -2701,7 +2822,7 @@ TEST_CASE("Check variable-length vecN array PropertyTablePropertyView") { glm::ivec3(0)}, std::nullopt, std::vector{glm::ivec3(1, 3, 4), glm::ivec3(1, 0, 1)}}; - checkVariableLengthArrayWithProperties( + checkVariableLengthArray( data, offsets, PropertyComponentType::Uint32, @@ -2742,7 +2863,7 @@ TEST_CASE("Check variable-length vecN array PropertyTablePropertyView") { glm::ivec3(0)}, std::vector{glm::ivec3(0)}, std::vector{glm::ivec3(1, 3, 4), glm::ivec3(1, 0, 1)}}; - checkVariableLengthArrayWithProperties( + checkVariableLengthArray( data, offsets, PropertyComponentType::Uint32, @@ -2751,6 +2872,41 @@ TEST_CASE("Check variable-length vecN array PropertyTablePropertyView") { noData, defaultValue); } +} + +TEST_CASE( + "Check variable-length vecN array PropertyTablePropertyView (normalized)") { + SECTION("Array of u8vec2") { + // clang-format off + std::vector data{ + glm::u8vec2(255, 0), glm::u8vec2(0, 64), + glm::u8vec2(0, 0), + glm::u8vec2(128, 255), glm::u8vec2(255, 255), glm::u8vec2(32, 64) + }; + // clang-format on + std::vector offsets{ + 0 * sizeof(glm::u8vec2), + 2 * sizeof(glm::u8vec2), + 3 * sizeof(glm::u8vec2), + 6 * sizeof(glm::u8vec2)}; + + std::vector>> expected{ + std::vector{ + glm::dvec2(1.0, 0.0), + glm::dvec2(0.0, 64.0 / 255.0)}, + std::vector{glm::dvec2(0.0, 0.0)}, + std::vector{ + glm::dvec2(128.0 / 255.0, 1.0), + glm::dvec2(1.0, 1.0), + glm::dvec2(32.0 / 255.0, 64.0 / 255.0)}, + }; + checkNormalizedVariableLengthArray( + data, + offsets, + PropertyComponentType::Uint32, + 3, + expected); + } SECTION("Array of normalized u8vec2 with NoData and DefaultValue") { // clang-format off @@ -2941,36 +3097,6 @@ TEST_CASE("Check variable-length matN array PropertyTablePropertyView") { checkVariableLengthArray(data, offsets, PropertyComponentType::Uint32, 3); } - SECTION("Array of normalized u8mat2x2") { - // clang-format off - std::vector data{ - glm::u8mat2x2(255), glm::u8mat2x2(64), - glm::u8mat2x2(0), - glm::u8mat2x2(128), glm::u8mat2x2(255), glm::u8mat2x2(32) - }; - // clang-format on - std::vector offsets{ - 0 * sizeof(glm::u8mat2x2), - 2 * sizeof(glm::u8mat2x2), - 3 * sizeof(glm::u8mat2x2), - 6 * sizeof(glm::u8mat2x2)}; - - std::vector>> expected{ - std::vector{glm::dmat2(1.0), glm::dmat2(64.0 / 255.0)}, - std::vector{glm::dmat2(0.0)}, - std::vector{ - glm::dmat2(128.0 / 255.0), - glm::dmat2(1.0), - glm::dmat2(32.0 / 255.0)}, - }; - checkNormalizedVariableLengthArray( - data, - offsets, - PropertyComponentType::Uint32, - 3, - expected); - } - SECTION("Array of imat3x3 with NoData") { // clang-format off std::vector data{ @@ -3006,7 +3132,7 @@ TEST_CASE("Check variable-length matN array PropertyTablePropertyView") { glm::imat3x3(0)}, std::nullopt, std::vector{glm::imat3x3(1), glm::imat3x3(24)}}; - checkVariableLengthArrayWithProperties( + checkVariableLengthArray( data, offsets, PropertyComponentType::Uint32, @@ -3055,7 +3181,7 @@ TEST_CASE("Check variable-length matN array PropertyTablePropertyView") { glm::imat3x3(0)}, std::vector{glm::imat3x3(99)}, std::vector{glm::imat3x3(1), glm::imat3x3(24)}}; - checkVariableLengthArrayWithProperties( + checkVariableLengthArray( data, offsets, PropertyComponentType::Uint32, @@ -3064,8 +3190,41 @@ TEST_CASE("Check variable-length matN array PropertyTablePropertyView") { noData, defaultValue); } +} + +TEST_CASE( + "Check variable-length matN array PropertyTablePropertyView (normalized)") { + SECTION("Array of u8mat2x2") { + // clang-format off + std::vector data{ + glm::u8mat2x2(255), glm::u8mat2x2(64), + glm::u8mat2x2(0), + glm::u8mat2x2(128), glm::u8mat2x2(255), glm::u8mat2x2(32) + }; + // clang-format on + std::vector offsets{ + 0 * sizeof(glm::u8mat2x2), + 2 * sizeof(glm::u8mat2x2), + 3 * sizeof(glm::u8mat2x2), + 6 * sizeof(glm::u8mat2x2)}; + + std::vector>> expected{ + std::vector{glm::dmat2(1.0), glm::dmat2(64.0 / 255.0)}, + std::vector{glm::dmat2(0.0)}, + std::vector{ + glm::dmat2(128.0 / 255.0), + glm::dmat2(1.0), + glm::dmat2(32.0 / 255.0)}, + }; + checkNormalizedVariableLengthArray( + data, + offsets, + PropertyComponentType::Uint32, + 3, + expected); + } - SECTION("Array of normalized u8mat2x2 with NoData and DefaultValue") { + SECTION("Array of u8mat2x2 with NoData and DefaultValue") { // clang-format off std::vector data{ glm::u8mat2x2(255), glm::u8mat2x2(64), diff --git a/CesiumGltf/test/TestPropertyTexturePropertyView.cpp b/CesiumGltf/test/TestPropertyTexturePropertyView.cpp index 638c721a4..7805964a1 100644 --- a/CesiumGltf/test/TestPropertyTexturePropertyView.cpp +++ b/CesiumGltf/test/TestPropertyTexturePropertyView.cpp @@ -3,10 +3,8 @@ #include #include -#include #include #include -#include #include using namespace CesiumGltf; @@ -696,7 +694,7 @@ TEST_CASE("Check scalar PropertyTexturePropertyView (normalized)") { } SECTION("uint8_t with all properties") { - std::vector data{12, 33, 56, 0, 67}; + std::vector data{12, 33, 56, 0}; const double offset = 1.0; const double scale = 2.0; const uint8_t noData = 0; @@ -706,7 +704,6 @@ TEST_CASE("Check scalar PropertyTexturePropertyView (normalized)") { normalize(data[1]) * scale + offset, normalize(data[2]) * scale + offset, 10.0, - normalize(data[4]) * scale + offset, }; checkNormalizedTextureValues( data, @@ -857,7 +854,7 @@ TEST_CASE("Check vecN PropertyTexturePropertyView") { 0, 0, 182, 1}; // clang-format on - std::optional noData = JsonValue::Array{0, 0}; + JsonValue::Array noData{0, 0}; std::vector expectedRaw{ glm::i8vec2(28, -1), glm::i8vec2(-2, 1), @@ -1144,7 +1141,7 @@ TEST_CASE("Check array PropertyTexturePropertyView") { 0, 0, 0, 0,}; // clang-format on - std::optional noData = JsonValue::Array{0, 0, 0, 0}; + JsonValue::Array noData{0, 0, 0, 0}; std::vector> expectedRaw{ {1, 2, 3, 0}, {4, 5, 6, 11}, @@ -1167,8 +1164,8 @@ TEST_CASE("Check array PropertyTexturePropertyView") { 0, 0, 0, 0,}; // clang-format on - std::optional noData = JsonValue::Array{0, 0, 0, 0}; - std::optional defaultValue = JsonValue::Array{255, 8, 12, 5}; + JsonValue::Array noData{0, 0, 0, 0}; + JsonValue::Array defaultValue{255, 8, 12, 5}; std::vector> expectedRaw{ {1, 2, 3, 0}, {4, 5, 6, 11}, @@ -1299,7 +1296,7 @@ TEST_CASE("Check array PropertyTexturePropertyView (normalized)") { 0, 0, 0, 0,}; // clang-format on - std::optional noData = JsonValue::Array{0, 0, 0, 0}; + JsonValue::Array noData{0, 0, 0, 0}; std::vector> expectedRaw{ {1, 2, 3, 0}, {4, 5, 6, 11}, @@ -1340,9 +1337,8 @@ TEST_CASE("Check array PropertyTexturePropertyView (normalized)") { const std::vector offset{1, 2, 0, 4}; const std::vector scale{1, -1, 3, -2}; - std::optional noData = JsonValue::Array{0, 0, 0, 0}; - std::optional defaultValue = - JsonValue::Array{1.0, 2.0, 3.0, 4.0}; + JsonValue::Array noData{0, 0, 0, 0}; + JsonValue::Array defaultValue{1.0, 2.0, 3.0, 4.0}; std::vector> expectedRaw{ {1, 2, 3, 0}, @@ -1375,7 +1371,7 @@ TEST_CASE("Check array PropertyTexturePropertyView (normalized)") { } } -TEST_CASE("Check that property values override class property values") { +TEST_CASE("Check that PropertyTextureProperty values override class property values") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::SCALAR; classProperty.componentType = ClassProperty::ComponentType::FLOAT32; @@ -1427,14 +1423,10 @@ TEST_CASE("Check that property values override class property values") { view(property, classProperty, sampler, image, channels); CHECK(view.getSwizzle() == "rgba"); - REQUIRE(view.offset()); - REQUIRE(*view.offset() == offset); - REQUIRE(view.scale()); - REQUIRE(*view.scale() == scale); - REQUIRE(view.min()); - REQUIRE(*view.min() == std::numeric_limits::lowest()); - REQUIRE(view.max()); - REQUIRE(*view.max() == std::numeric_limits::max()); + REQUIRE(view.offset() == offset); + REQUIRE(view.scale() == scale); + REQUIRE(view.min() == std::numeric_limits::lowest()); + REQUIRE(view.max() == std::numeric_limits::max()); std::vector texCoords{ glm::dvec2(0, 0), From a56f7eddec540003d5ee5b92ec48a63065f55138 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Tue, 29 Aug 2023 12:31:44 -0400 Subject: [PATCH 241/421] Move function bodies to cpp --- .../CesiumGltf/PropertyTablePropertyView.h | 15 +------ .../CesiumGltf/PropertyTexturePropertyView.h | 37 +---------------- CesiumGltf/src/PropertyTablePropertyView.cpp | 19 ++++++++- .../src/PropertyTexturePropertyView.cpp | 40 ++++++++++++++++++- 4 files changed, 59 insertions(+), 52 deletions(-) diff --git a/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h index 1a86d0972..e4804a67f 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h @@ -133,20 +133,7 @@ class PropertyTablePropertyViewStatus : public PropertyViewStatus { static const PropertyViewStatusType ErrorStringOffsetOutOfBounds = 29; }; -inline int64_t getOffsetTypeSize(PropertyComponentType offsetType) noexcept { - switch (offsetType) { - case PropertyComponentType::Uint8: - return sizeof(uint8_t); - case PropertyComponentType::Uint16: - return sizeof(uint16_t); - case PropertyComponentType::Uint32: - return sizeof(uint32_t); - case PropertyComponentType::Uint64: - return sizeof(uint64_t); - default: - return 0; - } -} +int64_t getOffsetTypeSize(PropertyComponentType offsetType) noexcept; /** * @brief A view on the data of the {@link PropertyTableProperty} that is created diff --git a/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h index 821e3932d..da9bc456f 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h @@ -190,41 +190,8 @@ assembleValueFromChannels(const std::vector& bytes) noexcept { } } -inline double applySamplerWrapS(const double u, const int32_t wrapS) { - if (wrapS == Sampler::WrapS::REPEAT) { - double integral = 0; - double fraction = std::modf(u, &integral); - return fraction < 0 ? 1.0 - fraction : fraction; - } - - if (wrapS == Sampler::WrapS::MIRRORED_REPEAT) { - double integral = 0; - double fraction = std::abs(std::modf(u, &integral)); - int64_t integer = static_cast(std::abs(integral)); - // If the integer part is odd, the direction is reversed. - return integer % 2 == 1 ? 1.0 - fraction : fraction; - } - - return glm::clamp(u, 0.0, 1.0); -} - -inline double applySamplerWrapT(const double v, const int32_t wrapT) { - if (wrapT == Sampler::WrapT::REPEAT) { - double integral = 0; - double fraction = std::modf(v, &integral); - return fraction < 0 ? 1.0 - fraction : fraction; - } - - if (wrapT == Sampler::WrapT::MIRRORED_REPEAT) { - double integral = 0; - double fraction = std::abs(std::modf(v, &integral)); - int64_t integer = static_cast(std::abs(integral)); - // If the integer part is odd, the direction is reversed. - return integer % 2 == 1 ? 1.0 - fraction : fraction; - } - - return glm::clamp(v, 0.0, 1.0); -} +double applySamplerWrapS(const double u, const int32_t wrapS); +double applySamplerWrapT(const double v, const int32_t wrapT); /** * @brief A view of the data specified by a {@link PropertyTextureProperty}. diff --git a/CesiumGltf/src/PropertyTablePropertyView.cpp b/CesiumGltf/src/PropertyTablePropertyView.cpp index 48f162915..8d2dabf53 100644 --- a/CesiumGltf/src/PropertyTablePropertyView.cpp +++ b/CesiumGltf/src/PropertyTablePropertyView.cpp @@ -1,7 +1,6 @@ #include "CesiumGltf/PropertyTablePropertyView.h" -using namespace CesiumGltf; - +namespace CesiumGltf { // Re-initialize consts here to avoid "undefined reference" errors with GCC / // Clang. const PropertyViewStatusType @@ -40,3 +39,19 @@ const PropertyViewStatusType PropertyTablePropertyViewStatus::ErrorArrayOffsetOutOfBounds; const PropertyViewStatusType PropertyTablePropertyViewStatus::ErrorStringOffsetOutOfBounds; + +int64_t getOffsetTypeSize(PropertyComponentType offsetType) noexcept { + switch (offsetType) { + case PropertyComponentType::Uint8: + return sizeof(uint8_t); + case PropertyComponentType::Uint16: + return sizeof(uint16_t); + case PropertyComponentType::Uint32: + return sizeof(uint32_t); + case PropertyComponentType::Uint64: + return sizeof(uint64_t); + default: + return 0; + } +} +} // namespace CesiumGltf diff --git a/CesiumGltf/src/PropertyTexturePropertyView.cpp b/CesiumGltf/src/PropertyTexturePropertyView.cpp index f5bd80aed..5b7f654ba 100644 --- a/CesiumGltf/src/PropertyTexturePropertyView.cpp +++ b/CesiumGltf/src/PropertyTexturePropertyView.cpp @@ -1,6 +1,6 @@ #include "CesiumGltf/PropertyTexturePropertyView.h" -using namespace CesiumGltf; +namespace CesiumGltf { // Re-initialize consts here to avoid "undefined reference" errors with GCC / // Clang. @@ -21,3 +21,41 @@ const PropertyViewStatusType PropertyTexturePropertyViewStatus::ErrorInvalidChannels; const PropertyViewStatusType PropertyTexturePropertyViewStatus::ErrorChannelsAndTypeMismatch; + +double applySamplerWrapS(const double u, const int32_t wrapS) { + if (wrapS == Sampler::WrapS::REPEAT) { + double integral = 0; + double fraction = std::modf(u, &integral); + return fraction < 0 ? 1.0 - fraction : fraction; + } + + if (wrapS == Sampler::WrapS::MIRRORED_REPEAT) { + double integral = 0; + double fraction = std::abs(std::modf(u, &integral)); + int64_t integer = static_cast(std::abs(integral)); + // If the integer part is odd, the direction is reversed. + return integer % 2 == 1 ? 1.0 - fraction : fraction; + } + + return glm::clamp(u, 0.0, 1.0); +} + +double applySamplerWrapT(const double v, const int32_t wrapT) { + if (wrapT == Sampler::WrapT::REPEAT) { + double integral = 0; + double fraction = std::modf(v, &integral); + return fraction < 0 ? 1.0 - fraction : fraction; + } + + if (wrapT == Sampler::WrapT::MIRRORED_REPEAT) { + double integral = 0; + double fraction = std::abs(std::modf(v, &integral)); + int64_t integer = static_cast(std::abs(integral)); + // If the integer part is odd, the direction is reversed. + return integer % 2 == 1 ? 1.0 - fraction : fraction; + } + + return glm::clamp(v, 0.0, 1.0); +} + +} // namespace CesiumGltf From 7bf2d18d05744fc40afe5821332a8720f3fdf48a Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Tue, 29 Aug 2023 15:32:42 -0400 Subject: [PATCH 242/421] Finish writing tests for PropertyAttributeView --- .../CesiumGltf/PropertyAttributeView.h | 139 +- .../CesiumGltf/PropertyTexturePropertyView.h | 6 +- .../src/PropertyAttributePropertyView.cpp | 28 + .../TestPropertyAttributePropertyView.cpp | 6 +- CesiumGltf/test/TestPropertyAttributeView.cpp | 3714 ++++++++--------- .../test/TestPropertyTablePropertyView.cpp | 3 + CesiumGltf/test/TestPropertyTableView.cpp | 172 +- .../test/TestPropertyTexturePropertyView.cpp | 5 +- CesiumGltf/test/TestPropertyTextureView.cpp | 4 +- 9 files changed, 1951 insertions(+), 2126 deletions(-) create mode 100644 CesiumGltf/src/PropertyAttributePropertyView.cpp diff --git a/CesiumGltf/include/CesiumGltf/PropertyAttributeView.h b/CesiumGltf/include/CesiumGltf/PropertyAttributeView.h index eb76620c5..af4949567 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyAttributeView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyAttributeView.h @@ -108,8 +108,8 @@ class PropertyAttributeView { */ template PropertyAttributePropertyView getPropertyView( - const std::string& propertyName, - const MeshPrimitive& primitive) const { + const MeshPrimitive& primitive, + const std::string& propertyName) const { if (this->_status != PropertyAttributeViewStatus::Valid) { return PropertyAttributePropertyView( PropertyAttributePropertyViewStatus::ErrorInvalidPropertyAttribute); @@ -128,9 +128,9 @@ class PropertyAttributeView { } return getPropertyViewImpl( + primitive, propertyName, - *pClassProperty, - primitive); + *pClassProperty); } /** @@ -148,14 +148,15 @@ class PropertyAttributeView { * with an error status will be passed to the callback. Otherwise, a valid * property view will be passed to the callback. * + * @param primitive The target primitive * @param propertyName The name of the property to retrieve data from * @tparam callback A callback function that accepts a property name and a * {@link PropertyAttributePropertyView} */ template void getPropertyView( - const std::string& propertyName, const MeshPrimitive& primitive, + const std::string& propertyName, Callback&& callback) const { if (this->_status != PropertyAttributeViewStatus::Valid) { callback( @@ -175,6 +176,14 @@ class PropertyAttributeView { return; } + if (pClassProperty->array) { + callback( + propertyName, + PropertyAttributePropertyView( + PropertyAttributePropertyViewStatus::ErrorUnsupportedProperty)); + return; + } + PropertyType type = convertStringToPropertyType(pClassProperty->type); PropertyComponentType componentType = PropertyComponentType::None; if (pClassProperty->componentType) { @@ -194,16 +203,16 @@ class PropertyAttributeView { if (type == PropertyType::Scalar) { if (normalized) { getScalarPropertyViewImpl( + primitive, propertyName, *pClassProperty, - primitive, componentType, std::forward(callback)); } else { getScalarPropertyViewImpl( + primitive, propertyName, *pClassProperty, - primitive, componentType, std::forward(callback)); } @@ -213,17 +222,17 @@ class PropertyAttributeView { if (isPropertyTypeVecN(type)) { if (normalized) { getVecNPropertyViewImpl( + primitive, propertyName, *pClassProperty, - primitive, type, componentType, std::forward(callback)); } else { getVecNPropertyViewImpl( + primitive, propertyName, *pClassProperty, - primitive, type, componentType, std::forward(callback)); @@ -234,17 +243,17 @@ class PropertyAttributeView { if (isPropertyTypeMatN(type)) { if (normalized) { getMatNPropertyViewImpl( + primitive, propertyName, *pClassProperty, - primitive, type, componentType, std::forward(callback)); } else { getMatNPropertyViewImpl( + primitive, propertyName, *pClassProperty, - primitive, type, componentType, std::forward(callback)); @@ -292,9 +301,9 @@ class PropertyAttributeView { private: template PropertyAttributePropertyView getPropertyViewImpl( + const MeshPrimitive& primitive, const std::string& propertyName, - const ClassProperty& classProperty, - const MeshPrimitive& primitive) const { + const ClassProperty& classProperty) const { auto propertyAttributePropertyIter = _pPropertyAttribute->properties.find(propertyName); if (propertyAttributePropertyIter == @@ -307,16 +316,16 @@ class PropertyAttributeView { propertyAttributePropertyIter->second; return createPropertyView( + primitive, classProperty, - propertyAttributeProperty, - primitive); + propertyAttributeProperty); } template void getScalarPropertyViewImpl( + const MeshPrimitive& primitive, const std::string& propertyName, const ClassProperty& classProperty, - const MeshPrimitive& primitive, PropertyComponentType componentType, Callback&& callback) const { switch (componentType) { @@ -324,49 +333,49 @@ class PropertyAttributeView { callback( propertyName, getPropertyViewImpl( + primitive, propertyName, - classProperty, - primitive)); + classProperty)); return; case PropertyComponentType::Uint8: callback( propertyName, getPropertyViewImpl( + primitive, propertyName, - classProperty, - primitive)); + classProperty)); return; case PropertyComponentType::Int16: callback( propertyName, getPropertyViewImpl( + primitive, propertyName, - classProperty, - primitive)); + classProperty)); return; case PropertyComponentType::Uint16: callback( propertyName, getPropertyViewImpl( + primitive, propertyName, - classProperty, - primitive)); + classProperty)); break; case PropertyComponentType::Uint32: callback( propertyName, getPropertyViewImpl( + primitive, propertyName, - classProperty, - primitive)); + classProperty)); break; case PropertyComponentType::Float32: callback( propertyName, getPropertyViewImpl( + primitive, propertyName, - classProperty, - primitive)); + classProperty)); break; default: callback( @@ -379,9 +388,9 @@ class PropertyAttributeView { template void getVecNPropertyViewImpl( + const MeshPrimitive& primitive, const std::string& propertyName, const ClassProperty& classProperty, - const MeshPrimitive& primitive, PropertyComponentType componentType, Callback&& callback) const { switch (componentType) { @@ -389,50 +398,49 @@ class PropertyAttributeView { callback( propertyName, getPropertyViewImpl, Normalized>( + primitive, propertyName, - classProperty, - primitive)); + classProperty)); break; case PropertyComponentType::Uint8: callback( propertyName, getPropertyViewImpl, Normalized>( + primitive, propertyName, - classProperty, - primitive)); + classProperty)); break; case PropertyComponentType::Int16: callback( propertyName, getPropertyViewImpl, Normalized>( + primitive, propertyName, - classProperty, - primitive)); + classProperty)); break; case PropertyComponentType::Uint16: callback( propertyName, getPropertyViewImpl, Normalized>( + primitive, propertyName, - classProperty, - primitive)); + classProperty)); break; - default: case PropertyComponentType::Uint32: callback( propertyName, getPropertyViewImpl, Normalized>( + primitive, propertyName, - classProperty, - primitive)); + classProperty)); break; case PropertyComponentType::Float32: callback( propertyName, getPropertyViewImpl, false>( + primitive, propertyName, - classProperty, - primitive)); + classProperty)); break; default: callback( @@ -445,9 +453,9 @@ class PropertyAttributeView { template void getVecNPropertyViewImpl( + const MeshPrimitive& primitive, const std::string& propertyName, const ClassProperty& classProperty, - const MeshPrimitive& primitive, PropertyType type, PropertyComponentType componentType, Callback&& callback) const { @@ -455,25 +463,25 @@ class PropertyAttributeView { switch (N) { case 2: getVecNPropertyViewImpl( + primitive, propertyName, classProperty, - primitive, componentType, std::forward(callback)); break; case 3: getVecNPropertyViewImpl( + primitive, propertyName, classProperty, - primitive, componentType, std::forward(callback)); break; case 4: getVecNPropertyViewImpl( + primitive, propertyName, classProperty, - primitive, componentType, std::forward(callback)); break; @@ -488,9 +496,9 @@ class PropertyAttributeView { template void getMatNPropertyViewImpl( + const MeshPrimitive& primitive, const std::string& propertyName, const ClassProperty& classProperty, - const MeshPrimitive& primitive, PropertyComponentType componentType, Callback&& callback) const { switch (componentType) { @@ -498,49 +506,49 @@ class PropertyAttributeView { callback( propertyName, getPropertyViewImpl, Normalized>( + primitive, propertyName, - classProperty, - primitive)); + classProperty)); break; case PropertyComponentType::Uint8: callback( propertyName, getPropertyViewImpl, Normalized>( + primitive, propertyName, - classProperty, - primitive)); + classProperty)); break; case PropertyComponentType::Int16: callback( propertyName, getPropertyViewImpl, Normalized>( + primitive, propertyName, - classProperty, - primitive)); + classProperty)); break; case PropertyComponentType::Uint16: callback( propertyName, getPropertyViewImpl, Normalized>( + primitive, propertyName, - classProperty, - primitive)); + classProperty)); break; case PropertyComponentType::Uint32: callback( propertyName, getPropertyViewImpl, Normalized>( + primitive, propertyName, - classProperty, - primitive)); + classProperty)); break; case PropertyComponentType::Float32: callback( propertyName, getPropertyViewImpl, false>( + primitive, propertyName, - classProperty, - primitive)); + classProperty)); break; default: callback( @@ -553,9 +561,9 @@ class PropertyAttributeView { template void getMatNPropertyViewImpl( + const MeshPrimitive& primitive, const std::string& propertyName, const ClassProperty& classProperty, - const MeshPrimitive& primitive, PropertyType type, PropertyComponentType componentType, Callback&& callback) const { @@ -563,25 +571,25 @@ class PropertyAttributeView { switch (N) { case 2: getMatNPropertyViewImpl( + primitive, propertyName, classProperty, - primitive, componentType, std::forward(callback)); break; case 3: getMatNPropertyViewImpl( + primitive, propertyName, classProperty, - primitive, componentType, std::forward(callback)); break; case 4: getMatNPropertyViewImpl( + primitive, propertyName, classProperty, - primitive, componentType, std::forward(callback)); break; @@ -596,9 +604,9 @@ class PropertyAttributeView { template PropertyAttributePropertyView createPropertyView( + const MeshPrimitive& primitive, const ClassProperty& classProperty, - const PropertyAttributeProperty& propertyAttributeProperty, - const MeshPrimitive& primitive) const { + const PropertyAttributeProperty& propertyAttributeProperty) const { const PropertyType type = convertStringToPropertyType(classProperty.type); if (TypeToPropertyType::value != type) { return PropertyAttributePropertyView( @@ -683,4 +691,5 @@ class PropertyAttributeView { PropertyAttributeViewStatus _status; }; + } // namespace CesiumGltf diff --git a/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h index da9bc456f..c1c82413e 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h @@ -620,9 +620,9 @@ class PropertyTexturePropertyView static_cast(0), static_cast(this->_pImage->height) - 1); - int64_t pixelIndex = this->_pImage->bytesPerChannel * - this->_pImage->channels * - (y * this->_pImage->width + x); + int64_t pixelIndex = static_cast(this->_pImage->bytesPerChannel) * + static_cast(this->_pImage->channels) * + (y * static_cast(this->_pImage->width) + x); // TODO: Currently stb only outputs uint8 pixel types. If that // changes this should account for additional pixel byte sizes. diff --git a/CesiumGltf/src/PropertyAttributePropertyView.cpp b/CesiumGltf/src/PropertyAttributePropertyView.cpp new file mode 100644 index 000000000..378486cda --- /dev/null +++ b/CesiumGltf/src/PropertyAttributePropertyView.cpp @@ -0,0 +1,28 @@ +#include "CesiumGltf/PropertyAttributePropertyView.h" + +namespace CesiumGltf { +// Re-initialize consts here to avoid "undefined reference" errors with GCC / +// Clang. +const PropertyViewStatusType + PropertyAttributePropertyViewStatus::ErrorInvalidPropertyAttribute; +const PropertyViewStatusType + PropertyAttributePropertyViewStatus::ErrorUnsupportedProperty; +const PropertyViewStatusType + PropertyAttributePropertyViewStatus::ErrorMissingAttribute; +const PropertyViewStatusType + PropertyAttributePropertyViewStatus::ErrorInvalidAccessor; +const PropertyViewStatusType + PropertyAttributePropertyViewStatus::ErrorAccessorTypeMismatch; +const PropertyViewStatusType + PropertyAttributePropertyViewStatus::ErrorAccessorComponentTypeMismatch; +const PropertyViewStatusType + PropertyAttributePropertyViewStatus::ErrorAccessorNormalizationMismatch; +const PropertyViewStatusType + PropertyAttributePropertyViewStatus::ErrorInvalidBufferView; +const PropertyViewStatusType + PropertyAttributePropertyViewStatus::ErrorInvalidBuffer; +const PropertyViewStatusType + PropertyAttributePropertyViewStatus::ErrorAccessorOutOfBounds; +const PropertyViewStatusType + PropertyAttributePropertyViewStatus::ErrorBufferViewOutOfBounds; +} // namespace CesiumGltf diff --git a/CesiumGltf/test/TestPropertyAttributePropertyView.cpp b/CesiumGltf/test/TestPropertyAttributePropertyView.cpp index c87139ab1..0dab05f2e 100644 --- a/CesiumGltf/test/TestPropertyAttributePropertyView.cpp +++ b/CesiumGltf/test/TestPropertyAttributePropertyView.cpp @@ -9,6 +9,8 @@ using namespace CesiumGltf; using namespace CesiumUtility; +namespace { + template const Accessor& addValuesToModel(Model& model, const std::vector& values) { Buffer& buffer = model.buffers.emplace_back(); @@ -192,6 +194,7 @@ void checkNormalizedAttributeValues( REQUIRE(view.get(i) == expected[static_cast(i)]); } } +} // namespace TEST_CASE("Check scalar PropertyAttributePropertyView") { SECTION("Uint8") { @@ -794,7 +797,8 @@ TEST_CASE("Check matN PropertyAttributePropertyView (normalized)") { } } -TEST_CASE("Check that PropertyAttributeProperty values override class property values") { +TEST_CASE("Check that PropertyAttributeProperty values override class property " + "values") { Model model; std::vector data{1.0f, 2.0f, 3.0f, 4.0f}; diff --git a/CesiumGltf/test/TestPropertyAttributeView.cpp b/CesiumGltf/test/TestPropertyAttributeView.cpp index a242bb6df..d7d13c7b1 100644 --- a/CesiumGltf/test/TestPropertyAttributeView.cpp +++ b/CesiumGltf/test/TestPropertyAttributeView.cpp @@ -210,7 +210,7 @@ TEST_CASE("Test scalar PropertyAttributeProperty") { SECTION("Access correct type") { PropertyAttributePropertyView uint16Property = - view.getPropertyView("TestClassProperty", primitive); + view.getPropertyView(primitive, "TestClassProperty"); REQUIRE( uint16Property.status() == PropertyAttributePropertyViewStatus::Valid); for (size_t i = 0; i < data.size(); ++i) { @@ -221,7 +221,7 @@ TEST_CASE("Test scalar PropertyAttributeProperty") { SECTION("Access wrong type") { PropertyAttributePropertyView u16vec2Invalid = - view.getPropertyView("TestClassProperty", primitive); + view.getPropertyView(primitive, "TestClassProperty"); REQUIRE( u16vec2Invalid.status() == PropertyAttributePropertyViewStatus::ErrorTypeMismatch); @@ -229,19 +229,19 @@ TEST_CASE("Test scalar PropertyAttributeProperty") { SECTION("Access wrong component type") { PropertyAttributePropertyView uint8Invalid = - view.getPropertyView("TestClassProperty", primitive); + view.getPropertyView(primitive, "TestClassProperty"); REQUIRE( uint8Invalid.status() == PropertyAttributePropertyViewStatus::ErrorComponentTypeMismatch); PropertyAttributePropertyView int32Invalid = - view.getPropertyView("TestClassProperty", primitive); + view.getPropertyView(primitive, "TestClassProperty"); REQUIRE( int32Invalid.status() == PropertyAttributePropertyViewStatus::ErrorComponentTypeMismatch); PropertyAttributePropertyView uint64Invalid = - view.getPropertyView("TestClassProperty", primitive); + view.getPropertyView(primitive, "TestClassProperty"); REQUIRE( uint64Invalid.status() == PropertyAttributePropertyViewStatus::ErrorComponentTypeMismatch); @@ -249,7 +249,7 @@ TEST_CASE("Test scalar PropertyAttributeProperty") { SECTION("Access incorrectly as normalized") { PropertyAttributePropertyView normalizedInvalid = - view.getPropertyView("TestClassProperty", primitive); + view.getPropertyView(primitive, "TestClassProperty"); REQUIRE( normalizedInvalid.status() == PropertyAttributePropertyViewStatus::ErrorNormalizationMismatch); @@ -258,7 +258,7 @@ TEST_CASE("Test scalar PropertyAttributeProperty") { SECTION("Buffer view points outside of the real buffer length") { model.buffers[bufferIndex].cesium.data.resize(4); PropertyAttributePropertyView property = - view.getPropertyView("TestClassProperty", primitive); + view.getPropertyView(primitive, "TestClassProperty"); REQUIRE( property.status() == PropertyAttributePropertyViewStatus::ErrorBufferViewOutOfBounds); @@ -267,16 +267,25 @@ TEST_CASE("Test scalar PropertyAttributeProperty") { SECTION("Wrong buffer index") { model.bufferViews[bufferViewIndex].buffer = 2; PropertyAttributePropertyView property = - view.getPropertyView("TestClassProperty", primitive); + view.getPropertyView(primitive, "TestClassProperty"); REQUIRE( property.status() == PropertyAttributePropertyViewStatus::ErrorInvalidBuffer); } + SECTION("Accessor view points outside of buffer viwe length") { + model.accessors[accessorIndex].count = 10; + PropertyAttributePropertyView property = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE( + property.status() == + PropertyAttributePropertyViewStatus::ErrorAccessorOutOfBounds); + } + SECTION("Wrong buffer view index") { model.accessors[accessorIndex].bufferView = -1; PropertyAttributePropertyView property = - view.getPropertyView("TestClassProperty", primitive); + view.getPropertyView(primitive, "TestClassProperty"); REQUIRE( property.status() == PropertyAttributePropertyViewStatus::ErrorInvalidBufferView); @@ -285,7 +294,7 @@ TEST_CASE("Test scalar PropertyAttributeProperty") { SECTION("Wrong accessor normalization") { model.accessors[accessorIndex].normalized = true; PropertyAttributePropertyView property = - view.getPropertyView("TestClassProperty", primitive); + view.getPropertyView(primitive, "TestClassProperty"); REQUIRE( property.status() == PropertyAttributePropertyViewStatus:: ErrorAccessorNormalizationMismatch); @@ -295,7 +304,7 @@ TEST_CASE("Test scalar PropertyAttributeProperty") { model.accessors[accessorIndex].componentType = Accessor::ComponentType::SHORT; PropertyAttributePropertyView property = - view.getPropertyView("TestClassProperty", primitive); + view.getPropertyView(primitive, "TestClassProperty"); REQUIRE( property.status() == PropertyAttributePropertyViewStatus:: ErrorAccessorComponentTypeMismatch); @@ -304,7 +313,7 @@ TEST_CASE("Test scalar PropertyAttributeProperty") { SECTION("Wrong accessor type") { model.accessors[accessorIndex].type = Accessor::Type::VEC2; PropertyAttributePropertyView property = - view.getPropertyView("TestClassProperty", primitive); + view.getPropertyView(primitive, "TestClassProperty"); REQUIRE( property.status() == PropertyAttributePropertyViewStatus::ErrorAccessorTypeMismatch); @@ -313,7 +322,7 @@ TEST_CASE("Test scalar PropertyAttributeProperty") { SECTION("Wrong accessor index") { primitive.attributes[attributeName] = -1; PropertyAttributePropertyView property = - view.getPropertyView("TestClassProperty", primitive); + view.getPropertyView(primitive, "TestClassProperty"); REQUIRE( property.status() == PropertyAttributePropertyViewStatus::ErrorInvalidAccessor); @@ -322,1961 +331,1736 @@ TEST_CASE("Test scalar PropertyAttributeProperty") { SECTION("Missing attribute") { primitive.attributes.clear(); PropertyAttributePropertyView property = - view.getPropertyView("TestClassProperty", primitive); + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE( + property.status() == + PropertyAttributePropertyViewStatus::ErrorMissingAttribute); + } +} + +TEST_CASE("Test scalar PropertyAttributeProperty (normalized)") { + Model model; + Mesh& mesh = model.meshes.emplace_back(); + MeshPrimitive& primitive = mesh.primitives.emplace_back(); + + const std::string attributeName = "_ATTRIBUTE"; + std::vector data = {12, 34, 30, 11}; + + addAttributeToModel(model, primitive, attributeName, data); + size_t accessorIndex = model.accessors.size() - 1; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::UINT8; + testClassProperty.normalized = true; + + PropertyAttribute& propertyAttribute = + metadata.propertyAttributes.emplace_back(); + propertyAttribute.classProperty = "TestClass"; + + PropertyAttributeProperty& propertyAttributeProperty = + propertyAttribute.properties["TestClassProperty"]; + propertyAttributeProperty.attribute = attributeName; + + PropertyAttributeView view(model, propertyAttribute); + REQUIRE(view.status() == PropertyAttributeViewStatus::Valid); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT8); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->array); + REQUIRE(classProperty->normalized); + + SECTION("Access correct type") { + PropertyAttributePropertyView uint8Property = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE( + uint8Property.status() == PropertyAttributePropertyViewStatus::Valid); + + for (size_t i = 0; i < data.size(); ++i) { + REQUIRE(uint8Property.getRaw(static_cast(i)) == data[i]); + REQUIRE(uint8Property.get(static_cast(i)) == normalize(data[i])); + } + } + + SECTION("Access wrong type") { + PropertyAttributePropertyView u8vec2Invalid = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE( + u8vec2Invalid.status() == + PropertyAttributePropertyViewStatus::ErrorTypeMismatch); + } + + SECTION("Access wrong component type") { + PropertyAttributePropertyView uint16Invalid = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE( + uint16Invalid.status() == + PropertyAttributePropertyViewStatus::ErrorComponentTypeMismatch); + + PropertyAttributePropertyView int32Invalid = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE( + int32Invalid.status() == + PropertyAttributePropertyViewStatus::ErrorComponentTypeMismatch); + } + + SECTION("Access incorrectly as non-normalized") { + PropertyAttributePropertyView normalizedInvalid = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE( + normalizedInvalid.status() == + PropertyAttributePropertyViewStatus::ErrorNormalizationMismatch); + } + + SECTION("Access incorrectly as double") { + PropertyAttributePropertyView doubleInvalid = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE( + doubleInvalid.status() == + PropertyAttributePropertyViewStatus::ErrorComponentTypeMismatch); + } + + SECTION("Wrong accessor normalization") { + model.accessors[accessorIndex].normalized = false; + PropertyAttributePropertyView property = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE( + property.status() == PropertyAttributePropertyViewStatus:: + ErrorAccessorNormalizationMismatch); + } +} + +TEST_CASE("Test vecN PropertyAttributeProperty") { + Model model; + Mesh& mesh = model.meshes.emplace_back(); + MeshPrimitive& primitive = mesh.primitives.emplace_back(); + + const std::string attributeName = "_ATTRIBUTE"; + std::vector data = { + glm::u8vec2(12, 34), + glm::u8vec2(10, 3), + glm::u8vec2(40, 0), + glm::u8vec2(30, 11)}; + + addAttributeToModel(model, primitive, attributeName, data); + size_t accessorIndex = model.accessors.size() - 1; + size_t bufferIndex = model.buffers.size() - 1; + size_t bufferViewIndex = model.bufferViews.size() - 1; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::VEC2; + testClassProperty.componentType = ClassProperty::ComponentType::UINT8; + + PropertyAttribute& propertyAttribute = + metadata.propertyAttributes.emplace_back(); + propertyAttribute.classProperty = "TestClass"; + + PropertyAttributeProperty& propertyAttributeProperty = + propertyAttribute.properties["TestClassProperty"]; + propertyAttributeProperty.attribute = attributeName; + + PropertyAttributeView view(model, propertyAttribute); + REQUIRE(view.status() == PropertyAttributeViewStatus::Valid); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::VEC2); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT8); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->array); + REQUIRE(!classProperty->normalized); + + SECTION("Access correct type") { + PropertyAttributePropertyView u8vec2Property = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE( + u8vec2Property.status() == PropertyAttributePropertyViewStatus::Valid); + for (size_t i = 0; i < data.size(); ++i) { + REQUIRE(u8vec2Property.getRaw(static_cast(i)) == data[i]); + REQUIRE(u8vec2Property.get(static_cast(i)) == data[i]); + } + } + + SECTION("Access wrong type") { + PropertyAttributePropertyView uint8Invalid = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE( + uint8Invalid.status() == + PropertyAttributePropertyViewStatus::ErrorTypeMismatch); + + PropertyAttributePropertyView u8vec3Invalid = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE( + u8vec3Invalid.status() == + PropertyAttributePropertyViewStatus::ErrorTypeMismatch); + } + + SECTION("Access wrong component type") { + PropertyAttributePropertyView vec2Invalid = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE( + vec2Invalid.status() == + PropertyAttributePropertyViewStatus::ErrorComponentTypeMismatch); + } + + SECTION("Access incorrectly as normalized") { + PropertyAttributePropertyView normalizedInvalid = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE( + normalizedInvalid.status() == + PropertyAttributePropertyViewStatus::ErrorNormalizationMismatch); + } + + SECTION("Buffer view points outside of the real buffer length") { + model.buffers[bufferIndex].cesium.data.resize(4); + PropertyAttributePropertyView property = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE( + property.status() == + PropertyAttributePropertyViewStatus::ErrorBufferViewOutOfBounds); + } + + SECTION("Wrong buffer index") { + model.bufferViews[bufferViewIndex].buffer = 2; + PropertyAttributePropertyView property = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE( + property.status() == + PropertyAttributePropertyViewStatus::ErrorInvalidBuffer); + } + + SECTION("Accessor view points outside of buffer viwe length") { + model.accessors[accessorIndex].count = 10; + PropertyAttributePropertyView property = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE( + property.status() == + PropertyAttributePropertyViewStatus::ErrorAccessorOutOfBounds); + } + + SECTION("Wrong buffer view index") { + model.accessors[accessorIndex].bufferView = -1; + PropertyAttributePropertyView property = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE( + property.status() == + PropertyAttributePropertyViewStatus::ErrorInvalidBufferView); + } + + SECTION("Wrong accessor normalization") { + model.accessors[accessorIndex].normalized = true; + PropertyAttributePropertyView property = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE( + property.status() == PropertyAttributePropertyViewStatus:: + ErrorAccessorNormalizationMismatch); + } + + SECTION("Wrong accessor component type") { + model.accessors[accessorIndex].componentType = + Accessor::ComponentType::BYTE; + PropertyAttributePropertyView property = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE( + property.status() == PropertyAttributePropertyViewStatus:: + ErrorAccessorComponentTypeMismatch); + } + + SECTION("Wrong accessor type") { + model.accessors[accessorIndex].type = Accessor::Type::SCALAR; + PropertyAttributePropertyView property = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE( + property.status() == + PropertyAttributePropertyViewStatus::ErrorAccessorTypeMismatch); + } + + SECTION("Wrong accessor index") { + primitive.attributes[attributeName] = -1; + PropertyAttributePropertyView property = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE( + property.status() == + PropertyAttributePropertyViewStatus::ErrorInvalidAccessor); + } + + SECTION("Missing attribute") { + primitive.attributes.clear(); + PropertyAttributePropertyView property = + view.getPropertyView(primitive, "TestClassProperty"); REQUIRE( property.status() == PropertyAttributePropertyViewStatus::ErrorMissingAttribute); } } -// TEST_CASE("Test scalar PropertyAttributeProperty (normalized)") { -// Model model; -// std::vector data = {12, 34, 30, 11}; -// -// addAttributeToModel( -// model, -// Sampler::WrapS::CLAMP_TO_EDGE, -// Sampler::WrapS::CLAMP_TO_EDGE, -// 2, -// 2, -// 1, -// data); -// size_t AttributeIndex = model.Attributes.size() - 1; -// size_t imageIndex = model.images.size() - 1; -// -// ExtensionModelExtStructuralMetadata& metadata = -// model.addExtension(); -// -// Schema& schema = metadata.schema.emplace(); -// Class& testClass = schema.classes["TestClass"]; -// ClassProperty& testClassProperty = -// testClass.properties["TestClassProperty"]; testClassProperty.type = -// ClassProperty::Type::SCALAR; testClassProperty.componentType = -// ClassProperty::ComponentType::UINT8; testClassProperty.normalized = true; -// -// PropertyAttribute& propertyAttribute = -// metadata.propertyAttributes.emplace_back(); propertyAttribute.classProperty -// = "TestClass"; -// -// PropertyAttributeProperty& propertyAttributeProperty = -// propertyAttribute.properties["TestClassProperty"]; -// propertyAttributeProperty.index = static_cast(AttributeIndex); -// propertyAttributeProperty.texCoord = 0; -// propertyAttributeProperty.channels = {0}; -// -// PropertyAttributeView view(model, propertyAttribute); -// REQUIRE(view.status() == PropertyAttributeViewStatus::Valid); -// -// const ClassProperty* classProperty = -// view.getClassProperty("TestClassProperty"); -// REQUIRE(classProperty); -// REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); -// REQUIRE(classProperty->componentType == -// ClassProperty::ComponentType::UINT8); REQUIRE(classProperty->count == -// std::nullopt); REQUIRE(!classProperty->array); -// REQUIRE(classProperty->normalized); -// -// SECTION("Access correct type") { -// PropertyAttributePropertyView uint8Property = -// view.getPropertyView("TestClassProperty"); -// REQUIRE(uint8Property.status() == -// PropertyAttributePropertyViewStatus::Valid); -// -// std::vector texCoords{ -// glm::dvec2(0, 0), -// glm::dvec2(0.5, 0), -// glm::dvec2(0, 0.5), -// glm::dvec2(0.5, 0.5)}; -// -// for (size_t i = 0; i < texCoords.size(); ++i) { -// glm::dvec2 uv = texCoords[i]; -// REQUIRE(uint8Property.getRaw(uv[0], uv[1]) == data[i]); -// REQUIRE(uint8Property.get(uv[0], uv[1]) == normalize(data[i])); -// } -// } -// -// SECTION("Access wrong type") { -// PropertyAttributePropertyView u8vec2Invalid = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// u8vec2Invalid.status() == -// PropertyAttributePropertyViewStatus::ErrorTypeMismatch); -// } -// -// SECTION("Access wrong component type") { -// PropertyAttributePropertyView uint16Invalid = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// uint16Invalid.status() == -// PropertyAttributePropertyViewStatus::ErrorComponentTypeMismatch); -// -// PropertyAttributePropertyView int32Invalid = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// int32Invalid.status() == -// PropertyAttributePropertyViewStatus::ErrorComponentTypeMismatch); -// } -// -// SECTION("Access incorrectly as array") { -// PropertyAttributePropertyView, true> -// arrayInvalid -// = -// view.getPropertyView, true>( -// "TestClassProperty"); -// REQUIRE( -// arrayInvalid.status() == -// PropertyAttributePropertyViewStatus::ErrorArrayTypeMismatch); -// } -// -// SECTION("Access incorrectly as non-normalized") { -// PropertyAttributePropertyView normalizedInvalid = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// normalizedInvalid.status() == -// PropertyAttributePropertyViewStatus::ErrorNormalizationMismatch); -// } -// -// SECTION("Access incorrectly as double") { -// PropertyAttributePropertyView doubleInvalid = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// doubleInvalid.status() == -// PropertyAttributePropertyViewStatus::ErrorComponentTypeMismatch); -// } -// -// SECTION("Channel and type mismatch") { -// model.images[imageIndex].cesium.channels = 2; -// propertyAttributeProperty.channels = {0, 1}; -// PropertyAttributePropertyView uint8Property = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// uint8Property.status() == -// PropertyAttributePropertyViewStatus::ErrorChannelsAndTypeMismatch); -// } -//} -// -// TEST_CASE("Test vecN PropertyAttributeProperty") { -// Model model; -// std::vector data = {12, 34, 10, 3, 40, 0, 30, 11}; -// -// addAttributeToModel( -// model, -// Sampler::WrapS::CLAMP_TO_EDGE, -// Sampler::WrapS::CLAMP_TO_EDGE, -// 2, -// 2, -// 2, -// data); -// size_t AttributeIndex = model.Attributes.size() - 1; -// size_t imageIndex = model.images.size() - 1; -// -// ExtensionModelExtStructuralMetadata& metadata = -// model.addExtension(); -// -// Schema& schema = metadata.schema.emplace(); -// Class& testClass = schema.classes["TestClass"]; -// ClassProperty& testClassProperty = -// testClass.properties["TestClassProperty"]; testClassProperty.type = -// ClassProperty::Type::VEC2; testClassProperty.componentType = -// ClassProperty::ComponentType::UINT8; -// -// PropertyAttribute& propertyAttribute = -// metadata.propertyAttributes.emplace_back(); propertyAttribute.classProperty -// = "TestClass"; -// -// PropertyAttributeProperty& propertyAttributeProperty = -// propertyAttribute.properties["TestClassProperty"]; -// propertyAttributeProperty.index = static_cast(AttributeIndex); -// propertyAttributeProperty.texCoord = 0; -// propertyAttributeProperty.channels = {0, 1}; -// -// PropertyAttributeView view(model, propertyAttribute); -// REQUIRE(view.status() == PropertyAttributeViewStatus::Valid); -// -// const ClassProperty* classProperty = -// view.getClassProperty("TestClassProperty"); -// REQUIRE(classProperty); -// REQUIRE(classProperty->type == ClassProperty::Type::VEC2); -// REQUIRE(classProperty->componentType == -// ClassProperty::ComponentType::UINT8); REQUIRE(classProperty->count == -// std::nullopt); REQUIRE(!classProperty->array); -// REQUIRE(!classProperty->normalized); -// -// SECTION("Access correct type") { -// PropertyAttributePropertyView u8vec2Property = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// u8vec2Property.status() == -// PropertyAttributePropertyViewStatus::Valid); -// -// std::vector texCoords{ -// glm::dvec2(0, 0), -// glm::dvec2(0.5, 0), -// glm::dvec2(0, 0.5), -// glm::dvec2(0.5, 0.5)}; -// -// std::vector expected{ -// glm::u8vec2(12, 34), -// glm::u8vec2(10, 3), -// glm::u8vec2(40, 0), -// glm::u8vec2(30, 11)}; -// for (size_t i = 0; i < texCoords.size(); ++i) { -// glm::dvec2 uv = texCoords[i]; -// REQUIRE(u8vec2Property.getRaw(uv[0], uv[1]) == expected[i]); -// REQUIRE(u8vec2Property.get(uv[0], uv[1]) == expected[i]); -// } -// } -// -// SECTION("Access wrong type") { -// PropertyAttributePropertyView uint8Invalid = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// uint8Invalid.status() == -// PropertyAttributePropertyViewStatus::ErrorTypeMismatch); -// -// PropertyAttributePropertyView u8vec3Invalid = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// u8vec3Invalid.status() == -// PropertyAttributePropertyViewStatus::ErrorTypeMismatch); -// } -// -// SECTION("Access wrong component type") { -// PropertyAttributePropertyView u16vec2Invalid = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// u16vec2Invalid.status() == -// PropertyAttributePropertyViewStatus::ErrorComponentTypeMismatch); -// -// PropertyAttributePropertyView i8vec2Invalid = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// i8vec2Invalid.status() == -// PropertyAttributePropertyViewStatus::ErrorComponentTypeMismatch); -// } -// -// SECTION("Access incorrectly as array") { -// PropertyAttributePropertyView> arrayInvalid -// = -// view.getPropertyView>( -// "TestClassProperty"); -// REQUIRE( -// arrayInvalid.status() == -// PropertyAttributePropertyViewStatus::ErrorArrayTypeMismatch); -// } -// -// SECTION("Access incorrectly as normalized") { -// PropertyAttributePropertyView normalizedInvalid = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// normalizedInvalid.status() == -// PropertyAttributePropertyViewStatus::ErrorNormalizationMismatch); -// } -// -// SECTION("Channel and type mismatch") { -// model.images[imageIndex].cesium.channels = 4; -// propertyAttributeProperty.channels = {0, 1, 2, 3}; -// PropertyAttributePropertyView u8vec2Property = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// u8vec2Property.status() == -// PropertyAttributePropertyViewStatus::ErrorChannelsAndTypeMismatch); -// } -// -// SECTION("Invalid channel values") { -// propertyAttributeProperty.channels = {0, 4}; -// PropertyAttributePropertyView u8vec2Property = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// u8vec2Property.status() == -// PropertyAttributePropertyViewStatus::ErrorInvalidChannels); -// } -// -// SECTION("Invalid bytes per channel") { -// model.images[imageIndex].cesium.bytesPerChannel = 2; -// PropertyAttributePropertyView u8vec2Property = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// u8vec2Property.status() == -// PropertyAttributePropertyViewStatus::ErrorInvalidBytesPerChannel); -// } -//} -// -// TEST_CASE("Test vecN PropertyAttributeProperty (normalized)") { -// Model model; -// std::vector data = {12, 34, 10, 3, 40, 0, 30, 11}; -// -// addAttributeToModel( -// model, -// Sampler::WrapS::CLAMP_TO_EDGE, -// Sampler::WrapS::CLAMP_TO_EDGE, -// 2, -// 2, -// 2, -// data); -// size_t AttributeIndex = model.Attributes.size() - 1; -// size_t imageIndex = model.images.size() - 1; -// -// ExtensionModelExtStructuralMetadata& metadata = -// model.addExtension(); -// -// Schema& schema = metadata.schema.emplace(); -// Class& testClass = schema.classes["TestClass"]; -// ClassProperty& testClassProperty = -// testClass.properties["TestClassProperty"]; testClassProperty.type = -// ClassProperty::Type::VEC2; testClassProperty.componentType = -// ClassProperty::ComponentType::UINT8; testClassProperty.normalized = true; -// -// PropertyAttribute& propertyAttribute = -// metadata.propertyAttributes.emplace_back(); propertyAttribute.classProperty -// = "TestClass"; -// -// PropertyAttributeProperty& propertyAttributeProperty = -// propertyAttribute.properties["TestClassProperty"]; -// propertyAttributeProperty.index = static_cast(AttributeIndex); -// propertyAttributeProperty.texCoord = 0; -// propertyAttributeProperty.channels = {0, 1}; -// -// PropertyAttributeView view(model, propertyAttribute); -// REQUIRE(view.status() == PropertyAttributeViewStatus::Valid); -// -// const ClassProperty* classProperty = -// view.getClassProperty("TestClassProperty"); -// REQUIRE(classProperty); -// REQUIRE(classProperty->type == ClassProperty::Type::VEC2); -// REQUIRE(classProperty->componentType == -// ClassProperty::ComponentType::UINT8); REQUIRE(classProperty->count == -// std::nullopt); REQUIRE(!classProperty->array); -// REQUIRE(classProperty->normalized); -// -// SECTION("Access correct type") { -// PropertyAttributePropertyView u8vec2Property = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// u8vec2Property.status() == -// PropertyAttributePropertyViewStatus::Valid); -// -// std::vector texCoords{ -// glm::dvec2(0, 0), -// glm::dvec2(0.5, 0), -// glm::dvec2(0, 0.5), -// glm::dvec2(0.5, 0.5)}; -// -// std::vector expected{ -// glm::u8vec2(12, 34), -// glm::u8vec2(10, 3), -// glm::u8vec2(40, 0), -// glm::u8vec2(30, 11)}; -// for (size_t i = 0; i < texCoords.size(); ++i) { -// glm::dvec2 uv = texCoords[i]; -// REQUIRE(u8vec2Property.getRaw(uv[0], uv[1]) == expected[i]); -// REQUIRE(u8vec2Property.get(uv[0], uv[1]) == normalize(expected[i])); -// } -// } -// -// SECTION("Access wrong type") { -// PropertyAttributePropertyView uint8Invalid = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// uint8Invalid.status() == -// PropertyAttributePropertyViewStatus::ErrorTypeMismatch); -// -// PropertyAttributePropertyView u8vec3Invalid = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// u8vec3Invalid.status() == -// PropertyAttributePropertyViewStatus::ErrorTypeMismatch); -// } -// -// SECTION("Access wrong component type") { -// PropertyAttributePropertyView u16vec2Invalid = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// u16vec2Invalid.status() == -// PropertyAttributePropertyViewStatus::ErrorComponentTypeMismatch); -// -// PropertyAttributePropertyView i8vec2Invalid = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// i8vec2Invalid.status() == -// PropertyAttributePropertyViewStatus::ErrorComponentTypeMismatch); -// } -// -// SECTION("Access incorrectly as array") { -// PropertyAttributePropertyView, true> -// arrayInvalid = -// view.getPropertyView, true>( -// "TestClassProperty"); -// REQUIRE( -// arrayInvalid.status() == -// PropertyAttributePropertyViewStatus::ErrorArrayTypeMismatch); -// } -// -// SECTION("Access incorrectly as non-normalized") { -// PropertyAttributePropertyView normalizedInvalid = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// normalizedInvalid.status() == -// PropertyAttributePropertyViewStatus::ErrorNormalizationMismatch); -// } -// -// SECTION("Access incorrectly as dvec2") { -// PropertyAttributePropertyView normalizedInvalid = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// normalizedInvalid.status() == -// PropertyAttributePropertyViewStatus::ErrorComponentTypeMismatch); -// } -// -// SECTION("Channel and type mismatch") { -// model.images[imageIndex].cesium.channels = 4; -// propertyAttributeProperty.channels = {0, 1, 2, 3}; -// PropertyAttributePropertyView u8vec2Property = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// u8vec2Property.status() == -// PropertyAttributePropertyViewStatus::ErrorChannelsAndTypeMismatch); -// } -//} -// -// TEST_CASE("Test array PropertyAttributeProperty") { -// Model model; -// // clang-format off -// std::vector data = { -// 12, 34, 10, -// 40, 0, 30, -// 80, 4, 2, -// 6, 3, 4, -// }; -// // clang-format on -// -// addAttributeToModel( -// model, -// Sampler::WrapS::CLAMP_TO_EDGE, -// Sampler::WrapS::CLAMP_TO_EDGE, -// 2, -// 2, -// 3, -// data); -// size_t AttributeIndex = model.Attributes.size() - 1; -// size_t imageIndex = model.images.size() - 1; -// -// ExtensionModelExtStructuralMetadata& metadata = -// model.addExtension(); -// -// Schema& schema = metadata.schema.emplace(); -// Class& testClass = schema.classes["TestClass"]; -// ClassProperty& testClassProperty = -// testClass.properties["TestClassProperty"]; testClassProperty.type = -// ClassProperty::Type::SCALAR; testClassProperty.componentType = -// ClassProperty::ComponentType::UINT8; testClassProperty.array = true; -// testClassProperty.count = 3; -// -// PropertyAttribute& propertyAttribute = -// metadata.propertyAttributes.emplace_back(); propertyAttribute.classProperty -// = "TestClass"; -// -// PropertyAttributeProperty& propertyAttributeProperty = -// propertyAttribute.properties["TestClassProperty"]; -// propertyAttributeProperty.index = static_cast(AttributeIndex); -// propertyAttributeProperty.texCoord = 0; -// propertyAttributeProperty.channels = {0, 1, 2}; -// -// PropertyAttributeView view(model, propertyAttribute); -// REQUIRE(view.status() == PropertyAttributeViewStatus::Valid); -// -// const ClassProperty* classProperty = -// view.getClassProperty("TestClassProperty"); -// REQUIRE(classProperty); -// REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); -// REQUIRE(classProperty->componentType == -// ClassProperty::ComponentType::UINT8); REQUIRE(classProperty->array); -// REQUIRE(classProperty->count == 3); -// REQUIRE(!classProperty->normalized); -// -// SECTION("Access correct type") { -// PropertyAttributePropertyView> -// uint8ArrayProperty -// = -// view.getPropertyView>("TestClassProperty"); -// REQUIRE( -// uint8ArrayProperty.status() == -// PropertyAttributePropertyViewStatus::Valid); -// -// std::vector texCoords{ -// glm::dvec2(0, 0), -// glm::dvec2(0.5, 0), -// glm::dvec2(0, 0.5), -// glm::dvec2(0.5, 0.5)}; -// -// int64_t size = static_cast(texCoords.size()); -// for (int64_t i = 0; i < size; ++i) { -// glm::dvec2 uv = texCoords[static_cast(i)]; -// -// auto dataStart = data.begin() + i * 3; -// std::vector expected(dataStart, dataStart + 3); -// -// const PropertyArrayView& value = -// uint8ArrayProperty.getRaw(uv[0], uv[1]); -// REQUIRE(static_cast(value.size()) == expected.size()); -// for (int64_t j = 0; j < value.size(); j++) { -// REQUIRE(value[j] == expected[static_cast(j)]); -// } -// -// auto maybeValue = uint8ArrayProperty.get(uv[0], uv[1]); -// REQUIRE(maybeValue); -// for (int64_t j = 0; j < maybeValue->size(); j++) { -// REQUIRE((*maybeValue)[j] == value[j]); -// } -// } -// } -// -// SECTION("Access wrong component type") { -// PropertyAttributePropertyView> int8ArrayInvalid -// = -// view.getPropertyView>("TestClassProperty"); -// REQUIRE( -// int8ArrayInvalid.status() == -// PropertyAttributePropertyViewStatus::ErrorComponentTypeMismatch); -// -// PropertyAttributePropertyView> -// uint16ArrayInvalid = -// view.getPropertyView>( -// "TestClassProperty"); -// REQUIRE( -// uint16ArrayInvalid.status() == -// PropertyAttributePropertyViewStatus::ErrorComponentTypeMismatch); -// } -// -// SECTION("Access incorrectly as non-array") { -// PropertyAttributePropertyView uint8Invalid = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// uint8Invalid.status() == -// PropertyAttributePropertyViewStatus::ErrorArrayTypeMismatch); -// -// PropertyAttributePropertyView u8vec3Invalid = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// u8vec3Invalid.status() == -// PropertyAttributePropertyViewStatus::ErrorArrayTypeMismatch); -// } -// -// SECTION("Access incorrectly as normalized") { -// PropertyAttributePropertyView, true> -// normalizedInvalid = -// view.getPropertyView, true>( -// "TestClassProperty"); -// REQUIRE( -// normalizedInvalid.status() == -// PropertyAttributePropertyViewStatus::ErrorNormalizationMismatch); -// } -// -// SECTION("Channel and type mismatch") { -// model.images[imageIndex].cesium.channels = 4; -// propertyAttributeProperty.channels = {0, 1, 2, 3}; -// PropertyAttributePropertyView> -// uint8ArrayProperty -// = -// view.getPropertyView>("TestClassProperty"); -// REQUIRE( -// uint8ArrayProperty.status() == -// PropertyAttributePropertyViewStatus::ErrorChannelsAndTypeMismatch); -// } -// -// SECTION("Invalid channel values") { -// propertyAttributeProperty.channels = {0, 4, 1}; -// PropertyAttributePropertyView> -// uint8ArrayProperty -// = -// view.getPropertyView>("TestClassProperty"); -// REQUIRE( -// uint8ArrayProperty.status() == -// PropertyAttributePropertyViewStatus::ErrorInvalidChannels); -// } -// -// SECTION("Invalid bytes per channel") { -// model.images[imageIndex].cesium.bytesPerChannel = 2; -// PropertyAttributePropertyView> -// uint8ArrayProperty -// = -// view.getPropertyView>("TestClassProperty"); -// REQUIRE( -// uint8ArrayProperty.status() == -// PropertyAttributePropertyViewStatus::ErrorInvalidBytesPerChannel); -// } -//} -// -// TEST_CASE("Test array PropertyAttributeProperty (normalized)") { -// Model model; -// // clang-format off -// std::vector data = { -// 12, 34, 10, -// 40, 0, 30, -// 80, 4, 2, -// 6, 3, 4, -// }; -// // clang-format on -// -// addAttributeToModel( -// model, -// Sampler::WrapS::CLAMP_TO_EDGE, -// Sampler::WrapS::CLAMP_TO_EDGE, -// 2, -// 2, -// 3, -// data); -// size_t AttributeIndex = model.Attributes.size() - 1; -// size_t imageIndex = model.images.size() - 1; -// -// ExtensionModelExtStructuralMetadata& metadata = -// model.addExtension(); -// -// Schema& schema = metadata.schema.emplace(); -// Class& testClass = schema.classes["TestClass"]; -// ClassProperty& testClassProperty = -// testClass.properties["TestClassProperty"]; testClassProperty.type = -// ClassProperty::Type::SCALAR; testClassProperty.componentType = -// ClassProperty::ComponentType::UINT8; testClassProperty.array = true; -// testClassProperty.count = 3; -// testClassProperty.normalized = true; -// -// PropertyAttribute& propertyAttribute = -// metadata.propertyAttributes.emplace_back(); propertyAttribute.classProperty -// = "TestClass"; -// -// PropertyAttributeProperty& propertyAttributeProperty = -// propertyAttribute.properties["TestClassProperty"]; -// propertyAttributeProperty.index = static_cast(AttributeIndex); -// propertyAttributeProperty.texCoord = 0; -// propertyAttributeProperty.channels = {0, 1, 2}; -// -// PropertyAttributeView view(model, propertyAttribute); -// REQUIRE(view.status() == PropertyAttributeViewStatus::Valid); -// -// const ClassProperty* classProperty = -// view.getClassProperty("TestClassProperty"); -// REQUIRE(classProperty); -// REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); -// REQUIRE(classProperty->componentType == -// ClassProperty::ComponentType::UINT8); REQUIRE(classProperty->array); -// REQUIRE(classProperty->count == 3); -// REQUIRE(classProperty->normalized); -// -// SECTION("Access correct type") { -// PropertyAttributePropertyView, true> -// uint8ArrayProperty = -// view.getPropertyView, true>( -// "TestClassProperty"); -// REQUIRE( -// uint8ArrayProperty.status() == -// PropertyAttributePropertyViewStatus::Valid); -// -// std::vector texCoords{ -// glm::dvec2(0, 0), -// glm::dvec2(0.5, 0), -// glm::dvec2(0, 0.5), -// glm::dvec2(0.5, 0.5)}; -// -// int64_t size = static_cast(texCoords.size()); -// for (int64_t i = 0; i < size; ++i) { -// glm::dvec2 uv = texCoords[static_cast(i)]; -// -// auto dataStart = data.begin() + i * 3; -// std::vector expected(dataStart, dataStart + 3); -// -// const PropertyArrayView& value = -// uint8ArrayProperty.getRaw(uv[0], uv[1]); -// REQUIRE(static_cast(value.size()) == expected.size()); -// for (int64_t j = 0; j < value.size(); j++) { -// REQUIRE(value[j] == expected[static_cast(j)]); -// } -// -// auto maybeValue = uint8ArrayProperty.get(uv[0], uv[1]); -// REQUIRE(maybeValue); -// for (int64_t j = 0; j < maybeValue->size(); j++) { -// REQUIRE((*maybeValue)[j] == normalize(value[j])); -// } -// } -// } -// -// SECTION("Access wrong component type") { -// PropertyAttributePropertyView, true> -// int8ArrayInvalid = -// view.getPropertyView, true>( -// "TestClassProperty"); -// REQUIRE( -// int8ArrayInvalid.status() == -// PropertyAttributePropertyViewStatus::ErrorComponentTypeMismatch); -// -// PropertyAttributePropertyView, true> -// uint16ArrayInvalid = -// view.getPropertyView, true>( -// "TestClassProperty"); -// REQUIRE( -// uint16ArrayInvalid.status() == -// PropertyAttributePropertyViewStatus::ErrorComponentTypeMismatch); -// } -// -// SECTION("Access incorrectly as non-array") { -// PropertyAttributePropertyView uint8Invalid = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// uint8Invalid.status() == -// PropertyAttributePropertyViewStatus::ErrorArrayTypeMismatch); -// -// PropertyAttributePropertyView u8vec3Invalid = -// view.getPropertyView("TestClassProperty"); -// REQUIRE( -// u8vec3Invalid.status() == -// PropertyAttributePropertyViewStatus::ErrorArrayTypeMismatch); -// } -// -// SECTION("Access incorrectly as normalized") { -// PropertyAttributePropertyView> -// normalizedInvalid -// = -// view.getPropertyView>("TestClassProperty"); -// REQUIRE( -// normalizedInvalid.status() == -// PropertyAttributePropertyViewStatus::ErrorNormalizationMismatch); -// } -// -// SECTION("Channel and type mismatch") { -// model.images[imageIndex].cesium.channels = 4; -// propertyAttributeProperty.channels = {0, 1, 2, 3}; -// PropertyAttributePropertyView, true> -// uint8ArrayProperty = -// view.getPropertyView, true>( -// "TestClassProperty"); -// REQUIRE( -// uint8ArrayProperty.status() == -// PropertyAttributePropertyViewStatus::ErrorChannelsAndTypeMismatch); -// } -//} -// -// TEST_CASE("Test with PropertyAttributeProperty offset, scale, min, max") { -// Model model; -// // clang-format off -// std::vector data{ -// 0, 0, 0, 1, -// 9, 0, 1, 0, -// 20, 2, 2, 0, -// 8, 1, 0, 1}; -// // clang-format on -// -// std::vector expectedUint{16777216, 65545, 131604, 16777480}; -// -// const float offset = 1.0f; -// const float scale = 2.0f; -// const float min = -10.0f; -// const float max = 10.0f; -// -// addAttributeToModel( -// model, -// Sampler::WrapS::CLAMP_TO_EDGE, -// Sampler::WrapS::CLAMP_TO_EDGE, -// 2, -// 2, -// 4, -// data); -// size_t AttributeIndex = model.Attributes.size() - 1; -// -// ExtensionModelExtStructuralMetadata& metadata = -// model.addExtension(); -// -// Schema& schema = metadata.schema.emplace(); -// Class& testClass = schema.classes["TestClass"]; -// ClassProperty& testClassProperty = -// testClass.properties["TestClassProperty"]; testClassProperty.type = -// ClassProperty::Type::SCALAR; testClassProperty.componentType = -// ClassProperty::ComponentType::FLOAT32; testClassProperty.offset = offset; -// testClassProperty.scale = scale; -// testClassProperty.min = min; -// testClassProperty.max = max; -// -// PropertyAttribute& propertyAttribute = -// metadata.propertyAttributes.emplace_back(); propertyAttribute.classProperty -// = "TestClass"; -// -// PropertyAttributeProperty& propertyAttributeProperty = -// propertyAttribute.properties["TestClassProperty"]; -// propertyAttributeProperty.index = static_cast(AttributeIndex); -// propertyAttributeProperty.texCoord = 0; -// propertyAttributeProperty.channels = {0, 1, 2, 3}; -// -// PropertyAttributeView view(model, propertyAttribute); -// REQUIRE(view.status() == PropertyAttributeViewStatus::Valid); -// -// const ClassProperty* classProperty = -// view.getClassProperty("TestClassProperty"); -// REQUIRE(classProperty); -// REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); -// REQUIRE( -// classProperty->componentType == ClassProperty::ComponentType::FLOAT32); -// REQUIRE(classProperty->count == std::nullopt); -// REQUIRE(!classProperty->array); -// REQUIRE(!classProperty->normalized); -// REQUIRE(classProperty->offset); -// REQUIRE(classProperty->scale); -// REQUIRE(classProperty->min); -// REQUIRE(classProperty->max); -// -// std::vector texCoords{ -// glm::dvec2(0, 0), -// glm::dvec2(0.5, 0), -// glm::dvec2(0, 0.5), -// glm::dvec2(0.5, 0.5)}; -// -// SECTION("Use class property values") { -// PropertyAttributePropertyView property = -// view.getPropertyView("TestClassProperty"); -// REQUIRE(property.status() == PropertyAttributePropertyViewStatus::Valid); -// REQUIRE(property.offset() == offset); -// REQUIRE(property.scale() == scale); -// REQUIRE(property.min() == min); -// REQUIRE(property.max() == max); -// -// std::vector expectedRaw(expectedUint.size()); -// std::vector expectedTransformed(expectedUint.size()); -// for (size_t i = 0; i < expectedUint.size(); i++) { -// float value = *reinterpret_cast(&expectedUint[i]); -// expectedRaw[i] = value; -// expectedTransformed[i] = value * scale + offset; -// } -// -// for (size_t i = 0; i < texCoords.size(); ++i) { -// glm::dvec2 uv = texCoords[i]; -// REQUIRE(property.getRaw(uv[0], uv[1]) == expectedRaw[i]); -// REQUIRE(property.get(uv[0], uv[1]) == expectedTransformed[i]); -// } -// } -// -// SECTION("Use own property values") { -// const float newOffset = 1.0f; -// const float newScale = -1.0f; -// const float newMin = -3.0f; -// const float newMax = 0.0f; -// propertyAttributeProperty.offset = newOffset; -// propertyAttributeProperty.scale = newScale; -// propertyAttributeProperty.min = newMin; -// propertyAttributeProperty.max = newMax; -// -// PropertyAttributePropertyView property = -// view.getPropertyView("TestClassProperty"); -// REQUIRE(property.status() == PropertyAttributePropertyViewStatus::Valid); -// REQUIRE(property.offset() == newOffset); -// REQUIRE(property.scale() == newScale); -// REQUIRE(property.min() == newMin); -// REQUIRE(property.max() == newMax); -// -// std::vector expectedRaw(expectedUint.size()); -// std::vector expectedTransformed(expectedUint.size()); -// for (size_t i = 0; i < expectedUint.size(); i++) { -// float value = *reinterpret_cast(&expectedUint[i]); -// expectedRaw[i] = value; -// expectedTransformed[i] = value * newScale + newOffset; -// } -// -// for (size_t i = 0; i < texCoords.size(); ++i) { -// glm::dvec2 uv = texCoords[i]; -// REQUIRE(property.getRaw(uv[0], uv[1]) == expectedRaw[i]); -// REQUIRE(property.get(uv[0], uv[1]) == expectedTransformed[i]); -// } -// } -//} -// -// TEST_CASE( -// "Test with PropertyAttributeProperty offset, scale, min, max -// (normalized)") -// { -// Model model; -// std::vector data = {12, 34, 30, 11}; -// -// const double offset = 1.0; -// const double scale = 2.0; -// const double min = 1.0; -// const double max = 3.0; -// -// addAttributeToModel( -// model, -// Sampler::WrapS::CLAMP_TO_EDGE, -// Sampler::WrapS::CLAMP_TO_EDGE, -// 2, -// 2, -// 1, -// data); -// size_t AttributeIndex = model.Attributes.size() - 1; -// -// ExtensionModelExtStructuralMetadata& metadata = -// model.addExtension(); -// -// Schema& schema = metadata.schema.emplace(); -// Class& testClass = schema.classes["TestClass"]; -// ClassProperty& testClassProperty = -// testClass.properties["TestClassProperty"]; testClassProperty.type = -// ClassProperty::Type::SCALAR; testClassProperty.componentType = -// ClassProperty::ComponentType::UINT8; testClassProperty.normalized = true; -// testClassProperty.offset = offset; -// testClassProperty.scale = scale; -// testClassProperty.min = min; -// testClassProperty.max = max; -// -// PropertyAttribute& propertyAttribute = -// metadata.propertyAttributes.emplace_back(); propertyAttribute.classProperty -// = "TestClass"; -// -// PropertyAttributeProperty& propertyAttributeProperty = -// propertyAttribute.properties["TestClassProperty"]; -// propertyAttributeProperty.index = static_cast(AttributeIndex); -// propertyAttributeProperty.texCoord = 0; -// propertyAttributeProperty.channels = {0}; -// -// PropertyAttributeView view(model, propertyAttribute); -// REQUIRE(view.status() == PropertyAttributeViewStatus::Valid); -// -// const ClassProperty* classProperty = -// view.getClassProperty("TestClassProperty"); -// REQUIRE(classProperty); -// REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); -// REQUIRE(classProperty->componentType == -// ClassProperty::ComponentType::UINT8); REQUIRE(classProperty->count == -// std::nullopt); REQUIRE(!classProperty->array); -// REQUIRE(classProperty->normalized); -// -// std::vector texCoords{ -// glm::dvec2(0, 0), -// glm::dvec2(0.5, 0), -// glm::dvec2(0, 0.5), -// glm::dvec2(0.5, 0.5)}; -// -// SECTION("Use class property values") { -// PropertyAttributePropertyView property = -// view.getPropertyView("TestClassProperty"); -// REQUIRE(property.status() == PropertyAttributePropertyViewStatus::Valid); -// REQUIRE(property.offset() == offset); -// REQUIRE(property.scale() == scale); -// REQUIRE(property.min() == min); -// REQUIRE(property.max() == max); -// -// for (size_t i = 0; i < texCoords.size(); ++i) { -// glm::dvec2 uv = texCoords[i]; -// REQUIRE(property.getRaw(uv[0], uv[1]) == data[i]); -// REQUIRE( -// property.get(uv[0], uv[1]) == normalize(data[i]) * scale + offset); -// } -// } -// -// SECTION("Use own property values") { -// const double newOffset = 2.0; -// const double newScale = 5.0; -// const double newMin = 10.0; -// const double newMax = 11.0; -// propertyAttributeProperty.offset = newOffset; -// propertyAttributeProperty.scale = newScale; -// propertyAttributeProperty.min = newMin; -// propertyAttributeProperty.max = newMax; -// -// PropertyAttributePropertyView property = -// view.getPropertyView("TestClassProperty"); -// REQUIRE(property.status() == PropertyAttributePropertyViewStatus::Valid); -// REQUIRE(property.offset() == newOffset); -// REQUIRE(property.scale() == newScale); -// REQUIRE(property.min() == newMin); -// REQUIRE(property.max() == newMax); -// -// for (size_t i = 0; i < texCoords.size(); ++i) { -// glm::dvec2 uv = texCoords[i]; -// REQUIRE(property.getRaw(uv[0], uv[1]) == data[i]); -// REQUIRE( -// property.get(uv[0], uv[1]) == -// normalize(data[i]) * newScale + newOffset); -// } -// } -//} -// -// TEST_CASE("Test with PropertyAttributeProperty noData") { -// Model model; -// std::vector data = {12, 34, 30, 11}; -// const uint8_t noData = 34; -// -// addAttributeToModel( -// model, -// Sampler::WrapS::CLAMP_TO_EDGE, -// Sampler::WrapS::CLAMP_TO_EDGE, -// 2, -// 2, -// 1, -// data); -// size_t AttributeIndex = model.Attributes.size() - 1; -// -// ExtensionModelExtStructuralMetadata& metadata = -// model.addExtension(); -// -// Schema& schema = metadata.schema.emplace(); -// Class& testClass = schema.classes["TestClass"]; -// ClassProperty& testClassProperty = -// testClass.properties["TestClassProperty"]; testClassProperty.type = -// ClassProperty::Type::SCALAR; testClassProperty.componentType = -// ClassProperty::ComponentType::UINT8; testClassProperty.noData = noData; -// -// PropertyAttribute& propertyAttribute = -// metadata.propertyAttributes.emplace_back(); propertyAttribute.classProperty -// = "TestClass"; -// -// PropertyAttributeProperty& propertyAttributeProperty = -// propertyAttribute.properties["TestClassProperty"]; -// propertyAttributeProperty.index = static_cast(AttributeIndex); -// propertyAttributeProperty.texCoord = 0; -// propertyAttributeProperty.channels = {0}; -// -// PropertyAttributeView view(model, propertyAttribute); -// REQUIRE(view.status() == PropertyAttributeViewStatus::Valid); -// -// const ClassProperty* classProperty = -// view.getClassProperty("TestClassProperty"); -// REQUIRE(classProperty); -// REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); -// REQUIRE(classProperty->componentType == -// ClassProperty::ComponentType::UINT8); REQUIRE(classProperty->count == -// std::nullopt); REQUIRE(!classProperty->array); -// REQUIRE(!classProperty->normalized); -// -// std::vector texCoords{ -// glm::dvec2(0, 0), -// glm::dvec2(0.5, 0), -// glm::dvec2(0, 0.5), -// glm::dvec2(0.5, 0.5)}; -// -// SECTION("Without default value") { -// PropertyAttributePropertyView property = -// view.getPropertyView("TestClassProperty"); -// REQUIRE(property.status() == PropertyAttributePropertyViewStatus::Valid); -// -// for (size_t i = 0; i < texCoords.size(); ++i) { -// glm::dvec2 uv = texCoords[i]; -// auto value = property.getRaw(uv[0], uv[1]); -// REQUIRE(value == data[i]); -// -// auto maybeValue = property.get(uv[0], uv[1]); -// if (value == noData) { -// REQUIRE(!maybeValue); -// } else { -// REQUIRE(maybeValue == data[i]); -// } -// } -// } -// -// SECTION("With default value") { -// const uint8_t defaultValue = 255; -// testClassProperty.defaultProperty = defaultValue; -// -// PropertyAttributePropertyView property = -// view.getPropertyView("TestClassProperty"); -// REQUIRE(property.status() == PropertyAttributePropertyViewStatus::Valid); -// -// for (size_t i = 0; i < texCoords.size(); ++i) { -// glm::dvec2 uv = texCoords[i]; -// auto value = property.getRaw(uv[0], uv[1]); -// REQUIRE(value == data[i]); -// -// auto maybeValue = property.get(uv[0], uv[1]); -// if (value == noData) { -// REQUIRE(maybeValue == defaultValue); -// } else { -// REQUIRE(maybeValue == data[i]); -// } -// } -// } -//} -// -// TEST_CASE("Test with PropertyAttributeProperty noData (normalized)") { -// Model model; -// std::vector data = {12, 34, 30, 11}; -// const uint8_t noData = 34; -// -// addAttributeToModel( -// model, -// Sampler::WrapS::CLAMP_TO_EDGE, -// Sampler::WrapS::CLAMP_TO_EDGE, -// 2, -// 2, -// 1, -// data); -// size_t AttributeIndex = model.Attributes.size() - 1; -// -// ExtensionModelExtStructuralMetadata& metadata = -// model.addExtension(); -// -// Schema& schema = metadata.schema.emplace(); -// Class& testClass = schema.classes["TestClass"]; -// ClassProperty& testClassProperty = -// testClass.properties["TestClassProperty"]; testClassProperty.type = -// ClassProperty::Type::SCALAR; testClassProperty.componentType = -// ClassProperty::ComponentType::UINT8; testClassProperty.normalized = true; -// testClassProperty.noData = noData; -// -// PropertyAttribute& propertyAttribute = -// metadata.propertyAttributes.emplace_back(); propertyAttribute.classProperty -// = "TestClass"; -// -// PropertyAttributeProperty& propertyAttributeProperty = -// propertyAttribute.properties["TestClassProperty"]; -// propertyAttributeProperty.index = static_cast(AttributeIndex); -// propertyAttributeProperty.texCoord = 0; -// propertyAttributeProperty.channels = {0}; -// -// PropertyAttributeView view(model, propertyAttribute); -// REQUIRE(view.status() == PropertyAttributeViewStatus::Valid); -// -// const ClassProperty* classProperty = -// view.getClassProperty("TestClassProperty"); -// REQUIRE(classProperty); -// REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); -// REQUIRE(classProperty->componentType == -// ClassProperty::ComponentType::UINT8); REQUIRE(classProperty->count == -// std::nullopt); REQUIRE(!classProperty->array); -// REQUIRE(classProperty->normalized); -// -// std::vector texCoords{ -// glm::dvec2(0, 0), -// glm::dvec2(0.5, 0), -// glm::dvec2(0, 0.5), -// glm::dvec2(0.5, 0.5)}; -// -// SECTION("Without default value") { -// PropertyAttributePropertyView property = -// view.getPropertyView("TestClassProperty"); -// REQUIRE(property.status() == PropertyAttributePropertyViewStatus::Valid); -// -// for (size_t i = 0; i < texCoords.size(); ++i) { -// glm::dvec2 uv = texCoords[i]; -// auto value = property.getRaw(uv[0], uv[1]); -// REQUIRE(value == data[i]); -// -// auto maybeValue = property.get(uv[0], uv[1]); -// if (value == noData) { -// REQUIRE(!maybeValue); -// } else { -// REQUIRE(maybeValue == normalize(data[i])); -// } -// } -// } -// -// SECTION("With default value") { -// const double defaultValue = -1.0; -// testClassProperty.defaultProperty = defaultValue; -// -// PropertyAttributePropertyView property = -// view.getPropertyView("TestClassProperty"); -// REQUIRE(property.status() == PropertyAttributePropertyViewStatus::Valid); -// -// for (size_t i = 0; i < texCoords.size(); ++i) { -// glm::dvec2 uv = texCoords[i]; -// auto value = property.getRaw(uv[0], uv[1]); -// REQUIRE(value == data[i]); -// -// auto maybeValue = property.get(uv[0], uv[1]); -// if (value == noData) { -// REQUIRE(maybeValue == defaultValue); -// } else { -// REQUIRE(maybeValue == normalize(data[i])); -// } -// } -// } -//} -// -// TEST_CASE("Test callback on invalid property Attribute view") { -// Model model; -// ExtensionModelExtStructuralMetadata& metadata = -// model.addExtension(); -// metadata.schema.emplace(); -// -// // Property Attribute has a nonexistent class. -// PropertyAttribute& propertyAttribute = -// metadata.propertyAttributes.emplace_back(); propertyAttribute.classProperty -// = "TestClass"; -// -// PropertyAttributeProperty& propertyAttributeProperty = -// propertyAttribute.properties["TestClassProperty"]; -// propertyAttributeProperty.index = -1; -// -// PropertyAttributeView view(model, propertyAttribute); -// REQUIRE(view.status() == PropertyAttributeViewStatus::ErrorClassNotFound); -// -// const ClassProperty* classProperty = -// view.getClassProperty("TestClassProperty"); -// REQUIRE(!classProperty); -// -// uint32_t invokedCallbackCount = 0; -// view.getPropertyView( -// "TestClassProperty", -// [&invokedCallbackCount]( -// const std::string& /*propertyName*/, -// auto propertyValue) mutable { -// invokedCallbackCount++; -// REQUIRE( -// propertyValue.status() == -// PropertyAttributePropertyViewStatus::ErrorInvalidPropertyAttribute); -// }); -// -// REQUIRE(invokedCallbackCount == 1); -//} -// -// TEST_CASE("Test callback on invalid PropertyAttributeProperty") { -// Model model; -// ExtensionModelExtStructuralMetadata& metadata = -// model.addExtension(); -// -// Schema& schema = metadata.schema.emplace(); -// Class& testClass = schema.classes["TestClass"]; -// ClassProperty& testClassProperty = testClass.properties["InvalidProperty"]; -// testClassProperty.type = ClassProperty::Type::SCALAR; -// testClassProperty.componentType = ClassProperty::ComponentType::UINT8; -// -// PropertyAttribute& propertyAttribute = -// metadata.propertyAttributes.emplace_back(); propertyAttribute.classProperty -// = "TestClass"; -// -// PropertyAttributeProperty& propertyAttributeProperty = -// propertyAttribute.properties["InvalidProperty"]; -// propertyAttributeProperty.index = -1; -// -// PropertyAttributeView view(model, propertyAttribute); -// REQUIRE(view.status() == PropertyAttributeViewStatus::Valid); -// -// const ClassProperty* classProperty = -// view.getClassProperty("InvalidProperty"); REQUIRE(classProperty); -// -// classProperty = view.getClassProperty("NonexistentProperty"); -// REQUIRE(!classProperty); -// -// uint32_t invokedCallbackCount = 0; -// auto testCallback = [&invokedCallbackCount]( -// const std::string& /*propertyName*/, -// auto propertyValue) mutable { -// invokedCallbackCount++; -// REQUIRE(propertyValue.status() != -// PropertyAttributePropertyViewStatus::Valid); -// }; -// -// view.getPropertyView("InvalidProperty", testCallback); -// view.getPropertyView("NonexistentProperty", testCallback); -// -// REQUIRE(invokedCallbackCount == 2); -//} -// -// TEST_CASE("Test callback on invalid normalized PropertyAttributeProperty") { -// Model model; -// ExtensionModelExtStructuralMetadata& metadata = -// model.addExtension(); -// -// Schema& schema = metadata.schema.emplace(); -// Class& testClass = schema.classes["TestClass"]; -// ClassProperty& testClassProperty = -// testClass.properties["TestClassProperty"]; testClassProperty.type = -// ClassProperty::Type::SCALAR; testClassProperty.componentType = -// ClassProperty::ComponentType::FLOAT32; testClassProperty.normalized = true; -// // This is erroneous. -// -// PropertyAttribute& propertyAttribute = -// metadata.propertyAttributes.emplace_back(); propertyAttribute.classProperty -// = "TestClass"; -// -// PropertyAttributeProperty& propertyAttributeProperty = -// propertyAttribute.properties["TestClassProperty"]; -// propertyAttributeProperty.index = static_cast(0); -// propertyAttributeProperty.texCoord = 0; -// propertyAttributeProperty.channels = {0, 1}; -// -// PropertyAttributeView view(model, propertyAttribute); -// REQUIRE(view.status() == PropertyAttributeViewStatus::Valid); -// -// const ClassProperty* classProperty = -// view.getClassProperty("TestClassProperty"); -// REQUIRE(classProperty); -// -// uint32_t invokedCallbackCount = 0; -// auto testCallback = [&invokedCallbackCount]( -// const std::string& /*propertyName*/, -// auto propertyValue) mutable { -// invokedCallbackCount++; -// REQUIRE( -// propertyValue.status() == -// PropertyAttributePropertyViewStatus::ErrorInvalidNormalization); -// }; -// -// view.getPropertyView("TestClassProperty", testCallback); -// -// REQUIRE(invokedCallbackCount == 1); -//} -// -// TEST_CASE("Test callback for scalar PropertyAttributeProperty") { -// Model model; -// std::vector data = {255, 255, 12, 1, 30, 2, 0, 255}; -// -// addAttributeToModel( -// model, -// Sampler::WrapS::CLAMP_TO_EDGE, -// Sampler::WrapS::CLAMP_TO_EDGE, -// 2, -// 2, -// 2, -// data); -// size_t AttributeIndex = model.Attributes.size() - 1; -// -// ExtensionModelExtStructuralMetadata& metadata = -// model.addExtension(); -// -// Schema& schema = metadata.schema.emplace(); -// Class& testClass = schema.classes["TestClass"]; -// ClassProperty& testClassProperty = -// testClass.properties["TestClassProperty"]; testClassProperty.type = -// ClassProperty::Type::SCALAR; testClassProperty.componentType = -// ClassProperty::ComponentType::INT16; -// -// PropertyAttribute& propertyAttribute = -// metadata.propertyAttributes.emplace_back(); propertyAttribute.classProperty -// = "TestClass"; -// -// PropertyAttributeProperty& propertyAttributeProperty = -// propertyAttribute.properties["TestClassProperty"]; -// propertyAttributeProperty.index = static_cast(AttributeIndex); -// propertyAttributeProperty.texCoord = 0; -// propertyAttributeProperty.channels = {0, 1}; -// -// PropertyAttributeView view(model, propertyAttribute); -// REQUIRE(view.status() == PropertyAttributeViewStatus::Valid); -// -// const ClassProperty* classProperty = -// view.getClassProperty("TestClassProperty"); -// REQUIRE(classProperty); -// REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); -// REQUIRE(classProperty->componentType == -// ClassProperty::ComponentType::INT16); REQUIRE(classProperty->count == -// std::nullopt); REQUIRE(!classProperty->array); -// REQUIRE(!classProperty->normalized); -// -// std::vector texCoords{ -// glm::dvec2(0, 0), -// glm::dvec2(0.5, 0), -// glm::dvec2(0, 0.5), -// glm::dvec2(0.5, 0.5)}; -// -// std::vector expected{-1, 268, 542, -256}; -// uint32_t invokedCallbackCount = 0; -// view.getPropertyView( -// "TestClassProperty", -// [&expected, &texCoords, &invokedCallbackCount]( -// const std::string& /*propertyName*/, -// auto propertyValue) mutable { -// invokedCallbackCount++; -// if constexpr (std::is_same_v< -// PropertyAttributePropertyView, -// decltype(propertyValue)>) { -// REQUIRE( -// propertyValue.status() == -// PropertyAttributePropertyViewStatus::Valid); -// -// for (size_t i = 0; i < expected.size(); ++i) { -// glm::dvec2& uv = texCoords[i]; -// REQUIRE(propertyValue.getRaw(uv[0], uv[1]) == expected[i]); -// REQUIRE(propertyValue.get(uv[0], uv[1]) == expected[i]); -// } -// } else { -// FAIL("getPropertyView returned PropertyAttributePropertyView of " -// "incorrect type for TestClassProperty."); -// } -// }); -// -// REQUIRE(invokedCallbackCount == 1); -//} -// -// TEST_CASE("Test callback for scalar PropertyAttributeProperty (normalized)") -// { -// Model model; -// std::vector data = {255, 255, 12, 1, 30, 2, 0, 255}; -// -// addAttributeToModel( -// model, -// Sampler::WrapS::CLAMP_TO_EDGE, -// Sampler::WrapS::CLAMP_TO_EDGE, -// 2, -// 2, -// 2, -// data); -// size_t AttributeIndex = model.Attributes.size() - 1; -// -// ExtensionModelExtStructuralMetadata& metadata = -// model.addExtension(); -// -// Schema& schema = metadata.schema.emplace(); -// Class& testClass = schema.classes["TestClass"]; -// ClassProperty& testClassProperty = -// testClass.properties["TestClassProperty"]; testClassProperty.type = -// ClassProperty::Type::SCALAR; testClassProperty.componentType = -// ClassProperty::ComponentType::INT16; testClassProperty.normalized = true; -// -// PropertyAttribute& propertyAttribute = -// metadata.propertyAttributes.emplace_back(); propertyAttribute.classProperty -// = "TestClass"; -// -// PropertyAttributeProperty& propertyAttributeProperty = -// propertyAttribute.properties["TestClassProperty"]; -// propertyAttributeProperty.index = static_cast(AttributeIndex); -// propertyAttributeProperty.texCoord = 0; -// propertyAttributeProperty.channels = {0, 1}; -// -// PropertyAttributeView view(model, propertyAttribute); -// REQUIRE(view.status() == PropertyAttributeViewStatus::Valid); -// -// const ClassProperty* classProperty = -// view.getClassProperty("TestClassProperty"); -// REQUIRE(classProperty); -// REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); -// REQUIRE(classProperty->componentType == -// ClassProperty::ComponentType::INT16); REQUIRE(classProperty->count == -// std::nullopt); REQUIRE(!classProperty->array); -// REQUIRE(classProperty->normalized); -// -// std::vector texCoords{ -// glm::dvec2(0, 0), -// glm::dvec2(0.5, 0), -// glm::dvec2(0, 0.5), -// glm::dvec2(0.5, 0.5)}; -// -// std::vector expected{-1, 268, 542, -256}; -// uint32_t invokedCallbackCount = 0; -// view.getPropertyView( -// "TestClassProperty", -// [&expected, &texCoords, &invokedCallbackCount]( -// const std::string& /*propertyName*/, -// auto propertyValue) mutable { -// invokedCallbackCount++; -// if constexpr (std::is_same_v< -// PropertyAttributePropertyView, -// decltype(propertyValue)>) { -// REQUIRE( -// propertyValue.status() == -// PropertyAttributePropertyViewStatus::Valid); -// -// for (size_t i = 0; i < expected.size(); ++i) { -// glm::dvec2& uv = texCoords[i]; -// REQUIRE(propertyValue.getRaw(uv[0], uv[1]) == expected[i]); -// REQUIRE(propertyValue.get(uv[0], uv[1]) == -// normalize(expected[i])); -// } -// } else { -// FAIL("getPropertyView returned PropertyAttributePropertyView of " -// "incorrect type for TestClassProperty."); -// } -// }); -// -// REQUIRE(invokedCallbackCount == 1); -//} -// -// TEST_CASE("Test callback for vecN PropertyAttributeProperty") { -// Model model; -// // clang-format off -// std::vector data = { -// 255, 255, -// 12, 1, -// 30, 2, -// 0, 255}; -// // clang-format on -// -// addAttributeToModel( -// model, -// Sampler::WrapS::CLAMP_TO_EDGE, -// Sampler::WrapS::CLAMP_TO_EDGE, -// 2, -// 2, -// 2, -// data); -// size_t AttributeIndex = model.Attributes.size() - 1; -// -// ExtensionModelExtStructuralMetadata& metadata = -// model.addExtension(); -// -// Schema& schema = metadata.schema.emplace(); -// Class& testClass = schema.classes["TestClass"]; -// ClassProperty& testClassProperty = -// testClass.properties["TestClassProperty"]; testClassProperty.type = -// ClassProperty::Type::VEC2; testClassProperty.componentType = -// ClassProperty::ComponentType::INT8; -// -// PropertyAttribute& propertyAttribute = -// metadata.propertyAttributes.emplace_back(); propertyAttribute.classProperty -// = "TestClass"; -// -// PropertyAttributeProperty& propertyAttributeProperty = -// propertyAttribute.properties["TestClassProperty"]; -// propertyAttributeProperty.index = static_cast(AttributeIndex); -// propertyAttributeProperty.texCoord = 0; -// propertyAttributeProperty.channels = {0, 1}; -// -// PropertyAttributeView view(model, propertyAttribute); -// REQUIRE(view.status() == PropertyAttributeViewStatus::Valid); -// -// const ClassProperty* classProperty = -// view.getClassProperty("TestClassProperty"); -// REQUIRE(classProperty); -// REQUIRE(classProperty->type == ClassProperty::Type::VEC2); -// REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT8); -// REQUIRE(classProperty->count == std::nullopt); -// REQUIRE(!classProperty->array); -// REQUIRE(!classProperty->normalized); -// -// std::vector expected{ -// glm::i8vec2(-1, -1), -// glm::i8vec2(12, 1), -// glm::i8vec2(30, 2), -// glm::i8vec2(0, -1)}; -// -// std::vector texCoords{ -// glm::dvec2(0, 0), -// glm::dvec2(0.5, 0), -// glm::dvec2(0, 0.5), -// glm::dvec2(0.5, 0.5)}; -// -// uint32_t invokedCallbackCount = 0; -// view.getPropertyView( -// "TestClassProperty", -// [&expected, &texCoords, &invokedCallbackCount]( -// const std::string& /*propertyName*/, -// auto propertyValue) mutable { -// invokedCallbackCount++; -// if constexpr (std::is_same_v< -// PropertyAttributePropertyView, -// decltype(propertyValue)>) { -// REQUIRE( -// propertyValue.status() == -// PropertyAttributePropertyViewStatus::Valid); -// -// for (size_t i = 0; i < expected.size(); ++i) { -// glm::dvec2& uv = texCoords[i]; -// REQUIRE(propertyValue.getRaw(uv[0], uv[1]) == expected[i]); -// REQUIRE(propertyValue.get(uv[0], uv[1]) == expected[i]); -// } -// } else { -// FAIL("getPropertyView returned PropertyAttributePropertyView of " -// "incorrect type for TestClassProperty."); -// } -// }); -// -// REQUIRE(invokedCallbackCount == 1); -//} -// -// TEST_CASE("Test callback for vecN PropertyAttributeProperty (normalized)") { -// Model model; -// // clang-format off -// std::vector data = { -// 255, 255, -// 12, 1, -// 30, 2, -// 0, 255}; -// // clang-format on -// -// addAttributeToModel( -// model, -// Sampler::WrapS::CLAMP_TO_EDGE, -// Sampler::WrapS::CLAMP_TO_EDGE, -// 2, -// 2, -// 2, -// data); -// size_t AttributeIndex = model.Attributes.size() - 1; -// -// ExtensionModelExtStructuralMetadata& metadata = -// model.addExtension(); -// -// Schema& schema = metadata.schema.emplace(); -// Class& testClass = schema.classes["TestClass"]; -// ClassProperty& testClassProperty = -// testClass.properties["TestClassProperty"]; testClassProperty.type = -// ClassProperty::Type::VEC2; testClassProperty.componentType = -// ClassProperty::ComponentType::INT8; testClassProperty.normalized = true; -// -// PropertyAttribute& propertyAttribute = -// metadata.propertyAttributes.emplace_back(); propertyAttribute.classProperty -// = "TestClass"; -// -// PropertyAttributeProperty& propertyAttributeProperty = -// propertyAttribute.properties["TestClassProperty"]; -// propertyAttributeProperty.index = static_cast(AttributeIndex); -// propertyAttributeProperty.texCoord = 0; -// propertyAttributeProperty.channels = {0, 1}; -// -// PropertyAttributeView view(model, propertyAttribute); -// REQUIRE(view.status() == PropertyAttributeViewStatus::Valid); -// -// const ClassProperty* classProperty = -// view.getClassProperty("TestClassProperty"); -// REQUIRE(classProperty); -// REQUIRE(classProperty->type == ClassProperty::Type::VEC2); -// REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT8); -// REQUIRE(classProperty->count == std::nullopt); -// REQUIRE(!classProperty->array); -// REQUIRE(classProperty->normalized); -// -// std::vector expected{ -// glm::i8vec2(-1, -1), -// glm::i8vec2(12, 1), -// glm::i8vec2(30, 2), -// glm::i8vec2(0, -1)}; -// -// std::vector texCoords{ -// glm::dvec2(0, 0), -// glm::dvec2(0.5, 0), -// glm::dvec2(0, 0.5), -// glm::dvec2(0.5, 0.5)}; -// -// uint32_t invokedCallbackCount = 0; -// view.getPropertyView( -// "TestClassProperty", -// [&expected, &texCoords, &invokedCallbackCount]( -// const std::string& /*propertyName*/, -// auto propertyValue) mutable { -// invokedCallbackCount++; -// if constexpr (std::is_same_v< -// PropertyAttributePropertyView, -// decltype(propertyValue)>) { -// REQUIRE( -// propertyValue.status() == -// PropertyAttributePropertyViewStatus::Valid); -// -// for (size_t i = 0; i < expected.size(); ++i) { -// glm::dvec2& uv = texCoords[i]; -// REQUIRE(propertyValue.getRaw(uv[0], uv[1]) == expected[i]); -// REQUIRE(propertyValue.get(uv[0], uv[1]) == -// normalize(expected[i])); -// } -// } else { -// FAIL("getPropertyView returned PropertyAttributePropertyView of " -// "incorrect type for TestClassProperty."); -// } -// }); -// -// REQUIRE(invokedCallbackCount == 1); -//} -// -// TEST_CASE("Test callback for array PropertyAttributeProperty") { -// Model model; -// // clang-format off -// std::vector data = { -// 254, 0, 253, 1, -// 10, 2, 40, 3, -// 30, 0, 0, 2, -// 10, 2, 255, 4}; -// // clang-format on -// -// addAttributeToModel( -// model, -// Sampler::WrapS::CLAMP_TO_EDGE, -// Sampler::WrapS::CLAMP_TO_EDGE, -// 2, -// 2, -// 4, -// data); -// size_t AttributeIndex = model.Attributes.size() - 1; -// -// ExtensionModelExtStructuralMetadata& metadata = -// model.addExtension(); -// -// Schema& schema = metadata.schema.emplace(); -// Class& testClass = schema.classes["TestClass"]; -// ClassProperty& testClassProperty = -// testClass.properties["TestClassProperty"]; testClassProperty.type = -// ClassProperty::Type::SCALAR; testClassProperty.componentType = -// ClassProperty::ComponentType::UINT16; testClassProperty.array = true; -// testClassProperty.count = 2; -// -// PropertyAttribute& propertyAttribute = -// metadata.propertyAttributes.emplace_back(); propertyAttribute.classProperty -// = "TestClass"; -// -// PropertyAttributeProperty& propertyAttributeProperty = -// propertyAttribute.properties["TestClassProperty"]; -// propertyAttributeProperty.index = static_cast(AttributeIndex); -// propertyAttributeProperty.texCoord = 0; -// propertyAttributeProperty.channels = {0, 1, 2, 3}; -// -// PropertyAttributeView view(model, propertyAttribute); -// REQUIRE(view.status() == PropertyAttributeViewStatus::Valid); -// -// const ClassProperty* classProperty = -// view.getClassProperty("TestClassProperty"); -// REQUIRE(classProperty); -// REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); -// REQUIRE(classProperty->componentType == -// ClassProperty::ComponentType::UINT16); REQUIRE(classProperty->array); -// REQUIRE(classProperty->count == 2); -// REQUIRE(!classProperty->normalized); -// -// std::vector> expected{ -// {254, 509}, -// {522, 808}, -// {30, 512}, -// {522, 1279}}; -// -// std::vector texCoords{ -// glm::dvec2(0, 0), -// glm::dvec2(0.5, 0), -// glm::dvec2(0, 0.5), -// glm::dvec2(0.5, 0.5)}; -// -// uint32_t invokedCallbackCount = 0; -// view.getPropertyView( -// "TestClassProperty", -// [&expected, &texCoords, &invokedCallbackCount]( -// const std::string& /*propertyName*/, -// auto propertyValue) mutable { -// invokedCallbackCount++; -// if constexpr (std::is_same_v< -// PropertyAttributePropertyView< -// PropertyArrayView>, -// decltype(propertyValue)>) { -// REQUIRE( -// propertyValue.status() == -// PropertyAttributePropertyViewStatus::Valid); -// -// for (size_t i = 0; i < expected.size(); ++i) { -// std::vector& expectedArray = expected[i]; -// glm::dvec2& uv = texCoords[i]; -// PropertyArrayView array = -// propertyValue.getRaw(uv[0], uv[1]); -// -// REQUIRE(static_cast(array.size()) == -// expectedArray.size()); for (int64_t j = 0; j < array.size(); j++) -// { -// REQUIRE(array[j] == expectedArray[static_cast(j)]); -// } -// -// auto maybeArray = propertyValue.get(uv[0], uv[1]); -// REQUIRE(maybeArray); -// REQUIRE( -// static_cast(maybeArray->size()) == -// expectedArray.size()); -// for (int64_t j = 0; j < array.size(); j++) { -// REQUIRE( -// (*maybeArray)[j] == expectedArray[static_cast(j)]); -// } -// } -// } else { -// FAIL("getPropertyView returned PropertyAttributePropertyView of " -// "incorrect type for TestClassProperty."); -// } -// }); -// -// REQUIRE(invokedCallbackCount == 1); -//} -// -// TEST_CASE("Test callback for array PropertyAttributeProperty (normalized)") { -// Model model; -// // clang-format off -// std::vector data = { -// 254, 0, 253, 1, -// 10, 2, 40, 3, -// 30, 0, 0, 2, -// 10, 2, 255, 4}; -// // clang-format on -// -// addAttributeToModel( -// model, -// Sampler::WrapS::CLAMP_TO_EDGE, -// Sampler::WrapS::CLAMP_TO_EDGE, -// 2, -// 2, -// 4, -// data); -// size_t AttributeIndex = model.Attributes.size() - 1; -// -// ExtensionModelExtStructuralMetadata& metadata = -// model.addExtension(); -// -// Schema& schema = metadata.schema.emplace(); -// Class& testClass = schema.classes["TestClass"]; -// ClassProperty& testClassProperty = -// testClass.properties["TestClassProperty"]; testClassProperty.type = -// ClassProperty::Type::SCALAR; testClassProperty.componentType = -// ClassProperty::ComponentType::UINT16; testClassProperty.array = true; -// testClassProperty.count = 2; -// testClassProperty.normalized = true; -// -// PropertyAttribute& propertyAttribute = -// metadata.propertyAttributes.emplace_back(); propertyAttribute.classProperty -// = "TestClass"; -// -// PropertyAttributeProperty& propertyAttributeProperty = -// propertyAttribute.properties["TestClassProperty"]; -// propertyAttributeProperty.index = static_cast(AttributeIndex); -// propertyAttributeProperty.texCoord = 0; -// propertyAttributeProperty.channels = {0, 1, 2, 3}; -// -// PropertyAttributeView view(model, propertyAttribute); -// REQUIRE(view.status() == PropertyAttributeViewStatus::Valid); -// -// const ClassProperty* classProperty = -// view.getClassProperty("TestClassProperty"); -// REQUIRE(classProperty); -// REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); -// REQUIRE(classProperty->componentType == -// ClassProperty::ComponentType::UINT16); REQUIRE(classProperty->array); -// REQUIRE(classProperty->count == 2); -// REQUIRE(classProperty->normalized); -// -// std::vector> expected{ -// {254, 509}, -// {522, 808}, -// {30, 512}, -// {522, 1279}}; -// -// std::vector texCoords{ -// glm::dvec2(0, 0), -// glm::dvec2(0.5, 0), -// glm::dvec2(0, 0.5), -// glm::dvec2(0.5, 0.5)}; -// -// uint32_t invokedCallbackCount = 0; -// view.getPropertyView( -// "TestClassProperty", -// [&expected, &texCoords, &invokedCallbackCount]( -// const std::string& /*propertyName*/, -// auto propertyValue) mutable { -// invokedCallbackCount++; -// if constexpr ( -// std::is_same_v< -// PropertyAttributePropertyView, -// true>, decltype(propertyValue)>) { -// REQUIRE( -// propertyValue.status() == -// PropertyAttributePropertyViewStatus::Valid); -// -// for (size_t i = 0; i < expected.size(); ++i) { -// std::vector& expectedArray = expected[i]; -// glm::dvec2& uv = texCoords[i]; -// PropertyArrayView array = -// propertyValue.getRaw(uv[0], uv[1]); -// -// REQUIRE(static_cast(array.size()) == -// expectedArray.size()); for (int64_t j = 0; j < array.size(); j++) -// { -// REQUIRE(array[j] == expectedArray[static_cast(j)]); -// } -// -// auto maybeArray = propertyValue.get(uv[0], uv[1]); -// REQUIRE(maybeArray); -// REQUIRE( -// static_cast(maybeArray->size()) == -// expectedArray.size()); -// for (int64_t j = 0; j < array.size(); j++) { -// auto rawValue = expectedArray[static_cast(j)]; -// REQUIRE((*maybeArray)[j] == normalize(rawValue)); -// } -// } -// } else { -// FAIL("getPropertyView returned PropertyAttributePropertyView of " -// "incorrect type for TestClassProperty."); -// } -// }); -// -// REQUIRE(invokedCallbackCount == 1); -//} -// -// TEST_CASE("Test callback on unsupported PropertyAttributeProperty") { -// Model model; -// // clang-format off -// std::vector data = { -// 254, 0, 253, 1, -// 10, 2, 40, 3, -// 30, 0, 0, 2, -// 10, 2, 255, 4}; -// // clang-format on -// -// addAttributeToModel( -// model, -// Sampler::WrapS::CLAMP_TO_EDGE, -// Sampler::WrapS::CLAMP_TO_EDGE, -// 2, -// 1, -// 8, -// data); -// size_t AttributeIndex = model.Attributes.size() - 1; -// -// ExtensionModelExtStructuralMetadata& metadata = -// model.addExtension(); -// -// Schema& schema = metadata.schema.emplace(); -// Class& testClass = schema.classes["TestClass"]; -// -// ClassProperty& doubleClassProperty = -// testClass.properties["DoubleClassProperty"]; -// doubleClassProperty.type = ClassProperty::Type::SCALAR; -// doubleClassProperty.componentType = ClassProperty::ComponentType::FLOAT64; -// -// ClassProperty& arrayClassProperty = -// testClass.properties["ArrayClassProperty"]; -// arrayClassProperty.type = ClassProperty::Type::VEC4; -// arrayClassProperty.componentType = ClassProperty::ComponentType::UINT8; -// arrayClassProperty.array = true; -// arrayClassProperty.count = 2; -// -// PropertyAttribute& propertyAttribute = -// metadata.propertyAttributes.emplace_back(); propertyAttribute.classProperty -// = "TestClass"; -// -// PropertyAttributeProperty& doubleProperty = -// propertyAttribute.properties["DoubleClassProperty"]; -// doubleProperty.index = static_cast(AttributeIndex); -// doubleProperty.texCoord = 0; -// doubleProperty.channels = {0, 1, 2, 3, 4, 5, 6, 7}; -// -// PropertyAttributeProperty& arrayProperty = -// propertyAttribute.properties["ArrayClassProperty"]; -// arrayProperty.index = static_cast(AttributeIndex); -// arrayProperty.texCoord = 0; -// arrayProperty.channels = {0, 1, 2, 3, 4, 5, 6, 7}; -// -// PropertyAttributeView view(model, propertyAttribute); -// REQUIRE(view.status() == PropertyAttributeViewStatus::Valid); -// -// const ClassProperty* classProperty = -// view.getClassProperty("DoubleClassProperty"); -// REQUIRE(classProperty); -// REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); -// REQUIRE( -// classProperty->componentType == ClassProperty::ComponentType::FLOAT64); -// REQUIRE(!classProperty->array); -// -// classProperty = view.getClassProperty("ArrayClassProperty"); -// REQUIRE(classProperty); -// REQUIRE(classProperty->type == ClassProperty::Type::VEC4); -// REQUIRE(classProperty->componentType == -// ClassProperty::ComponentType::UINT8); REQUIRE(classProperty->array); -// REQUIRE(classProperty->count == 2); -// -// uint32_t invokedCallbackCount = 0; -// view.getPropertyView( -// "DoubleClassProperty", -// [&invokedCallbackCount]( -// const std::string& /*propertyName*/, -// auto propertyValue) mutable { -// invokedCallbackCount++; -// REQUIRE( -// propertyValue.status() == -// PropertyAttributePropertyViewStatus::ErrorUnsupportedProperty); -// }); -// REQUIRE(invokedCallbackCount == 1); -// -// view.getPropertyView( -// "ArrayClassProperty", -// [&invokedCallbackCount]( -// const std::string& /*propertyName*/, -// auto propertyValue) mutable { -// invokedCallbackCount++; -// REQUIRE( -// propertyValue.status() == -// PropertyAttributePropertyViewStatus::ErrorUnsupportedProperty); -// }); -// REQUIRE(invokedCallbackCount == 2); -//} +TEST_CASE("Test vecN PropertyAttributeProperty (normalized)") { + Model model; + Mesh& mesh = model.meshes.emplace_back(); + MeshPrimitive& primitive = mesh.primitives.emplace_back(); + + const std::string attributeName = "_ATTRIBUTE"; + std::vector data = { + glm::u8vec2(12, 34), + glm::u8vec2(10, 3), + glm::u8vec2(40, 0), + glm::u8vec2(30, 11)}; + + addAttributeToModel(model, primitive, attributeName, data); + size_t accessorIndex = model.accessors.size() - 1; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::VEC2; + testClassProperty.componentType = ClassProperty::ComponentType::UINT8; + testClassProperty.normalized = true; + + PropertyAttribute& propertyAttribute = + metadata.propertyAttributes.emplace_back(); + propertyAttribute.classProperty = "TestClass"; + + PropertyAttributeProperty& propertyAttributeProperty = + propertyAttribute.properties["TestClassProperty"]; + propertyAttributeProperty.attribute = attributeName; + + PropertyAttributeView view(model, propertyAttribute); + REQUIRE(view.status() == PropertyAttributeViewStatus::Valid); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::VEC2); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT8); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->array); + REQUIRE(classProperty->normalized); + + SECTION("Access correct type") { + PropertyAttributePropertyView u8vec2Property = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE( + u8vec2Property.status() == PropertyAttributePropertyViewStatus::Valid); + for (size_t i = 0; i < data.size(); ++i) { + REQUIRE(u8vec2Property.getRaw(static_cast(i)) == data[i]); + REQUIRE( + u8vec2Property.get(static_cast(i)) == normalize(data[i])); + } + } + + SECTION("Access wrong type") { + PropertyAttributePropertyView uint8Invalid = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE( + uint8Invalid.status() == + PropertyAttributePropertyViewStatus::ErrorTypeMismatch); + + PropertyAttributePropertyView u8vec3Invalid = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE( + u8vec3Invalid.status() == + PropertyAttributePropertyViewStatus::ErrorTypeMismatch); + } + + SECTION("Access wrong component type") { + PropertyAttributePropertyView u16vec2Invalid = + view.getPropertyView( + primitive, + "TestClassProperty"); + REQUIRE( + u16vec2Invalid.status() == + PropertyAttributePropertyViewStatus::ErrorComponentTypeMismatch); + + PropertyAttributePropertyView i8vec2Invalid = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE( + i8vec2Invalid.status() == + PropertyAttributePropertyViewStatus::ErrorComponentTypeMismatch); + } + + SECTION("Access incorrectly as non-normalized") { + PropertyAttributePropertyView normalizedInvalid = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE( + normalizedInvalid.status() == + PropertyAttributePropertyViewStatus::ErrorNormalizationMismatch); + } + + SECTION("Access incorrectly as dvec2") { + PropertyAttributePropertyView dvec2Invalid = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE( + dvec2Invalid.status() == + PropertyAttributePropertyViewStatus::ErrorComponentTypeMismatch); + } + + SECTION("Wrong accessor normalization") { + model.accessors[accessorIndex].normalized = false; + PropertyAttributePropertyView property = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE( + property.status() == PropertyAttributePropertyViewStatus:: + ErrorAccessorNormalizationMismatch); + } +} + +TEST_CASE("Test matN PropertyAttributeProperty") { + Model model; + Mesh& mesh = model.meshes.emplace_back(); + MeshPrimitive& primitive = mesh.primitives.emplace_back(); + + const std::string attributeName = "_ATTRIBUTE"; + // clang-format off + std::vector data = { + glm::umat2x2( + 12, 34, + 30, 1), + glm::umat2x2( + 11, 8, + 73, 102), + glm::umat2x2( + 1, 0, + 63, 2), + glm::umat2x2( + 4, 8, + 3, 23)}; + // clang-format on + + addAttributeToModel(model, primitive, attributeName, data); + size_t accessorIndex = model.accessors.size() - 1; + size_t bufferIndex = model.buffers.size() - 1; + size_t bufferViewIndex = model.bufferViews.size() - 1; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::MAT2; + testClassProperty.componentType = ClassProperty::ComponentType::UINT32; + + PropertyAttribute& propertyAttribute = + metadata.propertyAttributes.emplace_back(); + propertyAttribute.classProperty = "TestClass"; + + PropertyAttributeProperty& propertyAttributeProperty = + propertyAttribute.properties["TestClassProperty"]; + propertyAttributeProperty.attribute = attributeName; + + PropertyAttributeView view(model, propertyAttribute); + REQUIRE(view.status() == PropertyAttributeViewStatus::Valid); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::MAT2); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT32); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->array); + REQUIRE(!classProperty->normalized); + + SECTION("Access correct type") { + PropertyAttributePropertyView umat2x2Property = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE( + umat2x2Property.status() == PropertyAttributePropertyViewStatus::Valid); + for (size_t i = 0; i < data.size(); ++i) { + REQUIRE(umat2x2Property.getRaw(static_cast(i)) == data[i]); + REQUIRE(umat2x2Property.get(static_cast(i)) == data[i]); + } + } + + SECTION("Access wrong type") { + PropertyAttributePropertyView uint32Invalid = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE( + uint32Invalid.status() == + PropertyAttributePropertyViewStatus::ErrorTypeMismatch); + + PropertyAttributePropertyView uvec2Invalid = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE( + uvec2Invalid.status() == + PropertyAttributePropertyViewStatus::ErrorTypeMismatch); + + PropertyAttributePropertyView umat4x4Invalid = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE( + umat4x4Invalid.status() == + PropertyAttributePropertyViewStatus::ErrorTypeMismatch); + } + + SECTION("Access wrong component type") { + PropertyAttributePropertyView mat2Invalid = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE( + mat2Invalid.status() == + PropertyAttributePropertyViewStatus::ErrorComponentTypeMismatch); + } + + SECTION("Access incorrectly as normalized") { + PropertyAttributePropertyView normalizedInvalid = + view.getPropertyView( + primitive, + "TestClassProperty"); + REQUIRE( + normalizedInvalid.status() == + PropertyAttributePropertyViewStatus::ErrorNormalizationMismatch); + } + + SECTION("Buffer view points outside of the real buffer length") { + model.buffers[bufferIndex].cesium.data.resize(4); + PropertyAttributePropertyView property = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE( + property.status() == + PropertyAttributePropertyViewStatus::ErrorBufferViewOutOfBounds); + } + + SECTION("Wrong buffer index") { + model.bufferViews[bufferViewIndex].buffer = 2; + PropertyAttributePropertyView property = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE( + property.status() == + PropertyAttributePropertyViewStatus::ErrorInvalidBuffer); + } + + SECTION("Accessor view points outside of buffer viwe length") { + model.accessors[accessorIndex].count = 10; + PropertyAttributePropertyView property = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE( + property.status() == + PropertyAttributePropertyViewStatus::ErrorAccessorOutOfBounds); + } + + SECTION("Wrong buffer view index") { + model.accessors[accessorIndex].bufferView = -1; + PropertyAttributePropertyView property = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE( + property.status() == + PropertyAttributePropertyViewStatus::ErrorInvalidBufferView); + } + + SECTION("Wrong accessor normalization") { + model.accessors[accessorIndex].normalized = true; + PropertyAttributePropertyView property = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE( + property.status() == PropertyAttributePropertyViewStatus:: + ErrorAccessorNormalizationMismatch); + } + + SECTION("Wrong accessor component type") { + model.accessors[accessorIndex].componentType = + Accessor::ComponentType::BYTE; + PropertyAttributePropertyView property = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE( + property.status() == PropertyAttributePropertyViewStatus:: + ErrorAccessorComponentTypeMismatch); + } + + SECTION("Wrong accessor type") { + model.accessors[accessorIndex].type = Accessor::Type::SCALAR; + PropertyAttributePropertyView property = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE( + property.status() == + PropertyAttributePropertyViewStatus::ErrorAccessorTypeMismatch); + } + + SECTION("Wrong accessor index") { + primitive.attributes[attributeName] = -1; + PropertyAttributePropertyView property = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE( + property.status() == + PropertyAttributePropertyViewStatus::ErrorInvalidAccessor); + } + + SECTION("Missing attribute") { + primitive.attributes.clear(); + PropertyAttributePropertyView property = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE( + property.status() == + PropertyAttributePropertyViewStatus::ErrorMissingAttribute); + } +} + +TEST_CASE("Test matN PropertyAttributeProperty (normalized)") { + Model model; + Mesh& mesh = model.meshes.emplace_back(); + MeshPrimitive& primitive = mesh.primitives.emplace_back(); + + const std::string attributeName = "_ATTRIBUTE"; + // clang-format off + std::vector data = { + glm::umat2x2( + 12, 34, + 30, 1), + glm::umat2x2( + 11, 8, + 73, 102), + glm::umat2x2( + 1, 0, + 63, 2), + glm::umat2x2( + 4, 8, + 3, 23)}; + // clang-format on + + addAttributeToModel( + model, + primitive, + attributeName, + data); + size_t accessorIndex = model.accessors.size() - 1; + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::MAT2; + testClassProperty.componentType = ClassProperty::ComponentType::UINT32; + testClassProperty.normalized = true; + + PropertyAttribute& propertyAttribute = + metadata.propertyAttributes.emplace_back(); + propertyAttribute.classProperty = "TestClass"; + + PropertyAttributeProperty& propertyAttributeProperty = + propertyAttribute.properties["TestClassProperty"]; + propertyAttributeProperty.attribute = attributeName; + + PropertyAttributeView view(model, propertyAttribute); + REQUIRE(view.status() == PropertyAttributeViewStatus::Valid); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::MAT2); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT32); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->array); + REQUIRE(classProperty->normalized); + + SECTION("Access correct type") { + PropertyAttributePropertyView umat2x2Property = + view.getPropertyView( + primitive, + "TestClassProperty"); + REQUIRE( + umat2x2Property.status() == PropertyAttributePropertyViewStatus::Valid); + for (size_t i = 0; i < data.size(); ++i) { + REQUIRE(umat2x2Property.getRaw(static_cast(i)) == data[i]); + REQUIRE( + umat2x2Property.get(static_cast(i)) == normalize(data[i])); + } + } + + SECTION("Access wrong type") { + PropertyAttributePropertyView uint32Invalid = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE( + uint32Invalid.status() == + PropertyAttributePropertyViewStatus::ErrorTypeMismatch); + + PropertyAttributePropertyView uvec2Invalid = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE( + uvec2Invalid.status() == + PropertyAttributePropertyViewStatus::ErrorTypeMismatch); + + PropertyAttributePropertyView umat4x4Invalid = + view.getPropertyView( + primitive, + "TestClassProperty"); + REQUIRE( + umat4x4Invalid.status() == + PropertyAttributePropertyViewStatus::ErrorTypeMismatch); + } + + SECTION("Access wrong component type") { + PropertyAttributePropertyView imat2Invalid = + view.getPropertyView( + primitive, + "TestClassProperty"); + REQUIRE( + imat2Invalid.status() == + PropertyAttributePropertyViewStatus::ErrorComponentTypeMismatch); + } + + SECTION("Access incorrectly as non-normalized") { + PropertyAttributePropertyView nonNormalizedInvalid = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE( + nonNormalizedInvalid.status() == + PropertyAttributePropertyViewStatus::ErrorNormalizationMismatch); + } + + SECTION("Access incorrectly as dmat2") { + PropertyAttributePropertyView dmat2Invalid = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE( + dmat2Invalid.status() == + PropertyAttributePropertyViewStatus::ErrorComponentTypeMismatch); + } + + SECTION("Wrong accessor normalization") { + model.accessors[accessorIndex].normalized = false; + PropertyAttributePropertyView property = + view.getPropertyView( + primitive, + "TestClassProperty"); + REQUIRE( + property.status() == PropertyAttributePropertyViewStatus:: + ErrorAccessorNormalizationMismatch); + } +} + +TEST_CASE("Test with PropertyAttributeProperty offset, scale, min, max") { + Model model; + Mesh& mesh = model.meshes.emplace_back(); + MeshPrimitive& primitive = mesh.primitives.emplace_back(); + + std::vector data{1.0f, 2.0f, 3.0f, 4.0f}; + + const float offset = 1.0f; + const float scale = 2.0f; + const float min = 3.0f; + const float max = 9.0f; + + const std::string attributeName = "_ATTRIBUTE"; + addAttributeToModel(model, primitive, attributeName, data); + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::FLOAT32; + testClassProperty.offset = offset; + testClassProperty.scale = scale; + testClassProperty.min = min; + testClassProperty.max = max; + + PropertyAttribute& propertyAttribute = + metadata.propertyAttributes.emplace_back(); + propertyAttribute.classProperty = "TestClass"; + + PropertyAttributeProperty& propertyAttributeProperty = + propertyAttribute.properties["TestClassProperty"]; + propertyAttributeProperty.attribute = attributeName; + + PropertyAttributeView view(model, propertyAttribute); + REQUIRE(view.status() == PropertyAttributeViewStatus::Valid); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); + REQUIRE( + classProperty->componentType == ClassProperty::ComponentType::FLOAT32); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->array); + REQUIRE(!classProperty->normalized); + REQUIRE(classProperty->offset); + REQUIRE(classProperty->scale); + REQUIRE(classProperty->min); + REQUIRE(classProperty->max); + + SECTION("Use class property values") { + PropertyAttributePropertyView property = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE(property.status() == PropertyAttributePropertyViewStatus::Valid); + REQUIRE(property.offset() == offset); + REQUIRE(property.scale() == scale); + REQUIRE(property.min() == min); + REQUIRE(property.max() == max); + + std::vector expected{3.0f, 5.0f, 7.0f, 9.0f}; + for (size_t i = 0; i < data.size(); ++i) { + REQUIRE(property.getRaw(static_cast(i)) == data[i]); + REQUIRE(property.get(static_cast(i)) == expected[i]); + } + } + + SECTION("Use own property values") { + const float newOffset = 0.5f; + const float newScale = -1.0f; + const float newMin = -3.5f; + const float newMax = -0.5f; + propertyAttributeProperty.offset = newOffset; + propertyAttributeProperty.scale = newScale; + propertyAttributeProperty.min = newMin; + propertyAttributeProperty.max = newMax; + + PropertyAttributePropertyView property = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE(property.status() == PropertyAttributePropertyViewStatus::Valid); + REQUIRE(property.offset() == newOffset); + REQUIRE(property.scale() == newScale); + REQUIRE(property.min() == newMin); + REQUIRE(property.max() == newMax); + + std::vector expected{-0.5f, -1.5f, -2.5f, -3.5f}; + for (size_t i = 0; i < data.size(); ++i) { + REQUIRE(property.getRaw(static_cast(i)) == data[i]); + REQUIRE(property.get(static_cast(i)) == expected[i]); + } + } +} + +TEST_CASE("Test with PropertyAttributeProperty offset, scale, min, max " + "(normalized)") { + Model model; + + Mesh& mesh = model.meshes.emplace_back(); + MeshPrimitive& primitive = mesh.primitives.emplace_back(); + + std::vector data{0, 128, 255, 32}; + + const double offset = 1.0; + const double scale = 2.0; + const double min = 1.0; + const double max = 3.0; + + const std::string attributeName = "_ATTRIBUTE"; + addAttributeToModel(model, primitive, attributeName, data); + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::UINT8; + testClassProperty.normalized = true; + testClassProperty.offset = offset; + testClassProperty.scale = scale; + testClassProperty.min = min; + testClassProperty.max = max; + + PropertyAttribute& propertyAttribute = + metadata.propertyAttributes.emplace_back(); + propertyAttribute.classProperty = "TestClass"; + + PropertyAttributeProperty& propertyAttributeProperty = + propertyAttribute.properties["TestClassProperty"]; + propertyAttributeProperty.attribute = attributeName; + + PropertyAttributeView view(model, propertyAttribute); + REQUIRE(view.status() == PropertyAttributeViewStatus::Valid); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT8); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->array); + REQUIRE(classProperty->normalized); + + std::vector texCoords{ + glm::dvec2(0, 0), + glm::dvec2(0.5, 0), + glm::dvec2(0, 0.5), + glm::dvec2(0.5, 0.5)}; + + SECTION("Use class property values") { + PropertyAttributePropertyView property = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE(property.status() == PropertyAttributePropertyViewStatus::Valid); + REQUIRE(property.offset() == offset); + REQUIRE(property.scale() == scale); + REQUIRE(property.min() == min); + REQUIRE(property.max() == max); + + for (size_t i = 0; i < data.size(); ++i) { + REQUIRE(property.getRaw(static_cast(i)) == data[i]); + REQUIRE( + property.get(static_cast(i)) == + normalize(data[i]) * scale + offset); + } + } + + SECTION("Use own property values") { + const double newOffset = 2.0; + const double newScale = 5.0; + const double newMin = 10.0; + const double newMax = 11.0; + propertyAttributeProperty.offset = newOffset; + propertyAttributeProperty.scale = newScale; + propertyAttributeProperty.min = newMin; + propertyAttributeProperty.max = newMax; + + PropertyAttributePropertyView property = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE(property.status() == PropertyAttributePropertyViewStatus::Valid); + REQUIRE(property.offset() == newOffset); + REQUIRE(property.scale() == newScale); + REQUIRE(property.min() == newMin); + REQUIRE(property.max() == newMax); + + for (size_t i = 0; i < data.size(); ++i) { + REQUIRE(property.getRaw(static_cast(i)) == data[i]); + REQUIRE( + property.get(static_cast(i)) == + normalize(data[i]) * newScale + newOffset); + } + } +} + +TEST_CASE("Test with PropertyAttributeProperty noData") { + Model model; + Mesh& mesh = model.meshes.emplace_back(); + MeshPrimitive& primitive = mesh.primitives.emplace_back(); + + std::vector data = {12, 34, 30, 11}; + const uint8_t noData = 34; + + const std::string attributeName = "_ATTRIBUTE"; + addAttributeToModel(model, primitive, attributeName, data); + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::UINT8; + testClassProperty.noData = noData; + + PropertyAttribute& propertyAttribute = + metadata.propertyAttributes.emplace_back(); + propertyAttribute.classProperty = "TestClass"; + + PropertyAttributeProperty& propertyAttributeProperty = + propertyAttribute.properties["TestClassProperty"]; + propertyAttributeProperty.attribute = attributeName; + + PropertyAttributeView view(model, propertyAttribute); + REQUIRE(view.status() == PropertyAttributeViewStatus::Valid); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT8); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->array); + REQUIRE(!classProperty->normalized); + + SECTION("Without default value") { + PropertyAttributePropertyView property = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE(property.status() == PropertyAttributePropertyViewStatus::Valid); + + for (size_t i = 0; i < data.size(); ++i) { + auto value = property.getRaw(static_cast(i)); + REQUIRE(value == data[i]); + + auto maybeValue = property.get(static_cast(i)); + if (value == noData) { + REQUIRE(!maybeValue); + } else { + REQUIRE(maybeValue == data[i]); + } + } + } + + SECTION("With default value") { + const uint8_t defaultValue = 255; + testClassProperty.defaultProperty = defaultValue; + + PropertyAttributePropertyView property = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE(property.status() == PropertyAttributePropertyViewStatus::Valid); + + for (size_t i = 0; i < data.size(); ++i) { + auto value = property.getRaw(static_cast(i)); + REQUIRE(value == data[i]); + + auto maybeValue = property.get(static_cast(i)); + if (value == noData) { + REQUIRE(maybeValue == defaultValue); + } else { + REQUIRE(maybeValue == data[i]); + } + } + } +} + +TEST_CASE("Test with PropertyAttributeProperty noData (normalized)") { + Model model; + Mesh& mesh = model.meshes.emplace_back(); + MeshPrimitive& primitive = mesh.primitives.emplace_back(); + + std::vector data = {12, 34, 30, 11}; + const uint8_t noData = 34; + + const std::string attributeName = "_ATTRIBUTE"; + addAttributeToModel(model, primitive, attributeName, data); + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::UINT8; + testClassProperty.normalized = true; + testClassProperty.noData = noData; + + PropertyAttribute& propertyAttribute = + metadata.propertyAttributes.emplace_back(); + propertyAttribute.classProperty = "TestClass"; + + PropertyAttributeProperty& propertyAttributeProperty = + propertyAttribute.properties["TestClassProperty"]; + propertyAttributeProperty.attribute = attributeName; + + PropertyAttributeView view(model, propertyAttribute); + REQUIRE(view.status() == PropertyAttributeViewStatus::Valid); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT8); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->array); + REQUIRE(classProperty->normalized); + + SECTION("Without default value") { + PropertyAttributePropertyView property = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE(property.status() == PropertyAttributePropertyViewStatus::Valid); + + for (size_t i = 0; i < data.size(); ++i) { + auto value = property.getRaw(static_cast(i)); + REQUIRE(value == data[i]); + + auto maybeValue = property.get(static_cast(i)); + if (value == noData) { + REQUIRE(!maybeValue); + } else { + REQUIRE(maybeValue == normalize(data[i])); + } + } + } + + SECTION("With default value") { + const double defaultValue = -1.0; + testClassProperty.defaultProperty = defaultValue; + + PropertyAttributePropertyView property = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE(property.status() == PropertyAttributePropertyViewStatus::Valid); + + for (size_t i = 0; i < data.size(); ++i) { + auto value = property.getRaw(static_cast(i)); + REQUIRE(value == data[i]); + + auto maybeValue = property.get(static_cast(i)); + if (value == noData) { + REQUIRE(maybeValue == defaultValue); + } else { + REQUIRE(maybeValue == normalize(data[i])); + } + } + } +} + +TEST_CASE("Test callback on invalid property Attribute view") { + Model model; + Mesh& mesh = model.meshes.emplace_back(); + MeshPrimitive& primitive = mesh.primitives.emplace_back(); + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + metadata.schema.emplace(); + + // Property Attribute has a nonexistent class. + PropertyAttribute& propertyAttribute = + metadata.propertyAttributes.emplace_back(); + propertyAttribute.classProperty = "TestClass"; + + PropertyAttributeProperty& propertyAttributeProperty = + propertyAttribute.properties["TestClassProperty"]; + propertyAttributeProperty.attribute = "_INVALID"; + + PropertyAttributeView view(model, propertyAttribute); + REQUIRE(view.status() == PropertyAttributeViewStatus::ErrorClassNotFound); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(!classProperty); + + uint32_t invokedCallbackCount = 0; + view.getPropertyView( + primitive, + "TestClassProperty", + [&invokedCallbackCount]( + const std::string& /*propertyName*/, + auto propertyValue) mutable { + invokedCallbackCount++; + REQUIRE( + propertyValue.status() == + PropertyAttributePropertyViewStatus::ErrorInvalidPropertyAttribute); + }); + + REQUIRE(invokedCallbackCount == 1); +} + +TEST_CASE("Test callback on invalid PropertyAttributeProperty") { + Model model; + Mesh& mesh = model.meshes.emplace_back(); + MeshPrimitive& primitive = mesh.primitives.emplace_back(); + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["InvalidProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::UINT8; + + PropertyAttribute& propertyAttribute = + metadata.propertyAttributes.emplace_back(); + propertyAttribute.classProperty = "TestClass"; + + PropertyAttributeProperty& propertyAttributeProperty = + propertyAttribute.properties["InvalidProperty"]; + propertyAttributeProperty.attribute = "_INVALID"; + + PropertyAttributeView view(model, propertyAttribute); + REQUIRE(view.status() == PropertyAttributeViewStatus::Valid); + + const ClassProperty* classProperty = view.getClassProperty("InvalidProperty"); + REQUIRE(classProperty); + + classProperty = view.getClassProperty("NonexistentProperty"); + REQUIRE(!classProperty); + + uint32_t invokedCallbackCount = 0; + auto testCallback = [&invokedCallbackCount]( + const std::string& /*propertyName*/, + auto propertyValue) mutable { + invokedCallbackCount++; + REQUIRE( + propertyValue.status() != PropertyAttributePropertyViewStatus::Valid); + }; + + view.getPropertyView(primitive, "InvalidProperty", testCallback); + view.getPropertyView(primitive, "NonexistentProperty", testCallback); + + REQUIRE(invokedCallbackCount == 2); +} + +TEST_CASE("Test callback on invalid normalized PropertyAttributeProperty") { + Model model; + Mesh& mesh = model.meshes.emplace_back(); + MeshPrimitive& primitive = mesh.primitives.emplace_back(); + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::FLOAT32; + testClassProperty.normalized = true; // This is erroneous. + + PropertyAttribute& propertyAttribute = + metadata.propertyAttributes.emplace_back(); + propertyAttribute.classProperty = "TestClass"; + + PropertyAttributeProperty& propertyAttributeProperty = + propertyAttribute.properties["TestClassProperty"]; + propertyAttributeProperty.attribute = "_INVALID"; + + PropertyAttributeView view(model, propertyAttribute); + REQUIRE(view.status() == PropertyAttributeViewStatus::Valid); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + + uint32_t invokedCallbackCount = 0; + auto testCallback = [&invokedCallbackCount]( + const std::string& /*propertyName*/, + auto propertyValue) mutable { + invokedCallbackCount++; + REQUIRE( + propertyValue.status() == + PropertyAttributePropertyViewStatus::ErrorInvalidNormalization); + }; + + view.getPropertyView(primitive, "TestClassProperty", testCallback); + + REQUIRE(invokedCallbackCount == 1); +} + +TEST_CASE("Test callback for scalar PropertyAttributeProperty") { + Model model; + Mesh& mesh = model.meshes.emplace_back(); + MeshPrimitive& primitive = mesh.primitives.emplace_back(); + + std::vector data{-1, 268, 542, -256}; + + const std::string attributeName = "_ATTRIBUTE"; + addAttributeToModel(model, primitive, attributeName, data); + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::INT16; + + PropertyAttribute& propertyAttribute = + metadata.propertyAttributes.emplace_back(); + propertyAttribute.classProperty = "TestClass"; + + PropertyAttributeProperty& propertyAttributeProperty = + propertyAttribute.properties["TestClassProperty"]; + propertyAttributeProperty.attribute = attributeName; + + PropertyAttributeView view(model, propertyAttribute); + REQUIRE(view.status() == PropertyAttributeViewStatus::Valid); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT16); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->array); + REQUIRE(!classProperty->normalized); + + uint32_t invokedCallbackCount = 0; + view.getPropertyView( + primitive, + "TestClassProperty", + [&data, &invokedCallbackCount]( + const std::string& /*propertyName*/, + auto propertyValue) mutable { + invokedCallbackCount++; + if constexpr (std::is_same_v< + PropertyAttributePropertyView, + decltype(propertyValue)>) { + REQUIRE( + propertyValue.status() == + PropertyAttributePropertyViewStatus::Valid); + + for (size_t i = 0; i < data.size(); ++i) { + REQUIRE(propertyValue.getRaw(static_cast(i)) == data[i]); + REQUIRE(propertyValue.get(static_cast(i)) == data[i]); + } + } else { + FAIL("getPropertyView returned PropertyAttributePropertyView of " + "incorrect type for TestClassProperty."); + } + }); + + REQUIRE(invokedCallbackCount == 1); +} + +TEST_CASE("Test callback for scalar PropertyAttributeProperty (normalized)") { + Model model; + Mesh& mesh = model.meshes.emplace_back(); + MeshPrimitive& primitive = mesh.primitives.emplace_back(); + + std::vector data{-1, 268, 542, -256}; + + const std::string attributeName = "_ATTRIBUTE"; + addAttributeToModel(model, primitive, attributeName, data); + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::INT16; + testClassProperty.normalized = true; + + PropertyAttribute& propertyAttribute = + metadata.propertyAttributes.emplace_back(); + propertyAttribute.classProperty = "TestClass"; + + PropertyAttributeProperty& propertyAttributeProperty = + propertyAttribute.properties["TestClassProperty"]; + propertyAttributeProperty.attribute = attributeName; + + PropertyAttributeView view(model, propertyAttribute); + REQUIRE(view.status() == PropertyAttributeViewStatus::Valid); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT16); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->array); + REQUIRE(classProperty->normalized); + + uint32_t invokedCallbackCount = 0; + view.getPropertyView( + primitive, + "TestClassProperty", + [&data, &invokedCallbackCount]( + const std::string& /*propertyName*/, + auto propertyValue) mutable { + invokedCallbackCount++; + if constexpr (std::is_same_v< + PropertyAttributePropertyView, + decltype(propertyValue)>) { + REQUIRE( + propertyValue.status() == + PropertyAttributePropertyViewStatus::Valid); + + for (size_t i = 0; i < data.size(); ++i) { + REQUIRE(propertyValue.getRaw(static_cast(i)) == data[i]); + REQUIRE( + propertyValue.get(static_cast(i)) == + normalize(data[i])); + } + } else { + FAIL("getPropertyView returned PropertyAttributePropertyView of " + "incorrect type for TestClassProperty."); + } + }); + + REQUIRE(invokedCallbackCount == 1); +} + +TEST_CASE("Test callback for vecN PropertyAttributeProperty") { + Model model; + Mesh& mesh = model.meshes.emplace_back(); + MeshPrimitive& primitive = mesh.primitives.emplace_back(); + + std::vector data{ + glm::i8vec2(-1, -1), + glm::i8vec2(12, 1), + glm::i8vec2(30, 2), + glm::i8vec2(0, -1)}; + + const std::string attributeName = "_ATTRIBUTE"; + addAttributeToModel(model, primitive, attributeName, data); + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::VEC2; + testClassProperty.componentType = ClassProperty::ComponentType::INT8; + + PropertyAttribute& propertyAttribute = + metadata.propertyAttributes.emplace_back(); + propertyAttribute.classProperty = "TestClass"; + + PropertyAttributeProperty& propertyAttributeProperty = + propertyAttribute.properties["TestClassProperty"]; + propertyAttributeProperty.attribute = attributeName; + + PropertyAttributeView view(model, propertyAttribute); + REQUIRE(view.status() == PropertyAttributeViewStatus::Valid); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::VEC2); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT8); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->array); + REQUIRE(!classProperty->normalized); + + uint32_t invokedCallbackCount = 0; + view.getPropertyView( + primitive, + "TestClassProperty", + [&data, &invokedCallbackCount]( + const std::string& /*propertyName*/, + auto propertyValue) mutable { + invokedCallbackCount++; + if constexpr (std::is_same_v< + PropertyAttributePropertyView, + decltype(propertyValue)>) { + REQUIRE( + propertyValue.status() == + PropertyAttributePropertyViewStatus::Valid); + + for (size_t i = 0; i < data.size(); ++i) { + REQUIRE(propertyValue.getRaw(static_cast(i)) == data[i]); + REQUIRE(propertyValue.get(static_cast(i)) == data[i]); + } + } else { + FAIL("getPropertyView returned PropertyAttributePropertyView of " + "incorrect type for TestClassProperty."); + } + }); + + REQUIRE(invokedCallbackCount == 1); +} + +TEST_CASE("Test callback for vecN PropertyAttributeProperty (normalized)") { + Model model; + Mesh& mesh = model.meshes.emplace_back(); + MeshPrimitive& primitive = mesh.primitives.emplace_back(); + + std::vector data{ + glm::i8vec2(-1, -1), + glm::i8vec2(12, 1), + glm::i8vec2(30, 2), + glm::i8vec2(0, -1)}; + + const std::string attributeName = "_ATTRIBUTE"; + addAttributeToModel(model, primitive, attributeName, data); + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::VEC2; + testClassProperty.componentType = ClassProperty::ComponentType::INT8; + testClassProperty.normalized = true; + + PropertyAttribute& propertyAttribute = + metadata.propertyAttributes.emplace_back(); + propertyAttribute.classProperty = "TestClass"; + + PropertyAttributeProperty& propertyAttributeProperty = + propertyAttribute.properties["TestClassProperty"]; + propertyAttributeProperty.attribute = attributeName; + + PropertyAttributeView view(model, propertyAttribute); + REQUIRE(view.status() == PropertyAttributeViewStatus::Valid); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::VEC2); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT8); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->array); + REQUIRE(classProperty->normalized); + + uint32_t invokedCallbackCount = 0; + view.getPropertyView( + primitive, + "TestClassProperty", + [&data, &invokedCallbackCount]( + const std::string& /*propertyName*/, + auto propertyValue) mutable { + invokedCallbackCount++; + if constexpr (std::is_same_v< + PropertyAttributePropertyView, + decltype(propertyValue)>) { + REQUIRE( + propertyValue.status() == + PropertyAttributePropertyViewStatus::Valid); + + for (size_t i = 0; i < data.size(); ++i) { + REQUIRE(propertyValue.getRaw(static_cast(i)) == data[i]); + REQUIRE( + propertyValue.get(static_cast(i)) == + normalize(data[i])); + } + } else { + FAIL("getPropertyView returned PropertyAttributePropertyView of " + "incorrect type for TestClassProperty."); + } + }); + + REQUIRE(invokedCallbackCount == 1); +} + +TEST_CASE("Test callback for matN PropertyAttributeProperty") { + Model model; + Mesh& mesh = model.meshes.emplace_back(); + MeshPrimitive& primitive = mesh.primitives.emplace_back(); + + // clang-format off + std::vector data = { + glm::umat2x2( + 12, 34, + 30, 1), + glm::umat2x2( + 11, 8, + 73, 102), + glm::umat2x2( + 1, 0, + 63, 2), + glm::umat2x2( + 4, 8, + 3, 23)}; + // clang-format on + + const std::string attributeName = "_ATTRIBUTE"; + addAttributeToModel(model, primitive, attributeName, data); + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::MAT2; + testClassProperty.componentType = ClassProperty::ComponentType::UINT32; + + PropertyAttribute& propertyAttribute = + metadata.propertyAttributes.emplace_back(); + propertyAttribute.classProperty = "TestClass"; + + PropertyAttributeProperty& propertyAttributeProperty = + propertyAttribute.properties["TestClassProperty"]; + propertyAttributeProperty.attribute = attributeName; + + PropertyAttributeView view(model, propertyAttribute); + REQUIRE(view.status() == PropertyAttributeViewStatus::Valid); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::MAT2); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT32); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->array); + REQUIRE(!classProperty->normalized); + + uint32_t invokedCallbackCount = 0; + view.getPropertyView( + primitive, + "TestClassProperty", + [&data, &invokedCallbackCount]( + const std::string& /*propertyName*/, + auto propertyValue) mutable { + invokedCallbackCount++; + if constexpr (std::is_same_v< + PropertyAttributePropertyView, + decltype(propertyValue)>) { + REQUIRE( + propertyValue.status() == + PropertyAttributePropertyViewStatus::Valid); + + for (size_t i = 0; i < data.size(); ++i) { + REQUIRE(propertyValue.getRaw(static_cast(i)) == data[i]); + REQUIRE(propertyValue.get(static_cast(i)) == data[i]); + } + } else { + FAIL("getPropertyView returned PropertyAttributePropertyView of " + "incorrect type for TestClassProperty."); + } + }); + + REQUIRE(invokedCallbackCount == 1); +} + +TEST_CASE("Test callback for matN PropertyAttributeProperty (normalized)") { + Model model; + Mesh& mesh = model.meshes.emplace_back(); + MeshPrimitive& primitive = mesh.primitives.emplace_back(); + + // clang-format off + std::vector data = { + glm::umat2x2( + 12, 34, + 30, 1), + glm::umat2x2( + 11, 8, + 73, 102), + glm::umat2x2( + 1, 0, + 63, 2), + glm::umat2x2( + 4, 8, + 3, 23)}; + // clang-format on + + const std::string attributeName = "_ATTRIBUTE"; + addAttributeToModel( + model, + primitive, + attributeName, + data); + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::MAT2; + testClassProperty.componentType = ClassProperty::ComponentType::UINT32; + testClassProperty.normalized = true; + + PropertyAttribute& propertyAttribute = + metadata.propertyAttributes.emplace_back(); + propertyAttribute.classProperty = "TestClass"; + + PropertyAttributeProperty& propertyAttributeProperty = + propertyAttribute.properties["TestClassProperty"]; + propertyAttributeProperty.attribute = attributeName; + + PropertyAttributeView view(model, propertyAttribute); + REQUIRE(view.status() == PropertyAttributeViewStatus::Valid); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::MAT2); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT32); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->array); + REQUIRE(classProperty->normalized); + + uint32_t invokedCallbackCount = 0; + view.getPropertyView( + primitive, + "TestClassProperty", + [&data, &invokedCallbackCount]( + const std::string& /*propertyName*/, + auto propertyValue) mutable { + invokedCallbackCount++; + if constexpr (std::is_same_v< + PropertyAttributePropertyView, + decltype(propertyValue)>) { + REQUIRE( + propertyValue.status() == + PropertyAttributePropertyViewStatus::Valid); + + for (size_t i = 0; i < data.size(); ++i) { + REQUIRE(propertyValue.getRaw(static_cast(i)) == data[i]); + REQUIRE( + propertyValue.get(static_cast(i)) == + normalize(data[i])); + } + } else { + FAIL("getPropertyView returned PropertyAttributePropertyView of " + "incorrect type for TestClassProperty."); + } + }); + + REQUIRE(invokedCallbackCount == 1); +} + +TEST_CASE("Test callback on unsupported PropertyAttributeProperty") { + Model model; + Mesh& mesh = model.meshes.emplace_back(); + MeshPrimitive& primitive = mesh.primitives.emplace_back(); + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + + ClassProperty& doubleClassProperty = + testClass.properties["DoubleClassProperty"]; + doubleClassProperty.type = ClassProperty::Type::SCALAR; + doubleClassProperty.componentType = ClassProperty::ComponentType::FLOAT64; + + ClassProperty& arrayClassProperty = + testClass.properties["ArrayClassProperty"]; + arrayClassProperty.type = ClassProperty::Type::SCALAR; + arrayClassProperty.componentType = ClassProperty::ComponentType::UINT8; + arrayClassProperty.array = true; + arrayClassProperty.count = 2; + + PropertyAttribute& propertyAttribute = + metadata.propertyAttributes.emplace_back(); + propertyAttribute.classProperty = "TestClass"; + + PropertyAttributeProperty& doubleProperty = + propertyAttribute.properties["DoubleClassProperty"]; + doubleProperty.attribute = "_DOUBLE"; + PropertyAttributeProperty& arrayProperty = + propertyAttribute.properties["ArrayClassProperty"]; + arrayProperty.attribute = "_ARRAY"; + + PropertyAttributeView view(model, propertyAttribute); + REQUIRE(view.status() == PropertyAttributeViewStatus::Valid); + + const ClassProperty* classProperty = + view.getClassProperty("DoubleClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); + REQUIRE( + classProperty->componentType == ClassProperty::ComponentType::FLOAT64); + REQUIRE(!classProperty->array); + + classProperty = view.getClassProperty("ArrayClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT8); + REQUIRE(classProperty->array); + REQUIRE(classProperty->count == 2); + + uint32_t invokedCallbackCount = 0; + view.getPropertyView( + primitive, + "DoubleClassProperty", + [&invokedCallbackCount]( + const std::string& /*propertyName*/, + auto propertyValue) mutable { + invokedCallbackCount++; + REQUIRE( + propertyValue.status() == + PropertyAttributePropertyViewStatus::ErrorUnsupportedProperty); + }); + REQUIRE(invokedCallbackCount == 1); + + view.getPropertyView( + primitive, + "ArrayClassProperty", + [&invokedCallbackCount]( + const std::string& /*propertyName*/, + auto propertyValue) mutable { + invokedCallbackCount++; + REQUIRE( + propertyValue.status() == + PropertyAttributePropertyViewStatus::ErrorUnsupportedProperty); + }); + REQUIRE(invokedCallbackCount == 2); +} diff --git a/CesiumGltf/test/TestPropertyTablePropertyView.cpp b/CesiumGltf/test/TestPropertyTablePropertyView.cpp index 12011cabb..524222d26 100644 --- a/CesiumGltf/test/TestPropertyTablePropertyView.cpp +++ b/CesiumGltf/test/TestPropertyTablePropertyView.cpp @@ -12,6 +12,8 @@ using namespace CesiumGltf; using namespace CesiumUtility; +namespace { + template static void checkArrayEqual(PropertyArrayView arrayView, std::vector expected) { @@ -560,6 +562,7 @@ static void checkNormalizedFixedLengthArray( } } } +} // namespace TEST_CASE("Check scalar PropertyTablePropertyView") { SECTION("Uint8") { diff --git a/CesiumGltf/test/TestPropertyTableView.cpp b/CesiumGltf/test/TestPropertyTableView.cpp index b3c4e33ef..6e7e85a32 100644 --- a/CesiumGltf/test/TestPropertyTableView.cpp +++ b/CesiumGltf/test/TestPropertyTableView.cpp @@ -155,10 +155,10 @@ TEST_CASE("Test scalar PropertyTableProperty") { uvec3Invalid.status() == PropertyTablePropertyViewStatus::ErrorTypeMismatch); - PropertyTablePropertyView u32mat3x3Invalid = - view.getPropertyView("TestClassProperty"); + PropertyTablePropertyView umat3x3Invalid = + view.getPropertyView("TestClassProperty"); REQUIRE( - u32mat3x3Invalid.status() == + umat3x3Invalid.status() == PropertyTablePropertyViewStatus::ErrorTypeMismatch); PropertyTablePropertyView boolInvalid = @@ -644,17 +644,17 @@ TEST_CASE("Test vecN PropertyTableProperty (normalized)") { TEST_CASE("Test matN PropertyTableProperty") { Model model; // clang-format off - std::vector values = { - glm::u32mat2x2( + std::vector values = { + glm::umat2x2( 12, 34, 30, 1), - glm::u32mat2x2( + glm::umat2x2( 11, 8, 73, 102), - glm::u32mat2x2( + glm::umat2x2( 1, 0, 63, 2), - glm::u32mat2x2( + glm::umat2x2( 4, 8, 3, 23)}; // clang-format on @@ -694,15 +694,14 @@ TEST_CASE("Test matN PropertyTableProperty") { REQUIRE(!classProperty->normalized); SECTION("Access correct type") { - PropertyTablePropertyView u32mat2x2Property = - view.getPropertyView("TestClassProperty"); - REQUIRE( - u32mat2x2Property.status() == PropertyTablePropertyViewStatus::Valid); - REQUIRE(u32mat2x2Property.size() > 0); - - for (int64_t i = 0; i < u32mat2x2Property.size(); ++i) { - REQUIRE(u32mat2x2Property.getRaw(i) == values[static_cast(i)]); - REQUIRE(u32mat2x2Property.get(i) == u32mat2x2Property.getRaw(i)); + PropertyTablePropertyView umat2x2Property = + view.getPropertyView("TestClassProperty"); + REQUIRE(umat2x2Property.status() == PropertyTablePropertyViewStatus::Valid); + REQUIRE(umat2x2Property.size() > 0); + + for (int64_t i = 0; i < umat2x2Property.size(); ++i) { + REQUIRE(umat2x2Property.getRaw(i) == values[static_cast(i)]); + REQUIRE(umat2x2Property.get(i) == umat2x2Property.getRaw(i)); } } @@ -719,10 +718,10 @@ TEST_CASE("Test matN PropertyTableProperty") { uvec2Invalid.status() == PropertyTablePropertyViewStatus::ErrorTypeMismatch); - PropertyTablePropertyView u32mat4x4Invalid = - view.getPropertyView("TestClassProperty"); + PropertyTablePropertyView umat4x4Invalid = + view.getPropertyView("TestClassProperty"); REQUIRE( - u32mat4x4Invalid.status() == + umat4x4Invalid.status() == PropertyTablePropertyViewStatus::ErrorTypeMismatch); PropertyTablePropertyView boolInvalid = @@ -759,8 +758,8 @@ TEST_CASE("Test matN PropertyTableProperty") { } SECTION("Access incorrectly as array") { - PropertyTablePropertyView> arrayInvalid = - view.getPropertyView>( + PropertyTablePropertyView> arrayInvalid = + view.getPropertyView>( "TestClassProperty"); REQUIRE( arrayInvalid.status() == @@ -768,8 +767,8 @@ TEST_CASE("Test matN PropertyTableProperty") { } SECTION("Access incorrectly as normalized") { - PropertyTablePropertyView normalizedInvalid = - view.getPropertyView("TestClassProperty"); + PropertyTablePropertyView normalizedInvalid = + view.getPropertyView("TestClassProperty"); REQUIRE( normalizedInvalid.status() == PropertyTablePropertyViewStatus::ErrorNormalizationMismatch); @@ -777,49 +776,49 @@ TEST_CASE("Test matN PropertyTableProperty") { SECTION("Wrong buffer index") { model.bufferViews[valueBufferViewIndex].buffer = 2; - PropertyTablePropertyView u32mat2x2Property = - view.getPropertyView("TestClassProperty"); + PropertyTablePropertyView umat2x2Property = + view.getPropertyView("TestClassProperty"); REQUIRE( - u32mat2x2Property.status() == + umat2x2Property.status() == PropertyTablePropertyViewStatus::ErrorInvalidValueBuffer); } SECTION("Wrong buffer view index") { propertyTableProperty.values = -1; - PropertyTablePropertyView u32mat2x2Property = - view.getPropertyView("TestClassProperty"); + PropertyTablePropertyView umat2x2Property = + view.getPropertyView("TestClassProperty"); REQUIRE( - u32mat2x2Property.status() == + umat2x2Property.status() == PropertyTablePropertyViewStatus::ErrorInvalidValueBufferView); } SECTION("Buffer view points outside of the real buffer length") { - model.buffers[valueBufferIndex].cesium.data.resize(sizeof(glm::u32mat2x2)); - PropertyTablePropertyView u32mat2x2Property = - view.getPropertyView("TestClassProperty"); + model.buffers[valueBufferIndex].cesium.data.resize(sizeof(glm::umat2x2)); + PropertyTablePropertyView umat2x2Property = + view.getPropertyView("TestClassProperty"); REQUIRE( - u32mat2x2Property.status() == + umat2x2Property.status() == PropertyTablePropertyViewStatus::ErrorBufferViewOutOfBounds); } SECTION("Buffer view length isn't multiple of sizeof(T)") { model.bufferViews[valueBufferViewIndex].byteLength = - sizeof(glm::u32mat2x2) * 4 - 1; - PropertyTablePropertyView u32mat2x2Property = - view.getPropertyView("TestClassProperty"); + sizeof(glm::umat2x2) * 4 - 1; + PropertyTablePropertyView umat2x2Property = + view.getPropertyView("TestClassProperty"); REQUIRE( - u32mat2x2Property.status() == + umat2x2Property.status() == PropertyTablePropertyViewStatus:: ErrorBufferViewSizeNotDivisibleByTypeSize); } SECTION("Buffer view length doesn't match with propertyTableCount") { - model.bufferViews[valueBufferViewIndex].byteLength = sizeof(glm::u32mat2x2); + model.bufferViews[valueBufferViewIndex].byteLength = sizeof(glm::umat2x2); - PropertyTablePropertyView u32mat2x2Property = - view.getPropertyView("TestClassProperty"); + PropertyTablePropertyView umat2x2Property = + view.getPropertyView("TestClassProperty"); REQUIRE( - u32mat2x2Property.status() == + umat2x2Property.status() == PropertyTablePropertyViewStatus:: ErrorBufferViewSizeDoesNotMatchPropertyTableCount); } @@ -828,17 +827,17 @@ TEST_CASE("Test matN PropertyTableProperty") { TEST_CASE("Test matN PropertyTableProperty (normalized)") { Model model; // clang-format off - std::vector values = { - glm::u32mat2x2( + std::vector values = { + glm::umat2x2( 12, 34, 30, 1), - glm::u32mat2x2( + glm::umat2x2( 11, 8, 73, 102), - glm::u32mat2x2( + glm::umat2x2( 1, 0, 63, 2), - glm::u32mat2x2( + glm::umat2x2( 4, 8, 3, 23)}; // clang-format on @@ -878,16 +877,15 @@ TEST_CASE("Test matN PropertyTableProperty (normalized)") { REQUIRE(classProperty->normalized); SECTION("Access correct type") { - PropertyTablePropertyView u32mat2x2Property = - view.getPropertyView("TestClassProperty"); - REQUIRE( - u32mat2x2Property.status() == PropertyTablePropertyViewStatus::Valid); - REQUIRE(u32mat2x2Property.size() > 0); + PropertyTablePropertyView umat2x2Property = + view.getPropertyView("TestClassProperty"); + REQUIRE(umat2x2Property.status() == PropertyTablePropertyViewStatus::Valid); + REQUIRE(umat2x2Property.size() > 0); - for (int64_t i = 0; i < u32mat2x2Property.size(); ++i) { - auto value = u32mat2x2Property.getRaw(i); + for (int64_t i = 0; i < umat2x2Property.size(); ++i) { + auto value = umat2x2Property.getRaw(i); REQUIRE(value == values[static_cast(i)]); - REQUIRE(u32mat2x2Property.get(i) == normalize(value)); + REQUIRE(umat2x2Property.get(i) == normalize(value)); } } @@ -904,10 +902,10 @@ TEST_CASE("Test matN PropertyTableProperty (normalized)") { uvec2Invalid.status() == PropertyTablePropertyViewStatus::ErrorTypeMismatch); - PropertyTablePropertyView u32mat4x4Invalid = - view.getPropertyView("TestClassProperty"); + PropertyTablePropertyView umat4x4Invalid = + view.getPropertyView("TestClassProperty"); REQUIRE( - u32mat4x4Invalid.status() == + umat4x4Invalid.status() == PropertyTablePropertyViewStatus::ErrorTypeMismatch); } @@ -926,9 +924,9 @@ TEST_CASE("Test matN PropertyTableProperty (normalized)") { } SECTION("Access incorrectly as array") { - PropertyTablePropertyView, true> + PropertyTablePropertyView, true> arrayInvalid = - view.getPropertyView, true>( + view.getPropertyView, true>( "TestClassProperty"); REQUIRE( arrayInvalid.status() == @@ -936,8 +934,8 @@ TEST_CASE("Test matN PropertyTableProperty (normalized)") { } SECTION("Access incorrectly as non-normalized") { - PropertyTablePropertyView nonNormalizedInvalid = - view.getPropertyView("TestClassProperty"); + PropertyTablePropertyView nonNormalizedInvalid = + view.getPropertyView("TestClassProperty"); REQUIRE( nonNormalizedInvalid.status() == PropertyTablePropertyViewStatus::ErrorNormalizationMismatch); @@ -2297,12 +2295,12 @@ TEST_CASE("Test fixed-length matN array") { } SECTION("Wrong component type") { - PropertyTablePropertyView> - u32mat2x2ArrayInvalid = - view.getPropertyView>( + PropertyTablePropertyView> + umat2x2ArrayInvalid = + view.getPropertyView>( "TestClassProperty"); REQUIRE( - u32mat2x2ArrayInvalid.status() == + umat2x2ArrayInvalid.status() == PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); } @@ -2457,12 +2455,12 @@ TEST_CASE("Test fixed-length matN array (normalized)") { } SECTION("Wrong component type") { - PropertyTablePropertyView, true> - u32mat2x2ArrayInvalid = - view.getPropertyView, true>( + PropertyTablePropertyView, true> + umat2x2ArrayInvalid = + view.getPropertyView, true>( "TestClassProperty"); REQUIRE( - u32mat2x2ArrayInvalid.status() == + umat2x2ArrayInvalid.status() == PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); } @@ -4237,9 +4235,7 @@ TEST_CASE("Test callback for vecN PropertyTableProperty (normalized)") { decltype(propertyValue)>) { for (int64_t i = 0; i < propertyValue.size(); ++i) { auto expectedValue = values[static_cast(i)]; - REQUIRE( - static_cast(propertyValue.getRaw(i)) == - expectedValue); + REQUIRE(propertyValue.getRaw(i) == expectedValue); REQUIRE(propertyValue.get(i) == normalize(expectedValue)); } } else { @@ -4254,17 +4250,17 @@ TEST_CASE("Test callback for vecN PropertyTableProperty (normalized)") { TEST_CASE("Test callback for matN PropertyTableProperty") { Model model; // clang-format off - std::vector values = { - glm::u32mat2x2( + std::vector values = { + glm::umat2x2( 12, 34, 30, 1), - glm::u32mat2x2( + glm::umat2x2( 11, 8, 73, 102), - glm::u32mat2x2( + glm::umat2x2( 1, 0, 63, 2), - glm::u32mat2x2( + glm::umat2x2( 4, 8, 3, 23)}; // clang-format on @@ -4313,13 +4309,11 @@ TEST_CASE("Test callback for matN PropertyTableProperty") { REQUIRE(propertyValue.size() > 0); if constexpr (std::is_same_v< - PropertyTablePropertyView, + PropertyTablePropertyView, decltype(propertyValue)>) { for (int64_t i = 0; i < propertyValue.size(); ++i) { auto expectedValue = values[static_cast(i)]; - REQUIRE( - static_cast(propertyValue.getRaw(i)) == - expectedValue); + REQUIRE(propertyValue.getRaw(i) == expectedValue); REQUIRE(propertyValue.get(i) == expectedValue); } } else { @@ -4335,17 +4329,17 @@ TEST_CASE("Test callback for matN PropertyTableProperty") { TEST_CASE("Test callback for matN PropertyTableProperty (normalized)") { Model model; // clang-format off - std::vector values = { - glm::u32mat2x2( + std::vector values = { + glm::umat2x2( 12, 34, 30, 1), - glm::u32mat2x2( + glm::umat2x2( 11, 8, 73, 102), - glm::u32mat2x2( + glm::umat2x2( 1, 0, 63, 2), - glm::u32mat2x2( + glm::umat2x2( 4, 8, 3, 23)}; // clang-format on @@ -4395,12 +4389,12 @@ TEST_CASE("Test callback for matN PropertyTableProperty (normalized)") { REQUIRE(propertyValue.size() > 0); if constexpr (std::is_same_v< - PropertyTablePropertyView, + PropertyTablePropertyView, decltype(propertyValue)>) { for (int64_t i = 0; i < propertyValue.size(); ++i) { auto expectedValue = values[static_cast(i)]; REQUIRE( - static_cast(propertyValue.getRaw(i)) == + static_cast(propertyValue.getRaw(i)) == expectedValue); REQUIRE(propertyValue.get(i) == normalize(expectedValue)); } diff --git a/CesiumGltf/test/TestPropertyTexturePropertyView.cpp b/CesiumGltf/test/TestPropertyTexturePropertyView.cpp index 7805964a1..227d6e385 100644 --- a/CesiumGltf/test/TestPropertyTexturePropertyView.cpp +++ b/CesiumGltf/test/TestPropertyTexturePropertyView.cpp @@ -10,6 +10,7 @@ using namespace CesiumGltf; using namespace CesiumUtility; +namespace { template void checkTextureValues( const std::vector& data, @@ -487,6 +488,7 @@ void checkNormalizedTextureArrayValues( } } } +} // namespace TEST_CASE("Check scalar PropertyTexturePropertyView") { SECTION("uint8_t") { @@ -1371,7 +1373,8 @@ TEST_CASE("Check array PropertyTexturePropertyView (normalized)") { } } -TEST_CASE("Check that PropertyTextureProperty values override class property values") { +TEST_CASE("Check that PropertyTextureProperty values override class property " + "values") { ClassProperty classProperty; classProperty.type = ClassProperty::Type::SCALAR; classProperty.componentType = ClassProperty::ComponentType::FLOAT32; diff --git a/CesiumGltf/test/TestPropertyTextureView.cpp b/CesiumGltf/test/TestPropertyTextureView.cpp index 626dd3891..13be12580 100644 --- a/CesiumGltf/test/TestPropertyTextureView.cpp +++ b/CesiumGltf/test/TestPropertyTextureView.cpp @@ -676,10 +676,10 @@ TEST_CASE("Test vecN PropertyTextureProperty (normalized)") { } SECTION("Access incorrectly as dvec2") { - PropertyTexturePropertyView normalizedInvalid = + PropertyTexturePropertyView dvec2Invalid = view.getPropertyView("TestClassProperty"); REQUIRE( - normalizedInvalid.status() == + dvec2Invalid.status() == PropertyTexturePropertyViewStatus::ErrorComponentTypeMismatch); } From 7a3a5f0d8671f0d4052920c44fd255f182be021d Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Tue, 29 Aug 2023 15:48:28 -0400 Subject: [PATCH 243/421] Update changelog and fix sign conversions --- CHANGES.md | 3 +++ CesiumGltf/test/TestPropertyAttributePropertyView.cpp | 4 ++-- CesiumGltf/test/TestPropertyAttributeView.cpp | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index e4b438c46..865cbfc8e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -28,6 +28,9 @@ - Added `PropertyViewStatus`, which defines public `static const` values for various property errors. - Added `PropertyTableViewStatus` to indicate whether a `PropertyTableView` is valid. - Added `PropertyComponentType` to reflect the values of `componentType` in a `ClassProperty` from `EXT_structural_metadata`. +- Added `PropertyAttributeView`, which views a `PropertyAttribute` in `EXT_structural_metadata`. +- Added `PropertyAttributePropertyView`, which views a `PropertyAttributeProperty` in `EXT_structural_metadata`. +- Added `PropertyAttributePropertyViewStatus`, which reflects the sattus of a `PropertyAttributePropertyView`. ### v0.25.0 - 2023-06-01 diff --git a/CesiumGltf/test/TestPropertyAttributePropertyView.cpp b/CesiumGltf/test/TestPropertyAttributePropertyView.cpp index 0dab05f2e..150dc5393 100644 --- a/CesiumGltf/test/TestPropertyAttributePropertyView.cpp +++ b/CesiumGltf/test/TestPropertyAttributePropertyView.cpp @@ -28,7 +28,7 @@ const Accessor& addValuesToModel(Model& model, const std::vector& values) { Accessor& accessor = model.accessors.emplace_back(); accessor.bufferView = static_cast(model.bufferViews.size() - 1); - accessor.count = values.size(); + accessor.count = static_cast(values.size()); accessor.byteOffset = 0; PropertyType type = TypeToPropertyType::value; @@ -817,7 +817,7 @@ TEST_CASE("Check that PropertyAttributeProperty values override class property " Accessor& accessor = model.accessors.emplace_back(); accessor.bufferView = static_cast(model.bufferViews.size() - 1); - accessor.count = data.size(); + accessor.count = static_cast(data.size()); accessor.byteOffset = 0; accessor.type = Accessor::Type::SCALAR; accessor.componentType = Accessor::ComponentType::FLOAT; diff --git a/CesiumGltf/test/TestPropertyAttributeView.cpp b/CesiumGltf/test/TestPropertyAttributeView.cpp index d7d13c7b1..5e9bae689 100644 --- a/CesiumGltf/test/TestPropertyAttributeView.cpp +++ b/CesiumGltf/test/TestPropertyAttributeView.cpp @@ -30,7 +30,7 @@ void addAttributeToModel( Accessor& accessor = model.accessors.emplace_back(); accessor.bufferView = static_cast(model.bufferViews.size() - 1); - accessor.count = values.size(); + accessor.count = static_cast(values.size()); accessor.byteOffset = 0; PropertyType type = TypeToPropertyType::value; From 2a113dc5ab15ec949aa78c94b36e5412a060b0d6 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 30 Aug 2023 19:19:32 +1000 Subject: [PATCH 244/421] Treat additive-refined tiles as rendered, because they are. --- CHANGES.md | 1 + Cesium3DTilesSelection/src/Tileset.cpp | 13 +++++++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index a974bf5f4..f6d9ab742 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -16,6 +16,7 @@ - Fixed a bug where an empty error message would get propagated to a tileset's `loadErrorCallback`. - Fixed several small build script issues to allow cesium-native to be used in Univeral Windows Platform (UWP) applications, such as those that run on Holo Lens 2. - When KTX transcoding fails, the image will now be fully decompressed instead of returning an error. +- Fixed a bug that could cause higher-detail tiles to continue showing when zooming out quickly on a tileset that uses "additive" refinement. ### v0.26.0 - 2023-08-01 diff --git a/Cesium3DTilesSelection/src/Tileset.cpp b/Cesium3DTilesSelection/src/Tileset.cpp index b005e2532..7e006a55f 100644 --- a/Cesium3DTilesSelection/src/Tileset.cpp +++ b/Cesium3DTilesSelection/src/Tileset.cpp @@ -457,11 +457,13 @@ static void markTileNonRendered( TileSelectionState::Result lastResult, Tile& tile, ViewUpdateResult& result) { - if (lastResult == TileSelectionState::Result::Rendered) { + if (lastResult == TileSelectionState::Result::Rendered || + (lastResult == TileSelectionState::Result::Refined && + tile.getRefine() == TileRefine::Add)) { + result.tilesFadingOut.insert(&tile); TileRenderContent* pRenderContent = tile.getContent().getRenderContent(); if (pRenderContent) { pRenderContent->setLodTransitionFadePercentage(0.0f); - result.tilesFadingOut.insert(&tile); } } } @@ -1453,10 +1455,13 @@ Tileset::TraversalDetails Tileset::createTraversalDetailsForSingleTile( const FrameState& frameState, const Tile& tile, const TileSelectionState& lastFrameSelectionState) { + TileSelectionState::Result lastFrameResult = + lastFrameSelectionState.getResult(frameState.lastFrameNumber); bool isRenderable = tile.isRenderable(); bool wasRenderedLastFrame = - lastFrameSelectionState.getResult(frameState.lastFrameNumber) == - TileSelectionState::Result::Rendered; + lastFrameResult == TileSelectionState::Result::Rendered || + (tile.getRefine() == TileRefine::Add && + lastFrameResult == TileSelectionState::Result::Refined); TraversalDetails traversalDetails; traversalDetails.allAreRenderable = isRenderable; From 5a9c23bd3aa8487e1f62b48c349236d14c685996 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 30 Aug 2023 21:26:58 +1000 Subject: [PATCH 245/421] Add a test. --- .../test/TestTilesetSelectionAlgorithm.cpp | 67 ++++++++++++++++++ .../data/AdditiveThreeLevels/content.b3dm | Bin 0 -> 9680 bytes .../data/AdditiveThreeLevels/tileset.json | 63 ++++++++++++++++ 3 files changed, 130 insertions(+) create mode 100644 Cesium3DTilesSelection/test/data/AdditiveThreeLevels/content.b3dm create mode 100644 Cesium3DTilesSelection/test/data/AdditiveThreeLevels/tileset.json diff --git a/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp b/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp index ed11faff2..90811bdc6 100644 --- a/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp +++ b/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp @@ -1215,3 +1215,70 @@ TEST_CASE("An unconditionally-refined tile is not rendered") { runUnconditionallyRefinedTestCase(options); } } + +TEST_CASE("Additive-refined tiles are added to the tilesFadingOut array") { + Cesium3DTilesSelection::registerAllTileContentTypes(); + + std::filesystem::path testDataPath = Cesium3DTilesSelection_TEST_DATA_DIR; + testDataPath = testDataPath / "AdditiveThreeLevels"; + std::vector files{"tileset.json", "content.b3dm"}; + + std::map> + mockCompletedRequests; + for (const auto& file : files) { + std::unique_ptr mockCompletedResponse = + std::make_unique( + static_cast(200), + "doesn't matter", + CesiumAsync::HttpHeaders{}, + readFile(testDataPath / file)); + mockCompletedRequests.insert( + {file, + std::make_shared( + "GET", + file, + CesiumAsync::HttpHeaders{}, + std::move(mockCompletedResponse))}); + } + + std::shared_ptr mockAssetAccessor = + std::make_shared(std::move(mockCompletedRequests)); + TilesetExternals tilesetExternals{ + mockAssetAccessor, + std::make_shared(), + AsyncSystem(std::make_shared()), + nullptr}; + + // create tileset and call updateView() to give it a chance to load + Tileset tileset(tilesetExternals, "tileset.json"); + initializeTileset(tileset); + + // Load until complete + ViewUpdateResult updateResult; + ViewState viewState = zoomToTileset(tileset); + while (tileset.getNumberOfTilesLoaded() == 0 || + tileset.computeLoadProgress() < 100.0f) { + updateResult = tileset.updateView({viewState}); + } + + // All three tiles should be rendered. + CHECK(updateResult.tilesToRenderThisFrame.size() == 3); + + // Zoom way out + std::optional position = viewState.getPositionCartographic(); + REQUIRE(position); + position->height += 100000; + + ViewState zoomedOut = ViewState::create( + Ellipsoid::WGS84.cartographicToCartesian(*position), + viewState.getDirection(), + viewState.getUp(), + viewState.getViewportSize(), + viewState.getHorizontalFieldOfView(), + viewState.getVerticalFieldOfView()); + updateResult = tileset.updateView({zoomedOut}); + + // Only the root tile is visible now, and the other two are fading out. + CHECK(updateResult.tilesToRenderThisFrame.size() == 1); + CHECK(updateResult.tilesFadingOut.size() == 2); +} diff --git a/Cesium3DTilesSelection/test/data/AdditiveThreeLevels/content.b3dm b/Cesium3DTilesSelection/test/data/AdditiveThreeLevels/content.b3dm new file mode 100644 index 0000000000000000000000000000000000000000..8cb958955234e1ecaac3837ecbf0162b8fd47bf8 GIT binary patch literal 9680 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 a{z>^DzQ7@TjxQ;Hj<4`F4pTggZ}1 Date: Wed, 30 Aug 2023 10:59:05 -0400 Subject: [PATCH 246/421] Update CesiumJsonReader/include/CesiumJsonReader/JsonReader.h --- CesiumJsonReader/include/CesiumJsonReader/JsonReader.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CesiumJsonReader/include/CesiumJsonReader/JsonReader.h b/CesiumJsonReader/include/CesiumJsonReader/JsonReader.h index 0200ace02..7dd67acdf 100644 --- a/CesiumJsonReader/include/CesiumJsonReader/JsonReader.h +++ b/CesiumJsonReader/include/CesiumJsonReader/JsonReader.h @@ -43,7 +43,7 @@ template struct ReadJsonResult { class CESIUMJSONREADER_API JsonReader { public: /** - * @brief Reads JSON from a byte buffer into a statically-type class. + * @brief Reads JSON from a byte buffer into a statically-typed class. * * @param data The buffer from which to read JSON. * @param handler The handler to receive the top-level JSON object. This From a431793d7f3a8a07a7a34223d0afc1302f462b6d Mon Sep 17 00:00:00 2001 From: Brian Langevin <130494071+csciguy8@users.noreply.github.com> Date: Wed, 30 Aug 2023 10:28:42 -0600 Subject: [PATCH 247/421] Update README.md Add more detail about generating projects from CMake for use with Visual Studio --- README.md | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 28c45d195..00eb09fdb 100644 --- a/README.md +++ b/README.md @@ -62,9 +62,7 @@ If you forget the `--recurse-submodules`, nothing will work because the git subm git submodule update --init --recursive ``` -#### Compile - -You can then build cesium-native on the command-line with CMake: +#### Compile from command line ```bash ## Windows compilation using Visual Studio @@ -77,7 +75,28 @@ cmake -B build -S . cmake --build build ``` -Or, you can easily build it in Visual Studio Code with the `CMake Tools` extension installed. It should prompt you to generate project files from CMake. On Windows, choose `Visual Studio 2017 Release - amd64` as the kit to build. Or choose an appropriate kit for your platform. Then press Ctrl-Shift-P and execute the `CMake: Build` task or press F7. +#### Compile from Visual Studio Code + +1) Install the `CMake Tools` extension. It should prompt you to generate project files from CMake. +2) On Windows, choose `Visual Studio 2017 Release - amd64` as the kit to build. Or choose an appropriate kit for your platform. +3) Then press Ctrl-Shift-P and execute the `CMake: Build` task or press F7. + +#### Compile with any Visual Studio version using CMake generated projects + +1) Open the CMake UI (cmake-gui) +2) Under "Where is the source code", point to your repo +3) Specify your output folder in "Where to build the binaries" +4) Click "Configure". +5) Under "Specify the generator for this project", choose the VS version on your system +6) Click Finish, wait for the process to finish +7) Click Generate + +Look for cesium-native.sln in your output folder. + +Unit tests can also be run from this solution, under the cesium-native-tests project. + +![image](https://github.com/CesiumGS/cesium-native/assets/130494071/4d398bfc-f770-49d4-8ef5-a995096ad4a1) + #### Generate Documentation From c7f01d437c7d6d24edcf59e4862ec7515441dc39 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Wed, 30 Aug 2023 15:28:06 -0400 Subject: [PATCH 248/421] Warn about EXT_feature_metadata --- CesiumGltfReader/src/GltfReader.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CesiumGltfReader/src/GltfReader.cpp b/CesiumGltfReader/src/GltfReader.cpp index 080896bc1..c4045c190 100644 --- a/CesiumGltfReader/src/GltfReader.cpp +++ b/CesiumGltfReader/src/GltfReader.cpp @@ -225,6 +225,18 @@ void postprocess( const GltfReaderOptions& options) { Model& model = readGltf.model.value(); + auto extFeatureMetadataIter = std::find( + model.extensionsUsed.begin(), + model.extensionsUsed.end(), + "EXT_feature_metadata"); + + if (extFeatureMetadataIter != model.extensionsUsed.end()) { + readGltf.warnings.emplace_back( + "glTF contains EXT_feature_metadata extension, which is no longer " + "supported. The model will still be loaded, but views cannot be " + "constructed on its metadata."); + } + if (options.decodeDataUrls) { decodeDataUrls(reader, readGltf, options); } From 09e84e2c52b3babe49d396080791f9f6595cd068 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Wed, 30 Aug 2023 15:28:20 -0400 Subject: [PATCH 249/421] Add getters for name, semantic, description --- .../CesiumGltf/PropertyAttributeView.h | 8 + .../include/CesiumGltf/PropertyTableView.h | 8 + .../include/CesiumGltf/PropertyTextureView.h | 8 + CesiumGltf/include/CesiumGltf/PropertyView.h | 402 ++++++++++++++---- 4 files changed, 345 insertions(+), 81 deletions(-) diff --git a/CesiumGltf/include/CesiumGltf/PropertyAttributeView.h b/CesiumGltf/include/CesiumGltf/PropertyAttributeView.h index af4949567..aac020193 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyAttributeView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyAttributeView.h @@ -72,6 +72,14 @@ class PropertyAttributeView { */ PropertyAttributeViewStatus status() const noexcept { return this->_status; } + /** + * @brief Gets the name of the property attribute being viewed. Returns + * std::nullopt if no name was specified. + */ + const std::optional& name() const noexcept { + return _pPropertyAttribute->name; + } + /** * @brief Finds the {@link ClassProperty} that * describes the type information of the property with the specified name. diff --git a/CesiumGltf/include/CesiumGltf/PropertyTableView.h b/CesiumGltf/include/CesiumGltf/PropertyTableView.h index b92e991ba..c4c89cdf0 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTableView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTableView.h @@ -71,6 +71,14 @@ class PropertyTableView { */ PropertyTableViewStatus status() const noexcept { return _status; } + /** + * @brief Gets the name of the property table being viewed. Returns + * std::nullopt if no name was specified. + */ + const std::optional& name() const noexcept { + return _pPropertyTable->name; + } + /** * @brief Get the number of elements in this PropertyTableView. If the * view is valid, this returns {@link PropertyTable::count}. Otherwise, this returns 0. diff --git a/CesiumGltf/include/CesiumGltf/PropertyTextureView.h b/CesiumGltf/include/CesiumGltf/PropertyTextureView.h index ed2546110..4573ddf77 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTextureView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTextureView.h @@ -68,6 +68,14 @@ class PropertyTextureView { */ PropertyTextureViewStatus status() const noexcept { return this->_status; } + /** + * @brief Gets the name of the property texture being viewed. Returns + * std::nullopt if no name was specified. + */ + const std::optional& name() const noexcept { + return _pPropertyTexture->name; + } + /** * @brief Finds the {@link ClassProperty} that * describes the type information of the property with the specified name. diff --git a/CesiumGltf/include/CesiumGltf/PropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyView.h index 5257de32b..ec6f05730 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyView.h @@ -258,6 +258,9 @@ template class PropertyView { */ PropertyView() : _status(PropertyViewStatus::ErrorNonexistentProperty), + _name(std::nullopt), + _semantic(std::nullopt), + _description(std::nullopt), _offset(std::nullopt), _scale(std::nullopt), _max(std::nullopt), @@ -271,6 +274,9 @@ template class PropertyView { */ PropertyView(const ClassProperty& classProperty) : _status(validatePropertyType(classProperty)), + _name(classProperty.name), + _semantic(classProperty.semantic), + _description(classProperty.description), _offset(std::nullopt), _scale(std::nullopt), _max(std::nullopt), @@ -328,6 +334,9 @@ template class PropertyView { */ PropertyView(PropertyViewStatusType status) : _status(status), + _name(std::nullopt), + _semantic(std::nullopt), + _description(std::nullopt), _offset(std::nullopt), _scale(std::nullopt), _max(std::nullopt), @@ -393,6 +402,30 @@ template class PropertyView { */ PropertyViewStatusType status() const noexcept { return _status; } + /** + * @brief Gets the name of the property being viewed. Returns std::nullopt if + * no description was provided. + */ + const std::optional& name() const noexcept { return _name; } + + /** + * @brief Gets the semantic of the property being viewed. The semantic is an + * identifier that describes how this property should be interpreted, and + * cannot be used by other properties in the class. Returns std::nullopt if no + * semantic was specified. + */ + const std::optional& semantic() const noexcept { + return _semantic; + } + + /** + * @brief Gets the description of the property being viewed. Returns + * std::nullopt if no name was specified. + */ + const std::optional& description() const noexcept { + return _description; + } + /** * @brief Get the element count of the fixed-length arrays in this property. * Only applicable when the property is an array type. @@ -403,8 +436,6 @@ template class PropertyView { /** * @brief Whether this property has a normalized integer type. - * - * @return Whether this property has a normalized integer type. */ bool normalized() const noexcept { return false; } @@ -460,6 +491,9 @@ template class PropertyView { * in the property wherever it appears. Also known as a sentinel value. This * is given as the plain property value, without the transforms from the * normalized, offset, and scale properties. + * + * @returns The property's "no data" value, or std::nullopt if it was not + * specified. */ std::optional noData() const noexcept { return _noData; } @@ -467,6 +501,9 @@ template class PropertyView { * @brief Gets the default value to use when encountering a "no data" value or * an omitted property. The value is given in its final form, taking the * effect of normalized, offset, and scale properties into account. + * + * @returns The property's default value, or std::nullopt if it was not + * specified. */ std::optional defaultValue() const noexcept { return _defaultValue; @@ -476,6 +513,10 @@ template class PropertyView { PropertyViewStatusType _status; private: + std::optional _name; + std::optional _semantic; + std::optional _description; + bool _required; std::optional _offset; @@ -603,6 +644,9 @@ template class PropertyView { */ PropertyView() : _status(PropertyViewStatus::ErrorNonexistentProperty), + _name(std::nullopt), + _semantic(std::nullopt), + _description(std::nullopt), _offset(std::nullopt), _scale(std::nullopt), _max(std::nullopt), @@ -616,6 +660,9 @@ template class PropertyView { */ PropertyView(const ClassProperty& classProperty) : _status(validatePropertyType(classProperty)), + _name(classProperty.name), + _semantic(classProperty.semantic), + _description(classProperty.description), _offset(std::nullopt), _scale(std::nullopt), _max(std::nullopt), @@ -670,6 +717,9 @@ template class PropertyView { */ PropertyView(PropertyViewStatusType status) : _status(status), + _name(std::nullopt), + _semantic(std::nullopt), + _description(std::nullopt), _offset(std::nullopt), _scale(std::nullopt), _max(std::nullopt), @@ -728,13 +778,29 @@ template class PropertyView { public: /** - * @brief Gets the status of this property view, indicating whether an error - * occurred. - * - * @return The status of this property view. + * @copydoc PropertyView::status */ PropertyViewStatusType status() const noexcept { return _status; } + /** + * @copydoc PropertyView::name + */ + const std::optional& name() const noexcept { return _name; } + + /** + * @copydoc PropertyView::semantic + */ + const std::optional& semantic() const noexcept { + return _semantic; + } + + /** + * @copydoc PropertyView::description + */ + const std::optional& description() const noexcept { + return _description; + } + /** * @copydoc PropertyView::arrayCount */ @@ -786,6 +852,10 @@ template class PropertyView { PropertyViewStatusType _status; private: + std::optional _name; + std::optional _semantic; + std::optional _description; + bool _required; std::optional _offset; @@ -884,6 +954,9 @@ template <> class PropertyView { */ PropertyView() : _status(PropertyViewStatus::ErrorNonexistentProperty), + _name(std::nullopt), + _semantic(std::nullopt), + _description(std::nullopt), _required(false), _defaultValue(std::nullopt) {} @@ -892,6 +965,9 @@ template <> class PropertyView { */ PropertyView(const ClassProperty& classProperty) : _status(validatePropertyType(classProperty)), + _name(classProperty.name), + _semantic(classProperty.semantic), + _description(classProperty.description), _required(classProperty.required), _defaultValue(std::nullopt) { if (_status != PropertyViewStatus::Valid) { @@ -918,7 +994,12 @@ template <> class PropertyView { * @param status The value of {@link PropertyViewStatus} indicating the error with the property. */ PropertyView(PropertyViewStatusType status) - : _status(status), _required(false), _defaultValue(std::nullopt) {} + : _status(status), + _name(std::nullopt), + _semantic(std::nullopt), + _description(std::nullopt), + _required(false), + _defaultValue(std::nullopt) {} /** * @brief Constructs a property instance from a property table property and @@ -935,6 +1016,25 @@ template <> class PropertyView { */ PropertyViewStatusType status() const noexcept { return _status; } + /** + * @copydoc PropertyView::name + */ + const std::optional& name() const noexcept { return _name; } + + /** + * @copydoc PropertyView::semantic + */ + const std::optional& semantic() const noexcept { + return _semantic; + } + + /** + * @copydoc PropertyView::description + */ + const std::optional& description() const noexcept { + return _description; + } + /** * @copydoc PropertyView::arrayCount */ @@ -984,6 +1084,10 @@ template <> class PropertyView { PropertyViewStatusType _status; private: + std::optional _name; + std::optional _semantic; + std::optional _description; + bool _required; std::optional _defaultValue; @@ -1008,6 +1112,9 @@ template <> class PropertyView { */ PropertyView() : _status(PropertyViewStatus::ErrorNonexistentProperty), + _name(std::nullopt), + _semantic(std::nullopt), + _description(std::nullopt), _required(false), _noData(std::nullopt), _defaultValue(std::nullopt) {} @@ -1017,6 +1124,9 @@ template <> class PropertyView { */ PropertyView(const ClassProperty& classProperty) : _status(validatePropertyType(classProperty)), + _name(classProperty.name), + _semantic(classProperty.semantic), + _description(classProperty.description), _required(classProperty.required), _noData(std::nullopt), _defaultValue(std::nullopt) { @@ -1057,6 +1167,9 @@ template <> class PropertyView { */ PropertyView(PropertyViewStatusType status) : _status(status), + _name(std::nullopt), + _semantic(std::nullopt), + _description(std::nullopt), _required(false), _noData(std::nullopt), _defaultValue(std::nullopt) {} @@ -1076,6 +1189,25 @@ template <> class PropertyView { */ PropertyViewStatusType status() const noexcept { return _status; } + /** + * @copydoc PropertyView::name + */ + const std::optional& name() const noexcept { return _name; } + + /** + * @copydoc PropertyView::semantic + */ + const std::optional& semantic() const noexcept { + return _semantic; + } + + /** + * @copydoc PropertyView::description + */ + const std::optional& description() const noexcept { + return _description; + } + /** * @copydoc PropertyView::arrayCount */ @@ -1139,6 +1271,10 @@ template <> class PropertyView { PropertyViewStatusType _status; private: + std::optional _name; + std::optional _semantic; + std::optional _description; + bool _required; std::optional _noData; std::optional _defaultValue; @@ -1174,6 +1310,9 @@ class PropertyView, false> { */ PropertyView() : _status(PropertyViewStatus::ErrorNonexistentProperty), + _name(std::nullopt), + _semantic(std::nullopt), + _description(std::nullopt), _count(0), _offset(std::nullopt), _scale(std::nullopt), @@ -1189,6 +1328,9 @@ class PropertyView, false> { PropertyView(const ClassProperty& classProperty) : _status(validateArrayPropertyType>( classProperty)), + _name(classProperty.name), + _semantic(classProperty.semantic), + _description(classProperty.description), _count(_count = classProperty.count ? *classProperty.count : 0), _offset(std::nullopt), _scale(std::nullopt), @@ -1243,6 +1385,9 @@ class PropertyView, false> { */ PropertyView(PropertyViewStatusType status) : _status(status), + _name(std::nullopt), + _semantic(std::nullopt), + _description(std::nullopt), _count(0), _offset(std::nullopt), _scale(std::nullopt), @@ -1290,6 +1435,25 @@ class PropertyView, false> { */ PropertyViewStatusType status() const noexcept { return _status; } + /** + * @copydoc PropertyView::name + */ + const std::optional& name() const noexcept { return _name; } + + /** + * @copydoc PropertyView::semantic + */ + const std::optional& semantic() const noexcept { + return _semantic; + } + + /** + * @copydoc PropertyView::description + */ + const std::optional& description() const noexcept { + return _description; + } + /** * @copydoc PropertyView::arrayCount */ @@ -1382,6 +1546,10 @@ class PropertyView, false> { PropertyViewStatusType _status; private: + std::optional _name; + std::optional _semantic; + std::optional _description; + int64_t _count; std::optional> _offset; @@ -1538,6 +1706,9 @@ class PropertyView, true> { */ PropertyView() : _status(PropertyViewStatus::ErrorNonexistentProperty), + _name(std::nullopt), + _semantic(std::nullopt), + _description(std::nullopt), _count(0), _offset(std::nullopt), _scale(std::nullopt), @@ -1553,6 +1724,9 @@ class PropertyView, true> { PropertyView(const ClassProperty& classProperty) : _status(validateArrayPropertyType>( classProperty)), + _name(classProperty.name), + _semantic(classProperty.semantic), + _description(classProperty.description), _count(_count = classProperty.count ? *classProperty.count : 0), _offset(std::nullopt), _scale(std::nullopt), @@ -1607,6 +1781,9 @@ class PropertyView, true> { */ PropertyView(PropertyViewStatusType status) : _status(status), + _name(std::nullopt), + _semantic(std::nullopt), + _description(std::nullopt), _count(0), _offset(std::nullopt), _scale(std::nullopt), @@ -1654,6 +1831,25 @@ class PropertyView, true> { */ PropertyViewStatusType status() const noexcept { return _status; } + /** + * @copydoc PropertyView::name + */ + const std::optional& name() const noexcept { return _name; } + + /** + * @copydoc PropertyView::semantic + */ + const std::optional& semantic() const noexcept { + return _semantic; + } + + /** + * @copydoc PropertyView::description + */ + const std::optional& description() const noexcept { + return _description; + } + /** * @copydoc PropertyView::arrayCount */ @@ -1747,6 +1943,10 @@ class PropertyView, true> { PropertyViewStatusType _status; private: + std::optional _name; + std::optional _semantic; + std::optional _description; + int64_t _count; std::optional> _offset; @@ -1872,6 +2072,9 @@ template <> class PropertyView> { */ PropertyView() : _status(PropertyViewStatus::ErrorNonexistentProperty), + _name(std::nullopt), + _semantic(std::nullopt), + _description(std::nullopt), _count(0), _required(false), _defaultValue(), @@ -1883,6 +2086,9 @@ template <> class PropertyView> { PropertyView(const ClassProperty& classProperty) : _status( validateArrayPropertyType>(classProperty)), + _name(classProperty.name), + _semantic(classProperty.semantic), + _description(classProperty.description), _count(classProperty.count ? *classProperty.count : 0), _required(classProperty.required), _defaultValue(), @@ -1912,6 +2118,9 @@ template <> class PropertyView> { */ PropertyView(PropertyViewStatusType status) : _status(status), + _name(std::nullopt), + _semantic(std::nullopt), + _description(std::nullopt), _count(0), _required(false), _defaultValue(), @@ -1932,6 +2141,25 @@ template <> class PropertyView> { */ PropertyViewStatusType status() const noexcept { return _status; } + /** + * @copydoc PropertyView::name + */ + const std::optional& name() const noexcept { return _name; } + + /** + * @copydoc PropertyView::semantic + */ + const std::optional& semantic() const noexcept { + return _semantic; + } + + /** + * @copydoc PropertyView::description + */ + const std::optional& description() const noexcept { + return _description; + } + /** * @copydoc PropertyView::arrayCount */ @@ -2002,6 +2230,10 @@ template <> class PropertyView> { PropertyViewStatusType _status; private: + std::optional _name; + std::optional _semantic; + std::optional _description; + int64_t _count; bool _required; @@ -2059,16 +2291,13 @@ template <> class PropertyView> { */ PropertyView() : _status(PropertyViewStatus::ErrorNonexistentProperty), + _name(std::nullopt), + _semantic(std::nullopt), + _description(std::nullopt), _count(0), _required(false), _noData(), - _noDataOffsets(), - _noDataOffsetType(PropertyComponentType::None), - _noDataSize(0), - _defaultValue(), - _defaultValueOffsets(), - _defaultValueOffsetType(PropertyComponentType::None), - _defaultValueSize(0) {} + _defaultValue() {} /** * @brief Constructs a property instance from a class definition only. @@ -2076,30 +2305,23 @@ template <> class PropertyView> { PropertyView(const ClassProperty& classProperty) : _status(validateArrayPropertyType>( classProperty)), + _name(classProperty.name), + _semantic(classProperty.semantic), + _description(classProperty.description), _count(classProperty.count ? *classProperty.count : 0), _required(classProperty.required), _noData(), - _noDataOffsets(), - _noDataOffsetType(PropertyComponentType::None), - _noDataSize(0), - _defaultValue(), - _defaultValueOffsets(), - _defaultValueOffsetType(PropertyComponentType::None), - _defaultValueSize(0) { + _defaultValue() { if (_status != PropertyViewStatus::Valid) { return; } if (classProperty.noData) { if (!_required) { - _noData = getStringArrayValue( - *classProperty.noData, - _noDataOffsets, - _noDataOffsetType, - _noDataSize); + _noData = getStringArrayValue(*classProperty.noData); } - if (_noDataSize == 0 || (_count > 0 && _noDataSize != _count)) { + if (_noData.size == 0 || (_count > 0 && _noData.size != _count)) { _status = PropertyViewStatus::ErrorInvalidNoDataValue; return; } @@ -2107,14 +2329,11 @@ template <> class PropertyView> { if (classProperty.defaultProperty) { if (!_required) { - _defaultValue = getStringArrayValue( - *classProperty.defaultProperty, - _defaultValueOffsets, - _defaultValueOffsetType, - _defaultValueSize); + _defaultValue = getStringArrayValue(*classProperty.defaultProperty); } - if (_defaultValueSize == 0 || (_count > 0 && _noDataSize != _count)) { + if (_defaultValue.size == 0 || + (_count > 0 && _defaultValue.size != _count)) { // The value was specified but something went wrong. _status = PropertyViewStatus::ErrorInvalidDefaultValue; return; @@ -2130,16 +2349,13 @@ template <> class PropertyView> { */ PropertyView(PropertyViewStatusType status) : _status(status), + _name(std::nullopt), + _semantic(std::nullopt), + _description(std::nullopt), _count(0), _required(false), _noData(), - _noDataOffsets(), - _noDataOffsetType(PropertyComponentType::None), - _noDataSize(0), - _defaultValue(), - _defaultValueOffsets(), - _defaultValueOffsetType(PropertyComponentType::None), - _defaultValueSize(0) {} + _defaultValue() {} /** * @brief Constructs a property instance from a property table property @@ -2156,6 +2372,25 @@ template <> class PropertyView> { */ PropertyViewStatusType status() const noexcept { return _status; } + /** + * @copydoc PropertyView::name + */ + const std::optional& name() const noexcept { return _name; } + + /** + * @copydoc PropertyView::semantic + */ + const std::optional& semantic() const noexcept { + return _semantic; + } + + /** + * @copydoc PropertyView::description + */ + const std::optional& description() const noexcept { + return _description; + } + /** * @copydoc PropertyView::arrayCount */ @@ -2203,14 +2438,14 @@ template <> class PropertyView> { * @copydoc PropertyView::noData */ std::optional> noData() const noexcept { - if (_noDataSize > 0) { + if (_noData.size > 0) { return PropertyArrayView( - gsl::span(_noData.data(), _noData.size()), + gsl::span(_noData.data.data(), _noData.data.size()), gsl::span( - _noDataOffsets.data(), - _noDataOffsets.size()), - _noDataOffsetType, - _noDataSize); + _noData.offsets.data(), + _noData.offsets.size()), + _noData.offsetType, + _noData.size); } return std::nullopt; @@ -2221,16 +2456,16 @@ template <> class PropertyView> { */ std::optional> defaultValue() const noexcept { - if (_defaultValueSize > 0) { + if (_defaultValue.size > 0) { return PropertyArrayView( gsl::span( - _defaultValue.data(), - _defaultValue.size()), + _defaultValue.data.data(), + _defaultValue.data.size()), gsl::span( - _defaultValueOffsets.data(), - _defaultValueOffsets.size()), - _defaultValueOffsetType, - _defaultValueSize); + _defaultValue.offsets.data(), + _defaultValue.offsets.size()), + _defaultValue.offsetType, + _defaultValue.size); } return std::nullopt; @@ -2243,23 +2478,25 @@ template <> class PropertyView> { int64_t _count; bool _required; - std::vector _noData; - std::vector _noDataOffsets; - PropertyComponentType _noDataOffsetType; - int64_t _noDataSize; + std::optional _name; + std::optional _semantic; + std::optional _description; - std::vector _defaultValue; - std::vector _defaultValueOffsets; - PropertyComponentType _defaultValueOffsetType; - int64_t _defaultValueSize; + struct StringArrayValue { + std::vector data; + std::vector offsets; + PropertyComponentType offsetType = PropertyComponentType::None; + int64_t size = 0; + }; - static std::vector getStringArrayValue( - const CesiumUtility::JsonValue& jsonValue, - std::vector& offsets, - PropertyComponentType& offsetType, - int64_t& size) { + StringArrayValue _noData; + StringArrayValue _defaultValue; + + static StringArrayValue + getStringArrayValue(const CesiumUtility::JsonValue& jsonValue) { + StringArrayValue result; if (!jsonValue.isArray()) { - return std::vector(); + return result; } std::vector strings; @@ -2273,7 +2510,7 @@ template <> class PropertyView> { for (size_t i = 0; i < array.size(); i++) { if (!array[i].isString()) { // The entire array is invalidated; return. - return std::vector(); + return result; } const std::string& string = array[i].getString(); @@ -2282,32 +2519,35 @@ template <> class PropertyView> { } uint64_t totalLength = stringOffsets.back(); - std::vector values(totalLength); + result.data.resize(totalLength); for (size_t i = 0; i < strings.size(); ++i) { std::memcpy( - values.data() + stringOffsets[i], + result.data.data() + stringOffsets[i], strings[i].data(), strings[i].size()); }; if (totalLength <= std::numeric_limits::max()) { - offsets = narrowOffsetsBuffer(stringOffsets); - offsetType = PropertyComponentType::Uint8; + result.offsets = narrowOffsetsBuffer(stringOffsets); + result.offsetType = PropertyComponentType::Uint8; } else if (totalLength <= std::numeric_limits::max()) { - offsets = narrowOffsetsBuffer(stringOffsets); - offsetType = PropertyComponentType::Uint16; + result.offsets = narrowOffsetsBuffer(stringOffsets); + result.offsetType = PropertyComponentType::Uint16; } else if (totalLength <= std::numeric_limits::max()) { - offsets = narrowOffsetsBuffer(stringOffsets); - offsetType = PropertyComponentType::Uint32; + result.offsets = narrowOffsetsBuffer(stringOffsets); + result.offsetType = PropertyComponentType::Uint32; } else { - offsets.resize(stringOffsets.size() * sizeof(uint64_t)); - std::memcpy(offsets.data(), stringOffsets.data(), offsets.size()); - offsetType = PropertyComponentType::Uint64; + result.offsets.resize(stringOffsets.size() * sizeof(uint64_t)); + std::memcpy( + result.offsets.data(), + stringOffsets.data(), + result.offsets.size()); + result.offsetType = PropertyComponentType::Uint64; } - size = static_cast(strings.size()); + result.size = static_cast(strings.size()); - return values; + return result; } template From bdf630b86fe170649c528eae2c9c17a98703c165 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Wed, 30 Aug 2023 18:25:03 -0400 Subject: [PATCH 250/421] Add support for sentinel values in batch tables --- .../BatchTableToGltfStructuralMetadata.cpp | 268 ++++++++++++++++-- ...gradeBatchTableToExtStructuralMetadata.cpp | 227 ++++++++++++--- 2 files changed, 433 insertions(+), 62 deletions(-) diff --git a/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp b/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp index b78b73764..c475c4ccc 100644 --- a/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp +++ b/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp @@ -71,6 +71,16 @@ struct MaskedType { isFloat64 &= source.isFloat64; isBool &= source.isBool; } + + /** + * Whether this is incompatible with every type. Fully-incompatible types will + * be treated as strings. + */ + bool isIncompatible() const noexcept { + return !isInt8 && !isUint8 && !isInt16 && !isUint16 && !isInt32 && + !isUint32 && !isInt64 && !isUint64 && !isFloat32 && !isFloat64 && + !isBool; + } }; /** @@ -109,22 +119,49 @@ struct MaskedArrayType { minArrayCount = glm::min(minArrayCount, source.minArrayCount); maxArrayCount = glm::max(maxArrayCount, source.maxArrayCount); } + + /** + * Whether this is incompatible with every type. Fully-incompatible types will + * be treated as strings. + */ + bool isIncompatible() const noexcept { return elementType.isIncompatible(); } }; /** * Indicates a batch table property's compatibility with C++ types. */ struct CompatibleTypes { - // std::monostate represents "complete" compatibility, in that nothing has - // been determined to be incompatible yet. Once something is either a - // MaskedType or MaskedArrayType, they are considered incompatible with the - // other type. private: + /** + * std::monostate represents "complete" compatibility, in that nothing has + * been determined to be incompatible yet. Once something is either a + * MaskedType or MaskedArrayType, they are considered incompatible with the + * other type. + */ std::variant _type; + /** + * Whether the type encountered a null value. + */ + bool _hasNullValue = false; + + /** + * These booleans are for possible sentinel values for the property. If a + * property contains null values, but all other non-null values are of the + * same type, then we should simply indicate the null value with a "noData" + * value in an EXT_structural_metadata property. We reserve several values as + * possible sentinel values: + * + * - 0, for signed or unsigned integers + * - -1, for signed integers + * - "null", for strings + */ + bool _canUseZeroSentinel = true; + bool _canUseNegativeOneSentinel = true; + bool _canUseNullStringSentinel = true; + public: CompatibleTypes() : _type(){}; - CompatibleTypes(const MaskedType& maskedType) : _type(maskedType){}; CompatibleTypes(const MaskedArrayType& maskedArrayType) : _type(maskedArrayType){}; @@ -132,20 +169,71 @@ struct CompatibleTypes { /** * Whether this is exclusively compatible with array types. */ - bool isExclusivelyArray() const { + bool isExclusivelyArray() const noexcept { return std::holds_alternative(_type); } + /** + * Whether this is compatible with at least one unsigned integer type. Does + * not count arrays. + */ + bool isUnsignedInteger() const noexcept { + if (std::holds_alternative(_type)) { + return false; + } + + if (std::holds_alternative(_type)) { + return true; + } + + MaskedType type = std::get(_type); + return type.isUint8 || type.isUint16 || type.isUint32 || type.isUint64; + } + + /** + * Whether this is compatible with at least one signed integer type. Does not + * count arrays. + */ + bool isSignedInteger() const noexcept { + if (std::holds_alternative(_type)) { + return false; + } + + if (std::holds_alternative(_type)) { + return true; + } + + MaskedType type = std::get(_type); + return type.isInt8 || type.isInt16 || type.isInt32 || type.isInt64; + } + + /** + * Whether this is incompatible with every type. Fully-incompatible types will + * be treated as strings. + */ + bool isIncompatible() const noexcept { + if (std::holds_alternative(_type)) { + return std::get(_type).isIncompatible(); + } + + if (std::holds_alternative(_type)) { + return std::get(_type).isIncompatible(); + } + + // std::monostate means compatibility with all types. + return false; + } + /** * Marks as incompatible with every type. Fully-incompatible types will be * treated as strings. */ - void makeIncompatible() { _type = MaskedType(false); } + void makeIncompatible() noexcept { _type = MaskedType(false); } /** * Merges a MaskedType into this CompatibleTypes. */ - void operator&=(const MaskedType& inMaskedType) { + void operator&=(const MaskedType& inMaskedType) noexcept { if (std::holds_alternative(_type)) { MaskedType& maskedType = std::get(_type); maskedType &= inMaskedType; @@ -163,7 +251,7 @@ struct CompatibleTypes { /** * Merges a MaskedArrayType into this CompatibleTypes. */ - void operator&=(const MaskedArrayType& inArrayType) { + void operator&=(const MaskedArrayType& inArrayType) noexcept { if (std::holds_alternative(_type)) { MaskedArrayType& arrayType = std::get(_type); arrayType &= inArrayType; @@ -181,7 +269,7 @@ struct CompatibleTypes { /** * Merges another CompatibleTypes into this one. */ - void operator&=(const CompatibleTypes& inCompatibleTypes) { + void operator&=(const CompatibleTypes& inCompatibleTypes) noexcept { if (std::holds_alternative(inCompatibleTypes._type)) { // The other CompatibleTypes is compatible with everything, so it does not // change this one. @@ -205,7 +293,7 @@ struct CompatibleTypes { * is only compatible with arrays, this will return an incompatible * MaskedType. */ - MaskedType toMaskedType() const { + MaskedType toMaskedType() const noexcept { if (std::holds_alternative(_type)) { return std::get(_type); } @@ -219,7 +307,7 @@ struct CompatibleTypes { * CompatibleTypes is not compatible with arrays, this will return an * incompatible MaskedArrayType. */ - MaskedArrayType toMaskedArrayType() const { + MaskedArrayType toMaskedArrayType() const noexcept { if (std::holds_alternative(_type)) { return std::get(_type); } @@ -227,6 +315,80 @@ struct CompatibleTypes { bool isNonArray = std::holds_alternative(_type); return MaskedArrayType(!isNonArray); } + + /** + * Gets whether the type includes a null value. + */ + bool hasNullValue() const noexcept { return _hasNullValue; } + + /** + * Sets whether the type includes a null value. If a null value has been + * encountered, a sentinel value can try to be provided. + */ + void setHasNullValue(bool value) noexcept { _hasNullValue = value; } + + /** + * Gets the first possible sentinel value for this type. If no sentinel value + * can be used, this returns std::nullopt. + */ + const std::optional + getSentinelValue() const noexcept { + if (isUnsignedInteger()) { + return _canUseZeroSentinel + ? std::make_optional(0) + : std::nullopt; + } + + if (isSignedInteger()) { + if (_canUseZeroSentinel) { + return 0; + } + + if (_canUseNegativeOneSentinel) { + return -1; + } + } + + if (isIncompatible()) { + if (_canUseNullStringSentinel) { + return "null"; + } + } + + return std::nullopt; + } + + /** + * Removes any sentinel values that are incompatible with the given value + * type. This also removes the sentinel values that equal the given value. + * + * This is helpful for when a property contains a sentinel value as non-null + * data; the sentinel value can then be removed from consideration. + */ + void removeSentinelValues(CesiumUtility::JsonValue value) noexcept { + if (value.isNumber()) { + if (value.isUint64()) { + _canUseZeroSentinel &= (value.getUint64() != 0); + } + + if (value.isInt64()) { + auto intValue = value.getInt64(); + _canUseZeroSentinel &= (intValue != 0); + _canUseNegativeOneSentinel &= (intValue != -1); + } + + return; + } + + if (value.isString()) { + auto stringValue = value.getString(); + if (stringValue == "null") { + _canUseNullStringSentinel = false; + } + + return; + } + } }; struct BinaryProperty { @@ -410,9 +572,24 @@ CompatibleTypes findCompatibleTypes(const TValueGetter& propertyValue) { booleanType.isBool = true; compatibleTypes &= booleanType; - } else if (it->IsNumber()) { + continue; + } + + if (it->IsNumber()) { compatibleTypes &= getCompatibleTypesForNumber(it); - } else if (it->IsArray()) { + + // Check that the value does not equal one of the possible sentinel + // values. + if (it->IsInt64()) { + compatibleTypes.removeSentinelValues(it->GetInt64()); + } else if (it->IsUint64()) { + compatibleTypes.removeSentinelValues(it->GetUint64()); + } + + continue; + } + + if (it->IsArray()) { // Iterate over all of the elements in the array // and determine their compatible type. CompatibleTypes arrayElementCompatibleTypes = @@ -425,10 +602,30 @@ CompatibleTypes findCompatibleTypes(const TValueGetter& propertyValue) { MaskedArrayType arrayType(elementType, it->Size(), it->Size()); compatibleTypes &= arrayType; - } else { - // A string, null, or something else. - compatibleTypes.makeIncompatible(); + + continue; } + + if (it->IsNull()) { + compatibleTypes.setHasNullValue(true); + + // If the value is null, check if there is still a possible sentinel + // values. If none exist, default the type to string. + if (!compatibleTypes.getSentinelValue()) { + compatibleTypes.makeIncompatible(); + } + + continue; + } + + // If this is a string, check that the value does not equal one of the + // possible sentinel values. + if (it->IsString()) { + compatibleTypes.removeSentinelValues(it->GetString()); + } + + // If this code is reached, the value is a string or something else. + compatibleTypes.makeIncompatible(); } return compatibleTypes; @@ -462,13 +659,18 @@ void updateExtensionWithJsonStringProperty( rapidjsonOffsets.reserve(static_cast(propertyTable.count + 1)); rapidjsonOffsets.emplace_back(0); + std::optional noDataValue; + if (classProperty.noData) { + noDataValue = classProperty.noData->getString(); + } + auto it = propertyValue.begin(); for (int64_t i = 0; i < propertyTable.count; ++i) { if (it == propertyValue.end()) { rapidjsonOffsets.emplace_back(rapidjsonStrBuffer.GetLength()); continue; } - if (!it->IsString()) { + if (!it->IsString() || (it->IsNull() && !noDataValue)) { // Everything else that is not string will be serialized by json rapidjson::Writer writer(rapidjsonStrBuffer); it->Accept(writer); @@ -476,7 +678,7 @@ void updateExtensionWithJsonStringProperty( // Because serialized string json will add double quotations in the // buffer which is not needed by us, we will manually add the string to // the buffer - const auto& rapidjsonStr = it->GetString(); + const auto& rapidjsonStr = it->IsNull() ? *noDataValue : it->GetString(); rapidjsonStrBuffer.Reserve(it->GetStringLength()); for (rapidjson::SizeType j = 0; j < it->GetStringLength(); ++j) { rapidjsonStrBuffer.PutUnsafe(rapidjsonStr[j]); @@ -551,8 +753,18 @@ void updateExtensionWithJsonScalarProperty( T* p = reinterpret_cast(buffer.data()); auto it = propertyValue.begin(); + + std::optional noDataValue; + if (classProperty.noData) { + noDataValue = classProperty.noData->getSafeNumber(); + } + for (int64_t i = 0; i < propertyTable.count; ++i, ++p, ++it) { - *p = static_cast(it->template Get()); + if (it->IsNull()) { + *p = *noDataValue; + } else { + *p = static_cast(it->template Get()); + } } propertyTableProperty.values = addBufferToGltf(gltf, std::move(buffer)); @@ -854,8 +1066,8 @@ void updateStringArrayProperty( // Handle variable-length arrays. // For string arrays, arrayOffsets indexes into the stringOffsets buffer, - // the size of which is the number of stringElements + 1. This determines the - // component type of the array offsets. + // the size of which is the number of stringElements + 1. This determines + // the component type of the array offsets. std::vector arrayOffsetBuffer; PropertyComponentType arrayOffsetType = PropertyComponentType::None; if (isInRangeForUnsignedInteger(stringCount + 1)) { @@ -1157,6 +1369,10 @@ void updateExtensionWithJsonProperty( // Figure out which types we can use for this data. // Use the smallest type we can, and prefer signed to unsigned. const CompatibleTypes compatibleTypes = findCompatibleTypes(propertyValue); + if (compatibleTypes.hasNullValue()) { + classProperty.noData = compatibleTypes.getSentinelValue(); + } + if (compatibleTypes.isExclusivelyArray()) { MaskedArrayType arrayType = compatibleTypes.toMaskedArrayType(); updateExtensionWithArrayProperty( @@ -1365,10 +1581,10 @@ void updateExtensionWithBatchTableHierarchy( PropertyTable& propertyTable, ErrorList& result, const rapidjson::Value& batchTableHierarchy) { - // EXT_structural_metadata can't support hierarchy, so we need to flatten it. - // It also can't support multiple classes with a single set of feature IDs, - // because IDs can only specify one property table. So essentially every - // property of every class gets added to the one class definition. + // EXT_structural_metadata can't support hierarchy, so we need to flatten + // it. It also can't support multiple classes with a single set of feature + // IDs, because IDs can only specify one property table. So essentially + // every property of every class gets added to the one class definition. auto classesIt = batchTableHierarchy.FindMember("classes"); if (classesIt == batchTableHierarchy.MemberEnd()) { result.emplaceWarning( diff --git a/Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp b/Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp index 3efdfec28..3086ba0f2 100644 --- a/Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp +++ b/Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp @@ -30,7 +30,8 @@ static void checkNonArrayProperty( const std::string& expectedType, const std::optional& expectedComponentType, const std::vector& expected, - size_t expectedTotalInstances) { + size_t expectedTotalInstances, + const std::optional& noDataValue = std::nullopt) { const ClassProperty& property = metaClass.properties.at(propertyName); REQUIRE(property.type == expectedType); REQUIRE(property.componentType == expectedComponentType); @@ -62,6 +63,12 @@ static void checkNonArrayProperty( static_cast(propertyView.getRaw(i)) == expected[static_cast(i)]); } + + if (noDataValue && propertyView.getRaw(i) == noDataValue) { + REQUIRE(!propertyView.get(i)); + } else { + REQUIRE(propertyView.get(i) == propertyView.getRaw(i)); + } } } @@ -110,6 +117,90 @@ static void checkArrayProperty( } } +template +static void createTestForNonArrayJson( + const std::vector& expected, + const std::string& expectedType, + const std::optional& expectedComponentType, + size_t totalInstances, + const std::optional expectedNoData) { + Model model; + + rapidjson::Document featureTableJson; + featureTableJson.SetObject(); + rapidjson::Value batchLength(rapidjson::kNumberType); + batchLength.SetUint64(totalInstances); + featureTableJson.AddMember( + "BATCH_LENGTH", + batchLength, + featureTableJson.GetAllocator()); + + rapidjson::Document batchTableJson; + batchTableJson.SetObject(); + rapidjson::Value scalarProperty(rapidjson::kArrayType); + for (size_t i = 0; i < expected.size(); ++i) { + if (static_cast(expected[i]) == expectedNoData) { + rapidjson::Value nullValue; + nullValue.SetNull(); + scalarProperty.PushBack(nullValue, batchTableJson.GetAllocator()); + continue; + } + + if constexpr (std::is_same_v) { + rapidjson::Value value(rapidjson::kStringType); + value.SetString( + expected[i].c_str(), + static_cast(expected[i].size()), + batchTableJson.GetAllocator()); + scalarProperty.PushBack(value, batchTableJson.GetAllocator()); + } else { + scalarProperty.PushBack( + ExpectedType(expected[i]), + batchTableJson.GetAllocator()); + } + } + + batchTableJson.AddMember( + "scalarProperty", + scalarProperty, + batchTableJson.GetAllocator()); + + auto errors = BatchTableToGltfStructuralMetadata::convertFromB3dm( + featureTableJson, + batchTableJson, + gsl::span(), + model); + + const ExtensionModelExtStructuralMetadata* pMetadata = + model.getExtension(); + REQUIRE(pMetadata); + + const std::optional schema = pMetadata->schema; + REQUIRE(schema); + + const std::unordered_map& classes = schema->classes; + REQUIRE(classes.size() == 1); + + const Class& defaultClass = classes.at("default"); + const std::unordered_map& properties = + defaultClass.properties; + REQUIRE(properties.size() == 1); + + REQUIRE(pMetadata->propertyTables.size() == 1); + + const PropertyTable& propertyTable = pMetadata->propertyTables[0]; + checkNonArrayProperty( + model, + propertyTable, + defaultClass, + "scalarProperty", + expectedType, + expectedComponentType, + expected, + totalInstances, + expectedNoData); +} + template static void createTestForNonArrayJson( const std::vector& expected, @@ -285,7 +376,7 @@ std::set getUniqueBufferViewIds( return result; } -TEST_CASE("Converts JSON B3DM batch table to EXT_feature_metadata") { +TEST_CASE("Converts JSON B3DM batch table to EXT_structural_metadata") { std::filesystem::path testFilePath = Cesium3DTilesSelection_TEST_DATA_DIR; testFilePath = testFilePath / "BatchTables" / "batchedWithJson.b3dm"; @@ -1709,6 +1800,59 @@ TEST_CASE("Upgrade JSON values") { } } +TEST_CASE("Uses sentinel values for JSON null values") { + SECTION("Uint32 with sentinel value 0") { + // Even though the values are typed uint32, they are small enough to be + // stored as int8s. Signed types are preferred over unsigned. + std::vector expected{32, 45, 0, 21, 0, 65, 78}; + createTestForNonArrayJson( + expected, + ClassProperty::Type::SCALAR, + ClassProperty::ComponentType::INT8, + expected.size(), + static_cast(0)); + } + + SECTION("Int32 with sentinel value 0") { + // Even though the values are typed int32, they are small enough to be + // stored as int8s. Signed types are preferred over unsigned. + std::vector expected{32, 45, -3, 0, 21, 0, -65, 78}; + createTestForNonArrayJson( + expected, + ClassProperty::Type::SCALAR, + ClassProperty::ComponentType::INT8, + expected.size(), + static_cast(0)); + } + + SECTION("Int32 with sentinel value -1") { + // Even though the values are typed int32, they are small enough to be + // stored as int8s. Signed types are preferred over unsigned. + std::vector expected{32, 45, -3, 0, 21, 0, -1, -65, 78}; + createTestForNonArrayJson( + expected, + ClassProperty::Type::SCALAR, + ClassProperty::ComponentType::INT8, + expected.size(), + static_cast(-1)); + } + + SECTION("String with 'null'") { + std::vector expected{ + "Test 0", + "Test 1", + "Test 2", + "null" + "Test 3"}; + createTestForNonArrayJson( + expected, + ClassProperty::Type::STRING, + std::nullopt, + expected.size(), + std::string_view("null")); + } +} + TEST_CASE("Cannot write past batch table length") { SECTION("Uint32") { std::vector expected{32, 45, 21, 65, 78, 20, 33, 12}; @@ -1929,56 +2073,67 @@ TEST_CASE("Converts \"Feature Classes\" 3DTILES_batch_table_hierarchy example " CHECK(propertyTable.classProperty == "default"); REQUIRE(propertyTable.properties.size() == 6); - // Even though some of these properties are scalars, they become strings - // because not every feature has every property, and only strings can - // represent "null". - struct Expected { + struct ExpectedString { std::string name; - std::string type; - std::optional componentType; std::vector values; + std::optional noDataValue; + }; + + struct ExpectedScalar { + std::string name; + std::vector values; + std::optional noDataValue; }; - std::vector expectedProperties{ - {"lampStrength", - ClassProperty::Type::STRING, - std::nullopt, - {"10", "5", "7", "null", "null", "null", "null", "null"}}, + std::vector expectedScalar{ + {"lampStrength", {10, 5, 7, 0, 0, 0, 0, 0}, 0}, + {"treeHeight", {0, 0, 0, 0, 0, 0, 10, 15}, 0}, + {"treeAge", {0, 0, 0, 0, 0, 0, 5, 8}, 0}}; + + std::vector expectedString{ {"lampColor", - ClassProperty::Type::STRING, - std::nullopt, - {"yellow", "white", "white", "null", "null", "null", "null", "null"}}, + {"yellow", "white", "white", "null", "null", "null", "null", "null"}, + "null"}, {"carType", - ClassProperty::Type::STRING, - std::nullopt, - {"null", "null", "null", "truck", "bus", "sedan", "null", "null"}}, + {"null", "null", "null", "truck", "bus", "sedan", "null", "null"}, + "null"}, {"carColor", - ClassProperty::Type::STRING, - std::nullopt, - {"null", "null", "null", "green", "blue", "red", "null", "null"}}, - {"treeHeight", - ClassProperty::Type::STRING, - std::nullopt, - {"null", "null", "null", "null", "null", "null", "10", "15"}}, - {"treeAge", - ClassProperty::Type::STRING, - std::nullopt, - {"null", "null", "null", "null", "null", "null", "5", "8"}}}; - - for (const auto& expected : expectedProperties) { + {"null", "null", "null", "green", "blue", "red", "null", "null"}, + "null"}}; + + for (const auto& expected : expectedScalar) { auto it = defaultClass.properties.find(expected.name); REQUIRE(it != defaultClass.properties.end()); - CHECK(it->second.type == expected.type); + CHECK(it->second.type == ClassProperty::Type::SCALAR); + CHECK(it->second.componentType == ClassProperty::ComponentType::INT8); + + checkNonArrayProperty( + gltf, + propertyTable, + defaultClass, + expected.name, + ClassProperty::Type::SCALAR, + ClassProperty::ComponentType::INT8, + expected.values, + expected.values.size(), + expected.noDataValue); + } + + for (const auto& expected : expectedString) { + auto it = defaultClass.properties.find(expected.name); + REQUIRE(it != defaultClass.properties.end()); + CHECK(it->second.type == ClassProperty::Type::STRING); checkNonArrayProperty( gltf, propertyTable, defaultClass, expected.name, - expected.type, - expected.componentType, + ClassProperty::Type::STRING, + std::nullopt, expected.values, - expected.values.size()); + expected.values.size(), + expected.noDataValue); } } From 29349a43e2903a650afc40628a5fff1ea32e0720 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Thu, 31 Aug 2023 09:07:39 +1000 Subject: [PATCH 251/421] Remove unnecessary overrides. --- CesiumJsonReader/src/JsonReaderOptions.cpp | 47 ---------------------- 1 file changed, 47 deletions(-) diff --git a/CesiumJsonReader/src/JsonReaderOptions.cpp b/CesiumJsonReader/src/JsonReaderOptions.cpp index ead57754d..40f041a87 100644 --- a/CesiumJsonReader/src/JsonReaderOptions.cpp +++ b/CesiumJsonReader/src/JsonReaderOptions.cpp @@ -26,53 +26,6 @@ class AnyExtensionJsonHandler : public JsonObjectJsonHandler, } virtual IJsonHandler& getHandler() override { return *this; } - - virtual IJsonHandler* readNull() override { - return JsonObjectJsonHandler::readNull(); - }; - virtual IJsonHandler* readBool(bool b) override { - return JsonObjectJsonHandler::readBool(b); - } - virtual IJsonHandler* readInt32(int32_t i) override { - return JsonObjectJsonHandler::readInt32(i); - } - virtual IJsonHandler* readUint32(uint32_t i) override { - return JsonObjectJsonHandler::readUint32(i); - } - virtual IJsonHandler* readInt64(int64_t i) override { - return JsonObjectJsonHandler::readInt64(i); - } - virtual IJsonHandler* readUint64(uint64_t i) override { - return JsonObjectJsonHandler::readUint64(i); - } - virtual IJsonHandler* readDouble(double d) override { - return JsonObjectJsonHandler::readDouble(d); - } - virtual IJsonHandler* readString(const std::string_view& str) override { - return JsonObjectJsonHandler::readString(str); - } - virtual IJsonHandler* readObjectStart() override { - return JsonObjectJsonHandler::readObjectStart(); - } - virtual IJsonHandler* readObjectKey(const std::string_view& str) override { - return JsonObjectJsonHandler::readObjectKey(str); - } - virtual IJsonHandler* readObjectEnd() override { - return JsonObjectJsonHandler::readObjectEnd(); - } - virtual IJsonHandler* readArrayStart() override { - return JsonObjectJsonHandler::readArrayStart(); - } - virtual IJsonHandler* readArrayEnd() override { - return JsonObjectJsonHandler::readArrayEnd(); - } - - virtual void reportWarning( - const std::string& warning, - std::vector&& context = - std::vector()) override { - JsonObjectJsonHandler::reportWarning(warning, std::move(context)); - } }; void JsonReaderOptions::setExtensionState( From 7c8f4b1473c1103baee348ea8fcfe49c01c5f1b8 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Thu, 31 Aug 2023 09:23:30 +1000 Subject: [PATCH 252/421] Fix test failures, change some CHECK to REQUIRE. --- Cesium3DTilesSelection/test/TestTilesetJsonLoader.cpp | 6 +++--- .../test/TestTilesetSelectionAlgorithm.cpp | 9 +++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Cesium3DTilesSelection/test/TestTilesetJsonLoader.cpp b/Cesium3DTilesSelection/test/TestTilesetJsonLoader.cpp index 4559e583d..64f370bdf 100644 --- a/Cesium3DTilesSelection/test/TestTilesetJsonLoader.cpp +++ b/Cesium3DTilesSelection/test/TestTilesetJsonLoader.cpp @@ -124,7 +124,7 @@ TEST_CASE("Test creating tileset json loader") { CHECK(pTilesetJson->getParent() == nullptr); auto pRootTile = &pTilesetJson->getChildren()[0]; CHECK(pRootTile->getParent() == pTilesetJson); - CHECK(pRootTile->getChildren().size() == 4); + REQUIRE(pRootTile->getChildren().size() == 4); CHECK(pRootTile->getGeometricError() == 70.0); CHECK(pRootTile->getRefine() == TileRefine::Replace); CHECK(std::get(pRootTile->getTileID()) == "parent.b3dm"); @@ -364,7 +364,7 @@ TEST_CASE("Test creating tileset json loader") { REQUIRE(loaderResult.pRootTile->getChildren().size() == 1); auto pRootTile = &loaderResult.pRootTile->getChildren()[0]; CHECK(pRootTile->isExternalContent()); - CHECK(pRootTile->getChildren().size() == 1); + REQUIRE(pRootTile->getChildren().size() == 1); CHECK(pRootTile->getTransform() == glm::dmat4(glm::dmat3(2.0))); const Tile& child = pRootTile->getChildren().front(); @@ -387,7 +387,7 @@ TEST_CASE("Test creating tileset json loader") { REQUIRE(loaderResult.pRootTile->getChildren().size() == 1); auto pRootTile = &loaderResult.pRootTile->getChildren()[0]; CHECK(pRootTile->isExternalContent()); - CHECK(pRootTile->getChildren().size() == 1); + REQUIRE(pRootTile->getChildren().size() == 1); CHECK(pRootTile->getTransform() == glm::dmat4(glm::dmat3(2.0))); const Tile& child = pRootTile->getChildren().front(); diff --git a/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp b/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp index 45db16e03..f8784addc 100644 --- a/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp +++ b/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp @@ -1655,8 +1655,8 @@ TEST_CASE("Additive-refined tiles are added to the tilesFadingOut array") { updateResult = tileset.updateView({viewState}); } - // All three tiles should be rendered. - CHECK(updateResult.tilesToRenderThisFrame.size() == 3); + // All three tiles (plus the tileset.json) should be rendered. + CHECK(updateResult.tilesToRenderThisFrame.size() == 4); // Zoom way out std::optional position = viewState.getPositionCartographic(); @@ -1672,7 +1672,8 @@ TEST_CASE("Additive-refined tiles are added to the tilesFadingOut array") { viewState.getVerticalFieldOfView()); updateResult = tileset.updateView({zoomedOut}); - // Only the root tile is visible now, and the other two are fading out. - CHECK(updateResult.tilesToRenderThisFrame.size() == 1); + // Only the root tile (plus the tileset.json) is visible now, and the other + // two are fading out. + CHECK(updateResult.tilesToRenderThisFrame.size() == 2); CHECK(updateResult.tilesFadingOut.size() == 2); } From 57476c30340b3f10813eda87e4ea60d883967648 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Thu, 31 Aug 2023 09:27:06 +1000 Subject: [PATCH 253/421] Changes from review. --- .../include/Cesium3DTilesSelection/Tileset.h | 2 +- Cesium3DTilesSelection/src/TileContent.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/Tileset.h b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/Tileset.h index 27fca4d01..5d959320f 100644 --- a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/Tileset.h +++ b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/Tileset.h @@ -253,7 +253,7 @@ class CESIUM3DTILESSELECTION_API Tileset final { * reject. * * @return A shared future that resolves to the loaded metadata. Once this - * future resolves, {@link findMetadata} can be used to synchronously obtain + * future resolves, {@link getMetadata} can be used to synchronously obtain * the same metadata instance. */ CesiumAsync::Future loadMetadata(); diff --git a/Cesium3DTilesSelection/src/TileContent.cpp b/Cesium3DTilesSelection/src/TileContent.cpp index 59813c2fd..8ab531ca3 100644 --- a/Cesium3DTilesSelection/src/TileContent.cpp +++ b/Cesium3DTilesSelection/src/TileContent.cpp @@ -137,10 +137,10 @@ TileRenderContent* TileContent::getRenderContent() noexcept { } const TileExternalContent* TileContent::getExternalContent() const noexcept { - const std::unique_ptr* pRenderContent = + const std::unique_ptr* pExternalContent = std::get_if>(&this->_contentKind); - if (pRenderContent) { - return pRenderContent->get(); + if (pExternalContent) { + return pExternalContent->get(); } return nullptr; From a2d5b92ea8aca8f90fc28c56b7bf702ddbd4f549 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Thu, 31 Aug 2023 09:28:36 +1000 Subject: [PATCH 254/421] Fix misnaming. --- Cesium3DTilesSelection/src/TileContent.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cesium3DTilesSelection/src/TileContent.cpp b/Cesium3DTilesSelection/src/TileContent.cpp index 8ab531ca3..2d357563c 100644 --- a/Cesium3DTilesSelection/src/TileContent.cpp +++ b/Cesium3DTilesSelection/src/TileContent.cpp @@ -147,10 +147,10 @@ const TileExternalContent* TileContent::getExternalContent() const noexcept { } TileExternalContent* TileContent::getExternalContent() noexcept { - std::unique_ptr* pRenderContent = + std::unique_ptr* pExternalContent = std::get_if>(&this->_contentKind); - if (pRenderContent) { - return pRenderContent->get(); + if (pExternalContent) { + return pExternalContent->get(); } return nullptr; From b13b640147c7319d63c2bf8996f98dd1dddf3aba Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Thu, 31 Aug 2023 14:44:18 -0400 Subject: [PATCH 255/421] Cleanup, add unit tests --- .../BatchTableToGltfStructuralMetadata.cpp | 133 +++++++------ ...gradeBatchTableToExtStructuralMetadata.cpp | 181 +++++++++++++++++- 2 files changed, 256 insertions(+), 58 deletions(-) diff --git a/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp b/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp index c475c4ccc..0f700dd46 100644 --- a/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp +++ b/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp @@ -24,8 +24,9 @@ using namespace Cesium3DTilesSelection::CesiumImpl; namespace Cesium3DTilesSelection { namespace { /** - * Indicates how a JSON value can be interpreted. Does not correspond one-to-one - * with types / component types in EXT_structural_metadata. + * Indicates how a JSON value can be interpreted as a primitive type. Does not + * correspond one-to-one with types / component types in + * EXT_structural_metadata. */ struct MaskedType { bool isInt8; @@ -73,8 +74,8 @@ struct MaskedType { } /** - * Whether this is incompatible with every type. Fully-incompatible types will - * be treated as strings. + * Whether this is incompatible with every primitive type. Fully-incompatible + * types will be treated as strings. */ bool isIncompatible() const noexcept { return !isInt8 && !isUint8 && !isInt16 && !isUint16 && !isInt32 && @@ -121,14 +122,15 @@ struct MaskedArrayType { } /** - * Whether this is incompatible with every type. Fully-incompatible types will - * be treated as strings. + * Whether this is incompatible with every primitive type. Fully-incompatible + * types will be treated as strings. */ bool isIncompatible() const noexcept { return elementType.isIncompatible(); } }; /** - * Indicates a batch table property's compatibility with C++ types. + * Represents information about a batch table property, indicating its + * compatibility with C++ types and whether it has encountered any null values. */ struct CompatibleTypes { private: @@ -141,20 +143,28 @@ struct CompatibleTypes { std::variant _type; /** - * Whether the type encountered a null value. + * Whether the property has encountered a null value. A + * property may contain null values even though all other values are of the + * same non-null type. In this case, it can simply replace the null with a + * "noData" value in the EXT_structural_metadata property. */ bool _hasNullValue = false; /** - * These booleans are for possible sentinel values for the property. If a - * property contains null values, but all other non-null values are of the - * same type, then we should simply indicate the null value with a "noData" - * value in an EXT_structural_metadata property. We reserve several values as - * possible sentinel values: + * The following booleans track possible "noData" (sentinel) values for the + * property. + * + * We don't want to spend too much effort finding a "noData" value, because + * with any given property there can be multiple candidates. Thus, there are + * only a few values that are reserved as potential sentinel values: * * - 0, for signed or unsigned integers * - -1, for signed integers * - "null", for strings + * + * If a property does not contain one of these values, then it may be used as + * the "noData" value in the property. The sentinel value will then be copied + * to the buffer, instead of the null value. */ bool _canUseZeroSentinel = true; bool _canUseNegativeOneSentinel = true; @@ -167,17 +177,19 @@ struct CompatibleTypes { : _type(maskedArrayType){}; /** - * Whether this is exclusively compatible with array types. + * Whether this is exclusively compatible with array types. This indicates an + * exclusively array property, as opposed to a newly initialized one that is + * "compatible" with everything. */ bool isExclusivelyArray() const noexcept { return std::holds_alternative(_type); } /** - * Whether this is compatible with at least one unsigned integer type. Does - * not count arrays. + * Whether this property is with at least one unsigned integer type. Does not + * count arrays. */ - bool isUnsignedInteger() const noexcept { + bool isCompatibleWithUnsignedInteger() const noexcept { if (std::holds_alternative(_type)) { return false; } @@ -191,10 +203,10 @@ struct CompatibleTypes { } /** - * Whether this is compatible with at least one signed integer type. Does not - * count arrays. + * Whether this property is compatible with at least one signed integer type. + * Does not count arrays. */ - bool isSignedInteger() const noexcept { + bool isCompatibleWithSignedInteger() const noexcept { if (std::holds_alternative(_type)) { return false; } @@ -208,8 +220,8 @@ struct CompatibleTypes { } /** - * Whether this is incompatible with every type. Fully-incompatible types will - * be treated as strings. + * Whether this property is incompatible with every primitive type. + * Fully-incompatible properties will be treated as string properties. */ bool isIncompatible() const noexcept { if (std::holds_alternative(_type)) { @@ -225,13 +237,13 @@ struct CompatibleTypes { } /** - * Marks as incompatible with every type. Fully-incompatible types will be - * treated as strings. + * Marks as incompatible with every primitive type. Fully-incompatible + * properties will be treated as string properties. */ void makeIncompatible() noexcept { _type = MaskedType(false); } /** - * Merges a MaskedType into this CompatibleTypes. + * Merges a MaskedType into this BatchTableProperty. */ void operator&=(const MaskedType& inMaskedType) noexcept { if (std::holds_alternative(_type)) { @@ -269,27 +281,29 @@ struct CompatibleTypes { /** * Merges another CompatibleTypes into this one. */ - void operator&=(const CompatibleTypes& inCompatibleTypes) noexcept { - if (std::holds_alternative(inCompatibleTypes._type)) { + void operator&=(const CompatibleTypes& inTypes) noexcept { + if (std::holds_alternative(inTypes._type)) { // The other CompatibleTypes is compatible with everything, so it does not // change this one. - return; - } + } else - if (std::holds_alternative(inCompatibleTypes._type)) { + if (std::holds_alternative(inTypes._type)) { const MaskedArrayType& arrayType = - std::get(inCompatibleTypes._type); + std::get(inTypes._type); operator&=(arrayType); - return; + } else { + const MaskedType& maskedType = std::get(inTypes._type); + operator&=(maskedType); } - const MaskedType& maskedType = - std::get(inCompatibleTypes._type); - operator&=(maskedType); + _hasNullValue |= inTypes._hasNullValue; + _canUseZeroSentinel &= inTypes._canUseZeroSentinel; + _canUseNegativeOneSentinel &= inTypes._canUseNegativeOneSentinel; + _canUseNullStringSentinel &= inTypes._canUseNullStringSentinel; } /** - * Derives MaskedType info from this CompatibleTypes. If this CompatibleTypes + * Derives MaskedType info from this CompatibleTypes. If this property * is only compatible with arrays, this will return an incompatible * MaskedType. */ @@ -304,8 +318,8 @@ struct CompatibleTypes { /** * Derives MaskedArrayType info from this CompatibleTypes. If this - * CompatibleTypes is not compatible with arrays, this will return an - * incompatible MaskedArrayType. + * property is not compatible with arrays, this will return an incompatible + * MaskedArrayType. */ MaskedArrayType toMaskedArrayType() const noexcept { if (std::holds_alternative(_type)) { @@ -317,29 +331,29 @@ struct CompatibleTypes { } /** - * Gets whether the type includes a null value. + * Gets whether the property of this type includes a null value. */ bool hasNullValue() const noexcept { return _hasNullValue; } /** - * Sets whether the type includes a null value. If a null value has been - * encountered, a sentinel value can try to be provided. + * Sets whether the property includes a null value. If a null value has been + * encountered, a sentinel value may potentially be provided. */ void setHasNullValue(bool value) noexcept { _hasNullValue = value; } /** - * Gets the first possible sentinel value for this type. If no sentinel value - * can be used, this returns std::nullopt. + * Gets a possible sentinel value for this type. If no value can be used, this + * returns std::nullopt. */ const std::optional getSentinelValue() const noexcept { - if (isUnsignedInteger()) { + if (isCompatibleWithUnsignedInteger()) { return _canUseZeroSentinel ? std::make_optional(0) : std::nullopt; } - if (isSignedInteger()) { + if (isCompatibleWithSignedInteger()) { if (_canUseZeroSentinel) { return 0; } @@ -359,14 +373,17 @@ struct CompatibleTypes { } /** - * Removes any sentinel values that are incompatible with the given value - * type. This also removes the sentinel values that equal the given value. + * Removes any sentinel values that are incompatible with the property. This + * also removes the sentinel values that equal the given value. * * This is helpful for when a property contains a sentinel value as non-null * data; the sentinel value can then be removed from consideration. */ void removeSentinelValues(CesiumUtility::JsonValue value) noexcept { if (value.isNumber()) { + _canUseNullStringSentinel = false; + + // Don't try to use string as sentinels for numbers. if (value.isUint64()) { _canUseZeroSentinel &= (value.getUint64() != 0); } @@ -376,17 +393,17 @@ struct CompatibleTypes { _canUseZeroSentinel &= (intValue != 0); _canUseNegativeOneSentinel &= (intValue != -1); } - - return; } if (value.isString()) { + // Don't try to use numbers as sentinels for strings. + _canUseZeroSentinel = false; + _canUseNegativeOneSentinel = false; + auto stringValue = value.getString(); if (stringValue == "null") { _canUseNullStringSentinel = false; } - - return; } } }; @@ -618,14 +635,14 @@ CompatibleTypes findCompatibleTypes(const TValueGetter& propertyValue) { continue; } + // If this code is reached, the value is a string or something else. + compatibleTypes.makeIncompatible(); + // If this is a string, check that the value does not equal one of the // possible sentinel values. if (it->IsString()) { compatibleTypes.removeSentinelValues(it->GetString()); } - - // If this code is reached, the value is a string or something else. - compatibleTypes.makeIncompatible(); } return compatibleTypes; @@ -1369,9 +1386,6 @@ void updateExtensionWithJsonProperty( // Figure out which types we can use for this data. // Use the smallest type we can, and prefer signed to unsigned. const CompatibleTypes compatibleTypes = findCompatibleTypes(propertyValue); - if (compatibleTypes.hasNullValue()) { - classProperty.noData = compatibleTypes.getSentinelValue(); - } if (compatibleTypes.isExclusivelyArray()) { MaskedArrayType arrayType = compatibleTypes.toMaskedArrayType(); @@ -1385,6 +1399,11 @@ void updateExtensionWithJsonProperty( return; } + // Set the "noData" value before copying the property (to avoid copying nulls) + if (compatibleTypes.hasNullValue()) { + classProperty.noData = compatibleTypes.getSentinelValue(); + } + MaskedType type = compatibleTypes.toMaskedType(); if (type.isBool) { updateExtensionWithJsonBooleanProperty( diff --git a/Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp b/Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp index 3086ba0f2..7eae64841 100644 --- a/Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp +++ b/Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp @@ -36,7 +36,7 @@ static void checkNonArrayProperty( REQUIRE(property.type == expectedType); REQUIRE(property.componentType == expectedComponentType); REQUIRE(!property.array); - REQUIRE(property.count == std::nullopt); + REQUIRE(!property.count); PropertyTableView view(model, propertyTable); REQUIRE(view.status() == PropertyTableViewStatus::Valid); @@ -1853,6 +1853,185 @@ TEST_CASE("Uses sentinel values for JSON null values") { } } +TEST_CASE("Defaults to string if no sentinel values are available") { + SECTION("Uint32") { + Model model; + // Even though the values are typed uint32, they are small enough to be + // stored as uint8s. Signed types are preferred over unsigned, but this + // exceeds the range for int8. + std::vector> + expected{32, 45, 0, 255, std::nullopt, 0, 65, 78}; + + rapidjson::Document featureTableJson; + featureTableJson.SetObject(); + rapidjson::Value batchLength(rapidjson::kNumberType); + batchLength.SetUint64(static_cast(expected.size())); + featureTableJson.AddMember( + "BATCH_LENGTH", + batchLength, + featureTableJson.GetAllocator()); + + rapidjson::Document batchTableJson; + batchTableJson.SetObject(); + rapidjson::Value scalarProperty(rapidjson::kArrayType); + for (size_t i = 0; i < expected.size(); ++i) { + if (!expected[i]) { + rapidjson::Value nullValue; + nullValue.SetNull(); + scalarProperty.PushBack(nullValue, batchTableJson.GetAllocator()); + continue; + } + + scalarProperty.PushBack(*expected[i], batchTableJson.GetAllocator()); + } + + batchTableJson.AddMember( + "scalarProperty", + scalarProperty, + batchTableJson.GetAllocator()); + + auto errors = BatchTableToGltfStructuralMetadata::convertFromB3dm( + featureTableJson, + batchTableJson, + gsl::span(), + model); + + const ExtensionModelExtStructuralMetadata* pMetadata = + model.getExtension(); + REQUIRE(pMetadata); + + const std::optional schema = pMetadata->schema; + REQUIRE(schema); + + const std::unordered_map& classes = schema->classes; + REQUIRE(classes.size() == 1); + + const Class& defaultClass = classes.at("default"); + const std::unordered_map& properties = + defaultClass.properties; + REQUIRE(properties.size() == 1); + + REQUIRE(pMetadata->propertyTables.size() == 1); + + const PropertyTable& propertyTable = pMetadata->propertyTables[0]; + const ClassProperty& property = + defaultClass.properties.at("scalarProperty"); + REQUIRE(property.type == ClassProperty::Type::STRING); + REQUIRE(!property.componentType); + REQUIRE(!property.array); + REQUIRE(!property.count); + + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + + PropertyTablePropertyView propertyView = + view.getPropertyView("scalarProperty"); + REQUIRE(propertyView.status() == PropertyTablePropertyViewStatus::Valid); + REQUIRE(propertyView.size() == propertyTable.count); + REQUIRE(propertyView.size() == static_cast(expected.size())); + for (int64_t i = 0; i < propertyView.size(); ++i) { + auto expectedValue = expected[static_cast(i)]; + if (expectedValue) { + std::string asString = std::to_string(*expectedValue); + REQUIRE(propertyView.getRaw(i) == asString); + } else { + REQUIRE(propertyView.getRaw(i) == "null"); + } + + REQUIRE(propertyView.get(i) == propertyView.getRaw(i)); + } + } + + SECTION("Int32") { + Model model; + // Even though the values are typed int32, they are small enough to be + // stored as int8s. + std::vector> + expected{32, 45, 0, -1, std::nullopt, 0, 65, 78}; + + rapidjson::Document featureTableJson; + featureTableJson.SetObject(); + rapidjson::Value batchLength(rapidjson::kNumberType); + batchLength.SetUint64(static_cast(expected.size())); + featureTableJson.AddMember( + "BATCH_LENGTH", + batchLength, + featureTableJson.GetAllocator()); + + rapidjson::Document batchTableJson; + batchTableJson.SetObject(); + rapidjson::Value scalarProperty(rapidjson::kArrayType); + for (size_t i = 0; i < expected.size(); ++i) { + if (!expected[i]) { + rapidjson::Value nullValue; + nullValue.SetNull(); + scalarProperty.PushBack(nullValue, batchTableJson.GetAllocator()); + continue; + } + + scalarProperty.PushBack(*expected[i], batchTableJson.GetAllocator()); + } + + batchTableJson.AddMember( + "scalarProperty", + scalarProperty, + batchTableJson.GetAllocator()); + + auto errors = BatchTableToGltfStructuralMetadata::convertFromB3dm( + featureTableJson, + batchTableJson, + gsl::span(), + model); + + const ExtensionModelExtStructuralMetadata* pMetadata = + model.getExtension(); + REQUIRE(pMetadata); + + const std::optional schema = pMetadata->schema; + REQUIRE(schema); + + const std::unordered_map& classes = schema->classes; + REQUIRE(classes.size() == 1); + + const Class& defaultClass = classes.at("default"); + const std::unordered_map& properties = + defaultClass.properties; + REQUIRE(properties.size() == 1); + + REQUIRE(pMetadata->propertyTables.size() == 1); + + const PropertyTable& propertyTable = pMetadata->propertyTables[0]; + const ClassProperty& property = + defaultClass.properties.at("scalarProperty"); + REQUIRE(property.type == ClassProperty::Type::STRING); + REQUIRE(!property.componentType); + REQUIRE(!property.array); + REQUIRE(!property.count); + + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + + PropertyTablePropertyView propertyView = + view.getPropertyView("scalarProperty"); + REQUIRE(propertyView.status() == PropertyTablePropertyViewStatus::Valid); + REQUIRE(propertyView.size() == propertyTable.count); + REQUIRE(propertyView.size() == static_cast(expected.size())); + for (int64_t i = 0; i < propertyView.size(); ++i) { + auto expectedValue = expected[static_cast(i)]; + if (expectedValue) { + std::string asString = std::to_string(*expectedValue); + REQUIRE(propertyView.getRaw(i) == asString); + } else { + REQUIRE(propertyView.getRaw(i) == "null"); + } + + REQUIRE(propertyView.get(i) == propertyView.getRaw(i)); + } + } +} + TEST_CASE("Cannot write past batch table length") { SECTION("Uint32") { std::vector expected{32, 45, 21, 65, 78, 20, 33, 12}; From 36cf0673683be6517f47a9ef3eab8f2ceea2dd32 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Thu, 31 Aug 2023 15:08:29 -0400 Subject: [PATCH 256/421] Final unit test pass --- .../src/BatchTableToGltfStructuralMetadata.cpp | 2 +- Cesium3DTilesSelection/test/TestPntsToGltfConverter.cpp | 5 +++++ .../test/TestUpgradeBatchTableToExtStructuralMetadata.cpp | 6 +++--- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp b/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp index 0f700dd46..f56128b1e 100644 --- a/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp +++ b/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp @@ -1831,7 +1831,7 @@ ErrorList BatchTableToGltfStructuralMetadata::convertFromB3dm( FeatureId& featureID = extension.featureIds.emplace_back(); // No fast way to count the unique feature IDs in this primitive, so - // subtitute the batch table length. + // substitute the batch table length. featureID.featureCount = batchLength; featureID.attribute = 0; featureID.label = "_FEATURE_ID_0"; diff --git a/Cesium3DTilesSelection/test/TestPntsToGltfConverter.cpp b/Cesium3DTilesSelection/test/TestPntsToGltfConverter.cpp index e93e333f2..8cce744a9 100644 --- a/Cesium3DTilesSelection/test/TestPntsToGltfConverter.cpp +++ b/Cesium3DTilesSelection/test/TestPntsToGltfConverter.cpp @@ -681,6 +681,7 @@ TEST_CASE("Converts point cloud with batch IDs to glTF with " FeatureId& featureId = primitiveExtension->featureIds[0]; CHECK(featureId.featureCount == 8); CHECK(featureId.attribute == 0); + CHECK(featureId.propertyTable == 0); CHECK(gltf.materials.size() == 1); @@ -760,6 +761,7 @@ TEST_CASE("Converts point cloud with per-point properties to glTF with " // Check for implicit feature IDs CHECK(featureId.featureCount == pointsLength); CHECK(!featureId.attribute); + CHECK(featureId.propertyTable == 0); CHECK(gltf.materials.size() == 1); @@ -818,6 +820,7 @@ TEST_CASE("Converts point cloud with Draco compression to glTF") { // 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]; @@ -961,6 +964,7 @@ TEST_CASE("Converts point cloud with partial Draco compression to glTF") { // 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]; @@ -1098,6 +1102,7 @@ TEST_CASE("Converts batched point cloud with Draco compression to glTF") { FeatureId& featureId = primitiveExtension->featureIds[0]; CHECK(featureId.featureCount == 8); CHECK(featureId.attribute == 0); + CHECK(featureId.propertyTable == 0); CHECK(gltf.materials.size() == 1); diff --git a/Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp b/Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp index 7eae64841..93151e148 100644 --- a/Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp +++ b/Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp @@ -2265,9 +2265,9 @@ TEST_CASE("Converts \"Feature Classes\" 3DTILES_batch_table_hierarchy example " }; std::vector expectedScalar{ - {"lampStrength", {10, 5, 7, 0, 0, 0, 0, 0}, 0}, - {"treeHeight", {0, 0, 0, 0, 0, 0, 10, 15}, 0}, - {"treeAge", {0, 0, 0, 0, 0, 0, 5, 8}, 0}}; + {"lampStrength", {10, 5, 7, 0, 0, 0, 0, 0}, static_cast(0)}, + {"treeHeight", {0, 0, 0, 0, 0, 0, 10, 15}, static_cast(0)}, + {"treeAge", {0, 0, 0, 0, 0, 0, 5, 8}, static_cast(0)}}; std::vector expectedString{ {"lampColor", From e406287a91621817c9230952445736a2fee6020f Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Fri, 1 Sep 2023 11:39:58 +1000 Subject: [PATCH 257/421] Finish loading parent tile when needed for upsampling. --- CHANGES.md | 1 + Cesium3DTilesSelection/src/Tileset.cpp | 5 +---- .../src/TilesetContentManager.cpp | 13 ++++++++----- Cesium3DTilesSelection/src/TilesetContentManager.h | 11 +++-------- .../test/TestTilesetContentManager.cpp | 8 ++++---- 5 files changed, 17 insertions(+), 21 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 59ec01f94..3aa77cae0 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -28,6 +28,7 @@ - Fixed several small build script issues to allow cesium-native to be used in Univeral Windows Platform (UWP) applications, such as those that run on Holo Lens 2. - When KTX transcoding fails, the image will now be fully decompressed instead of returning an error. - Fixed a bug that could cause higher-detail tiles to continue showing when zooming out quickly on a tileset that uses "additive" refinement. +- Fixed a bug that could cause a tile to never finish upsampling because its non-rendered parent never finishes loading. ### v0.26.0 - 2023-08-01 diff --git a/Cesium3DTilesSelection/src/Tileset.cpp b/Cesium3DTilesSelection/src/Tileset.cpp index 31873f20a..3c095e277 100644 --- a/Cesium3DTilesSelection/src/Tileset.cpp +++ b/Cesium3DTilesSelection/src/Tileset.cpp @@ -808,10 +808,7 @@ Tileset::TraversalDetails Tileset::_visitTileIfNeeded( double tilePriority = computeTilePriority(tile, frameState.frustums, distances); - this->_pTilesetContentManager->updateTileContent( - tile, - tilePriority, - _options); + this->_pTilesetContentManager->updateTileContent(tile, _options); this->_markTileVisited(tile); CullResult cullResult{}; diff --git a/Cesium3DTilesSelection/src/TilesetContentManager.cpp b/Cesium3DTilesSelection/src/TilesetContentManager.cpp index ee315ddfe..54cd5f609 100644 --- a/Cesium3DTilesSelection/src/TilesetContentManager.cpp +++ b/Cesium3DTilesSelection/src/TilesetContentManager.cpp @@ -878,6 +878,12 @@ void TilesetContentManager::loadTileContent( if (pParentTile) { if (pParentTile->getState() != TileLoadState::Done) { loadTileContent(*pParentTile, tilesetOptions); + + // Finalize the parent if necessary, otherwise it may never reach the + // Done state. + if (pParentTile->getState() == TileLoadState::ContentLoaded) { + finishLoading(*pParentTile, tilesetOptions); + } return; } } else { @@ -970,14 +976,13 @@ void TilesetContentManager::loadTileContent( void TilesetContentManager::updateTileContent( Tile& tile, - double priority, const TilesetOptions& tilesetOptions) { if (tile.getState() == TileLoadState::Unloading) { unloadTileContent(tile); } if (tile.getState() == TileLoadState::ContentLoaded) { - updateContentLoadedState(tile, priority, tilesetOptions); + updateContentLoadedState(tile, tilesetOptions); } if (tile.getState() == TileLoadState::Done) { @@ -1188,8 +1193,7 @@ void TilesetContentManager::finishLoading( // This allows the raster tile to be updated and children to be created, if // necessary. - // Priority doesn't matter here since loading is complete. - updateTileContent(tile, 0.0, tilesetOptions); + updateTileContent(tile, tilesetOptions); } void TilesetContentManager::setTileContent( @@ -1230,7 +1234,6 @@ void TilesetContentManager::setTileContent( void TilesetContentManager::updateContentLoadedState( Tile& tile, - double /*priority*/, const TilesetOptions& tilesetOptions) { // initialize this tile content first TileContent& content = tile.getContent(); diff --git a/Cesium3DTilesSelection/src/TilesetContentManager.h b/Cesium3DTilesSelection/src/TilesetContentManager.h index 273db7cb5..deb32257f 100644 --- a/Cesium3DTilesSelection/src/TilesetContentManager.h +++ b/Cesium3DTilesSelection/src/TilesetContentManager.h @@ -62,10 +62,7 @@ class TilesetContentManager void loadTileContent(Tile& tile, const TilesetOptions& tilesetOptions); - void updateTileContent( - Tile& tile, - double priority, - const TilesetOptions& tilesetOptions); + void updateTileContent(Tile& tile, const TilesetOptions& tilesetOptions); bool unloadTileContent(Tile& tile); @@ -115,10 +112,8 @@ class TilesetContentManager TileLoadResult&& result, void* pWorkerRenderResources); - void updateContentLoadedState( - Tile& tile, - double priority, - const TilesetOptions& tilesetOptions); + void + updateContentLoadedState(Tile& tile, const TilesetOptions& tilesetOptions); void updateDoneState(Tile& tile, const TilesetOptions& tilesetOptions); diff --git a/Cesium3DTilesSelection/test/TestTilesetContentManager.cpp b/Cesium3DTilesSelection/test/TestTilesetContentManager.cpp index 9755229ea..b563f4046 100644 --- a/Cesium3DTilesSelection/test/TestTilesetContentManager.cpp +++ b/Cesium3DTilesSelection/test/TestTilesetContentManager.cpp @@ -372,7 +372,7 @@ TEST_CASE("Test tile state machine") { // ContentLoaded -> Done // update tile content to move from ContentLoaded -> Done - pManager->updateTileContent(tile, 0.0, options); + pManager->updateTileContent(tile, options); CHECK(tile.getState() == TileLoadState::Done); CHECK(tile.getChildren().size() == 1); CHECK(tile.getChildren().front().getContent().isEmptyContent()); @@ -473,7 +473,7 @@ TEST_CASE("Test tile state machine") { // FailedTemporarily -> FailedTemporarily // tile is failed temporarily but the loader can still add children to it - pManager->updateTileContent(tile, 0.0, options); + pManager->updateTileContent(tile, options); CHECK(pManager->getNumberOfTilesLoading() == 0); CHECK(tile.getChildren().size() == 1); CHECK(tile.getChildren().front().isEmptyContent()); @@ -548,7 +548,7 @@ TEST_CASE("Test tile state machine") { // Failed -> Failed // tile is failed but the loader can still add children to it - pManager->updateTileContent(tile, 0.0, options); + pManager->updateTileContent(tile, options); CHECK(pManager->getNumberOfTilesLoading() == 0); CHECK(tile.getChildren().size() == 1); CHECK(tile.getChildren().front().isEmptyContent()); @@ -648,7 +648,7 @@ TEST_CASE("Test tile state machine") { CHECK(upsampledTile.getState() == TileLoadState::Unloaded); // parent moves from ContentLoaded -> Done - pManager->updateTileContent(tile, 0.0, options); + pManager->updateTileContent(tile, options); CHECK(tile.getState() == TileLoadState::Done); CHECK(tile.getChildren().size() == 1); CHECK(&tile.getChildren().back() == &upsampledTile); From b4aaf58acfa7c5b4149f529d5330c05b1fb42b76 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Fri, 1 Sep 2023 14:49:15 +1000 Subject: [PATCH 258/421] Bump to v0.27.0. --- CHANGES.md | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 3aa77cae0..91def1bca 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,6 @@ # Change Log -### ? - ? +### v0.27.0 - 2023-09-01 ##### Breaking Changes :mega: @@ -26,7 +26,7 @@ - Fixed a bug where an empty error message would get propagated to a tileset's `loadErrorCallback`. - Fixed several small build script issues to allow cesium-native to be used in Univeral Windows Platform (UWP) applications, such as those that run on Holo Lens 2. -- When KTX transcoding fails, the image will now be fully decompressed instead of returning an error. +- When KTX2 transcoding fails, the image will now be fully decompressed instead of returning an error. - Fixed a bug that could cause higher-detail tiles to continue showing when zooming out quickly on a tileset that uses "additive" refinement. - Fixed a bug that could cause a tile to never finish upsampling because its non-rendered parent never finishes loading. diff --git a/package.json b/package.json index 95e6f5f37..b5cbb34fe 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cesium-native", - "version": "0.26.0", + "version": "0.27.0", "description": "Cesium 3D Geospatial for C++", "main": "index.js", "directories": { From af9707f14958a3498eeb92ecd229ce7ad9f39827 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Fri, 1 Sep 2023 13:20:49 -0400 Subject: [PATCH 259/421] Add additional PropertyView typedefs --- ...ropertyViewTypes.h => PropertyViewTypes.h} | 174 +++++++++++++++++- 1 file changed, 167 insertions(+), 7 deletions(-) rename CesiumGltf/include/CesiumGltf/{PropertyTablePropertyViewTypes.h => PropertyViewTypes.h} (68%) diff --git a/CesiumGltf/include/CesiumGltf/PropertyTablePropertyViewTypes.h b/CesiumGltf/include/CesiumGltf/PropertyViewTypes.h similarity index 68% rename from CesiumGltf/include/CesiumGltf/PropertyTablePropertyViewTypes.h rename to CesiumGltf/include/CesiumGltf/PropertyViewTypes.h index 257c99615..f51a8b489 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTablePropertyViewTypes.h +++ b/CesiumGltf/include/CesiumGltf/PropertyViewTypes.h @@ -1,13 +1,11 @@ #pragma once +#include "CesiumGltf/PropertyAttributePropertyView.h" #include "CesiumGltf/PropertyTablePropertyView.h" +#include "CesiumGltf/PropertyTexturePropertyView.h" #include -#include -#include -#include -#include #include namespace CesiumGltf { @@ -166,11 +164,11 @@ typedef std::variant< View>, false>, View>>, View>>> - NonNormalizedPropertyTablePropertyView; + PropertyTablePropertyViewType; /** * @brief An alias for all of the potential types of - * {@link PropertyTablePropertyView} possible with normalization. + * {@link PropertyTablePropertyView} possible with normalization. * Can be useful for applications that want to implement abstract * representations of PropertyTablePropertyView, without the mess of templated * types. @@ -288,7 +286,169 @@ typedef std::variant< View>, true>, View>, true>, View>, true>> - NormalizedPropertyTablePropertyView; + NormalizedPropertyTablePropertyViewType; + +#undef View + +#define View PropertyTexturePropertyView +/** + * + * @brief An alias for all of the potential types of + * {@link PropertyTexturePropertyView} possible without normalization. + * Can be useful for applications that want to implement abstract + * representations of PropertyTexturePropertyView, without the mess of templated + * types. + */ +typedef std::variant< + View, + View, + View, + View, + View, + View, + View, + View, false>, + View, false>, + View, false>, + View, false>, + View, false>, + View, false>, + View, false>, + View, false>, + View, false>, + View, false>, + View, false>, + View, false>> + PropertyTexturePropertyViewType; + +/** + * @brief An alias for all of the potential types of + * {@link PropertyTexturePropertyView} possible with normalization. + * Can be useful for applications that want to implement abstract + * representations of PropertyTexturePropertyView, without the mess of templated + * types. + */ +typedef std::variant< + View, + View, + View, + View, + View, + View, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>> + NormalizedPropertyTexturePropertyViewType; + +#undef View + +#define View PropertyAttributePropertyView +/** + * + * @brief An alias for all of the potential types of + * {@link PropertyAttributePropertyView} possible without normalization. + * Can be useful for applications that want to implement abstract + * representations of PropertyAttributePropertyView, without the mess of + * templated types. + */ +typedef std::variant< + View, + View, + View, + View, + View, + View, + View, false>, + View, false>, + View, false>, + View, false>, + View, false>, + View>, + View, false>, + View, false>, + View, false>, + View, false>, + View, false>, + View>, + View, false>, + View, false>, + View, false>, + View, false>, + View, false>, + View>, + View, false>, + View, false>, + View, false>, + View, false>, + View, false>, + View>, + View, false>, + View, false>, + View, false>, + View, false>, + View, false>, + View>, + View, false>, + View, false>, + View, false>, + View, false>, + View, false>, + View>> + PropertyAttributePropertyViewType; + +/** + * @brief An alias for all of the potential types of + * {@link PropertyAttributePropertyView} possible with normalization. + * Can be useful for applications that want to implement abstract + * representations of PropertyAttributePropertyView, without the mess of + * templated types. + */ +typedef std::variant< + View, + View, + View, + View, + View, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>, + View, true>> + NormalizedPropertyAttributePropertyViewType; #undef View } // namespace CesiumGltf From 74fbef5474e7bdd9b65215426f84711c9eedb2f9 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Fri, 1 Sep 2023 14:56:01 -0400 Subject: [PATCH 260/421] Add missing pragma once --- CesiumGltf/include/CesiumGltf/PropertyView.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CesiumGltf/include/CesiumGltf/PropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyView.h index ec6f05730..133d57136 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyView.h @@ -1,3 +1,5 @@ +#pragma once + #include "CesiumGltf/ClassProperty.h" #include "CesiumGltf/PropertyAttributeProperty.h" #include "CesiumGltf/PropertyTableProperty.h" From 5dba5686a5887ee40ebd409e2434a5185ea13238 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Fri, 1 Sep 2023 15:32:45 -0400 Subject: [PATCH 261/421] Reorder member variables to avoid initialization warnings --- CesiumGltf/include/CesiumGltf/PropertyView.h | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/CesiumGltf/include/CesiumGltf/PropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyView.h index 133d57136..034127750 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyView.h @@ -519,13 +519,12 @@ template class PropertyView { std::optional _semantic; std::optional _description; - bool _required; - std::optional _offset; std::optional _scale; std::optional _max; std::optional _min; + bool _required; std::optional _noData; std::optional _defaultValue; @@ -858,13 +857,12 @@ template class PropertyView { std::optional _semantic; std::optional _description; - bool _required; - std::optional _offset; std::optional _scale; std::optional _max; std::optional _min; + bool _required; std::optional _noData; std::optional _defaultValue; @@ -2477,13 +2475,13 @@ template <> class PropertyView> { PropertyViewStatusType _status; private: - int64_t _count; - bool _required; - std::optional _name; std::optional _semantic; std::optional _description; + int64_t _count; + bool _required; + struct StringArrayValue { std::vector data; std::vector offsets; From 9cc9f83d8169351bbc9018fab0d0d7e6e89e40ce Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Sun, 3 Sep 2023 10:38:33 +1000 Subject: [PATCH 262/421] Fix crash cause by redundant finishLoading call. --- CHANGES.md | 6 ++++++ Cesium3DTilesSelection/src/Tileset.cpp | 9 ++++++++- Cesium3DTilesSelection/src/TilesetContentManager.cpp | 8 ++++++-- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 91def1bca..123a9a2c5 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,11 @@ # Change Log +### v0.27.1 - 2023-09-03 + +##### Fixes :wrench: + +- Fixed a bug that could cause a crash when loading tiles with a raster overlay. + ### v0.27.0 - 2023-09-01 ##### Breaking Changes :mega: diff --git a/Cesium3DTilesSelection/src/Tileset.cpp b/Cesium3DTilesSelection/src/Tileset.cpp index 3c095e277..c6a95fa2b 100644 --- a/Cesium3DTilesSelection/src/Tileset.cpp +++ b/Cesium3DTilesSelection/src/Tileset.cpp @@ -1426,7 +1426,14 @@ void Tileset::_processMainThreadLoadQueue() { auto end = start + std::chrono::milliseconds(static_cast(timeBudget)); for (TileLoadTask& task : this->_mainThreadLoadQueue) { - this->_pTilesetContentManager->finishLoading(*task.pTile, this->_options); + // We double-check that the tile is still in the ContentLoaded state here, + // in case something (such as a child that needs to upsample from this + // parent) already pushed the tile into the Done state. Because in that + // case, calling finishLoading here would assert or crash. + if (task.pTile->getState() == TileLoadState::ContentLoaded && + task.pTile->isRenderContent()) { + this->_pTilesetContentManager->finishLoading(*task.pTile, this->_options); + } auto time = std::chrono::system_clock::now(); if (timeBudget > 0.0 && time >= end) { break; diff --git a/Cesium3DTilesSelection/src/TilesetContentManager.cpp b/Cesium3DTilesSelection/src/TilesetContentManager.cpp index 54cd5f609..2d18a6278 100644 --- a/Cesium3DTilesSelection/src/TilesetContentManager.cpp +++ b/Cesium3DTilesSelection/src/TilesetContentManager.cpp @@ -880,8 +880,12 @@ void TilesetContentManager::loadTileContent( loadTileContent(*pParentTile, tilesetOptions); // Finalize the parent if necessary, otherwise it may never reach the - // Done state. - if (pParentTile->getState() == TileLoadState::ContentLoaded) { + // Done state. Also double check that we have render content in ensure + // we don't assert / crash in finishLoading. The latter will only ever + // be a problem in a pathological tileset with a non-renderable leaf + // tile, but that sort of thing does happen. + if (pParentTile->getState() == TileLoadState::ContentLoaded && + pParentTile->isRenderContent()) { finishLoading(*pParentTile, tilesetOptions); } return; From af81691315575c5d7ae5f282a4774dd4a65842c3 Mon Sep 17 00:00:00 2001 From: assiduous Date: Tue, 5 Sep 2023 13:24:49 -0700 Subject: [PATCH 263/421] Fix libjpeg-turbo build on Android --- extern/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/CMakeLists.txt b/extern/CMakeLists.txt index beab376ce..42d39235e 100644 --- a/extern/CMakeLists.txt +++ b/extern/CMakeLists.txt @@ -188,7 +188,7 @@ include(ExternalProject) ExternalProject_Add(libjpeg-turbo SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/libjpeg-turbo" PREFIX "libjpeg-turbo" - CONFIGURE_COMMAND ${CMAKE_COMMAND} -B ${CMAKE_CURRENT_BINARY_DIR}/libjpeg-turbo -S ${CMAKE_CURRENT_LIST_DIR}/libjpeg-turbo -DCMAKE_INSTALL_PREFIX=${TJ_INSTALL_PREFIX} -DENABLE_SHARED=0 -DWITH_CRT_DLL=1 -G ${CMAKE_GENERATOR} -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME} "-DCMAKE_SYSTEM_VERSION=${CMAKE_SYSTEM_VERSION}" -DCMAKE_SYSTEM_PROCESSOR=$,${CMAKE_SYSTEM_PROCESSOR},unknown> -DCMAKE_BUILD_TYPE=$ -DCMAKE_POSITION_INDEPENDENT_CODE=${CMAKE_POSITION_INDEPENDENT_CODE} -DCMAKE_ANDROID_ARCH_ABI=${CMAKE_ANDROID_ARCH_ABI} -DCMAKE_GENERATOR_PLATFORM=${CMAKE_GENERATOR_PLATFORM} -DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM} -DCMAKE_OSX_ARCHITECTURES=${CMAKE_OSX_ARCHITECTURES} -DCMAKE_OSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET} -DCMAKE_DEBUG_POSTFIX=${CMAKE_DEBUG_POSTFIX} -DCMAKE_TOOLCHAIN_FILE=${TJ_TOOLCHAIN_FILE} -DCMAKE_POSITION_INDEPENDENT_CODE=${CMAKE_POSITION_INDEPENDENT_CODE} + CONFIGURE_COMMAND ${CMAKE_COMMAND} -B ${CMAKE_CURRENT_BINARY_DIR}/libjpeg-turbo -S ${CMAKE_CURRENT_LIST_DIR}/libjpeg-turbo -DCMAKE_INSTALL_PREFIX=${TJ_INSTALL_PREFIX} -DENABLE_SHARED=0 -DWITH_CRT_DLL=1 -G ${CMAKE_GENERATOR} -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME} "-DCMAKE_SYSTEM_VERSION=${CMAKE_SYSTEM_VERSION}" -DCMAKE_SYSTEM_PROCESSOR=$,${CMAKE_SYSTEM_PROCESSOR},unknown> -DCMAKE_BUILD_TYPE=$ -DCMAKE_POSITION_INDEPENDENT_CODE=${CMAKE_POSITION_INDEPENDENT_CODE} -DCMAKE_ANDROID_ARCH_ABI=${CMAKE_ANDROID_ARCH_ABI} -DCMAKE_GENERATOR_PLATFORM=${CMAKE_GENERATOR_PLATFORM} -DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM} -DCMAKE_OSX_ARCHITECTURES=${CMAKE_OSX_ARCHITECTURES} -DCMAKE_OSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET} -DCMAKE_DEBUG_POSTFIX=${CMAKE_DEBUG_POSTFIX} -DCMAKE_TOOLCHAIN_FILE=${TJ_TOOLCHAIN_FILE} -DCMAKE_POSITION_INDEPENDENT_CODE=${CMAKE_POSITION_INDEPENDENT_CODE} -DANDROID_ABI=${CMAKE_ANDROID_ARCH_ABI} BUILD_COMMAND ${CMAKE_COMMAND} --build ${CMAKE_CURRENT_BINARY_DIR}/libjpeg-turbo --config $ --target install INSTALL_COMMAND "" BUILD_BYPRODUCTS ${TJ_INSTALL_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}turbojpeg${CMAKE_STATIC_LIBRARY_SUFFIX} From 0004ba45210743d31f12e474f5fb660f7cda57d6 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Thu, 7 Sep 2023 10:45:48 -0400 Subject: [PATCH 264/421] PR feedback --- .../include/CesiumGltf/PropertyArrayView.h | 13 +++ .../CesiumGltf/PropertyTexturePropertyView.h | 109 +++++------------- .../src/PropertyTexturePropertyView.cpp | 41 +++++++ 3 files changed, 80 insertions(+), 83 deletions(-) diff --git a/CesiumGltf/include/CesiumGltf/PropertyArrayView.h b/CesiumGltf/include/CesiumGltf/PropertyArrayView.h index bc58d0160..7f08dfe9c 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyArrayView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyArrayView.h @@ -70,6 +70,10 @@ template class PropertyArrayView { return true; } + bool operator!=(const PropertyArrayView& other) const noexcept { + return !operator==(other); + } + private: using ArrayType = std::variant, std::vector>; @@ -121,6 +125,10 @@ template <> class PropertyArrayView { return true; } + bool operator!=(const PropertyArrayView& other) const noexcept { + return !operator==(other); + } + private: gsl::span _values; int64_t _bitOffset; @@ -185,6 +193,11 @@ template <> class PropertyArrayView { return true; } + bool + operator!=(const PropertyArrayView& other) const noexcept { + return !operator==(other); + } + private: gsl::span _values; gsl::span _stringOffsets; diff --git a/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h index da9bc456f..8b58998f5 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h @@ -7,6 +7,7 @@ #include "CesiumGltf/PropertyView.h" #include "CesiumGltf/Sampler.h" +#include #include #include #include @@ -79,7 +80,7 @@ class PropertyTexturePropertyViewStatus : public PropertyViewStatus { }; template -ElementType assembleScalarValue(const std::vector& bytes) noexcept { +ElementType assembleScalarValue(const gsl::span bytes) noexcept { if constexpr (std::is_same_v) { assert( bytes.size() == sizeof(float) && @@ -106,7 +107,7 @@ ElementType assembleScalarValue(const std::vector& bytes) noexcept { } template -ElementType assembleVecNValue(const std::vector& bytes) noexcept { +ElementType assembleVecNValue(const gsl::span bytes) noexcept { ElementType result = ElementType(); const glm::length_t N = @@ -152,7 +153,7 @@ ElementType assembleVecNValue(const std::vector& bytes) noexcept { template PropertyArrayView -assembleArrayValue(const std::vector& bytes) noexcept { +assembleArrayValue(const gsl::span bytes) noexcept { std::vector result(bytes.size() / sizeof(T)); if constexpr (sizeof(T) == 2) { @@ -172,8 +173,7 @@ assembleArrayValue(const std::vector& bytes) noexcept { } template -ElementType -assembleValueFromChannels(const std::vector& bytes) noexcept { +ElementType assembleValueFromChannels(const gsl::span bytes) noexcept { assert(bytes.size() > 0 && "Channel input must have at least one value."); if constexpr (IsMetadataScalar::value) { @@ -193,16 +193,24 @@ assembleValueFromChannels(const std::vector& bytes) noexcept { double applySamplerWrapS(const double u, const int32_t wrapS); double applySamplerWrapT(const double v, const int32_t wrapT); +std::array sampleNearestPixel( + const ImageCesium& image, + const std::vector& channels, + const double u, + const double v); + /** * @brief A view of the data specified by a {@link PropertyTextureProperty}. * * Provides utilities to sample the property texture property using texture - * coordinates. + * coordinates. Property values are retrieved from the NEAREST texel without + * additional filtering applied. * - * @tparam ElementType The type of the elements represented in the property view - * @tparam Normalized Whether or not the property is normalized. If normalized, - * the elements can be retrieved as normalized floating-point numbers, as - * opposed to their integer values. + * @tparam ElementType The type of the elements represented in the property + * view + * @tparam Normalized Whether or not the property is normalized. If + * normalized, the elements can be retrieved as normalized floating-point + * numbers, as opposed to their integer values. */ template class PropertyTexturePropertyView; @@ -352,43 +360,10 @@ class PropertyTexturePropertyView double wrappedU = applySamplerWrapS(u, this->_pSampler->wrapS); double wrappedV = applySamplerWrapT(v, this->_pSampler->wrapT); - // TODO: account for sampler's filter (can be nearest or linear) - - // For nearest filtering, std::floor is used instead of std::round. - // This is because filtering is supposed to consider the pixel centers. But - // memory access here acts as sampling the beginning of the pixel. Example: - // 0.4 * 2 = 0.8. In a 2x1 pixel image, that should be closer to the left - // pixel's center. But it will round to 1.0 which corresponds to the right - // pixel. So the right pixel has a bigger range than the left one, which is - // incorrect. - double xCoord = std::floor(wrappedU * this->_pImage->width); - double yCoord = std::floor(wrappedV * this->_pImage->height); - - // Clamp to ensure no out-of-bounds data access - int64_t x = glm::clamp( - static_cast(xCoord), - static_cast(0), - static_cast(this->_pImage->width) - 1); - int64_t y = glm::clamp( - static_cast(yCoord), - static_cast(0), - static_cast(this->_pImage->height) - 1); - - int64_t pixelIndex = this->_pImage->bytesPerChannel * - this->_pImage->channels * - (y * this->_pImage->width + x); - - // TODO: Currently stb only outputs uint8 pixel types. If that - // changes this should account for additional pixel byte sizes. - const uint8_t* pValue = reinterpret_cast( - this->_pImage->pixelData.data() + pixelIndex); - - std::vector channelValues(this->_channels.size()); - for (size_t i = 0; i < this->_channels.size(); i++) { - channelValues[i] = *(pValue + this->_channels[i]); - } - - return assembleValueFromChannels(channelValues); + std::array sample = + sampleNearestPixel(*this->_pImage, this->_channels, wrappedU, wrappedV); + return assembleValueFromChannels( + gsl::span(sample.data(), this->_channels.size())); } /** @@ -598,43 +573,11 @@ class PropertyTexturePropertyView double wrappedU = applySamplerWrapS(u, this->_pSampler->wrapS); double wrappedV = applySamplerWrapT(v, this->_pSampler->wrapT); - // TODO: account for sampler's filter (can be nearest or linear) - - // For nearest filtering, std::floor is used instead of std::round. - // This is because filtering is supposed to consider the pixel centers. But - // memory access here acts as sampling the beginning of the pixel. Example: - // 0.4 * 2 = 0.8. In a 2x1 pixel image, that should be closer to the left - // pixel's center. But it will round to 1.0 which corresponds to the right - // pixel. So the right pixel has a bigger range than the left one, which is - // incorrect. - double xCoord = std::floor(wrappedU * this->_pImage->width); - double yCoord = std::floor(wrappedV * this->_pImage->height); - - // Clamp to ensure no out-of-bounds data access - int64_t x = glm::clamp( - static_cast(xCoord), - static_cast(0), - static_cast(this->_pImage->width) - 1); - int64_t y = glm::clamp( - static_cast(yCoord), - static_cast(0), - static_cast(this->_pImage->height) - 1); - - int64_t pixelIndex = this->_pImage->bytesPerChannel * - this->_pImage->channels * - (y * this->_pImage->width + x); - - // TODO: Currently stb only outputs uint8 pixel types. If that - // changes this should account for additional pixel byte sizes. - const uint8_t* pValue = reinterpret_cast( - this->_pImage->pixelData.data() + pixelIndex); - - std::vector channelValues(this->_channels.size()); - for (size_t i = 0; i < this->_channels.size(); i++) { - channelValues[i] = *(pValue + this->_channels[i]); - } + std::array sample = + sampleNearestPixel(*this->_pImage, this->_channels, wrappedU, wrappedV); - return assembleValueFromChannels(channelValues); + return assembleValueFromChannels( + gsl::span(sample.data(), this->_channels.size())); } /** diff --git a/CesiumGltf/src/PropertyTexturePropertyView.cpp b/CesiumGltf/src/PropertyTexturePropertyView.cpp index 5b7f654ba..e759b6b03 100644 --- a/CesiumGltf/src/PropertyTexturePropertyView.cpp +++ b/CesiumGltf/src/PropertyTexturePropertyView.cpp @@ -58,4 +58,45 @@ double applySamplerWrapT(const double v, const int32_t wrapT) { return glm::clamp(v, 0.0, 1.0); } +std::array sampleNearestPixel( + const ImageCesium& image, + const std::vector& channels, + const double u, + const double v) { + // For nearest filtering, std::floor is used instead of std::round. + // This is because filtering is supposed to consider the pixel centers. But + // memory access here acts as sampling the beginning of the pixel. Example: + // 0.4 * 2 = 0.8. In a 2x1 pixel image, that should be closer to the left + // pixel's center. But it will round to 1.0 which corresponds to the right + // pixel. So the right pixel has a bigger range than the left one, which is + // incorrect. + double xCoord = std::floor(u * image.width); + double yCoord = std::floor(v * image.height); + + // Clamp to ensure no out-of-bounds data access + int64_t x = glm::clamp( + static_cast(xCoord), + static_cast(0), + static_cast(image.width) - 1); + int64_t y = glm::clamp( + static_cast(yCoord), + static_cast(0), + static_cast(image.height) - 1); + + int64_t pixelIndex = + image.bytesPerChannel * image.channels * (y * image.width + x); + + // TODO: Currently stb only outputs uint8 pixel types. If that + // changes this should account for additional pixel byte sizes. + const uint8_t* pValue = + reinterpret_cast(image.pixelData.data() + pixelIndex); + + std::array channelValues{0, 0, 0, 0}; + for (size_t i = 0; i < channels.size(); i++) { + channelValues[i] = *(pValue + channels[i]); + } + + return channelValues; +} + } // namespace CesiumGltf From b7c3daea055e84f9be175ed41383abc37c2e92d4 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Thu, 7 Sep 2023 14:55:35 -0400 Subject: [PATCH 265/421] Check for all-null properties --- .../src/BatchTableToGltfStructuralMetadata.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp b/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp index f56128b1e..4ff3abc25 100644 --- a/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp +++ b/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp @@ -219,6 +219,14 @@ struct CompatibleTypes { return type.isInt8 || type.isInt16 || type.isInt32 || type.isInt64; } + /** + * Whether this property is compatible with every type. This only really + * happens when a CompatibleTypes is initialized and never modified. + */ + bool isFullyCompatible() const noexcept { + return std::holds_alternative(_type); + } + /** * Whether this property is incompatible with every primitive type. * Fully-incompatible properties will be treated as string properties. @@ -1386,6 +1394,11 @@ void updateExtensionWithJsonProperty( // Figure out which types we can use for this data. // Use the smallest type we can, and prefer signed to unsigned. const CompatibleTypes compatibleTypes = findCompatibleTypes(propertyValue); + if (compatibleTypes.isFullyCompatible()) { + // If this is "fully compatible", then the property contained no values (or + // rather, no non-null values). Exclude it from the model to avoid errors. + return; + } if (compatibleTypes.isExclusivelyArray()) { MaskedArrayType arrayType = compatibleTypes.toMaskedArrayType(); From f3b43c57360f9a6410654508ca553d9605076a06 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Fri, 8 Sep 2023 11:43:48 +1000 Subject: [PATCH 266/421] Slightly improve safety of sampleNearestPixel. --- CesiumGltf/src/PropertyTexturePropertyView.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CesiumGltf/src/PropertyTexturePropertyView.cpp b/CesiumGltf/src/PropertyTexturePropertyView.cpp index e759b6b03..144b92e1d 100644 --- a/CesiumGltf/src/PropertyTexturePropertyView.cpp +++ b/CesiumGltf/src/PropertyTexturePropertyView.cpp @@ -92,7 +92,8 @@ std::array sampleNearestPixel( reinterpret_cast(image.pixelData.data() + pixelIndex); std::array channelValues{0, 0, 0, 0}; - for (size_t i = 0; i < channels.size(); i++) { + size_t len = glm::min(channels.size(), channelValues.size()); + for (size_t i = 0; i < len; i++) { channelValues[i] = *(pValue + channels[i]); } From b9aace5cca9212e614bbdbb3e61641d3a6daeefe Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Fri, 8 Sep 2023 13:37:25 +1000 Subject: [PATCH 267/421] Add missing include. --- CesiumGltf/include/CesiumGltf/FeatureTexturePropertyView.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CesiumGltf/include/CesiumGltf/FeatureTexturePropertyView.h b/CesiumGltf/include/CesiumGltf/FeatureTexturePropertyView.h index adc6942a6..8f6b291af 100644 --- a/CesiumGltf/include/CesiumGltf/FeatureTexturePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/FeatureTexturePropertyView.h @@ -11,6 +11,7 @@ #include "ImageCesium.h" #include "Model.h" +#include #include #include #include @@ -252,4 +253,4 @@ class FeatureTexturePropertyView { int64_t _componentCount; bool _normalized; }; -} // namespace CesiumGltf \ No newline at end of file +} // namespace CesiumGltf From 2002f0d3137c975367d4804f7775d66824943d46 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Fri, 8 Sep 2023 15:41:52 +1000 Subject: [PATCH 268/421] Add missing generated file. --- .../src/FeatureIdTextureJsonHandler.h | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 CesiumGltfReader/generated/src/FeatureIdTextureJsonHandler.h diff --git a/CesiumGltfReader/generated/src/FeatureIdTextureJsonHandler.h b/CesiumGltfReader/generated/src/FeatureIdTextureJsonHandler.h new file mode 100644 index 000000000..703c486b4 --- /dev/null +++ b/CesiumGltfReader/generated/src/FeatureIdTextureJsonHandler.h @@ -0,0 +1,39 @@ +// This file was generated by generate-classes. +// DO NOT EDIT THIS FILE! +#pragma once + +#include "TextureInfoJsonHandler.h" + +#include +#include +#include + +namespace CesiumJsonReader { +class JsonReaderOptions; +} + +namespace CesiumGltfReader { +class FeatureIdTextureJsonHandler : public TextureInfoJsonHandler { +public: + using ValueType = CesiumGltf::FeatureIdTexture; + + FeatureIdTextureJsonHandler( + const CesiumJsonReader::JsonReaderOptions& options) noexcept; + void + reset(IJsonHandler* pParentHandler, CesiumGltf::FeatureIdTexture* pObject); + + virtual IJsonHandler* readObjectKey(const std::string_view& str) override; + +protected: + IJsonHandler* readObjectKeyFeatureIdTexture( + const std::string& objectType, + const std::string_view& str, + CesiumGltf::FeatureIdTexture& o); + +private: + CesiumGltf::FeatureIdTexture* _pObject = nullptr; + CesiumJsonReader:: + ArrayJsonHandler> + _channels; +}; +} // namespace CesiumGltfReader From 272f0c95c89b66a081bdb6a3cb60929c0c78804f Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Fri, 8 Sep 2023 22:45:14 +1000 Subject: [PATCH 269/421] Formatting. --- .../include/CesiumGltf/PropertyTypeTraits.h | 32 ++++++++++++++----- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/CesiumGltf/include/CesiumGltf/PropertyTypeTraits.h b/CesiumGltf/include/CesiumGltf/PropertyTypeTraits.h index 286cb0609..2dfeddce2 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTypeTraits.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTypeTraits.h @@ -293,14 +293,30 @@ struct CanBeNormalized> : CanBeNormalized {}; */ template struct TypeToNormalizedType; -template <> struct TypeToNormalizedType { using type = double; }; -template <> struct TypeToNormalizedType { using type = double; }; -template <> struct TypeToNormalizedType { using type = double; }; -template <> struct TypeToNormalizedType { using type = double; }; -template <> struct TypeToNormalizedType { using type = double; }; -template <> struct TypeToNormalizedType { using type = double; }; -template <> struct TypeToNormalizedType { using type = double; }; -template <> struct TypeToNormalizedType { using type = double; }; +template <> struct TypeToNormalizedType { + using type = double; +}; +template <> struct TypeToNormalizedType { + using type = double; +}; +template <> struct TypeToNormalizedType { + using type = double; +}; +template <> struct TypeToNormalizedType { + using type = double; +}; +template <> struct TypeToNormalizedType { + using type = double; +}; +template <> struct TypeToNormalizedType { + using type = double; +}; +template <> struct TypeToNormalizedType { + using type = double; +}; +template <> struct TypeToNormalizedType { + using type = double; +}; template struct TypeToNormalizedType> { From c7a5c70e37db4e68d88e555b6bce493fad61c381 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Fri, 8 Sep 2023 22:57:38 +1000 Subject: [PATCH 270/421] Fix case of filename. --- .../{FeatureIDTextureReader.h => FeatureIdTextureReader.h} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename CesiumGltfReader/generated/include/CesiumGltfReader/{FeatureIDTextureReader.h => FeatureIdTextureReader.h} (100%) diff --git a/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureIDTextureReader.h b/CesiumGltfReader/generated/include/CesiumGltfReader/FeatureIdTextureReader.h similarity index 100% rename from CesiumGltfReader/generated/include/CesiumGltfReader/FeatureIDTextureReader.h rename to CesiumGltfReader/generated/include/CesiumGltfReader/FeatureIdTextureReader.h From 5831764f4c378a7781f510d4227762f77cf8f3b5 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Fri, 8 Sep 2023 23:44:01 +1000 Subject: [PATCH 271/421] Bump to v0.28.0, update CHANGES.md. --- CHANGES.md | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 8de3ee660..e632779c9 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,6 @@ # Change Log -### ? - ? +### v0.28.0 - 2023-09-08 ##### Breaking Changes :mega: @@ -30,7 +30,7 @@ - Added `PropertyComponentType` to reflect the values of `componentType` in a `ClassProperty` from `EXT_structural_metadata`. - Added `PropertyAttributeView`, which views a `PropertyAttribute` in `EXT_structural_metadata`. - Added `PropertyAttributePropertyView`, which views a `PropertyAttributeProperty` in `EXT_structural_metadata`. -- Added `PropertyAttributePropertyViewStatus`, which reflects the sattus of a `PropertyAttributePropertyView`. +- Added `PropertyAttributePropertyViewStatus`, which reflects the status of a `PropertyAttributePropertyView`. ### v0.27.1 - 2023-09-03 diff --git a/package.json b/package.json index b5cbb34fe..56706fac4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cesium-native", - "version": "0.27.0", + "version": "0.28.0", "description": "Cesium 3D Geospatial for C++", "main": "index.js", "directories": { From 9d5d43126ad187093b1e89b6607ca61b5f86678f Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Mon, 11 Sep 2023 14:17:11 -0400 Subject: [PATCH 272/421] Add getClass method --- CesiumGltf/include/CesiumGltf/PropertyTableView.h | 13 ++++++++++--- CesiumGltf/src/PropertyTableView.cpp | 2 ++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/CesiumGltf/include/CesiumGltf/PropertyTableView.h b/CesiumGltf/include/CesiumGltf/PropertyTableView.h index c4c89cdf0..6dfbe2634 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTableView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTableView.h @@ -90,13 +90,20 @@ class PropertyTableView { : 0; } + /** + * @brief Gets the {@link Class} that this property table conforms to. + * + * @return A pointer to the {@link Class}. Returns nullptr if the PropertyTable did not + * specify a valid class. + */ + const Class* getClass() const; + /** * @brief Finds the {@link ClassProperty} that * describes the type information of the property with the specified name. * @param propertyName The name of the property to retrieve the class for. - * @return A pointer to the {@link ClassProperty}. - * Return nullptr if the PropertyTableView is invalid or if no class - * property was found. + * @return A pointer to the {@link ClassProperty}. Returns nullptr if the + * PropertyTableView is invalid or if no class property was found. */ const ClassProperty* getClassProperty(const std::string& propertyName) const; diff --git a/CesiumGltf/src/PropertyTableView.cpp b/CesiumGltf/src/PropertyTableView.cpp index 3ba261999..aeb99b1d6 100644 --- a/CesiumGltf/src/PropertyTableView.cpp +++ b/CesiumGltf/src/PropertyTableView.cpp @@ -133,6 +133,8 @@ PropertyTableView::PropertyTableView( _pClass = &classIter->second; } +const Class* PropertyTableView::getClass() const { return _pClass; } + const ClassProperty* PropertyTableView::getClassProperty(const std::string& propertyName) const { if (_status != PropertyTableViewStatus::Valid) { From 5162db0c19069f6e9bbcbfd9a146f4c76052bc8a Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Mon, 11 Sep 2023 14:22:51 -0400 Subject: [PATCH 273/421] Add method to other property classes --- CesiumGltf/include/CesiumGltf/PropertyAttributeView.h | 8 ++++++++ CesiumGltf/include/CesiumGltf/PropertyTableView.h | 6 +++--- CesiumGltf/include/CesiumGltf/PropertyTextureView.h | 8 ++++++++ CesiumGltf/src/PropertyTableView.cpp | 2 -- 4 files changed, 19 insertions(+), 5 deletions(-) diff --git a/CesiumGltf/include/CesiumGltf/PropertyAttributeView.h b/CesiumGltf/include/CesiumGltf/PropertyAttributeView.h index aac020193..f8fb2ed90 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyAttributeView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyAttributeView.h @@ -80,6 +80,14 @@ class PropertyAttributeView { return _pPropertyAttribute->name; } + /** + * @brief Gets the {@link Class} that this property attribute conforms to. + * + * @return A pointer to the {@link Class}. Returns nullptr if the + * PropertyAttribute did not specify a valid class. + */ + const Class* getClass() const noexcept { return _pClass; } + /** * @brief Finds the {@link ClassProperty} that * describes the type information of the property with the specified name. diff --git a/CesiumGltf/include/CesiumGltf/PropertyTableView.h b/CesiumGltf/include/CesiumGltf/PropertyTableView.h index 6dfbe2634..e1f5d3e17 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTableView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTableView.h @@ -93,10 +93,10 @@ class PropertyTableView { /** * @brief Gets the {@link Class} that this property table conforms to. * - * @return A pointer to the {@link Class}. Returns nullptr if the PropertyTable did not - * specify a valid class. + * @return A pointer to the {@link Class}. Returns nullptr if the PropertyTable + * did not specify a valid class. */ - const Class* getClass() const; + const Class* getClass() const noexcept { return _pClass; } /** * @brief Finds the {@link ClassProperty} that diff --git a/CesiumGltf/include/CesiumGltf/PropertyTextureView.h b/CesiumGltf/include/CesiumGltf/PropertyTextureView.h index 4573ddf77..976c7c946 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTextureView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTextureView.h @@ -76,6 +76,14 @@ class PropertyTextureView { return _pPropertyTexture->name; } + /** + * @brief Gets the {@link Class} that this property texture conforms to. + * + * @return A pointer to the {@link Class}. Returns nullptr if the PropertyTexture + * did not specify a valid class. + */ + const Class* getClass() const noexcept { return _pClass; } + /** * @brief Finds the {@link ClassProperty} that * describes the type information of the property with the specified name. diff --git a/CesiumGltf/src/PropertyTableView.cpp b/CesiumGltf/src/PropertyTableView.cpp index aeb99b1d6..3ba261999 100644 --- a/CesiumGltf/src/PropertyTableView.cpp +++ b/CesiumGltf/src/PropertyTableView.cpp @@ -133,8 +133,6 @@ PropertyTableView::PropertyTableView( _pClass = &classIter->second; } -const Class* PropertyTableView::getClass() const { return _pClass; } - const ClassProperty* PropertyTableView::getClassProperty(const std::string& propertyName) const { if (_status != PropertyTableViewStatus::Valid) { From 856d34ea19e576b08214a86c2fd488aa7fe11a93 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Wed, 13 Sep 2023 14:26:20 -0400 Subject: [PATCH 274/421] Add new status code for empty properties with default values --- .../PropertyAttributePropertyView.h | 136 ++++++++++---- .../CesiumGltf/PropertyAttributeView.h | 35 ++++ .../CesiumGltf/PropertyTablePropertyView.h | 130 ++++++++++--- .../include/CesiumGltf/PropertyTableView.h | 10 + .../CesiumGltf/PropertyTexturePropertyView.h | 88 ++++++++- .../include/CesiumGltf/PropertyTextureView.h | 7 + CesiumGltf/include/CesiumGltf/PropertyView.h | 33 ++-- CesiumGltf/test/TestPropertyAttributeView.cpp | 174 ++++++++++++++++++ CesiumGltf/test/TestPropertyTableView.cpp | 150 +++++++++++++++ CesiumGltf/test/TestPropertyTextureView.cpp | 159 ++++++++++++++++ 10 files changed, 836 insertions(+), 86 deletions(-) diff --git a/CesiumGltf/include/CesiumGltf/PropertyAttributePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyAttributePropertyView.h index 7439758da..2c375efb5 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyAttributePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyAttributePropertyView.h @@ -25,66 +25,66 @@ class PropertyAttributePropertyViewStatus : public PropertyViewStatus { * @brief This property view was initialized from an invalid * {@link PropertyAttribute}. */ - static const int ErrorInvalidPropertyAttribute = 13; + static const int ErrorInvalidPropertyAttribute = 14; /** * @brief This property view is associated with a {@link ClassProperty} of an * unsupported type. */ - static const int ErrorUnsupportedProperty = 14; + static const int ErrorUnsupportedProperty = 15; /** * @brief This property view was initialized with a primitive that does not * contain the specified attribute. */ - static const int ErrorMissingAttribute = 15; + static const int ErrorMissingAttribute = 16; /** * @brief This property view's attribute does not have a valid accessor index. */ - static const int ErrorInvalidAccessor = 16; + static const int ErrorInvalidAccessor = 17; /** * @brief This property view's type does not match the type of the accessor it * uses. */ - static const int ErrorAccessorTypeMismatch = 17; + static const int ErrorAccessorTypeMismatch = 18; /** * @brief This property view's component type does not match the type of the * accessor it uses. */ - static const int ErrorAccessorComponentTypeMismatch = 18; + static const int ErrorAccessorComponentTypeMismatch = 19; /** * @brief This property view's normalization does not match the normalization * of the accessor it uses. */ - static const int ErrorAccessorNormalizationMismatch = 19; + static const int ErrorAccessorNormalizationMismatch = 20; /** * @brief This property view uses an accessor that does not have a valid * buffer view index. */ - static const int ErrorInvalidBufferView = 20; + static const int ErrorInvalidBufferView = 21; /** * @brief This property view uses a buffer view that does not have a valid * buffer index. */ - static const int ErrorInvalidBuffer = 21; + static const int ErrorInvalidBuffer = 22; /** * @brief This property view uses an accessor that points outside the bounds * of its target buffer view. */ - static const PropertyViewStatusType ErrorAccessorOutOfBounds = 22; + static const PropertyViewStatusType ErrorAccessorOutOfBounds = 23; /** * @brief This property view uses a buffer view that points outside the bounds * of its target buffer. */ - static const PropertyViewStatusType ErrorBufferViewOutOfBounds = 23; + static const PropertyViewStatusType ErrorBufferViewOutOfBounds = 24; }; /** @@ -122,26 +122,56 @@ class PropertyAttributePropertyView * @brief Constructs an invalid instance for a non-existent property. */ PropertyAttributePropertyView() noexcept - : PropertyView(), _accessor{} {} + : PropertyView(), _accessor{}, _size{0} {} /** * @brief Constructs an invalid instance for an erroneous property. * - * @param status The code from {@link PropertyAttributePropertyViewStatus} indicating the error with the property. + * @param status The value of {@link PropertyAttributePropertyViewStatus} indicating the error with the property. */ PropertyAttributePropertyView(PropertyViewStatusType status) noexcept - : PropertyView(status), _accessor{} { + : PropertyView(status), _accessor{}, _size{0} { assert( this->_status != PropertyAttributePropertyViewStatus::Valid && "An empty property view should not be constructed with a valid status"); } + /** + * @brief Constructs an instance of an empty property that specifies a default + * value. Although this property has no data, it can return the default value + * when {@link PropertyAttributePropertyView::get} is called. However, + * {@link PropertyAttributePropertyView::getRaw} cannot be used. + * + * @param classProperty The {@link ClassProperty} this property conforms to. + * @param size The number of elements in the primitive's POSITION accessor. + * Used as a substitute since no actual accessor is defined. + */ + PropertyAttributePropertyView( + const ClassProperty& classProperty, + int64_t size) noexcept + : PropertyView(classProperty), _accessor{}, _size{0} { + // Don't override the status / size if something is wrong with the class + // property's definition. + if (this->_status != PropertyAttributePropertyViewStatus::Valid) { + return; + } + + assert( + classProperty.defaultProperty && + "Cannot construct a valid property view for an empty property with no " + "default value."); + + this->_status = + PropertyAttributePropertyViewStatus::EmptyPropertyWithDefault; + this->_size = size; + } + /** * @brief Construct a view of the data specified by a {@link PropertyAttributeProperty}. * * @param property The {@link PropertyAttributeProperty} * @param classProperty The {@link ClassProperty} this property conforms to. - * @param accsesorView The {@link AccessorView} for the data that this property is + * @param accessorView The {@link AccessorView} for the data that this property is * associated with. */ PropertyAttributePropertyView( @@ -149,7 +179,11 @@ class PropertyAttributePropertyView const ClassProperty& classProperty, const AccessorView& accessorView) noexcept : PropertyView(classProperty, property), - _accessor{accessorView} {} + _accessor{accessorView}, + _size{ + this->_status == PropertyAttributePropertyViewStatus::Valid + ? accessorView.size() + : 0} {} /** * @brief Gets the value of the property for the given texture coordinates @@ -167,6 +201,11 @@ class PropertyAttributePropertyView * it matches the "no data" value */ std::optional get(int64_t index) const noexcept { + if (this->_status == + PropertyAttributePropertyViewStatus::EmptyPropertyWithDefault) { + return this->defaultValue(); + } + ElementType value = getRaw(index); if (value == this->noData()) { @@ -206,16 +245,11 @@ class PropertyAttributePropertyView * * @return The number of elements in this PropertyAttributePropertyView. */ - int64_t size() const noexcept { - if (this->_status != PropertyAttributePropertyViewStatus::Valid) { - return 0; - } - - return _accessor.size(); - } + int64_t size() const noexcept { return _size; } private: AccessorView _accessor; + int64_t _size; }; /** @@ -240,7 +274,7 @@ class PropertyAttributePropertyView * @brief Constructs an invalid instance for a non-existent property. */ PropertyAttributePropertyView() noexcept - : PropertyView(), _accessor{} {} + : PropertyView(), _accessor{}, _size{0} {} /** * @brief Constructs an invalid instance for an erroneous property. @@ -248,18 +282,48 @@ class PropertyAttributePropertyView * @param status The code from {@link PropertyAttributePropertyViewStatus} indicating the error with the property. */ PropertyAttributePropertyView(PropertyViewStatusType status) noexcept - : PropertyView(status), _accessor{} { + : PropertyView(status), _accessor{}, _size{0} { assert( this->_status != PropertyAttributePropertyViewStatus::Valid && "An empty property view should not be constructed with a valid status"); } + /** + * @brief Constructs an instance of an empty property that specifies a default + * value. Although this property has no data, it can return the default value + * when {@link PropertyAttributePropertyView::get} is called. However, + * {@link PropertyAttributePropertyView::getRaw} cannot be used. + * + * @param classProperty The {@link ClassProperty} this property conforms to. + * @param size The number of elements in the primitive's POSITION accessor. + * Used as a substitute since no actual accessor is defined. + */ + PropertyAttributePropertyView( + const ClassProperty& classProperty, + int64_t size) noexcept + : PropertyView(classProperty), _accessor{}, _size{0} { + // Don't override the status / size if something is wrong with the class + // property's definition. + if (this->_status != PropertyAttributePropertyViewStatus::Valid) { + return; + } + + assert( + classProperty.defaultProperty && + "Cannot construct a valid property view for an empty property with no " + "default value."); + + this->_status = + PropertyAttributePropertyViewStatus::EmptyPropertyWithDefault; + this->_size = size; + } + /** * @brief Construct a view of the data specified by a {@link PropertyAttributeProperty}. * * @param property The {@link PropertyAttributeProperty} * @param classProperty The {@link ClassProperty} this property conforms to. - * @param accsesorView The {@link AccessorView} for the data that this property is + * @param accessorView The {@link AccessorView} for the data that this property is * associated with. */ PropertyAttributePropertyView( @@ -267,7 +331,11 @@ class PropertyAttributePropertyView const ClassProperty& classProperty, const AccessorView& accessorView) noexcept : PropertyView(classProperty, property), - _accessor{accessorView} {} + _accessor{accessorView}, + _size{ + this->_status == PropertyAttributePropertyViewStatus::Valid + ? accessorView.size() + : 0} {} /** * @brief Gets the value of the property for the given texture coordinates @@ -285,6 +353,11 @@ class PropertyAttributePropertyView * it matches the "no data" value */ std::optional get(int64_t index) const noexcept { + if (this->_status == + PropertyAttributePropertyViewStatus::EmptyPropertyWithDefault) { + return this->defaultValue(); + } + ElementType value = getRaw(index); if (value == this->noData()) { @@ -349,16 +422,11 @@ class PropertyAttributePropertyView * * @return The number of elements in this PropertyAttributePropertyView. */ - int64_t size() const noexcept { - if (this->_status != PropertyAttributePropertyViewStatus::Valid) { - return 0; - } - - return _accessor.size(); - } + int64_t size() const noexcept { return _size; } private: AccessorView _accessor; + int64_t _size; }; } // namespace CesiumGltf diff --git a/CesiumGltf/include/CesiumGltf/PropertyAttributeView.h b/CesiumGltf/include/CesiumGltf/PropertyAttributeView.h index f8fb2ed90..f07682ecc 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyAttributeView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyAttributeView.h @@ -315,6 +315,31 @@ class PropertyAttributeView { } private: + template + PropertyAttributePropertyView getEmptyPropertyViewWithDefault( + const MeshPrimitive& primitive, + const ClassProperty& classProperty) const { + // To make the view have a nonzero size, find the POSITION attribute and get + // its accessor count. If it doesn't exist or is somehow erroneous, just + // mark the property as nonexistent. + if (primitive.attributes.find("POSITION") == primitive.attributes.end()) { + return PropertyAttributePropertyView( + PropertyAttributePropertyViewStatus::ErrorNonexistentProperty); + } + + const Accessor* pAccessor = _pModel->getSafe( + &_pModel->accessors, + primitive.attributes.at("POSITION")); + if (!pAccessor) { + return PropertyAttributePropertyView( + PropertyAttributePropertyViewStatus::ErrorNonexistentProperty); + } + + return PropertyAttributePropertyView( + classProperty, + pAccessor->count); + } + template PropertyAttributePropertyView getPropertyViewImpl( const MeshPrimitive& primitive, @@ -324,6 +349,16 @@ class PropertyAttributeView { _pPropertyAttribute->properties.find(propertyName); if (propertyAttributePropertyIter == _pPropertyAttribute->properties.end()) { + if (!classProperty.required && classProperty.defaultProperty) { + // If the property was omitted from the property attribute, it is still + // technically valid if it specifies a default value. Try to create a + // view that just returns the default value. + return getEmptyPropertyViewWithDefault( + primitive, + classProperty); + } + + // Otherwise, the property is erroneously nonexistent. return PropertyAttributePropertyView( PropertyAttributePropertyViewStatus::ErrorNonexistentProperty); } diff --git a/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h index e4804a67f..3c28f2f0e 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h @@ -29,108 +29,108 @@ class PropertyTablePropertyViewStatus : public PropertyViewStatus { * @brief This property view was initialized from an invalid * {@link PropertyTable}. */ - static const PropertyViewStatusType ErrorInvalidPropertyTable = 13; + static const PropertyViewStatusType ErrorInvalidPropertyTable = 14; /** * @brief This property view does not have a valid value buffer view index. */ - static const PropertyViewStatusType ErrorInvalidValueBufferView = 14; + static const PropertyViewStatusType ErrorInvalidValueBufferView = 15; /** * @brief This array property view does not have a valid array offset buffer * view index. */ - static const PropertyViewStatusType ErrorInvalidArrayOffsetBufferView = 15; + static const PropertyViewStatusType ErrorInvalidArrayOffsetBufferView = 16; /** * @brief This string property view does not have a valid string offset buffer * view index. */ - static const PropertyViewStatusType ErrorInvalidStringOffsetBufferView = 16; + static const PropertyViewStatusType ErrorInvalidStringOffsetBufferView = 17; /** * @brief This property view has a valid value buffer view, but the buffer * view specifies an invalid buffer index. */ - static const PropertyViewStatusType ErrorInvalidValueBuffer = 17; + static const PropertyViewStatusType ErrorInvalidValueBuffer = 18; /** * @brief This property view has a valid array string buffer view, but the * buffer view specifies an invalid buffer index. */ - static const PropertyViewStatusType ErrorInvalidArrayOffsetBuffer = 18; + static const PropertyViewStatusType ErrorInvalidArrayOffsetBuffer = 19; /** * @brief This property view has a valid string offset buffer view, but the * buffer view specifies an invalid buffer index. */ - static const PropertyViewStatusType ErrorInvalidStringOffsetBuffer = 19; + static const PropertyViewStatusType ErrorInvalidStringOffsetBuffer = 20; /** * @brief This property view has a buffer view that points outside the bounds * of its target buffer. */ - static const PropertyViewStatusType ErrorBufferViewOutOfBounds = 20; + static const PropertyViewStatusType ErrorBufferViewOutOfBounds = 21; /** * @brief This property view has an invalid buffer view; its length is not * a multiple of the size of its type / offset type. */ static const PropertyViewStatusType - ErrorBufferViewSizeNotDivisibleByTypeSize = 21; + ErrorBufferViewSizeNotDivisibleByTypeSize = 22; /** * @brief This property view has an invalid buffer view; its length does not * match the size of the property table. */ static const PropertyViewStatusType - ErrorBufferViewSizeDoesNotMatchPropertyTableCount = 22; + ErrorBufferViewSizeDoesNotMatchPropertyTableCount = 23; /** * @brief This array property view has both a fixed length and an offset * buffer view defined. */ static const PropertyViewStatusType ErrorArrayCountAndOffsetBufferCoexist = - 23; + 24; /** * @brief This array property view has neither a fixed length nor an offset * buffer view defined. */ static const PropertyViewStatusType ErrorArrayCountAndOffsetBufferDontExist = - 24; + 25; /** * @brief This property view has an unknown array offset type. */ - static const PropertyViewStatusType ErrorInvalidArrayOffsetType = 25; + static const PropertyViewStatusType ErrorInvalidArrayOffsetType = 26; /** * @brief This property view has an unknown string offset type. */ - static const PropertyViewStatusType ErrorInvalidStringOffsetType = 26; + static const PropertyViewStatusType ErrorInvalidStringOffsetType = 27; /** * @brief This property view's array offset values are not sorted in ascending * order. */ - static const PropertyViewStatusType ErrorArrayOffsetsNotSorted = 27; + static const PropertyViewStatusType ErrorArrayOffsetsNotSorted = 28; /** * @brief This property view's string offset values are not sorted in * ascending order. */ - static const PropertyViewStatusType ErrorStringOffsetsNotSorted = 28; + static const PropertyViewStatusType ErrorStringOffsetsNotSorted = 29; /** * @brief This property view has an array offset that is out of bounds. */ - static const PropertyViewStatusType ErrorArrayOffsetOutOfBounds = 29; + static const PropertyViewStatusType ErrorArrayOffsetOutOfBounds = 30; /** * @brief This property view has a string offset that is out of bounds. */ - static const PropertyViewStatusType ErrorStringOffsetOutOfBounds = 29; + static const PropertyViewStatusType ErrorStringOffsetOutOfBounds = 31; }; int64_t getOffsetTypeSize(PropertyComponentType offsetType) noexcept; @@ -177,7 +177,7 @@ class PropertyTablePropertyView /** * @brief Constructs an invalid instance for an erroneous property. * - * @param status The value of {@link PropertyTablePropertyViewStatus} indicating the error with the property. + * @param status The code from {@link PropertyTablePropertyViewStatus} indicating the error with the property. */ PropertyTablePropertyView(PropertyViewStatusType status) : PropertyView(status), @@ -194,6 +194,40 @@ class PropertyTablePropertyView "An empty property view should not be constructed with a valid status"); } + /** + * @brief Constructs an instance of an empty property that specifies a default + * value. Although this property has no data, it can return the default value + * when {@link PropertyTablePropertyView::get} is called. However, + * {@link PropertyTablePropertyView::getRaw} cannot be used. + * + * @param classProperty The {@link ClassProperty} this property conforms to. + * @param size The number of elements in the property table specified by {@link PropertyTable::count} + */ + PropertyTablePropertyView(const ClassProperty& classProperty, int64_t size) + : PropertyView(classProperty), + _values{}, + _size{0}, + _arrayOffsets{}, + _arrayOffsetType{PropertyComponentType::None}, + _arrayOffsetTypeSize{0}, + _stringOffsets{}, + _stringOffsetType{PropertyComponentType::None}, + _stringOffsetTypeSize{0} { + // Don't override the status / size if something is wrong with the class + // property's definition. + if (this->_status != PropertyTablePropertyViewStatus::Valid) { + return; + } + + assert( + classProperty.defaultProperty && + "Cannot construct a valid property view for an empty property with no " + "default value."); + + this->_status = PropertyTablePropertyViewStatus::EmptyPropertyWithDefault; + this->_size = size; + } + /** * @brief Construct an instance pointing to data specified by a {@link PropertyTableProperty}. * Used for non-array or fixed-length array data. @@ -267,6 +301,14 @@ class PropertyTablePropertyView * data" value */ std::optional get(int64_t index) const noexcept { + if (this->_status == + PropertyTablePropertyViewStatus::EmptyPropertyWithDefault) { + assert(index >= 0 && "index must be non-negative"); + assert(index < size() && "index must be less than size"); + + return this->defaultValue(); + } + ElementType value = getRaw(index); if (value == this->noData()) { @@ -337,9 +379,7 @@ class PropertyTablePropertyView * * @return The number of elements in this PropertyTablePropertyView. */ - int64_t size() const noexcept { - return this->_status == PropertyTablePropertyViewStatus::Valid ? _size : 0; - } + int64_t size() const noexcept { return _size; } private: ElementType getNumericValue(int64_t index) const noexcept { @@ -506,6 +546,37 @@ class PropertyTablePropertyView "An empty property view should not be constructed with a valid status"); } + /** + * @brief Constructs an instance of an empty property that specifies a default + * value. Although this property has no data, it can return the default value + * when {@link PropertyTablePropertyView::get} is called. However, + * {@link PropertyTablePropertyView::getRaw} cannot be used. + * + * @param classProperty The {@link ClassProperty} this property conforms to. + * @param size The number of elements in the property table specified by {@link PropertyTable::count} + */ + PropertyTablePropertyView(const ClassProperty& classProperty, int64_t size) + : PropertyView(classProperty), + _values{}, + _size{0}, + _arrayOffsets{}, + _arrayOffsetType{PropertyComponentType::None}, + _arrayOffsetTypeSize{0} { + // Don't override the status / size if something is wrong with the class + // property's definition. + if (this->_status != PropertyTablePropertyViewStatus::Valid) { + return; + } + + assert( + classProperty.defaultProperty && + "Cannot construct a valid property view for an empty property with no " + "default value."); + + this->_status = PropertyTablePropertyViewStatus::EmptyPropertyWithDefault; + this->_size = size; + } + /** * @brief Construct an instance pointing to data specified by a {@link PropertyTableProperty}. * Used for non-array or fixed-length array data. @@ -570,14 +641,13 @@ class PropertyTablePropertyView * data" value */ std::optional get(int64_t index) const noexcept { - assert( - this->_status == PropertyTablePropertyViewStatus::Valid && - "Check the status() first to make sure view is valid"); - assert( - size() > 0 && - "Check the size() of the view to make sure it's not empty"); - assert(index >= 0 && "index must be non-negative"); - assert(index < size() && "index must be less than size"); + if (this->_status == + PropertyTablePropertyViewStatus::EmptyPropertyWithDefault) { + assert(index >= 0 && "index must be non-negative"); + assert(index < size() && "index must be less than size"); + + return this->defaultValue(); + } ElementType value = getRaw(index); if (this->noData() && value == *(this->noData())) { diff --git a/CesiumGltf/include/CesiumGltf/PropertyTableView.h b/CesiumGltf/include/CesiumGltf/PropertyTableView.h index e1f5d3e17..bdd678d93 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTableView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTableView.h @@ -1054,6 +1054,16 @@ class PropertyTableView { auto propertyTablePropertyIter = _pPropertyTable->properties.find(propertyName); if (propertyTablePropertyIter == _pPropertyTable->properties.end()) { + if (!classProperty.required && classProperty.defaultProperty) { + // If the property was omitted from the property table, it is still + // technically valid if it specifies a default value. Create a view that + // just returns the default value. + return PropertyTablePropertyView( + classProperty, + _pPropertyTable->count); + } + + // Otherwise, the property is erroneously nonexistent. return PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorNonexistentProperty); } diff --git a/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h index 8b58998f5..516d78c9a 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h @@ -27,39 +27,39 @@ class PropertyTexturePropertyViewStatus : public PropertyViewStatus { * @brief This property view was initialized from an invalid * {@link PropertyTexture}. */ - static const int ErrorInvalidPropertyTexture = 13; + static const int ErrorInvalidPropertyTexture = 14; /** * @brief This property view is associated with a {@link ClassProperty} of an * unsupported type. */ - static const int ErrorUnsupportedProperty = 14; + static const int ErrorUnsupportedProperty = 15; /** * @brief This property view does not have a valid texture index. */ - static const int ErrorInvalidTexture = 15; + static const int ErrorInvalidTexture = 16; /** * @brief This property view does not have a valid sampler index. */ - static const int ErrorInvalidSampler = 16; + static const int ErrorInvalidSampler = 17; /** * @brief This property view does not have a valid image index. */ - static const int ErrorInvalidImage = 17; + static const int ErrorInvalidImage = 18; /** * @brief This property is viewing an empty image. */ - static const int ErrorEmptyImage = 18; + static const int ErrorEmptyImage = 19; /** * @brief This property uses an image with multi-byte channels. Only * single-byte channels are supported. */ - static const int ErrorInvalidBytesPerChannel = 19; + static const int ErrorInvalidBytesPerChannel = 20; /** * @brief The channels of this property texture property are invalid. @@ -68,7 +68,7 @@ class PropertyTexturePropertyViewStatus : public PropertyViewStatus { * more than four channels can be defined for specialized texture * formats, this implementation only supports four channels max. */ - static const int ErrorInvalidChannels = 20; + static const int ErrorInvalidChannels = 21; /** * @brief The channels of this property texture property do not provide @@ -76,7 +76,7 @@ class PropertyTexturePropertyViewStatus : public PropertyViewStatus { * because an incorrect number of channels was provided, or because the * image itself has a different channel count / byte size than expected. */ - static const int ErrorChannelsAndTypeMismatch = 21; + static const int ErrorChannelsAndTypeMismatch = 22; }; template @@ -256,6 +256,35 @@ class PropertyTexturePropertyView "An empty property view should not be constructed with a valid status"); } + /** + * @brief Constructs an instance of an empty property that specifies a default + * value. Although this property has no data, it can return the default value + * when {@link PropertyTexturePropertyView::get} is called. However, + * {@link PropertyTexturePropertyView::getRaw} cannot be used. + * + * @param classProperty The {@link ClassProperty} this property conforms to. + */ + PropertyTexturePropertyView(const ClassProperty& classProperty) noexcept + : PropertyView(classProperty), + _pSampler(nullptr), + _pImage(nullptr), + _texCoordSetIndex(0), + _channels(), + _swizzle() { + // Don't override the status / size if something is wrong with the class + // property's definition. + if (this->_status != PropertyTexturePropertyViewStatus::Valid) { + return; + } + + assert( + classProperty.defaultProperty && + "Cannot construct a valid property view for an empty property with no " + "default value."); + + this->_status = PropertyTexturePropertyViewStatus::EmptyPropertyWithDefault; + } + /** * @brief Construct a view of the data specified by a {@link PropertyTextureProperty}. * @@ -263,7 +292,7 @@ class PropertyTexturePropertyView * @param classProperty The {@link ClassProperty} this property conforms to. * @param sampler The {@link Sampler} used by the property. * @param image The {@link ImageCesium} used by the property. - * @param channels The value of {@link PropertyTextureProperty::channels}. + * @param channels The code from {@link PropertyTextureProperty::channels}. */ PropertyTexturePropertyView( const PropertyTextureProperty& property, @@ -321,6 +350,11 @@ class PropertyTexturePropertyView * data" value */ std::optional get(double u, double v) const noexcept { + if (this->_status == + PropertyTexturePropertyViewStatus::EmptyPropertyWithDefault) { + return this->defaultValue(); + } + ElementType value = getRaw(u, v); if (value == this->noData()) { @@ -443,6 +477,35 @@ class PropertyTexturePropertyView "An empty property view should not be constructed with a valid status"); } + /** + * @brief Constructs an instance of an empty property that specifies a default + * value. Although this property has no data, it can return the default value + * when {@link PropertyTexturePropertyView::get} is called. However, + * {@link PropertyTexturePropertyView::getRaw} cannot be used. + * + * @param classProperty The {@link ClassProperty} this property conforms to. + */ + PropertyTexturePropertyView(const ClassProperty& classProperty) noexcept + : PropertyView(classProperty), + _pSampler(nullptr), + _pImage(nullptr), + _texCoordSetIndex(0), + _channels(), + _swizzle() { + // Don't override the status / size if something is wrong with the class + // property's definition. + if (this->_status != PropertyTexturePropertyViewStatus::Valid) { + return; + } + + assert( + classProperty.defaultProperty && + "Cannot construct a valid property view for an empty property with no " + "default value."); + + this->_status = PropertyTexturePropertyViewStatus::EmptyPropertyWithDefault; + } + /** * @brief Construct a view of the data specified by a {@link PropertyTextureProperty}. * @@ -508,6 +571,11 @@ class PropertyTexturePropertyView * data" value */ std::optional get(double u, double v) const noexcept { + if (this->_status == + PropertyTexturePropertyViewStatus::EmptyPropertyWithDefault) { + return this->defaultValue(); + } + ElementType value = getRaw(u, v); if (value == this->noData()) { diff --git a/CesiumGltf/include/CesiumGltf/PropertyTextureView.h b/CesiumGltf/include/CesiumGltf/PropertyTextureView.h index 976c7c946..f7ca8526d 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTextureView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTextureView.h @@ -285,6 +285,13 @@ class PropertyTextureView { auto propertyTexturePropertyIter = _pPropertyTexture->properties.find(propertyName); if (propertyTexturePropertyIter == _pPropertyTexture->properties.end()) { + if (!classProperty.required && classProperty.defaultProperty) { + // If the property was omitted from the property texture, it is still + // technically valid if it specifies a default value. Create a view that + // just returns the default value. + return PropertyTexturePropertyView(classProperty); + } + return PropertyTexturePropertyView( PropertyTexturePropertyViewStatus::ErrorNonexistentProperty); } diff --git a/CesiumGltf/include/CesiumGltf/PropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyView.h index 034127750..b912b9abc 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyView.h @@ -31,71 +31,80 @@ class PropertyViewStatus { */ static const PropertyViewStatusType Valid = 0; + /** + * @brief This property view does not contain any data, but specifies a + * default value. This happens when a class property is defined with a default + * value and omitted from an instance of the class's collective properties. In + * this case, it is not possible to retrieve the raw data from a property, but + * its default value will be accessible. + */ + static const PropertyViewStatusType EmptyPropertyWithDefault = 1; + /** * @brief This property view is trying to view a property that does not * exist. */ - static const PropertyViewStatusType ErrorNonexistentProperty = 1; + static const PropertyViewStatusType ErrorNonexistentProperty = 2; /** * @brief This property view's type does not match what is * specified in {@link ClassProperty::type}. */ - static const PropertyViewStatusType ErrorTypeMismatch = 2; + static const PropertyViewStatusType ErrorTypeMismatch = 3; /** * @brief This property view's component type does not match what * is specified in {@link ClassProperty::componentType}. */ - static const PropertyViewStatusType ErrorComponentTypeMismatch = 3; + static const PropertyViewStatusType ErrorComponentTypeMismatch = 4; /** * @brief This property view differs from what is specified in * {@link ClassProperty::array}. */ - static const PropertyViewStatusType ErrorArrayTypeMismatch = 4; + static const PropertyViewStatusType ErrorArrayTypeMismatch = 5; /** * @brief This property says it is normalized, but it does not have an integer * component type. */ - static const PropertyViewStatusType ErrorInvalidNormalization = 5; + static const PropertyViewStatusType ErrorInvalidNormalization = 6; /** * @brief This property view's normalization differs from what * is specified in {@link ClassProperty::normalized} */ - static const PropertyViewStatusType ErrorNormalizationMismatch = 6; + static const PropertyViewStatusType ErrorNormalizationMismatch = 7; /** * @brief The property provided an invalid offset value. */ - static const PropertyViewStatusType ErrorInvalidOffset = 7; + static const PropertyViewStatusType ErrorInvalidOffset = 8; /** * @brief The property provided an invalid scale value. */ - static const PropertyViewStatusType ErrorInvalidScale = 8; + static const PropertyViewStatusType ErrorInvalidScale = 9; /** * @brief The property provided an invalid maximum value. */ - static const PropertyViewStatusType ErrorInvalidMax = 9; + static const PropertyViewStatusType ErrorInvalidMax = 10; /** * @brief The property provided an invalid minimum value. */ - static const PropertyViewStatusType ErrorInvalidMin = 10; + static const PropertyViewStatusType ErrorInvalidMin = 11; /** * @brief The property provided an invalid "no data" value. */ - static const PropertyViewStatusType ErrorInvalidNoDataValue = 11; + static const PropertyViewStatusType ErrorInvalidNoDataValue = 12; /** * @brief The property provided an invalid default value. */ - static const PropertyViewStatusType ErrorInvalidDefaultValue = 12; + static const PropertyViewStatusType ErrorInvalidDefaultValue = 13; }; template diff --git a/CesiumGltf/test/TestPropertyAttributeView.cpp b/CesiumGltf/test/TestPropertyAttributeView.cpp index 5e9bae689..3447b0f82 100644 --- a/CesiumGltf/test/TestPropertyAttributeView.cpp +++ b/CesiumGltf/test/TestPropertyAttributeView.cpp @@ -1402,6 +1402,108 @@ TEST_CASE("Test with PropertyAttributeProperty noData (normalized)") { } } +TEST_CASE( + "Test nonexistent PropertyAttributeProperty with class property default") { + Model model; + Mesh& mesh = model.meshes.emplace_back(); + MeshPrimitive& primitive = mesh.primitives.emplace_back(); + + // Add a position attribute so it can be used to substitute the property + // accessor size. + const std::string attributeName = "POSITION"; + std::vector data = { + glm::vec3(0), + glm::vec3(1, 2, 3), + glm::vec3(0, 1, 0)}; + + addAttributeToModel(model, primitive, attributeName, data); + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::UINT16; + + const uint16_t defaultValue = 10; + testClassProperty.defaultProperty = defaultValue; + + PropertyAttribute& propertyAttribute = + metadata.propertyAttributes.emplace_back(); + propertyAttribute.classProperty = "TestClass"; + + PropertyAttributeView view(model, propertyAttribute); + REQUIRE(view.status() == PropertyAttributeViewStatus::Valid); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT16); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->array); + REQUIRE(!classProperty->normalized); + REQUIRE(classProperty->defaultProperty); + + SECTION("Access correct type") { + PropertyAttributePropertyView uint16Property = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE( + uint16Property.status() == + PropertyAttributePropertyViewStatus::EmptyPropertyWithDefault); + REQUIRE(uint16Property.size() == static_cast(data.size())); + REQUIRE(uint16Property.defaultValue() == defaultValue); + + for (size_t i = 0; i < data.size(); ++i) { + REQUIRE(uint16Property.get(static_cast(i)) == defaultValue); + } + } + + SECTION("Access wrong type") { + PropertyAttributePropertyView u16vec2Invalid = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE( + u16vec2Invalid.status() == + PropertyAttributePropertyViewStatus::ErrorTypeMismatch); + } + + SECTION("Access wrong component type") { + PropertyAttributePropertyView uint8Invalid = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE( + uint8Invalid.status() == + PropertyAttributePropertyViewStatus::ErrorComponentTypeMismatch); + } + + SECTION("Access incorrectly as normalized") { + PropertyAttributePropertyView normalizedInvalid = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE( + normalizedInvalid.status() == + PropertyAttributePropertyViewStatus::ErrorNormalizationMismatch); + } + + SECTION("Invalid default value") { + testClassProperty.defaultProperty = "not a number"; + PropertyAttributePropertyView uint16Property = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE( + uint16Property.status() == + PropertyAttributePropertyViewStatus::ErrorInvalidDefaultValue); + } + + SECTION("No default value") { + testClassProperty.defaultProperty.reset(); + PropertyAttributePropertyView uint16Property = + view.getPropertyView(primitive, "TestClassProperty"); + REQUIRE( + uint16Property.status() == + PropertyAttributePropertyViewStatus::ErrorNonexistentProperty); + } +} + TEST_CASE("Test callback on invalid property Attribute view") { Model model; Mesh& mesh = model.meshes.emplace_back(); @@ -2064,3 +2166,75 @@ TEST_CASE("Test callback on unsupported PropertyAttributeProperty") { }); REQUIRE(invokedCallbackCount == 2); } + +TEST_CASE( + "Test callback for empty PropertyAttributeProperty with default value") { + Model model; + Mesh& mesh = model.meshes.emplace_back(); + MeshPrimitive& primitive = mesh.primitives.emplace_back(); + + // Add a position attribute so it can be used to substitute the property + // accessor size. + const std::string attributeName = "POSITION"; + std::vector data = { + glm::vec3(0), + glm::vec3(1, 2, 3), + glm::vec3(0, 1, 0)}; + addAttributeToModel(model, primitive, attributeName, data); + + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::INT16; + + const int16_t defaultValue = 10; + testClassProperty.defaultProperty = defaultValue; + + PropertyAttribute& propertyAttribute = + metadata.propertyAttributes.emplace_back(); + propertyAttribute.classProperty = "TestClass"; + + PropertyAttributeView view(model, propertyAttribute); + REQUIRE(view.status() == PropertyAttributeViewStatus::Valid); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT16); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->array); + REQUIRE(!classProperty->normalized); + REQUIRE(classProperty->defaultProperty); + + uint32_t invokedCallbackCount = 0; + view.getPropertyView( + primitive, + "TestClassProperty", + [defaultValue, &data, &invokedCallbackCount]( + const std::string& /*propertyName*/, + auto propertyValue) mutable { + invokedCallbackCount++; + if constexpr (std::is_same_v< + PropertyAttributePropertyView, + decltype(propertyValue)>) { + REQUIRE( + propertyValue.status() == + PropertyAttributePropertyViewStatus::EmptyPropertyWithDefault); + REQUIRE(propertyValue.size() == static_cast(data.size())); + REQUIRE(propertyValue.defaultValue() == defaultValue); + for (size_t i = 0; i < data.size(); ++i) { + REQUIRE(propertyValue.get(static_cast(i)) == defaultValue); + } + } else { + FAIL("getPropertyView returned PropertyAttributePropertyView of " + "incorrect type for TestClassProperty."); + } + }); + + REQUIRE(invokedCallbackCount == 1); +} diff --git a/CesiumGltf/test/TestPropertyTableView.cpp b/CesiumGltf/test/TestPropertyTableView.cpp index 6e7e85a32..e15d2c835 100644 --- a/CesiumGltf/test/TestPropertyTableView.cpp +++ b/CesiumGltf/test/TestPropertyTableView.cpp @@ -3838,6 +3838,96 @@ TEST_CASE("Test with PropertyTableProperty noData value (normalized)") { } } +TEST_CASE( + "Test nonexistent PropertyTableProperty with class property default") { + Model model; + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::UINT32; + + const uint32_t defaultValue = 10; + testClassProperty.defaultProperty = defaultValue; + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = 4; + + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT32); + REQUIRE(!classProperty->array); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->normalized); + REQUIRE(classProperty->defaultProperty); + + SECTION("Access correct type") { + PropertyTablePropertyView uint32Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uint32Property.status() == + PropertyTablePropertyViewStatus::EmptyPropertyWithDefault); + REQUIRE(uint32Property.size() == propertyTable.count); + REQUIRE(uint32Property.defaultValue() == defaultValue); + + for (int64_t i = 0; i < uint32Property.size(); ++i) { + REQUIRE(uint32Property.get(i) == defaultValue); + } + } + + SECTION("Access wrong type") { + PropertyTablePropertyView uvec3Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uvec3Invalid.status() == + PropertyTablePropertyViewStatus::ErrorTypeMismatch); + } + + SECTION("Access wrong component type") { + PropertyTablePropertyView uint8Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uint8Invalid.status() == + PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); + } + + SECTION("Access incorrectly as normalized") { + PropertyTablePropertyView uint32NormalizedInvalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uint32NormalizedInvalid.status() == + PropertyTablePropertyViewStatus::ErrorNormalizationMismatch); + } + + SECTION("Invalid default value") { + testClassProperty.defaultProperty = "not a number"; + PropertyTablePropertyView uint32Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uint32Property.status() == + PropertyTablePropertyViewStatus::ErrorInvalidDefaultValue); + } + + SECTION("No default value") { + testClassProperty.defaultProperty.reset(); + PropertyTablePropertyView uint32Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uint32Property.status() == + PropertyTablePropertyViewStatus::ErrorNonexistentProperty); + } +} + TEST_CASE("Test callback on invalid property table view") { Model model; ExtensionModelExtStructuralMetadata& metadata = @@ -5199,3 +5289,63 @@ TEST_CASE("Test callback for string array PropertyTableProperty") { REQUIRE(invokedCallbackCount == 1); } + +TEST_CASE("Test callback for empty PropertyTableProperty with default value") { + Model model; + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::UINT32; + + const uint32_t defaultValue = 10; + testClassProperty.defaultProperty = defaultValue; + + PropertyTable& propertyTable = metadata.propertyTables.emplace_back(); + propertyTable.classProperty = "TestClass"; + propertyTable.count = 4; + + PropertyTableView view(model, propertyTable); + REQUIRE(view.status() == PropertyTableViewStatus::Valid); + REQUIRE(view.size() == propertyTable.count); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT32); + REQUIRE(!classProperty->array); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->normalized); + REQUIRE(classProperty->defaultProperty); + + uint32_t invokedCallbackCount = 0; + view.getPropertyView( + "TestClassProperty", + [defaultValue, count = propertyTable.count, &invokedCallbackCount]( + const std::string& /*propertyName*/, + auto propertyValue) mutable { + invokedCallbackCount++; + if constexpr (std::is_same_v< + PropertyTablePropertyView, + decltype(propertyValue)>) { + REQUIRE( + propertyValue.status() == + PropertyTablePropertyViewStatus::EmptyPropertyWithDefault); + REQUIRE(propertyValue.size() == count); + REQUIRE(propertyValue.defaultValue() == defaultValue); + + for (int64_t i = 0; i < propertyValue.size(); ++i) { + REQUIRE(propertyValue.get(i) == defaultValue); + } + } else { + FAIL("getPropertyView returned PropertyTablePropertyView of " + "incorrect type for TestClassProperty."); + } + }); + + REQUIRE(invokedCallbackCount == 1); +} diff --git a/CesiumGltf/test/TestPropertyTextureView.cpp b/CesiumGltf/test/TestPropertyTextureView.cpp index 13be12580..5ea2436df 100644 --- a/CesiumGltf/test/TestPropertyTextureView.cpp +++ b/CesiumGltf/test/TestPropertyTextureView.cpp @@ -1421,6 +1421,100 @@ TEST_CASE("Test with PropertyTextureProperty noData (normalized)") { } } +TEST_CASE( + "Test nonexistent PropertyTextureProperty with class property default") { + Model model; + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::UINT8; + + const uint8_t defaultValue = 10; + testClassProperty.defaultProperty = defaultValue; + + PropertyTexture& propertyTexture = metadata.propertyTextures.emplace_back(); + propertyTexture.classProperty = "TestClass"; + + PropertyTextureView view(model, propertyTexture); + REQUIRE(view.status() == PropertyTextureViewStatus::Valid); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::UINT8); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->array); + REQUIRE(!classProperty->normalized); + REQUIRE(classProperty->defaultProperty); + + SECTION("Access correct type") { + PropertyTexturePropertyView uint8Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uint8Property.status() == + PropertyTexturePropertyViewStatus::EmptyPropertyWithDefault); + REQUIRE(uint8Property.defaultValue() == defaultValue); + + std::vector texCoords{ + glm::dvec2(0, 0), + glm::dvec2(0.5, 0), + glm::dvec2(0, 0.5), + glm::dvec2(0.5, 0.5)}; + + for (size_t i = 0; i < texCoords.size(); ++i) { + glm::dvec2 uv = texCoords[i]; + REQUIRE(uint8Property.get(uv[0], uv[1]) == defaultValue); + } + } + + SECTION("Access wrong type") { + PropertyTexturePropertyView u8vec2Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + u8vec2Invalid.status() == + PropertyTexturePropertyViewStatus::ErrorTypeMismatch); + } + + SECTION("Access wrong component type") { + PropertyTexturePropertyView uint16Invalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uint16Invalid.status() == + PropertyTexturePropertyViewStatus::ErrorComponentTypeMismatch); + } + + SECTION("Access incorrectly as normalized") { + PropertyTexturePropertyView normalizedInvalid = + view.getPropertyView("TestClassProperty"); + REQUIRE( + normalizedInvalid.status() == + PropertyTexturePropertyViewStatus::ErrorNormalizationMismatch); + } + + SECTION("Invalid default value") { + testClassProperty.defaultProperty = "not a number"; + PropertyTexturePropertyView uint8Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uint8Property.status() == + PropertyTexturePropertyViewStatus::ErrorInvalidDefaultValue); + } + + SECTION("No default value") { + testClassProperty.defaultProperty.reset(); + PropertyTexturePropertyView uint8Property = + view.getPropertyView("TestClassProperty"); + REQUIRE( + uint8Property.status() == + PropertyTexturePropertyViewStatus::ErrorNonexistentProperty); + } +} + TEST_CASE("Test callback on invalid property texture view") { Model model; ExtensionModelExtStructuralMetadata& metadata = @@ -2196,3 +2290,68 @@ TEST_CASE("Test callback on unsupported PropertyTextureProperty") { }); REQUIRE(invokedCallbackCount == 2); } + +TEST_CASE( + "Test callback for empty PropertyTextureProperty with default value") { + Model model; + ExtensionModelExtStructuralMetadata& metadata = + model.addExtension(); + + Schema& schema = metadata.schema.emplace(); + Class& testClass = schema.classes["TestClass"]; + ClassProperty& testClassProperty = testClass.properties["TestClassProperty"]; + testClassProperty.type = ClassProperty::Type::SCALAR; + testClassProperty.componentType = ClassProperty::ComponentType::INT16; + + const int16_t defaultValue = 10; + testClassProperty.defaultProperty = defaultValue; + + PropertyTexture& propertyTexture = metadata.propertyTextures.emplace_back(); + propertyTexture.classProperty = "TestClass"; + + PropertyTextureView view(model, propertyTexture); + REQUIRE(view.status() == PropertyTextureViewStatus::Valid); + + const ClassProperty* classProperty = + view.getClassProperty("TestClassProperty"); + REQUIRE(classProperty); + REQUIRE(classProperty->type == ClassProperty::Type::SCALAR); + REQUIRE(classProperty->componentType == ClassProperty::ComponentType::INT16); + REQUIRE(classProperty->count == std::nullopt); + REQUIRE(!classProperty->array); + REQUIRE(!classProperty->normalized); + REQUIRE(classProperty->defaultProperty); + + std::vector texCoords{ + glm::dvec2(0, 0), + glm::dvec2(0.5, 0), + glm::dvec2(0, 0.5), + glm::dvec2(0.5, 0.5)}; + + uint32_t invokedCallbackCount = 0; + view.getPropertyView( + "TestClassProperty", + [defaultValue, &texCoords, &invokedCallbackCount]( + const std::string& /*propertyName*/, + auto propertyValue) mutable { + invokedCallbackCount++; + if constexpr (std::is_same_v< + PropertyTexturePropertyView, + decltype(propertyValue)>) { + REQUIRE( + propertyValue.status() == + PropertyTexturePropertyViewStatus::EmptyPropertyWithDefault); + REQUIRE(propertyValue.defaultValue() == defaultValue); + + for (size_t i = 0; i < texCoords.size(); ++i) { + glm::dvec2& uv = texCoords[i]; + REQUIRE(propertyValue.get(uv[0], uv[1]) == defaultValue); + } + } else { + FAIL("getPropertyView returned PropertyTexturePropertyView of " + "incorrect type for TestClassProperty."); + } + }); + + REQUIRE(invokedCallbackCount == 1); +} From 67e8139fbd0372a6b8e5bca0a63830bee94aa257 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Wed, 13 Sep 2023 15:06:21 -0400 Subject: [PATCH 275/421] Update changelog and add missing static const declaration --- CHANGES.md | 11 +++++++++++ CesiumGltf/src/PropertyView.cpp | 1 + 2 files changed, 12 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index e632779c9..e72d10f9d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,16 @@ # Change Log +### ? - ? + +##### Additions :tada: + +- Added `getClass` to `PropertyTableView`, `PropertyTextureView`, and `PropertyAttributeView`. This can be used to retrieve the metadata `Class` associated with the view. +- Added `PropertyViewStatus::EmptyPropertyWithDefault` to indicate when a property contains no data, but has a valid default value. + +##### Fixes :wrench: + +- Fixed the handling of omitted metadata properties in `PropertyTableView`, `PropertyTextureView`, and `PropertyAttributeView` instances. Previously, if a property was not `required` and omitted, it would be initialized as invalid with the `ErrorNonexistentProperty` status. Now, it will be treated as valid as long as the property defines a valid `defaultProperty`. Now, a special instance of `PropertyTablePropertyView`, `PropertyTexturePropertyView`, or `PropertyAttributePropertyView` will be constructed to allow the property's default value to be retrieved, either via `defaultValue` or `get`. `getRaw` may not be called on this special instance. + ### v0.28.0 - 2023-09-08 ##### Breaking Changes :mega: diff --git a/CesiumGltf/src/PropertyView.cpp b/CesiumGltf/src/PropertyView.cpp index 0f62eeed7..007210460 100644 --- a/CesiumGltf/src/PropertyView.cpp +++ b/CesiumGltf/src/PropertyView.cpp @@ -5,6 +5,7 @@ using namespace CesiumGltf; // Re-initialize consts here to avoid "undefined reference" errors with GCC / // Clang. const PropertyViewStatusType PropertyViewStatus::Valid; +const PropertyViewStatusType PropertyViewStatus::EmptyPropertyWithDefault; const PropertyViewStatusType PropertyViewStatus::ErrorNonexistentProperty; const PropertyViewStatusType PropertyViewStatus::ErrorTypeMismatch; const PropertyViewStatusType PropertyViewStatus::ErrorComponentTypeMismatch; From a960473cccc410fdcd46f8d377f64353e4036e29 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Wed, 13 Sep 2023 16:40:01 -0400 Subject: [PATCH 276/421] Revert small doc change --- CesiumGltf/include/CesiumGltf/PropertyAttributePropertyView.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CesiumGltf/include/CesiumGltf/PropertyAttributePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyAttributePropertyView.h index 2c375efb5..f62579f78 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyAttributePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyAttributePropertyView.h @@ -127,7 +127,7 @@ class PropertyAttributePropertyView /** * @brief Constructs an invalid instance for an erroneous property. * - * @param status The value of {@link PropertyAttributePropertyViewStatus} indicating the error with the property. + * @param status The code from {@link PropertyAttributePropertyViewStatus} indicating the error with the property. */ PropertyAttributePropertyView(PropertyViewStatusType status) noexcept : PropertyView(status), _accessor{}, _size{0} { From b8c5a9ebf54837b227d833978f57a5b31e03b391 Mon Sep 17 00:00:00 2001 From: Janine Liu <32226860+j9liu@users.noreply.github.com> Date: Thu, 14 Sep 2023 15:42:37 -0400 Subject: [PATCH 277/421] Fix changelog entry --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index e72d10f9d..30a5a4c87 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -9,7 +9,7 @@ ##### Fixes :wrench: -- Fixed the handling of omitted metadata properties in `PropertyTableView`, `PropertyTextureView`, and `PropertyAttributeView` instances. Previously, if a property was not `required` and omitted, it would be initialized as invalid with the `ErrorNonexistentProperty` status. Now, it will be treated as valid as long as the property defines a valid `defaultProperty`. Now, a special instance of `PropertyTablePropertyView`, `PropertyTexturePropertyView`, or `PropertyAttributePropertyView` will be constructed to allow the property's default value to be retrieved, either via `defaultValue` or `get`. `getRaw` may not be called on this special instance. +- Fixed the handling of omitted metadata properties in `PropertyTableView`, `PropertyTextureView`, and `PropertyAttributeView` instances. Previously, if a property was not `required` and omitted, it would be initialized as invalid with the `ErrorNonexistentProperty` status. Now, it will be treated as valid as long as the property defines a valid `defaultProperty`. A special instance of `PropertyTablePropertyView`, `PropertyTexturePropertyView`, or `PropertyAttributePropertyView` will be constructed to allow the property's default value to be retrieved, either via `defaultValue` or `get`. `getRaw` may not be called on this special instance. ### v0.28.0 - 2023-09-08 From f2564b8223e69adefab30794138b785bed8de118 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Fri, 15 Sep 2023 17:01:27 -0400 Subject: [PATCH 278/421] Remove redundant PropertyTexturePropertyView param --- .../CesiumGltf/PropertyTexturePropertyView.h | 10 +- .../include/CesiumGltf/PropertyTextureView.h | 3 +- .../test/TestPropertyTexturePropertyView.cpp | 156 +++++++++++------- 3 files changed, 101 insertions(+), 68 deletions(-) diff --git a/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h index 516d78c9a..951deb10f 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h @@ -298,13 +298,12 @@ class PropertyTexturePropertyView const PropertyTextureProperty& property, const ClassProperty& classProperty, const Sampler& sampler, - const ImageCesium& image, - const std::vector& channels) noexcept + const ImageCesium& image) noexcept : PropertyView(classProperty, property), _pSampler(&sampler), _pImage(&image), _texCoordSetIndex(property.texCoord), - _channels(channels), + _channels(property.channels), _swizzle() { if (this->_status != PropertyTexturePropertyViewStatus::Valid) { return; @@ -519,13 +518,12 @@ class PropertyTexturePropertyView const PropertyTextureProperty& property, const ClassProperty& classProperty, const Sampler& sampler, - const ImageCesium& image, - const std::vector& channels) noexcept + const ImageCesium& image) noexcept : PropertyView(classProperty, property), _pSampler(&sampler), _pImage(&image), _texCoordSetIndex(property.texCoord), - _channels(channels), + _channels(property.channels), _swizzle() { if (this->_status != PropertyTexturePropertyViewStatus::Valid) { return; diff --git a/CesiumGltf/include/CesiumGltf/PropertyTextureView.h b/CesiumGltf/include/CesiumGltf/PropertyTextureView.h index f7ca8526d..ccaa04312 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTextureView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTextureView.h @@ -709,8 +709,7 @@ class PropertyTextureView { propertyTextureProperty, classProperty, _pModel->samplers[samplerIndex], - image, - channels); + image); } PropertyViewStatusType getTextureSafe( diff --git a/CesiumGltf/test/TestPropertyTexturePropertyView.cpp b/CesiumGltf/test/TestPropertyTexturePropertyView.cpp index 227d6e385..dbc5f744f 100644 --- a/CesiumGltf/test/TestPropertyTexturePropertyView.cpp +++ b/CesiumGltf/test/TestPropertyTexturePropertyView.cpp @@ -35,13 +35,12 @@ void checkTextureValues( imageData.resize(data.size()); std::memcpy(imageData.data(), data.data(), data.size()); - std::vector channels; - for (int32_t i = 0; i < image.channels; i++) { - channels.push_back(i); + property.channels.resize(static_cast(image.channels)); + for (size_t i = 0; i < property.channels.size(); i++) { + property.channels[i] = static_cast(i); } - PropertyTexturePropertyView - view(property, classProperty, sampler, image, channels); + PropertyTexturePropertyView view(property, classProperty, sampler, image); switch (sizeof(T)) { case 1: CHECK(view.getSwizzle() == "r"); @@ -108,13 +107,12 @@ void checkTextureValues( imageData.resize(data.size()); std::memcpy(imageData.data(), data.data(), data.size()); - std::vector channels; - for (int32_t i = 0; i < image.channels; i++) { - channels.push_back(i); + property.channels.resize(static_cast(image.channels)); + for (size_t i = 0; i < property.channels.size(); i++) { + property.channels[i] = static_cast(i); } - PropertyTexturePropertyView - view(property, classProperty, sampler, image, channels); + PropertyTexturePropertyView view(property, classProperty, sampler, image); switch (sizeof(T)) { case 1: CHECK(view.getSwizzle() == "r"); @@ -183,13 +181,16 @@ void checkNormalizedTextureValues( imageData.resize(data.size()); std::memcpy(imageData.data(), data.data(), data.size()); - std::vector channels; - for (int32_t i = 0; i < image.channels; i++) { - channels.push_back(i); + property.channels.resize(static_cast(image.channels)); + for (size_t i = 0; i < property.channels.size(); i++) { + property.channels[i] = static_cast(i); } - PropertyTexturePropertyView - view(property, classProperty, sampler, image, channels); + PropertyTexturePropertyView view( + property, + classProperty, + sampler, + image); switch (sizeof(T)) { case 1: CHECK(view.getSwizzle() == "r"); @@ -251,13 +252,16 @@ void checkTextureArrayValues( imageData.resize(data.size()); std::memcpy(imageData.data(), data.data(), data.size()); - std::vector channels; - for (int32_t i = 0; i < image.channels; i++) { - channels.push_back(i); + property.channels.resize(static_cast(image.channels)); + for (size_t i = 0; i < property.channels.size(); i++) { + property.channels[i] = static_cast(i); } - PropertyTexturePropertyView> - view(property, classProperty, sampler, image, channels); + PropertyTexturePropertyView> view( + property, + classProperty, + sampler, + image); switch (image.channels) { case 1: CHECK(view.getSwizzle() == "r"); @@ -334,13 +338,16 @@ void checkTextureArrayValues( imageData.resize(data.size()); std::memcpy(imageData.data(), data.data(), data.size()); - std::vector channels; - for (int32_t i = 0; i < image.channels; i++) { - channels.push_back(i); + property.channels.resize(static_cast(image.channels)); + for (size_t i = 0; i < property.channels.size(); i++) { + property.channels[i] = static_cast(i); } - PropertyTexturePropertyView> - view(property, classProperty, sampler, image, channels); + PropertyTexturePropertyView> view( + property, + classProperty, + sampler, + image); switch (count) { case 1: CHECK(view.getSwizzle() == "r"); @@ -431,13 +438,16 @@ void checkNormalizedTextureArrayValues( imageData.resize(data.size()); std::memcpy(imageData.data(), data.data(), data.size()); - std::vector channels; - for (int32_t i = 0; i < image.channels; i++) { - channels.push_back(i); + property.channels.resize(static_cast(image.channels)); + for (size_t i = 0; i < property.channels.size(); i++) { + property.channels[i] = static_cast(i); } - PropertyTexturePropertyView, true> - view(property, classProperty, sampler, image, channels); + PropertyTexturePropertyView, true> view( + property, + classProperty, + sampler, + image); switch (image.channels) { case 1: CHECK(view.getSwizzle() == "r"); @@ -1414,16 +1424,18 @@ TEST_CASE("Check that PropertyTextureProperty values override class property " imageData.resize(data.size()); std::memcpy(imageData.data(), data.data(), data.size()); - std::vector channels{0, 1, 2, 3}; - PropertyTextureProperty property; property.offset = offset; property.scale = scale; property.min = std::numeric_limits::lowest(); property.max = std::numeric_limits::max(); + property.channels = {0, 1, 2, 3}; - PropertyTexturePropertyView - view(property, classProperty, sampler, image, channels); + PropertyTexturePropertyView view( + property, + classProperty, + sampler, + image); CHECK(view.getSwizzle() == "rgba"); REQUIRE(view.offset() == offset); @@ -1453,6 +1465,8 @@ TEST_CASE("Check that non-adjacent channels resolve to expected output") { SECTION("single-byte scalar") { PropertyTextureProperty property; + property.channels = {3}; + ClassProperty classProperty; classProperty.type = ClassProperty::Type::SCALAR; classProperty.componentType = ClassProperty::ComponentType::UINT8; @@ -1476,10 +1490,11 @@ TEST_CASE("Check that non-adjacent channels resolve to expected output") { imageData.resize(data.size()); std::memcpy(imageData.data(), data.data(), data.size()); - std::vector channels{3}; - - PropertyTexturePropertyView - view(property, classProperty, sampler, image, channels); + PropertyTexturePropertyView view( + property, + classProperty, + sampler, + image); CHECK(view.getSwizzle() == "a"); std::vector expected{3, 4, 0, 1}; @@ -1492,6 +1507,8 @@ TEST_CASE("Check that non-adjacent channels resolve to expected output") { SECTION("multi-byte scalar") { PropertyTextureProperty property; + property.channels = {2, 0}; + ClassProperty classProperty; classProperty.type = ClassProperty::Type::SCALAR; classProperty.componentType = ClassProperty::ComponentType::UINT16; @@ -1514,10 +1531,11 @@ TEST_CASE("Check that non-adjacent channels resolve to expected output") { imageData.resize(data.size()); std::memcpy(imageData.data(), data.data(), data.size()); - std::vector channels{2, 0}; - - PropertyTexturePropertyView - view(property, classProperty, sampler, image, channels); + PropertyTexturePropertyView view( + property, + classProperty, + sampler, + image); CHECK(view.getSwizzle() == "br"); std::vector expected{2, 259, 257, 520}; @@ -1530,6 +1548,8 @@ TEST_CASE("Check that non-adjacent channels resolve to expected output") { SECTION("vecN") { PropertyTextureProperty property; + property.channels = {3, 2, 1}; + ClassProperty classProperty; classProperty.type = ClassProperty::Type::VEC3; classProperty.componentType = ClassProperty::ComponentType::UINT8; @@ -1552,10 +1572,11 @@ TEST_CASE("Check that non-adjacent channels resolve to expected output") { imageData.resize(data.size()); std::memcpy(imageData.data(), data.data(), data.size()); - std::vector channels{3, 2, 1}; - - PropertyTexturePropertyView - view(property, classProperty, sampler, image, channels); + PropertyTexturePropertyView view( + property, + classProperty, + sampler, + image); CHECK(view.getSwizzle() == "abg"); std::vector expected{ @@ -1572,6 +1593,8 @@ TEST_CASE("Check that non-adjacent channels resolve to expected output") { SECTION("array") { PropertyTextureProperty property; + property.channels = {1, 0, 3, 2}; + ClassProperty classProperty; classProperty.type = ClassProperty::Type::SCALAR; classProperty.componentType = ClassProperty::ComponentType::UINT8; @@ -1596,10 +1619,11 @@ TEST_CASE("Check that non-adjacent channels resolve to expected output") { imageData.resize(data.size()); std::memcpy(imageData.data(), data.data(), data.size()); - std::vector channels{1, 0, 3, 2}; - - PropertyTexturePropertyView> - view(property, classProperty, sampler, image, channels); + PropertyTexturePropertyView> view( + property, + classProperty, + sampler, + image); CHECK(view.getSwizzle() == "grab"); std::vector> expected{ @@ -1628,6 +1652,8 @@ TEST_CASE("Check that non-adjacent channels resolve to expected output") { TEST_CASE("Check sampling with different sampler values") { PropertyTextureProperty property; + property.channels = {0}; + ClassProperty classProperty; classProperty.type = ClassProperty::Type::SCALAR; classProperty.componentType = ClassProperty::ComponentType::UINT8; @@ -1643,15 +1669,16 @@ TEST_CASE("Check sampling with different sampler values") { imageData.resize(data.size()); std::memcpy(imageData.data(), data.data(), data.size()); - std::vector channels{0}; - SECTION("REPEAT") { Sampler sampler; sampler.wrapS = Sampler::WrapS::REPEAT; sampler.wrapT = Sampler::WrapT::REPEAT; - PropertyTexturePropertyView - view(property, classProperty, sampler, image, channels); + PropertyTexturePropertyView view( + property, + classProperty, + sampler, + image); CHECK(view.getSwizzle() == "r"); std::vector uvs{ @@ -1674,8 +1701,11 @@ TEST_CASE("Check sampling with different sampler values") { // MIRRORED: | 1 2 3 | 3 2 1 | // Sampling 0.6 is equal to sampling 1.4 or -0.6. - PropertyTexturePropertyView - view(property, classProperty, sampler, image, channels); + PropertyTexturePropertyView view( + property, + classProperty, + sampler, + image); CHECK(view.getSwizzle() == "r"); std::vector uvs{ @@ -1695,8 +1725,11 @@ TEST_CASE("Check sampling with different sampler values") { sampler.wrapS = Sampler::WrapS::CLAMP_TO_EDGE; sampler.wrapT = Sampler::WrapT::CLAMP_TO_EDGE; - PropertyTexturePropertyView - view(property, classProperty, sampler, image, channels); + PropertyTexturePropertyView view( + property, + classProperty, + sampler, + image); CHECK(view.getSwizzle() == "r"); std::vector uvs{ @@ -1716,8 +1749,11 @@ TEST_CASE("Check sampling with different sampler values") { sampler.wrapS = Sampler::WrapS::REPEAT; sampler.wrapT = Sampler::WrapT::CLAMP_TO_EDGE; - PropertyTexturePropertyView - view(property, classProperty, sampler, image, channels); + PropertyTexturePropertyView view( + property, + classProperty, + sampler, + image); CHECK(view.getSwizzle() == "r"); std::vector uvs{ From d2881dac6623a36cd0de4811e46de053f41f0ebe Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Mon, 18 Sep 2023 16:52:47 -0400 Subject: [PATCH 279/421] Add getSampler --- .../CesiumGltf/PropertyTexturePropertyView.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h index 951deb10f..f466b4f77 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h @@ -406,6 +406,15 @@ class PropertyTexturePropertyView return this->_texCoordSetIndex; } + /** + * @brief Get the sampler describing how to sample the data from the + * property's texture. + * + * This will be nullptr if the property texture property view runs into + * problems during construction. + */ + const Sampler* getSampler() const noexcept { return this->_pSampler; } + /** * @brief Get the image containing this property's data. * @@ -653,6 +662,15 @@ class PropertyTexturePropertyView return this->_texCoordSetIndex; } + /** + * @brief Get the sampler describing how to sample the data from the + * property's texture. + * + * This will be nullptr if the property texture property view runs into + * problems during construction. + */ + const Sampler* getSampler() const noexcept { return this->_pSampler; } + /** * @brief Get the image containing this property's data. * From 8a1fb1a41aeddb92de22f0de8998a74484703881 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Tue, 19 Sep 2023 12:26:37 -0400 Subject: [PATCH 280/421] Replace asserts with ifs --- .../PropertyAttributePropertyView.h | 32 ++++++++++++------- .../CesiumGltf/PropertyTablePropertyView.h | 30 ++++++++++------- .../CesiumGltf/PropertyTexturePropertyView.h | 32 ++++++++++++------- 3 files changed, 58 insertions(+), 36 deletions(-) diff --git a/CesiumGltf/include/CesiumGltf/PropertyAttributePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyAttributePropertyView.h index f62579f78..69a7689a3 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyAttributePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyAttributePropertyView.h @@ -150,16 +150,20 @@ class PropertyAttributePropertyView const ClassProperty& classProperty, int64_t size) noexcept : PropertyView(classProperty), _accessor{}, _size{0} { - // Don't override the status / size if something is wrong with the class - // property's definition. if (this->_status != PropertyAttributePropertyViewStatus::Valid) { + // Don't override the status / size if something is wrong with the class + // property's definition. return; } - assert( - classProperty.defaultProperty && - "Cannot construct a valid property view for an empty property with no " - "default value."); + if (!classProperty.defaultProperty) { + // This constructor should only be called if the class property *has* a + // default value. But in the case that it does not, this property view + // becomes invalid. + this->_status = + PropertyAttributePropertyViewStatus::ErrorNonexistentProperty; + return; + } this->_status = PropertyAttributePropertyViewStatus::EmptyPropertyWithDefault; @@ -302,16 +306,20 @@ class PropertyAttributePropertyView const ClassProperty& classProperty, int64_t size) noexcept : PropertyView(classProperty), _accessor{}, _size{0} { - // Don't override the status / size if something is wrong with the class - // property's definition. if (this->_status != PropertyAttributePropertyViewStatus::Valid) { + // Don't override the status / size if something is wrong with the class + // property's definition. return; } - assert( - classProperty.defaultProperty && - "Cannot construct a valid property view for an empty property with no " - "default value."); + if (!classProperty.defaultProperty) { + // This constructor should only be called if the class property *has* a + // default value. But in the case that it does not, this property view + // becomes invalid. + this->_status = + PropertyAttributePropertyViewStatus::ErrorNonexistentProperty; + return; + } this->_status = PropertyAttributePropertyViewStatus::EmptyPropertyWithDefault; diff --git a/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h index 3c28f2f0e..7369fc958 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h @@ -213,16 +213,19 @@ class PropertyTablePropertyView _stringOffsets{}, _stringOffsetType{PropertyComponentType::None}, _stringOffsetTypeSize{0} { - // Don't override the status / size if something is wrong with the class - // property's definition. if (this->_status != PropertyTablePropertyViewStatus::Valid) { + // Don't override the status / size if something is wrong with the class + // property's definition. return; } - assert( - classProperty.defaultProperty && - "Cannot construct a valid property view for an empty property with no " - "default value."); + if (!classProperty.defaultProperty) { + // This constructor should only be called if the class property *has* a + // default value. But in the case that it does not, this property view + // becomes invalid. + this->_status = PropertyTablePropertyViewStatus::ErrorNonexistentProperty; + return; + } this->_status = PropertyTablePropertyViewStatus::EmptyPropertyWithDefault; this->_size = size; @@ -562,16 +565,19 @@ class PropertyTablePropertyView _arrayOffsets{}, _arrayOffsetType{PropertyComponentType::None}, _arrayOffsetTypeSize{0} { - // Don't override the status / size if something is wrong with the class - // property's definition. if (this->_status != PropertyTablePropertyViewStatus::Valid) { + // Don't override the status / size if something is wrong with the class + // property's definition. return; } - assert( - classProperty.defaultProperty && - "Cannot construct a valid property view for an empty property with no " - "default value."); + if (!classProperty.defaultProperty) { + // This constructor should only be called if the class property *has* a + // default value. But in the case that it does not, this property view + // becomes invalid. + this->_status = PropertyTablePropertyViewStatus::ErrorNonexistentProperty; + return; + } this->_status = PropertyTablePropertyViewStatus::EmptyPropertyWithDefault; this->_size = size; diff --git a/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h index f466b4f77..03ba314a9 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h @@ -271,16 +271,20 @@ class PropertyTexturePropertyView _texCoordSetIndex(0), _channels(), _swizzle() { - // Don't override the status / size if something is wrong with the class - // property's definition. if (this->_status != PropertyTexturePropertyViewStatus::Valid) { + // Don't override the status / size if something is wrong with the class + // property's definition. return; } - assert( - classProperty.defaultProperty && - "Cannot construct a valid property view for an empty property with no " - "default value."); + if (!classProperty.defaultProperty) { + // This constructor should only be called if the class property *has* a + // default value. But in the case that it does not, this property view + // becomes invalid. + this->_status = + PropertyTexturePropertyViewStatus::ErrorNonexistentProperty; + return; + } this->_status = PropertyTexturePropertyViewStatus::EmptyPropertyWithDefault; } @@ -500,16 +504,20 @@ class PropertyTexturePropertyView _texCoordSetIndex(0), _channels(), _swizzle() { - // Don't override the status / size if something is wrong with the class - // property's definition. if (this->_status != PropertyTexturePropertyViewStatus::Valid) { + // Don't override the status / size if something is wrong with the class + // property's definition. return; } - assert( - classProperty.defaultProperty && - "Cannot construct a valid property view for an empty property with no " - "default value."); + if (!classProperty.defaultProperty) { + // This constructor should only be called if the class property *has* a + // default value. But in the case that it does not, this property view + // becomes invalid. + this->_status = + PropertyTexturePropertyViewStatus::ErrorNonexistentProperty; + return; + } this->_status = PropertyTexturePropertyViewStatus::EmptyPropertyWithDefault; } From d7aaea36429a76144471d43998f649460838cd80 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 20 Sep 2023 08:29:49 +1000 Subject: [PATCH 281/421] Bump to v0.27.2. --- CHANGES.md | 11 +++++++++++ package.json | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 123a9a2c5..6aed3cc11 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,16 @@ # Change Log +### v0.27.2 - 2023-09-20 + +##### Additions :tada: + +- Added `CESIUM_GLM_STRICT_ENABLED` option to the CMake scripts. It is ON by default, but when set to OFF it disables the `GLM_FORCE_XYZW_ONLY`, `GLM_FORCE_EXPLICIT_CTOR`, and `GLM_FORCE_SIZE_T_LENGTH` options in the GLM library. + +##### Fixes :wrench: + +- Added a missing include to `FeatureTexturePropertyView.h`. +- The CMake scripts no longer attempt to add the `Catch2` subdirectory when the tests are disabled. + ### v0.27.1 - 2023-09-03 ##### Fixes :wrench: diff --git a/package.json b/package.json index b5cbb34fe..f697ec115 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cesium-native", - "version": "0.27.0", + "version": "0.27.2", "description": "Cesium 3D Geospatial for C++", "main": "index.js", "directories": { From 1b3a583399daccec59f990735cc3617f63175851 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 20 Sep 2023 14:37:21 +1000 Subject: [PATCH 282/421] Add a first GitHub Actions job. --- .github/workflows/build.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 .github/workflows/build.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 000000000..04bfd45a2 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,13 @@ +name: cesium-native +on: [push, pr] +jobs: + QuickChecks: + name: "Quick Checks" + runs-on: ubuntu-latest + steps: + - name: Check out repository code + uses: actions/checkout@v3 + - name: Check source formatting + run: | + npm install + npm run format -- --dry-run -Werror From 40e3fcc059342548f4778bf600e04aa644f14484 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 20 Sep 2023 14:47:55 +1000 Subject: [PATCH 283/421] pr -> pull_request --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 04bfd45a2..8f84cf01e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,5 +1,5 @@ name: cesium-native -on: [push, pr] +on: [push, pull_request] jobs: QuickChecks: name: "Quick Checks" From e3f9efdfd70d58ee6ee7c873159fb5662d813c59 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 20 Sep 2023 15:06:05 +1000 Subject: [PATCH 284/421] Add VS2019 build. --- .github/workflows/build.yml | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8f84cf01e..fab489aca 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,3 +11,37 @@ jobs: run: | npm install npm run format -- --dry-run -Werror + VS2019: + name: "Windows + VS2019" + runs-on: windows-2019 + steps: + - name: Install Doxygen + run: | + choco install -y doxygen.install --version=1.9.2 + - name: Install nasm + run: | + choco install -y nasm + - name: Compile Debug Configuration + run: | + cmake -B build -S . + cmake --build build --config Debug --parallel 4 + - name: Test Debug Configuration + run: | + cd build + ctest -V + - name: Compile RelWithDebInfo Configuration + run: | + cmake --build build --config RelWithDebInfo --parallel 4 + - name: Test RelWithDebInfo Configuration + run: | + cd build + ctest -V + - name: Generate Documentation + run: | + cmake --build build --config Debug --target cesium-native-docs + - name: Publish Documentation Artifact + if: ${{ success() }} + uses: actions/upload-artifact@v3 + with: + name: ReferenceDocumentation + path: build/doc/html From 394fed53da4c6cddf9beca861be82d249678398e Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 20 Sep 2023 15:10:13 +1000 Subject: [PATCH 285/421] Old cmake version? --- .github/workflows/build.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fab489aca..691d7ad6d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -23,6 +23,8 @@ jobs: choco install -y nasm - name: Compile Debug Configuration run: | + cmake --version + mkdir build cmake -B build -S . cmake --build build --config Debug --parallel 4 - name: Test Debug Configuration From ce2006308e083cc83d89901b3dd3dc83d934d18f Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 20 Sep 2023 15:12:27 +1000 Subject: [PATCH 286/421] Checking out the code isn't automatic. --- .github/workflows/build.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 691d7ad6d..2197001b1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -21,6 +21,10 @@ jobs: - name: Install nasm run: | choco install -y nasm + - name: Check out repository code + uses: actions/checkout@v3 + with: + submodules: recursive - name: Compile Debug Configuration run: | cmake --version From 824e56b1e5ed81efd7a1556e6f93cfbf714e78f5 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 20 Sep 2023 15:15:25 +1000 Subject: [PATCH 287/421] Creating the directory shouldn't be necessary. --- .github/workflows/build.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2197001b1..a71c33773 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -27,8 +27,6 @@ jobs: submodules: recursive - name: Compile Debug Configuration run: | - cmake --version - mkdir build cmake -B build -S . cmake --build build --config Debug --parallel 4 - name: Test Debug Configuration From 56b36494bfcb28f76b60908adc3c28438901e880 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 20 Sep 2023 15:26:57 +1000 Subject: [PATCH 288/421] Separate doc step, add VS2022 and Linux builds. --- .github/workflows/build.yml | 109 +++++++++++++++++++++++++++++++++--- 1 file changed, 100 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a71c33773..624bf437f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,13 +11,56 @@ jobs: run: | npm install npm run format -- --dry-run -Werror + Documentation: + runs-on: ubuntu-latest + steps: + - name: Install Doxygen + run: | + sudo apt install -y doxygen + - name: Check out repository code + uses: actions/checkout@v3 + with: + submodules: recursive + - name: Generate Documentation + run: | + cmake -B build -S . + cmake --build build --target cesium-native-docs + - name: Publish Documentation Artifact + if: ${{ success() }} + uses: actions/upload-artifact@v3 + with: + name: ReferenceDocumentation + path: build/doc/html VS2019: name: "Windows + VS2019" runs-on: windows-2019 steps: - - name: Install Doxygen + - name: Install nasm + run: | + choco install -y nasm + - name: Check out repository code + uses: actions/checkout@v3 + with: + submodules: recursive + - name: Compile Debug Configuration + run: | + cmake -B build -S . + cmake --build build --config Debug --parallel 4 + - name: Test Debug Configuration run: | - choco install -y doxygen.install --version=1.9.2 + cd build + ctest -V + - name: Compile RelWithDebInfo Configuration + run: | + cmake --build build --config RelWithDebInfo --parallel 4 + - name: Test RelWithDebInfo Configuration + run: | + cd build + ctest -V + VS2022: + name: "Windows + VS2022" + runs-on: windows-2022 + steps: - name: Install nasm run: | choco install -y nasm @@ -40,12 +83,60 @@ jobs: run: | cd build ctest -V - - name: Generate Documentation + LinuxGCC: + name: "Linux + GCC" + runs-on: ubuntu-latest + steps: + - name: Install nasm run: | - cmake --build build --config Debug --target cesium-native-docs - - name: Publish Documentation Artifact - if: ${{ success() }} - uses: actions/upload-artifact@v3 + sudo apt-get install nasm + - name: Check out repository code + uses: actions/checkout@v3 with: - name: ReferenceDocumentation - path: build/doc/html + submodules: recursive + - name: Compile Debug Configuration + run: | + cmake -B build-debug -S . -DCMAKE_BUILD_TYPE:STRING=Debug + cmake --build build-debug --parallel 4 + - name: Test Debug Configuration + run: | + cd build-debug + ctest -V + - name: Compile RelWithDebInfo Configuration + run: | + cmake -B build-release -S . -DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo + cmake --build build-release --config RelWithDebInfo --parallel 4 + - name: Test RelWithDebInfo Configuration + run: | + cd build-release + ctest -V + LinuxClang: + name: "Linux + Clang" + runs-on: ubuntu-latest + env: + CC: clang-12 + CXX: clang++-12 + steps: + - name: Install nasm + run: | + sudo apt-get install nasm + - name: Check out repository code + uses: actions/checkout@v3 + with: + submodules: recursive + - name: Compile Debug Configuration + run: | + cmake -B build-debug -S . -DCMAKE_BUILD_TYPE:STRING=Debug + cmake --build build-debug --parallel 4 + - name: Test Debug Configuration + run: | + cd build-debug + ctest -V + - name: Compile RelWithDebInfo Configuration + run: | + cmake -B build-release -S . -DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo + cmake --build build-release --config RelWithDebInfo --parallel 4 + - name: Test RelWithDebInfo Configuration + run: | + cd build-release + ctest -V From aff2303e415d73dcc85f1efc8659883b0be303a1 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 20 Sep 2023 15:31:25 +1000 Subject: [PATCH 289/421] Add macOS build. --- .github/workflows/build.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 624bf437f..2f8be5c87 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -140,3 +140,29 @@ jobs: run: | cd build-release ctest -V + macOS: + runs-on: macos-latest + steps: + - name: Install nasm + run: | + sudo apt-get install nasm + - name: Check out repository code + uses: actions/checkout@v3 + with: + submodules: recursive + - name: Compile Debug Configuration + run: | + cmake -B build-debug -S . -DCMAKE_BUILD_TYPE:STRING=Debug + cmake --build build-debug --parallel 4 + - name: Test Debug Configuration + run: | + cd build-debug + ctest -V + - name: Compile RelWithDebInfo Configuration + run: | + cmake -B build-release -S . -DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo + cmake --build build-release --config RelWithDebInfo --parallel 4 + - name: Test RelWithDebInfo Configuration + run: | + cd build-release + ctest -V From fd6a58543cd297d710a9b4bdf3263a754bafcade Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 20 Sep 2023 15:36:49 +1000 Subject: [PATCH 290/421] Don't install nasm on macOS. --- .github/workflows/build.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2f8be5c87..3a58a4ee2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -143,9 +143,6 @@ jobs: macOS: runs-on: macos-latest steps: - - name: Install nasm - run: | - sudo apt-get install nasm - name: Check out repository code uses: actions/checkout@v3 with: From fcd8c41d134609901472f2b5f12f736b184fa7a3 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 20 Sep 2023 16:01:59 +1000 Subject: [PATCH 291/421] Include Async++ with target_link_libraries_system. --- CesiumAsync/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CesiumAsync/CMakeLists.txt b/CesiumAsync/CMakeLists.txt index 00d235ffa..1007f5f5f 100644 --- a/CesiumAsync/CMakeLists.txt +++ b/CesiumAsync/CMakeLists.txt @@ -41,7 +41,6 @@ target_link_libraries(CesiumAsync PUBLIC CesiumUtility GSL - Async++ PRIVATE sqlite3 ) @@ -49,6 +48,7 @@ target_link_libraries(CesiumAsync # These libraries erroneously do NOT list their headers as `SYSTEM` headers target_link_libraries_system(CesiumAsync PUBLIC spdlog + Async++ ) install(TARGETS CesiumAsync From fdf5e36742273408a5a725a2da4c0cc4609d2bb8 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 20 Sep 2023 16:09:11 +1000 Subject: [PATCH 292/421] Remove .travis.yml. --- .travis.yml | 67 ----------------------------------------------------- 1 file changed, 67 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 04bb2c857..000000000 --- a/.travis.yml +++ /dev/null @@ -1,67 +0,0 @@ -language: cpp -# Default scripts, which may be overridden by some jobs -before_script: - - mkdir -p build - - cd build - - cmake -DCMAKE_BUILD_TYPE:STRING=Debug .. -script: - - cmake --build . --config Debug - - ctest -V - - cmake --build . --config Debug --target cesium-native-docs -jobs: - include: - - name: Check Source Formatting - os: linux - dist: focal - git: - submodules: false - before_script: - script: - - npm install - - npm run format -- --dry-run -Werror - - name: Windows + VS2017 - os: windows - install: - - choco install doxygen.install --version=1.9.2 - - choco install nasm - before_script: - - mkdir -p build - - cd build - - cmake .. -A x64 - - name: Windows + VS2019 - os: windows - install: - - choco install visualstudio2019buildtools --package-parameters "--add Microsoft.VisualStudio.Component.VC.Tools.x86.x64" - - choco install doxygen.install --version=1.9.2 - - choco install nasm - - name: Linux + GCC - os: linux - dist: focal - install: - - # As of 2021-08-23, the server listed in the rabbitmq PPA has an expired certificate - - # and breaks our ability to update. We don't need it, so remove it. - - sudo rm /etc/apt/sources.list.d/rabbitmq.list - - sudo apt-get update - - sudo apt-get install cmake doxygen nasm - - name: Linux + Clang - os: linux - dist: focal - # We're using Clang 10 instead of the default (Clang 7) because of std::variant related failures - # on Clang 7, possibly related to https://gcc.gnu.org/bugzilla/show_bug.cgi?id=90397 - install: - - # As of 2021-08-23, the server listed in the rabbitmq PPA has an expired certificate - - # and breaks our ability to update. We don't need it, so remove it. - - sudo rm /etc/apt/sources.list.d/rabbitmq.list - - sudo apt-get update - - sudo apt-get install clang-10 cmake doxygen nasm - env: - - CC=clang-10 - - CXX=clang++-10 - - name: macOS - os: osx - osx_image: xcode12 - script: - - cmake --build . --config Debug - - ctest -V - - # Doc build doesn't work on Mac (missing Doxygen). Let's just skip it. - - #cmake --build . --config Debug --target cesium-native-docs From d7a32f255124ab5c1438b91d2e2c01e058bf1005 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 20 Sep 2023 16:54:25 +1000 Subject: [PATCH 293/421] Disable warning for async++. --- CesiumAsync/include/CesiumAsync/Impl/cesium-async++.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CesiumAsync/include/CesiumAsync/Impl/cesium-async++.h b/CesiumAsync/include/CesiumAsync/Impl/cesium-async++.h index 72227957b..91dfb270c 100644 --- a/CesiumAsync/include/CesiumAsync/Impl/cesium-async++.h +++ b/CesiumAsync/include/CesiumAsync/Impl/cesium-async++.h @@ -2,7 +2,7 @@ #ifdef _MSC_VER #pragma warning(push) -#pragma warning(disable : 4458 4324) +#pragma warning(disable : 4458 4324 2220) #endif #ifndef _MSC_VER From f67776bacbe8d8c4dfd35e69916c0f3e2a310ac6 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 20 Sep 2023 17:21:24 +1000 Subject: [PATCH 294/421] Fix warning number. --- CesiumAsync/include/CesiumAsync/Impl/cesium-async++.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CesiumAsync/include/CesiumAsync/Impl/cesium-async++.h b/CesiumAsync/include/CesiumAsync/Impl/cesium-async++.h index 91dfb270c..0629b71b3 100644 --- a/CesiumAsync/include/CesiumAsync/Impl/cesium-async++.h +++ b/CesiumAsync/include/CesiumAsync/Impl/cesium-async++.h @@ -2,7 +2,7 @@ #ifdef _MSC_VER #pragma warning(push) -#pragma warning(disable : 4458 4324 2220) +#pragma warning(disable : 4458 4324 4702) #endif #ifndef _MSC_VER From 828491cff32e5ab6749527d83bdf5bdb5dc95e27 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 27 Sep 2023 10:42:03 +1000 Subject: [PATCH 295/421] Add support for Cesium ion "externalType" assets. --- CHANGES.md | 6 +++++ .../src/CesiumIonTilesetLoader.cpp | 23 +++++++++++++++---- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 6aed3cc11..f98213202 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,11 @@ # Change Log +### v0.27.x - ? + +##### Additions :tada: + +- Added support for Cesium ion `"externalType"` assets. + ### v0.27.2 - 2023-09-20 ##### Additions :tada: diff --git a/Cesium3DTilesSelection/src/CesiumIonTilesetLoader.cpp b/Cesium3DTilesSelection/src/CesiumIonTilesetLoader.cpp index 27b25994c..5131205db 100644 --- a/Cesium3DTilesSelection/src/CesiumIonTilesetLoader.cpp +++ b/Cesium3DTilesSelection/src/CesiumIonTilesetLoader.cpp @@ -272,18 +272,33 @@ mainThreadHandleEndpointResponse( } } + std::string type = + CesiumUtility::JsonHelpers::getStringOrDefault(ionResponse, "type", ""); std::string url = CesiumUtility::JsonHelpers::getStringOrDefault(ionResponse, "url", ""); std::string accessToken = CesiumUtility::JsonHelpers::getStringOrDefault( ionResponse, "accessToken", ""); + std::string externalType = CesiumUtility::JsonHelpers::getStringOrDefault( + ionResponse, + "externalType", + ""); + const auto optionsIt = ionResponse.FindMember("options"); + + if (!externalType.empty()) { + type = externalType; + if (optionsIt != ionResponse.MemberEnd()) { + url = CesiumUtility::JsonHelpers::getStringOrDefault( + optionsIt->value, + "url", + url); + } + } - std::string type = - CesiumUtility::JsonHelpers::getStringOrDefault(ionResponse, "type", ""); if (type == "TERRAIN") { - // For terrain resources, we need to append `/layer.json` to the end of the - // URL. + // For terrain resources, we need to append `/layer.json` to the end of + // the URL. url = CesiumUtility::Uri::resolve(url, "layer.json", true); endpoint.type = type; endpoint.url = url; From 4bf6a3e76ea809185af205aa4f750fc549bef3db Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 27 Sep 2023 10:42:37 +1000 Subject: [PATCH 296/421] Revert "Add support for Cesium ion "externalType" assets." This reverts commit 828491cff32e5ab6749527d83bdf5bdb5dc95e27. --- CHANGES.md | 6 ----- .../src/CesiumIonTilesetLoader.cpp | 23 ++++--------------- 2 files changed, 4 insertions(+), 25 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index f98213202..6aed3cc11 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,11 +1,5 @@ # Change Log -### v0.27.x - ? - -##### Additions :tada: - -- Added support for Cesium ion `"externalType"` assets. - ### v0.27.2 - 2023-09-20 ##### Additions :tada: diff --git a/Cesium3DTilesSelection/src/CesiumIonTilesetLoader.cpp b/Cesium3DTilesSelection/src/CesiumIonTilesetLoader.cpp index 5131205db..27b25994c 100644 --- a/Cesium3DTilesSelection/src/CesiumIonTilesetLoader.cpp +++ b/Cesium3DTilesSelection/src/CesiumIonTilesetLoader.cpp @@ -272,33 +272,18 @@ mainThreadHandleEndpointResponse( } } - std::string type = - CesiumUtility::JsonHelpers::getStringOrDefault(ionResponse, "type", ""); std::string url = CesiumUtility::JsonHelpers::getStringOrDefault(ionResponse, "url", ""); std::string accessToken = CesiumUtility::JsonHelpers::getStringOrDefault( ionResponse, "accessToken", ""); - std::string externalType = CesiumUtility::JsonHelpers::getStringOrDefault( - ionResponse, - "externalType", - ""); - const auto optionsIt = ionResponse.FindMember("options"); - - if (!externalType.empty()) { - type = externalType; - if (optionsIt != ionResponse.MemberEnd()) { - url = CesiumUtility::JsonHelpers::getStringOrDefault( - optionsIt->value, - "url", - url); - } - } + std::string type = + CesiumUtility::JsonHelpers::getStringOrDefault(ionResponse, "type", ""); if (type == "TERRAIN") { - // For terrain resources, we need to append `/layer.json` to the end of - // the URL. + // For terrain resources, we need to append `/layer.json` to the end of the + // URL. url = CesiumUtility::Uri::resolve(url, "layer.json", true); endpoint.type = type; endpoint.url = url; From 08f26ad4d8f88197a8c1d37dea2ffb76f5dcfd85 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 27 Sep 2023 10:43:09 +1000 Subject: [PATCH 297/421] Add support for Cesium ion "externalType" assets. --- CHANGES.md | 6 +++++ .../src/CesiumIonTilesetLoader.cpp | 23 +++++++++++++++---- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 6aed3cc11..f98213202 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,11 @@ # Change Log +### v0.27.x - ? + +##### Additions :tada: + +- Added support for Cesium ion `"externalType"` assets. + ### v0.27.2 - 2023-09-20 ##### Additions :tada: diff --git a/Cesium3DTilesSelection/src/CesiumIonTilesetLoader.cpp b/Cesium3DTilesSelection/src/CesiumIonTilesetLoader.cpp index 27b25994c..5131205db 100644 --- a/Cesium3DTilesSelection/src/CesiumIonTilesetLoader.cpp +++ b/Cesium3DTilesSelection/src/CesiumIonTilesetLoader.cpp @@ -272,18 +272,33 @@ mainThreadHandleEndpointResponse( } } + std::string type = + CesiumUtility::JsonHelpers::getStringOrDefault(ionResponse, "type", ""); std::string url = CesiumUtility::JsonHelpers::getStringOrDefault(ionResponse, "url", ""); std::string accessToken = CesiumUtility::JsonHelpers::getStringOrDefault( ionResponse, "accessToken", ""); + std::string externalType = CesiumUtility::JsonHelpers::getStringOrDefault( + ionResponse, + "externalType", + ""); + const auto optionsIt = ionResponse.FindMember("options"); + + if (!externalType.empty()) { + type = externalType; + if (optionsIt != ionResponse.MemberEnd()) { + url = CesiumUtility::JsonHelpers::getStringOrDefault( + optionsIt->value, + "url", + url); + } + } - std::string type = - CesiumUtility::JsonHelpers::getStringOrDefault(ionResponse, "type", ""); if (type == "TERRAIN") { - // For terrain resources, we need to append `/layer.json` to the end of the - // URL. + // For terrain resources, we need to append `/layer.json` to the end of + // the URL. url = CesiumUtility::Uri::resolve(url, "layer.json", true); endpoint.type = type; endpoint.url = url; From 9fb91de77992b536cfa724407b4a2f64cffe5dbd Mon Sep 17 00:00:00 2001 From: Brian L <130494071+csciguy8@users.noreply.github.com> Date: Thu, 28 Sep 2023 09:45:44 -0600 Subject: [PATCH 298/421] Track kicked tiles and use in ::ComputeLoadProgress --- .../Cesium3DTilesSelection/ViewUpdateResult.h | 1 + Cesium3DTilesSelection/src/Tileset.cpp | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/ViewUpdateResult.h b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/ViewUpdateResult.h index 3d507eed4..70c9034d5 100644 --- a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/ViewUpdateResult.h +++ b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/ViewUpdateResult.h @@ -55,6 +55,7 @@ class CESIUM3DTILESSELECTION_API ViewUpdateResult final { uint32_t tilesCulled = 0; uint32_t tilesOccluded = 0; uint32_t tilesWaitingForOcclusionResults = 0; + uint32_t tilesKicked = 0; uint32_t maxDepthVisited = 0; //! @endcond diff --git a/Cesium3DTilesSelection/src/Tileset.cpp b/Cesium3DTilesSelection/src/Tileset.cpp index c6a95fa2b..787545a88 100644 --- a/Cesium3DTilesSelection/src/Tileset.cpp +++ b/Cesium3DTilesSelection/src/Tileset.cpp @@ -314,6 +314,7 @@ Tileset::updateView(const std::vector& frustums, float deltaTime) { result.tilesCulled = 0; result.tilesOccluded = 0; result.tilesWaitingForOcclusionResults = 0; + result.tilesKicked = 0; result.maxDepthVisited = 0; if (!_options.enableLodTransitionPeriod) { @@ -437,8 +438,16 @@ float Tileset::computeLoadProgress() noexcept { this->_pTilesetContentManager->getNumberOfTilesLoading(); int32_t numOfTilesLoaded = this->_pTilesetContentManager->getNumberOfTilesLoaded(); + int32_t numOfTilesKicked = + static_cast(this->_updateResult.tilesKicked); + + // Amount of work actively being done int32_t inProgressSum = numOfTilesLoading + queueSizeSum; - int32_t totalNum = numOfTilesLoaded + inProgressSum; + + // Total work so far. Add already loaded tiles and kicked tiles. + // Kicked tiles are transient, and never in progress, but are an indicator + // that there is more work to do next frame. + int32_t totalNum = inProgressSum + numOfTilesLoaded + numOfTilesKicked; float percentage = static_cast(numOfTilesLoaded) / static_cast(totalNum); return (percentage * 100.f); @@ -1053,7 +1062,10 @@ bool Tileset::_kickDescendantsAndRenderTile( traversalDetails.notYetRenderableCount > this->_options.loadingDescendantLimit && !tile.isExternalContent() && !tile.getUnconditionallyRefine()) { + // Remove all descendants from the load queues. + size_t allQueueStartSize = + _workerThreadLoadQueue.size() + _mainThreadLoadQueue.size(); this->_workerThreadLoadQueue.erase( this->_workerThreadLoadQueue.begin() + static_cast::iterator::difference_type>( @@ -1064,6 +1076,10 @@ bool Tileset::_kickDescendantsAndRenderTile( static_cast::iterator::difference_type>( mainThreadLoadQueueIndex), this->_mainThreadLoadQueue.end()); + size_t allQueueEndSize = + _workerThreadLoadQueue.size() + _mainThreadLoadQueue.size(); + result.tilesKicked += + static_cast(allQueueStartSize - allQueueEndSize); if (!queuedForLoad) { addTileToLoadQueue(tile, TileLoadPriorityGroup::Normal, tilePriority); From ec52b248d36e1068ae0f139b5944fa996706e614 Mon Sep 17 00:00:00 2001 From: Brian Langevin <130494071+csciguy8@users.noreply.github.com> Date: Thu, 28 Sep 2023 09:55:43 -0600 Subject: [PATCH 299/421] Update CHANGES.md --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index 6aed3cc11..3be60c00d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -10,6 +10,7 @@ - Added a missing include to `FeatureTexturePropertyView.h`. - The CMake scripts no longer attempt to add the `Catch2` subdirectory when the tests are disabled. +- Fixed corner cases where `Tileset::ComputeLoadProgress` can report done (100%) in error, before all work is actually complete in the current view ### v0.27.1 - 2023-09-03 From b55ac3a0f0c0dd06dcca0a1814a2ac31641e898c Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Fri, 29 Sep 2023 07:00:03 +1000 Subject: [PATCH 300/421] Changes from review. --- Cesium3DTilesSelection/src/CesiumIonTilesetLoader.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cesium3DTilesSelection/src/CesiumIonTilesetLoader.cpp b/Cesium3DTilesSelection/src/CesiumIonTilesetLoader.cpp index 5131205db..5ea6e2bae 100644 --- a/Cesium3DTilesSelection/src/CesiumIonTilesetLoader.cpp +++ b/Cesium3DTilesSelection/src/CesiumIonTilesetLoader.cpp @@ -284,11 +284,11 @@ mainThreadHandleEndpointResponse( ionResponse, "externalType", ""); - const auto optionsIt = ionResponse.FindMember("options"); if (!externalType.empty()) { type = externalType; - if (optionsIt != ionResponse.MemberEnd()) { + const auto optionsIt = ionResponse.FindMember("options"); + if (optionsIt != ionResponse.MemberEnd() && optionsIt->value.IsObject()) { url = CesiumUtility::JsonHelpers::getStringOrDefault( optionsIt->value, "url", From d431b44d7c31cab15b5f2324e0030968f13fd75a Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Tue, 19 Sep 2023 12:26:37 -0400 Subject: [PATCH 301/421] Replace asserts with ifs --- .../PropertyAttributePropertyView.h | 32 ++++++++++++------- .../CesiumGltf/PropertyTablePropertyView.h | 30 ++++++++++------- .../CesiumGltf/PropertyTexturePropertyView.h | 32 ++++++++++++------- 3 files changed, 58 insertions(+), 36 deletions(-) diff --git a/CesiumGltf/include/CesiumGltf/PropertyAttributePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyAttributePropertyView.h index f62579f78..69a7689a3 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyAttributePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyAttributePropertyView.h @@ -150,16 +150,20 @@ class PropertyAttributePropertyView const ClassProperty& classProperty, int64_t size) noexcept : PropertyView(classProperty), _accessor{}, _size{0} { - // Don't override the status / size if something is wrong with the class - // property's definition. if (this->_status != PropertyAttributePropertyViewStatus::Valid) { + // Don't override the status / size if something is wrong with the class + // property's definition. return; } - assert( - classProperty.defaultProperty && - "Cannot construct a valid property view for an empty property with no " - "default value."); + if (!classProperty.defaultProperty) { + // This constructor should only be called if the class property *has* a + // default value. But in the case that it does not, this property view + // becomes invalid. + this->_status = + PropertyAttributePropertyViewStatus::ErrorNonexistentProperty; + return; + } this->_status = PropertyAttributePropertyViewStatus::EmptyPropertyWithDefault; @@ -302,16 +306,20 @@ class PropertyAttributePropertyView const ClassProperty& classProperty, int64_t size) noexcept : PropertyView(classProperty), _accessor{}, _size{0} { - // Don't override the status / size if something is wrong with the class - // property's definition. if (this->_status != PropertyAttributePropertyViewStatus::Valid) { + // Don't override the status / size if something is wrong with the class + // property's definition. return; } - assert( - classProperty.defaultProperty && - "Cannot construct a valid property view for an empty property with no " - "default value."); + if (!classProperty.defaultProperty) { + // This constructor should only be called if the class property *has* a + // default value. But in the case that it does not, this property view + // becomes invalid. + this->_status = + PropertyAttributePropertyViewStatus::ErrorNonexistentProperty; + return; + } this->_status = PropertyAttributePropertyViewStatus::EmptyPropertyWithDefault; diff --git a/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h index 3c28f2f0e..7369fc958 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTablePropertyView.h @@ -213,16 +213,19 @@ class PropertyTablePropertyView _stringOffsets{}, _stringOffsetType{PropertyComponentType::None}, _stringOffsetTypeSize{0} { - // Don't override the status / size if something is wrong with the class - // property's definition. if (this->_status != PropertyTablePropertyViewStatus::Valid) { + // Don't override the status / size if something is wrong with the class + // property's definition. return; } - assert( - classProperty.defaultProperty && - "Cannot construct a valid property view for an empty property with no " - "default value."); + if (!classProperty.defaultProperty) { + // This constructor should only be called if the class property *has* a + // default value. But in the case that it does not, this property view + // becomes invalid. + this->_status = PropertyTablePropertyViewStatus::ErrorNonexistentProperty; + return; + } this->_status = PropertyTablePropertyViewStatus::EmptyPropertyWithDefault; this->_size = size; @@ -562,16 +565,19 @@ class PropertyTablePropertyView _arrayOffsets{}, _arrayOffsetType{PropertyComponentType::None}, _arrayOffsetTypeSize{0} { - // Don't override the status / size if something is wrong with the class - // property's definition. if (this->_status != PropertyTablePropertyViewStatus::Valid) { + // Don't override the status / size if something is wrong with the class + // property's definition. return; } - assert( - classProperty.defaultProperty && - "Cannot construct a valid property view for an empty property with no " - "default value."); + if (!classProperty.defaultProperty) { + // This constructor should only be called if the class property *has* a + // default value. But in the case that it does not, this property view + // becomes invalid. + this->_status = PropertyTablePropertyViewStatus::ErrorNonexistentProperty; + return; + } this->_status = PropertyTablePropertyViewStatus::EmptyPropertyWithDefault; this->_size = size; diff --git a/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h index f466b4f77..03ba314a9 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h @@ -271,16 +271,20 @@ class PropertyTexturePropertyView _texCoordSetIndex(0), _channels(), _swizzle() { - // Don't override the status / size if something is wrong with the class - // property's definition. if (this->_status != PropertyTexturePropertyViewStatus::Valid) { + // Don't override the status / size if something is wrong with the class + // property's definition. return; } - assert( - classProperty.defaultProperty && - "Cannot construct a valid property view for an empty property with no " - "default value."); + if (!classProperty.defaultProperty) { + // This constructor should only be called if the class property *has* a + // default value. But in the case that it does not, this property view + // becomes invalid. + this->_status = + PropertyTexturePropertyViewStatus::ErrorNonexistentProperty; + return; + } this->_status = PropertyTexturePropertyViewStatus::EmptyPropertyWithDefault; } @@ -500,16 +504,20 @@ class PropertyTexturePropertyView _texCoordSetIndex(0), _channels(), _swizzle() { - // Don't override the status / size if something is wrong with the class - // property's definition. if (this->_status != PropertyTexturePropertyViewStatus::Valid) { + // Don't override the status / size if something is wrong with the class + // property's definition. return; } - assert( - classProperty.defaultProperty && - "Cannot construct a valid property view for an empty property with no " - "default value."); + if (!classProperty.defaultProperty) { + // This constructor should only be called if the class property *has* a + // default value. But in the case that it does not, this property view + // becomes invalid. + this->_status = + PropertyTexturePropertyViewStatus::ErrorNonexistentProperty; + return; + } this->_status = PropertyTexturePropertyViewStatus::EmptyPropertyWithDefault; } From 2398567333ea0967a39d35db335365509d8bbfa4 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Fri, 29 Sep 2023 11:39:54 +1000 Subject: [PATCH 302/421] Update compiler requirements and changelog. --- CHANGES.md | 4 ++++ README.md | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 30a5a4c87..7ac13d8a2 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,6 +2,10 @@ ### ? - ? +##### Breaking Changes :mega: + +- Cesium Native is now only regularly tested on Visual Studio 2019+, GCC 11.x+, and Clang 12+. Other compilers - including older ones - are likely to work, but are not tested. + ##### Additions :tada: - Added `getClass` to `PropertyTableView`, `PropertyTextureView`, and `PropertyAttributeView`. This can be used to retrieve the metadata `Class` associated with the view. diff --git a/README.md b/README.md index 00eb09fdb..34854df57 100644 --- a/README.md +++ b/README.md @@ -42,8 +42,8 @@ Cesium Native powers Cesium's runtime integrations for [Cesium for Unreal](https ### ⭐Prerequisites -* Visual Studio 2017 (or newer), GCC v7.x+, Clang 10+. Other compilers may work but haven't been tested. -* CMake +* Visual Studio 2019 (or newer), GCC v11.x+, Clang 12+. Other compilers are likely to work but are not regularly tested. +* CMake 3.15+ * For best JPEG-decoding performance, you must have [nasm](https://www.nasm.us/) installed so that CMake can find it. Everything will work fine without it, just slower. ### :rocket:Getting Started From 0f19eea6e94da9743403cc014fbf422b78092336 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Fri, 29 Sep 2023 21:08:06 +1000 Subject: [PATCH 303/421] Use ubuntu-20.04 instead of ubuntu-latest So that we can use Clang 12. --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3a58a4ee2..2191d6033 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -112,7 +112,7 @@ jobs: ctest -V LinuxClang: name: "Linux + Clang" - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 env: CC: clang-12 CXX: clang++-12 From 6e720ff0fff9fd29cd16183ae19f1c399cda5e5c Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Sun, 1 Oct 2023 16:09:23 +1100 Subject: [PATCH 304/421] Bump to v0.27.3. --- CHANGES.md | 7 +++++-- package.json | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index abc13171e..56d9412de 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,11 +1,15 @@ # Change Log -### v0.27.x - ? +### v0.27.3 - 2023-10-01 ##### Additions :tada: - Added support for Cesium ion `"externalType"` assets. +##### Fixes :wrench: + +- Fixed corner cases where `Tileset::ComputeLoadProgress` can incorrectly report done (100%) before all tiles are actually loaded for the current view. + ### v0.27.2 - 2023-09-20 ##### Additions :tada: @@ -16,7 +20,6 @@ - Added a missing include to `FeatureTexturePropertyView.h`. - The CMake scripts no longer attempt to add the `Catch2` subdirectory when the tests are disabled. -- Fixed corner cases where `Tileset::ComputeLoadProgress` can report done (100%) in error, before all work is actually complete in the current view ### v0.27.1 - 2023-09-03 diff --git a/package.json b/package.json index f697ec115..d11a4df18 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cesium-native", - "version": "0.27.2", + "version": "0.27.3", "description": "Cesium 3D Geospatial for C++", "main": "index.js", "directories": { From 374a26a8ac07082e1c291aa58eb9d4b3a775d020 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Tue, 3 Oct 2023 10:21:43 -0400 Subject: [PATCH 305/421] Remove PropertyViewTypes.h --- .../include/CesiumGltf/PropertyViewTypes.h | 454 ------------------ 1 file changed, 454 deletions(-) delete mode 100644 CesiumGltf/include/CesiumGltf/PropertyViewTypes.h diff --git a/CesiumGltf/include/CesiumGltf/PropertyViewTypes.h b/CesiumGltf/include/CesiumGltf/PropertyViewTypes.h deleted file mode 100644 index f51a8b489..000000000 --- a/CesiumGltf/include/CesiumGltf/PropertyViewTypes.h +++ /dev/null @@ -1,454 +0,0 @@ -#pragma once - -#include "CesiumGltf/PropertyAttributePropertyView.h" -#include "CesiumGltf/PropertyTablePropertyView.h" -#include "CesiumGltf/PropertyTexturePropertyView.h" - -#include - -#include - -namespace CesiumGltf { - -#define View PropertyTablePropertyView - -/** - * @brief An alias for all of the potential types of - * {@link PropertyTablePropertyView} possible without normalization. - * Can be useful for applications that want to implement abstract - * representations of PropertyTablePropertyView, without the mess of templated - * types. - */ -typedef std::variant< - View, - View, - View, - View, - View, - View, - View, - View, - View, - View, - View, - View, - View, false>, - View, false>, - View, false>, - View, false>, - View, false>, - View, false>, - View, false>, - View, false>, - View>, - View>, - View, false>, - View, false>, - View, false>, - View, false>, - View, false>, - View, false>, - View, false>, - View, false>, - View>, - View>, - View, false>, - View, false>, - View, false>, - View, false>, - View, false>, - View, false>, - View, false>, - View, false>, - View>, - View>, - View, false>, - View, false>, - View, false>, - View, false>, - View, false>, - View, false>, - View, false>, - View, false>, - View>, - View>, - View, false>, - View, false>, - View, false>, - View, false>, - View, false>, - View, false>, - View, false>, - View, false>, - View>, - View>, - View, false>, - View, false>, - View, false>, - View, false>, - View, false>, - View, false>, - View, false>, - View, false>, - View>, - View>, - View, false>, - View, false>, - View, false>, - View, false>, - View, false>, - View, false>, - View, false>, - View, false>, - View>, - View>, - View>, - View>, - View>, false>, - View>, false>, - View>, false>, - View>, false>, - View>, false>, - View>, false>, - View>, false>, - View>, false>, - View>>, - View>>, - View>, false>, - View>, false>, - View>, false>, - View>, false>, - View>, false>, - View>, false>, - View>, false>, - View>, false>, - View>>, - View>>, - View>, false>, - View>, false>, - View>, false>, - View>, false>, - View>, false>, - View>, false>, - View>, false>, - View>, false>, - View>>, - View>>, - View>, false>, - View>, false>, - View>, false>, - View>, false>, - View>, false>, - View>, false>, - View>, false>, - View>, false>, - View>>, - View>>, - View>, false>, - View>, false>, - View>, false>, - View>, false>, - View>, false>, - View>, false>, - View>, false>, - View>, false>, - View>>, - View>>, - View>, false>, - View>, false>, - View>, false>, - View>, false>, - View>, false>, - View>, false>, - View>, false>, - View>, false>, - View>>, - View>>> - PropertyTablePropertyViewType; - -/** - * @brief An alias for all of the potential types of - * {@link PropertyTablePropertyView} possible with normalization. - * Can be useful for applications that want to implement abstract - * representations of PropertyTablePropertyView, without the mess of templated - * types. - */ -typedef std::variant< - View, - View, - View, - View, - View, - View, - View, - View, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>, - View>, true>> - NormalizedPropertyTablePropertyViewType; - -#undef View - -#define View PropertyTexturePropertyView -/** - * - * @brief An alias for all of the potential types of - * {@link PropertyTexturePropertyView} possible without normalization. - * Can be useful for applications that want to implement abstract - * representations of PropertyTexturePropertyView, without the mess of templated - * types. - */ -typedef std::variant< - View, - View, - View, - View, - View, - View, - View, - View, false>, - View, false>, - View, false>, - View, false>, - View, false>, - View, false>, - View, false>, - View, false>, - View, false>, - View, false>, - View, false>, - View, false>> - PropertyTexturePropertyViewType; - -/** - * @brief An alias for all of the potential types of - * {@link PropertyTexturePropertyView} possible with normalization. - * Can be useful for applications that want to implement abstract - * representations of PropertyTexturePropertyView, without the mess of templated - * types. - */ -typedef std::variant< - View, - View, - View, - View, - View, - View, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>> - NormalizedPropertyTexturePropertyViewType; - -#undef View - -#define View PropertyAttributePropertyView -/** - * - * @brief An alias for all of the potential types of - * {@link PropertyAttributePropertyView} possible without normalization. - * Can be useful for applications that want to implement abstract - * representations of PropertyAttributePropertyView, without the mess of - * templated types. - */ -typedef std::variant< - View, - View, - View, - View, - View, - View, - View, false>, - View, false>, - View, false>, - View, false>, - View, false>, - View>, - View, false>, - View, false>, - View, false>, - View, false>, - View, false>, - View>, - View, false>, - View, false>, - View, false>, - View, false>, - View, false>, - View>, - View, false>, - View, false>, - View, false>, - View, false>, - View, false>, - View>, - View, false>, - View, false>, - View, false>, - View, false>, - View, false>, - View>, - View, false>, - View, false>, - View, false>, - View, false>, - View, false>, - View>> - PropertyAttributePropertyViewType; - -/** - * @brief An alias for all of the potential types of - * {@link PropertyAttributePropertyView} possible with normalization. - * Can be useful for applications that want to implement abstract - * representations of PropertyAttributePropertyView, without the mess of - * templated types. - */ -typedef std::variant< - View, - View, - View, - View, - View, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>, - View, true>> - NormalizedPropertyAttributePropertyViewType; - -#undef View -} // namespace CesiumGltf From a9c27487a6b24506c02a068d9f3fbe99509efd40 Mon Sep 17 00:00:00 2001 From: "Adam N. Morris" Date: Tue, 3 Oct 2023 16:58:59 -0500 Subject: [PATCH 306/421] Updated ion authorization url to the current url --- .gitignore | 1 + CesiumIonClient/include/CesiumIonClient/Connection.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index d56187cae..b1a847ea6 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ CMakeSettings.json *.DS_Store test.db build-wsl +.idea diff --git a/CesiumIonClient/include/CesiumIonClient/Connection.h b/CesiumIonClient/include/CesiumIonClient/Connection.h index ba04177b2..15bd09fc9 100644 --- a/CesiumIonClient/include/CesiumIonClient/Connection.h +++ b/CesiumIonClient/include/CesiumIonClient/Connection.h @@ -103,7 +103,7 @@ class CESIUMASYNC_API Connection { const std::vector& scopes, std::function&& openUrlCallback, const std::string& ionApiUrl = "https://api.cesium.com/", - const std::string& ionAuthorizeUrl = "https://cesium.com/ion/oauth"); + const std::string& ionAuthorizeUrl = "https://ion.cesium.com/oauth"); /** * @brief Creates a connection to Cesium ion using the provided access token. From e318370cc8c89c9a38c837f31a0903f0942f226a Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Wed, 4 Oct 2023 17:25:54 -0400 Subject: [PATCH 307/421] Update changelog --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index 98f5ee399..dcfe0407d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,6 +5,7 @@ ##### Breaking Changes :mega: - Cesium Native is now only regularly tested on Visual Studio 2019+, GCC 11.x+, and Clang 12+. Other compilers - including older ones - are likely to work, but are not tested. +- Removed `PropertyTablePropertyViewType` and `NormalizedPropertyTablePropertyViewType`, as well as their counterparts for property textures and property attributes. When compiled with Clang, the large `std::variant` definitions would significantly stall compilation. ##### Additions :tada: From bd8bd6cae719d98a76ab8db70bc1ade0f40471fa Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Fri, 13 Oct 2023 18:20:31 +1100 Subject: [PATCH 308/421] Update CHANGES.md --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index dcfe0407d..fa604c59d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -15,6 +15,7 @@ ##### Fixes :wrench: - Fixed the handling of omitted metadata properties in `PropertyTableView`, `PropertyTextureView`, and `PropertyAttributeView` instances. Previously, if a property was not `required` and omitted, it would be initialized as invalid with the `ErrorNonexistentProperty` status. Now, it will be treated as valid as long as the property defines a valid `defaultProperty`. A special instance of `PropertyTablePropertyView`, `PropertyTexturePropertyView`, or `PropertyAttributePropertyView` will be constructed to allow the property's default value to be retrieved, either via `defaultValue` or `get`. `getRaw` may not be called on this special instance. +- Updated the Cesium ion OAuth2 URL from `https://cesium.com/ion/oauth` to `https://ion.cesium.com/oauth`, avoiding a redirect. ### v0.28.0 - 2023-09-08 From f10e6dc60ea82000f7795e1fb1c782a92d6a0aa3 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Fri, 20 Oct 2023 14:43:19 +1100 Subject: [PATCH 309/421] Install uriparser includes to the right location. --- CMakeLists.txt | 2 +- extern/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cb537bda8..1cc56c489 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -162,7 +162,7 @@ endif() add_subdirectory(doc) # Installation of third-party libraries required to use cesium-native -install(TARGETS uriparser OPTIONAL) # Skips headers +install(TARGETS uriparser OPTIONAL PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/uriparser") install(DIRECTORY extern/glm/glm DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} diff --git a/extern/CMakeLists.txt b/extern/CMakeLists.txt index 42d39235e..432a34679 100644 --- a/extern/CMakeLists.txt +++ b/extern/CMakeLists.txt @@ -34,7 +34,7 @@ add_subdirectory(libwebp) option(URIPARSER_BUILD_TESTS "" off) option(URIPARSER_BUILD_DOCS "" off) -option(URIPARSER_ENABLE_INSTALL "" on) +option(URIPARSER_ENABLE_INSTALL "" off) option(URIPARSER_BUILD_TOOLS "" off) add_subdirectory(uriparser) From e8990ce10897cdfcbec7bf22b841dcaf01258d08 Mon Sep 17 00:00:00 2001 From: Joseph Kaile Date: Mon, 23 Oct 2023 15:18:58 -0400 Subject: [PATCH 310/421] add utility function to extract key-value from url --- CesiumIonClient/src/Connection.cpp | 2 +- CesiumUtility/include/CesiumUtility/Uri.h | 2 ++ CesiumUtility/src/Uri.cpp | 36 +++++++++++++++++++++++ 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/CesiumIonClient/src/Connection.cpp b/CesiumIonClient/src/Connection.cpp index ac2b2169e..966338a5a 100644 --- a/CesiumIonClient/src/Connection.cpp +++ b/CesiumIonClient/src/Connection.cpp @@ -134,7 +134,7 @@ std::string createAuthorizationErrorHtml( authorizeUrl = Uri::addQuery(authorizeUrl, "client_id", std::to_string(clientID)); authorizeUrl = - Uri::addQuery(authorizeUrl, "scope", joinToString(scopes, " ")); + Uri::addQuery(authorizeUrl, "scope", joinToString(scopes, "%20")); authorizeUrl = Uri::addQuery(authorizeUrl, "redirect_uri", redirectUrl); authorizeUrl = Uri::addQuery(authorizeUrl, "state", state); authorizeUrl = Uri::addQuery(authorizeUrl, "code_challenge_method", "S256"); diff --git a/CesiumUtility/include/CesiumUtility/Uri.h b/CesiumUtility/include/CesiumUtility/Uri.h index c87ec835a..63b95a941 100644 --- a/CesiumUtility/include/CesiumUtility/Uri.h +++ b/CesiumUtility/include/CesiumUtility/Uri.h @@ -14,6 +14,8 @@ class Uri final { const std::string& uri, const std::string& key, const std::string& value); + static std::string + getQueryValue(const std::string& uri, const std::string& key); typedef std::string SubstitutionCallbackSignature(const std::string& placeholder); diff --git a/CesiumUtility/src/Uri.cpp b/CesiumUtility/src/Uri.cpp index 63b4c3982..f580cc7d1 100644 --- a/CesiumUtility/src/Uri.cpp +++ b/CesiumUtility/src/Uri.cpp @@ -100,6 +100,42 @@ std::string Uri::addQuery( // uriFreeUriMembersA(&baseUri); } +std::string Uri::getQueryValue(const std::string& url, const std::string& key) { + UriUriA uri; + auto err = uriParseSingleUriA(&uri, url.c_str(), nullptr); + + if (err != URI_SUCCESS) { + return ""; + } + UriQueryListA* queryList; + int itemCount; + err = uriDissectQueryMallocA( + &queryList, + &itemCount, + uri.query.first, + uri.query.afterLast); + if (err != URI_SUCCESS) { + uriFreeUriMembersA(&uri); + return ""; + } + + UriQueryListA* p = queryList; + while (p) { + if (p->key && std::strcmp(p->key, key.c_str()) == 0) { + std::string value = p->value ? p->value : ""; + uriUnescapeInPlaceA(value.data()); + uriFreeQueryListA(queryList); + uriFreeUriMembersA(&uri); + return value; + } + p = p->next; + } + + uriFreeQueryListA(queryList); + uriFreeUriMembersA(&uri); + return ""; +} + std::string Uri::substituteTemplateParameters( const std::string& templateUri, const std::function& substitutionCallback) { From 087abd079e71d94921cc66be305ffd74ac3dccf9 Mon Sep 17 00:00:00 2001 From: Joseph Kaile Date: Mon, 23 Oct 2023 15:25:17 -0400 Subject: [PATCH 311/421] extract key-value from uri --- CesiumUtility/src/Uri.cpp | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/CesiumUtility/src/Uri.cpp b/CesiumUtility/src/Uri.cpp index f580cc7d1..5bff85396 100644 --- a/CesiumUtility/src/Uri.cpp +++ b/CesiumUtility/src/Uri.cpp @@ -102,23 +102,19 @@ std::string Uri::addQuery( std::string Uri::getQueryValue(const std::string& url, const std::string& key) { UriUriA uri; - auto err = uriParseSingleUriA(&uri, url.c_str(), nullptr); - - if (err != URI_SUCCESS) { + if (uriParseSingleUriA(&uri, url.c_str(), nullptr) != URI_SUCCESS) { return ""; } UriQueryListA* queryList; int itemCount; - err = uriDissectQueryMallocA( - &queryList, - &itemCount, - uri.query.first, - uri.query.afterLast); - if (err != URI_SUCCESS) { + if (uriDissectQueryMallocA( + &queryList, + &itemCount, + uri.query.first, + uri.query.afterLast) != URI_SUCCESS) { uriFreeUriMembersA(&uri); return ""; } - UriQueryListA* p = queryList; while (p) { if (p->key && std::strcmp(p->key, key.c_str()) == 0) { @@ -130,7 +126,6 @@ std::string Uri::getQueryValue(const std::string& url, const std::string& key) { } p = p->next; } - uriFreeQueryListA(queryList); uriFreeUriMembersA(&uri); return ""; From 4468b87f961b66b686a44e18bbcef54417797c7d Mon Sep 17 00:00:00 2001 From: Joseph Kaile Date: Thu, 26 Oct 2023 22:28:12 -0400 Subject: [PATCH 312/421] add method to get api url async --- .../include/CesiumIonClient/Connection.h | 5 +++ CesiumIonClient/src/Connection.cpp | 38 +++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/CesiumIonClient/include/CesiumIonClient/Connection.h b/CesiumIonClient/include/CesiumIonClient/Connection.h index 15bd09fc9..4f3f7d46a 100644 --- a/CesiumIonClient/include/CesiumIonClient/Connection.h +++ b/CesiumIonClient/include/CesiumIonClient/Connection.h @@ -105,6 +105,11 @@ class CESIUMASYNC_API Connection { const std::string& ionApiUrl = "https://api.cesium.com/", const std::string& ionAuthorizeUrl = "https://ion.cesium.com/oauth"); + static CesiumAsync::Future> getApiUrl( + const CesiumAsync::AsyncSystem& asyncSystem, + const std::shared_ptr& pAssetAccessor, + const std::string& ionUrl); + /** * @brief Creates a connection to Cesium ion using the provided access token. * diff --git a/CesiumIonClient/src/Connection.cpp b/CesiumIonClient/src/Connection.cpp index 966338a5a..c6c293896 100644 --- a/CesiumIonClient/src/Connection.cpp +++ b/CesiumIonClient/src/Connection.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include @@ -373,6 +374,43 @@ Asset jsonToAsset(const rapidjson::Value& item) { } // namespace +CesiumAsync::Future> +CesiumIonClient::Connection::getApiUrl( + const CesiumAsync::AsyncSystem& asyncSystem, + const std::shared_ptr& pAssetAccessor, + const std::string& ionUrl) { + std::string configUrl = Uri::resolve(ionUrl, "config.json"); + if (configUrl == "config.json") { + return asyncSystem.createResolvedFuture>( + std::nullopt); + } + return pAssetAccessor->get(asyncSystem, configUrl) + .thenImmediately([ionUrl](std::shared_ptr&& pRequest) { + const IAssetResponse* pResponse = pRequest->response(); + if (pResponse && pResponse->statusCode() >= 200 && + pResponse->statusCode() < 300) { + rapidjson::Document d; + if (parseJsonObject(pResponse, d) && d.IsObject()) { + const auto itr = d.FindMember("apiHostname"); + if (itr != d.MemberEnd() && itr->value.IsString()) { + return std::make_optional(itr->value.GetString()); + } + } + } + + UriUriA newUri; + if (uriParseSingleUriA(&newUri, ionUrl.c_str(), nullptr) != + URI_SUCCESS) { + return std::optional(); + } + std::string hostName = + std::string(newUri.hostText.first, newUri.hostText.afterLast); + std::string scheme = + std::string(newUri.scheme.first, newUri.scheme.afterLast); + return std::make_optional(scheme + "://api." + hostName); + }); +} + CesiumAsync::Future> Connection::assets() const { return this->_pAssetAccessor ->get( From f52ade460c7076fff5cd1bd98e3c2c1906ab8b44 Mon Sep 17 00:00:00 2001 From: Joseph Kaile Date: Tue, 31 Oct 2023 14:16:05 -0400 Subject: [PATCH 313/421] free URI, return valid URL --- CesiumIonClient/src/Connection.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/CesiumIonClient/src/Connection.cpp b/CesiumIonClient/src/Connection.cpp index c6c293896..5fdc3cce8 100644 --- a/CesiumIonClient/src/Connection.cpp +++ b/CesiumIonClient/src/Connection.cpp @@ -403,11 +403,19 @@ CesiumIonClient::Connection::getApiUrl( URI_SUCCESS) { return std::optional(); } + std::string hostName = std::string(newUri.hostText.first, newUri.hostText.afterLast); std::string scheme = std::string(newUri.scheme.first, newUri.scheme.afterLast); - return std::make_optional(scheme + "://api." + hostName); + + uriFreeUriMembersA(&newUri); + + return std::make_optional( + scheme + "://api." + hostName + '/'); + }) + .catchImmediately([](std::exception&&) { + return std::optional(std::nullopt); }); } From fa2025ff689bef71e929cd637537fea3ba4a7997 Mon Sep 17 00:00:00 2001 From: Joseph Kaile Date: Tue, 31 Oct 2023 14:16:30 -0400 Subject: [PATCH 314/421] add URI get query value tests --- CesiumUtility/test/TestGetQueryValue.cpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 CesiumUtility/test/TestGetQueryValue.cpp diff --git a/CesiumUtility/test/TestGetQueryValue.cpp b/CesiumUtility/test/TestGetQueryValue.cpp new file mode 100644 index 000000000..baf526e2c --- /dev/null +++ b/CesiumUtility/test/TestGetQueryValue.cpp @@ -0,0 +1,22 @@ +#include "CesiumUtility/Uri.h" + +#include + +using namespace CesiumUtility; + +TEST_CASE("Uri::getQueryValue") { + std::string url = "https://example.com/?name=John&age=25"; + CHECK(Uri::getQueryValue(url, "name") == "John"); + CHECK(Uri::getQueryValue(url, "age") == std::to_string(25)); + CHECK(Uri::getQueryValue(url, "gender") == ""); + CHECK(Uri::getQueryValue(url, "") == ""); + CHECK(Uri::getQueryValue("", "name") == ""); + CHECK( + Uri::getQueryValue( + "https://example.com/?name=John&name=Jane&age=25", + "name") == "John"); + CHECK( + Uri::getQueryValue( + "https://example.com/?name=John%20Doe&age=25", + "name") == "John Doe"); +} From 262d3e080b3d6d76e41df38604d717cfa298ea5f Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 1 Nov 2023 11:27:49 +1100 Subject: [PATCH 315/421] Bump to v0.29.0, update changelog. --- CHANGES.md | 14 +++++++++++--- package.json | 2 +- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index fa604c59d..bbadb1d92 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,12 +1,21 @@ # Change Log -### ? - ? +### v0.29.0 - 2023-11-01 ##### Breaking Changes :mega: -- Cesium Native is now only regularly tested on Visual Studio 2019+, GCC 11.x+, and Clang 12+. Other compilers - including older ones - are likely to work, but are not tested. - Removed `PropertyTablePropertyViewType` and `NormalizedPropertyTablePropertyViewType`, as well as their counterparts for property textures and property attributes. When compiled with Clang, the large `std::variant` definitions would significantly stall compilation. +##### Fixes :wrench: + +- Updated the Cesium ion OAuth2 URL from `https://cesium.com/ion/oauth` to `https://ion.cesium.com/oauth`, avoiding a redirect. + +### v0.28.1 - 2023-10-02 + +##### Breaking Changes :mega: + +- Cesium Native is now only regularly tested on Visual Studio 2019+, GCC 11.x+, and Clang 12+. Other compilers - including older ones - are likely to work, but are not tested. + ##### Additions :tada: - Added `getClass` to `PropertyTableView`, `PropertyTextureView`, and `PropertyAttributeView`. This can be used to retrieve the metadata `Class` associated with the view. @@ -15,7 +24,6 @@ ##### Fixes :wrench: - Fixed the handling of omitted metadata properties in `PropertyTableView`, `PropertyTextureView`, and `PropertyAttributeView` instances. Previously, if a property was not `required` and omitted, it would be initialized as invalid with the `ErrorNonexistentProperty` status. Now, it will be treated as valid as long as the property defines a valid `defaultProperty`. A special instance of `PropertyTablePropertyView`, `PropertyTexturePropertyView`, or `PropertyAttributePropertyView` will be constructed to allow the property's default value to be retrieved, either via `defaultValue` or `get`. `getRaw` may not be called on this special instance. -- Updated the Cesium ion OAuth2 URL from `https://cesium.com/ion/oauth` to `https://ion.cesium.com/oauth`, avoiding a redirect. ### v0.28.0 - 2023-09-08 diff --git a/package.json b/package.json index 56706fac4..e64481a6b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cesium-native", - "version": "0.28.0", + "version": "0.29.0", "description": "Cesium 3D Geospatial for C++", "main": "index.js", "directories": { From af24dbe5b17a81005e997f048e3db0ccc043ca73 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Thu, 2 Nov 2023 16:30:40 +1100 Subject: [PATCH 316/421] Fix doc typos. --- .../include/Cesium3DTilesSelection/GltfUtilities.h | 2 +- .../include/Cesium3DTilesSelection/RasterOverlayTileProvider.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/GltfUtilities.h b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/GltfUtilities.h index dd823816d..73e7a98ed 100644 --- a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/GltfUtilities.h +++ b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/GltfUtilities.h @@ -92,7 +92,7 @@ struct CESIUM3DTILESSELECTION_API GltfUtilities { * @param projections The projections for which to generate texture * coordinates. There is a linear relationship between the coordinates of this * projection and the generated texture coordinates. - * @return The detailed of the generated texture coordinates. + * @return The details of the generated texture coordinates. */ static std::optional createRasterOverlayTextureCoordinates( diff --git a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/RasterOverlayTileProvider.h b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/RasterOverlayTileProvider.h index 6d392c049..a84538286 100644 --- a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/RasterOverlayTileProvider.h +++ b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/RasterOverlayTileProvider.h @@ -302,7 +302,7 @@ class CESIUM3DTILESSELECTION_API RasterOverlayTileProvider /** * @brief Loads a tile immediately, without throttling requests. * - * If the tile is not in the `Tile::LoadState::Unloading` state, this method + * If the tile is not in the `Tile::LoadState::Unloaded` state, this method * returns without doing anything. Otherwise, it puts the tile into the * `Tile::LoadState::Loading` state and begins the asynchronous process * to load the tile. When the process completes, the tile will be in the From a71a9829868136ba467f4ee789be2729808d2e84 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Thu, 2 Nov 2023 17:41:09 +1100 Subject: [PATCH 317/421] Add new Cesium3DTilesContent library. --- CHANGES.md | 6 ++ CMakeLists.txt | 1 + Cesium3DTilesContent/CMakeLists.txt | 65 +++++++++++++++++++ .../B3dmToGltfConverter.h | 3 +- .../BinaryToGltfConverter.h | 2 +- .../GltfConverterResult.h | 6 +- .../include/Cesium3DTilesContent/Library.h | 16 +++++ .../PntsToGltfConverter.h | 3 +- .../src/B3dmToGltfConverter.cpp | 5 +- .../src/BatchTableHierarchyPropertyValues.cpp | 0 .../src/BatchTableHierarchyPropertyValues.h | 0 .../BatchTableToGltfStructuralMetadata.cpp | 2 +- .../src/BatchTableToGltfStructuralMetadata.h | 6 +- .../src/BinaryToGltfConverter.cpp | 2 +- .../src/PntsToGltfConverter.cpp | 5 +- ...gradeBatchTableToExtStructuralMetadata.cpp | 0 Cesium3DTilesSelection/CMakeLists.txt | 1 + .../Cesium3DTilesSelection/GltfConverters.h | 2 +- .../src/CmptToGltfConverter.h | 2 +- Cesium3DTilesSelection/src/GltfConverters.cpp | 2 + .../src/QuantizedMeshLoader.h | 6 +- .../src/TilesetContentLoaderResult.h | 6 +- .../src/TilesetJsonLoader.cpp | 2 + .../src/logTileLoadResult.cpp | 2 + .../src/logTileLoadResult.h | 4 +- .../src/registerAllTileContentTypes.cpp | 6 +- .../test/ConvertTileToGltf.h | 5 +- CesiumUtility/CMakeLists.txt | 1 + .../include/CesiumUtility}/ErrorList.h | 11 ++-- .../src/ErrorList.cpp | 8 ++- 30 files changed, 140 insertions(+), 40 deletions(-) create mode 100644 Cesium3DTilesContent/CMakeLists.txt rename {Cesium3DTilesSelection/src => Cesium3DTilesContent/include/Cesium3DTilesContent}/B3dmToGltfConverter.h (87%) rename {Cesium3DTilesSelection/src => Cesium3DTilesContent/include/Cesium3DTilesContent}/BinaryToGltfConverter.h (88%) rename {Cesium3DTilesSelection/include/Cesium3DTilesSelection => Cesium3DTilesContent/include/Cesium3DTilesContent}/GltfConverterResult.h (85%) create mode 100644 Cesium3DTilesContent/include/Cesium3DTilesContent/Library.h rename {Cesium3DTilesSelection/src => Cesium3DTilesContent/include/Cesium3DTilesContent}/PntsToGltfConverter.h (87%) rename {Cesium3DTilesSelection => Cesium3DTilesContent}/src/B3dmToGltfConverter.cpp (98%) rename {Cesium3DTilesSelection => Cesium3DTilesContent}/src/BatchTableHierarchyPropertyValues.cpp (100%) rename {Cesium3DTilesSelection => Cesium3DTilesContent}/src/BatchTableHierarchyPropertyValues.h (100%) rename {Cesium3DTilesSelection => Cesium3DTilesContent}/src/BatchTableToGltfStructuralMetadata.cpp (99%) rename {Cesium3DTilesSelection => Cesium3DTilesContent}/src/BatchTableToGltfStructuralMetadata.h (81%) rename {Cesium3DTilesSelection => Cesium3DTilesContent}/src/BinaryToGltfConverter.cpp (91%) rename {Cesium3DTilesSelection => Cesium3DTilesContent}/src/PntsToGltfConverter.cpp (99%) rename {Cesium3DTilesSelection => Cesium3DTilesContent}/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp (100%) rename {Cesium3DTilesSelection/include/Cesium3DTilesSelection => CesiumUtility/include/CesiumUtility}/ErrorList.h (94%) rename {Cesium3DTilesSelection => CesiumUtility}/src/ErrorList.cpp (86%) diff --git a/CHANGES.md b/CHANGES.md index bbadb1d92..ab402e045 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,11 @@ # Change Log +### ? - ? + +##### Breaking Changes :mega: + +- Moved `ErrorList` from `Cesium3DTilesSelection` to `CesiumUtility`. + ### v0.29.0 - 2023-11-01 ##### Breaking Changes :mega: diff --git a/CMakeLists.txt b/CMakeLists.txt index 1cc56c489..efd419bef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -148,6 +148,7 @@ add_subdirectory(CesiumAsync) add_subdirectory(Cesium3DTiles) add_subdirectory(Cesium3DTilesReader) add_subdirectory(Cesium3DTilesWriter) +add_subdirectory(Cesium3DTilesContent) add_subdirectory(Cesium3DTilesSelection) add_subdirectory(CesiumIonClient) diff --git a/Cesium3DTilesContent/CMakeLists.txt b/Cesium3DTilesContent/CMakeLists.txt new file mode 100644 index 000000000..ac061db7c --- /dev/null +++ b/Cesium3DTilesContent/CMakeLists.txt @@ -0,0 +1,65 @@ +add_library(Cesium3DTilesContent "") +configure_cesium_library(Cesium3DTilesContent) + +cesium_glob_files(CESIUM_3DTILES_CONTENT_SOURCES + ${CMAKE_CURRENT_LIST_DIR}/src/*.cpp + ${CMAKE_CURRENT_LIST_DIR}/generated/src/*.cpp +) +cesium_glob_files(CESIUM_3DTILES_CONTENT_HEADERS + ${CMAKE_CURRENT_LIST_DIR}/src/*.h + ${CMAKE_CURRENT_LIST_DIR}/generated/src/*.h +) +cesium_glob_files(CESIUM_3DTILES_CONTENT_PUBLIC_HEADERS + ${CMAKE_CURRENT_LIST_DIR}/include/Cesium3DTilesContent/*.h + ${CMAKE_CURRENT_LIST_DIR}/generated/include/Cesium3DTilesContent/*.h +) +cesium_glob_files(CESIUM_3DTILES_CONTENT_TEST_SOURCES + ${CMAKE_CURRENT_LIST_DIR}/test/*.cpp +) +cesium_glob_files(CESIUM_3DTILES_CONTENT_TEST_HEADERS + ${CMAKE_CURRENT_LIST_DIR}/test/*.h +) + +set_target_properties(Cesium3DTilesContent + PROPERTIES + TEST_SOURCES "${CESIUM_3DTILES_CONTENT_TEST_SOURCES}" + TEST_HEADERS "${CESIUM_3DTILES_CONTENT_TEST_HEADERS}" + TEST_DATA_DIR ${CMAKE_CURRENT_LIST_DIR}/test/data +) + +set_target_properties(Cesium3DTilesContent + PROPERTIES + PUBLIC_HEADER "${CESIUM_3DTILES_CONTENT_PUBLIC_HEADERS}" +) + +target_sources( + Cesium3DTilesContent + PRIVATE + ${CESIUM_3DTILES_CONTENT_SOURCES} + ${CESIUM_3DTILES_CONTENT_HEADERS} + PUBLIC + ${CESIUM_3DTILES_CONTENT_PUBLIC_HEADERS} +) + +target_include_directories( + Cesium3DTilesContent + SYSTEM PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/include + ${CMAKE_CURRENT_LIST_DIR}/generated/include + PRIVATE + ${CMAKE_CURRENT_LIST_DIR}/src + ${CMAKE_CURRENT_LIST_DIR}/generated/src +) + +target_link_libraries(Cesium3DTilesContent + PUBLIC + CesiumAsync + CesiumGeometry + CesiumGltf + CesiumGltfReader +) + +install(TARGETS Cesium3DTilesContent + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/Cesium3DTilesContent +) diff --git a/Cesium3DTilesSelection/src/B3dmToGltfConverter.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/B3dmToGltfConverter.h similarity index 87% rename from Cesium3DTilesSelection/src/B3dmToGltfConverter.h rename to Cesium3DTilesContent/include/Cesium3DTilesContent/B3dmToGltfConverter.h index 299106f1c..c5291b1bc 100644 --- a/Cesium3DTilesSelection/src/B3dmToGltfConverter.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/B3dmToGltfConverter.h @@ -1,6 +1,7 @@ #pragma once -#include +#include "GltfConverterResult.h" + #include #include diff --git a/Cesium3DTilesSelection/src/BinaryToGltfConverter.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/BinaryToGltfConverter.h similarity index 88% rename from Cesium3DTilesSelection/src/BinaryToGltfConverter.h rename to Cesium3DTilesContent/include/Cesium3DTilesContent/BinaryToGltfConverter.h index 85eeaa566..a3364835f 100644 --- a/Cesium3DTilesSelection/src/BinaryToGltfConverter.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/BinaryToGltfConverter.h @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #include diff --git a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/GltfConverterResult.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverterResult.h similarity index 85% rename from Cesium3DTilesSelection/include/Cesium3DTilesSelection/GltfConverterResult.h rename to Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverterResult.h index 83f50cf4b..02d433b51 100644 --- a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/GltfConverterResult.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverterResult.h @@ -2,8 +2,8 @@ #include "Library.h" -#include #include +#include #include @@ -15,7 +15,7 @@ namespace Cesium3DTilesSelection { * {@link GltfConverters}, when the response to a network request for * loading the tile content was received. */ -struct CESIUM3DTILESSELECTION_API GltfConverterResult { +struct CESIUM3DTILESCONTENT_API GltfConverterResult { /** * @brief The gltf model converted from a binary content. This is empty if * there are errors during the conversion @@ -26,6 +26,6 @@ struct CESIUM3DTILESSELECTION_API GltfConverterResult { * @brief The error and warning list when converting a binary content to gltf * model. This is empty if there are no errors during the conversion */ - ErrorList errors; + CesiumUtility::ErrorList errors; }; } // namespace Cesium3DTilesSelection diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/Library.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/Library.h new file mode 100644 index 000000000..1fd764600 --- /dev/null +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/Library.h @@ -0,0 +1,16 @@ +#pragma once + +/** + * @brief Classes that support the 3D Tiles tile content types. + */ +namespace Cesium3DTilesContent {} + +#if defined(_WIN32) && defined(CESIUM_SHARED) +#ifdef CESIUM3DTILESCONTENT_BUILDING +#define CESIUM3DTILESCONTENT_API __declspec(dllexport) +#else +#define CESIUM3DTILESCONTENT_API __declspec(dllimport) +#endif +#else +#define CESIUM3DTILESCONTENT_API +#endif diff --git a/Cesium3DTilesSelection/src/PntsToGltfConverter.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/PntsToGltfConverter.h similarity index 87% rename from Cesium3DTilesSelection/src/PntsToGltfConverter.h rename to Cesium3DTilesContent/include/Cesium3DTilesContent/PntsToGltfConverter.h index 47e6aa07b..a30ddf0a8 100644 --- a/Cesium3DTilesSelection/src/PntsToGltfConverter.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/PntsToGltfConverter.h @@ -1,6 +1,7 @@ #pragma once -#include +#include "GltfConverterResult.h" + #include #include diff --git a/Cesium3DTilesSelection/src/B3dmToGltfConverter.cpp b/Cesium3DTilesContent/src/B3dmToGltfConverter.cpp similarity index 98% rename from Cesium3DTilesSelection/src/B3dmToGltfConverter.cpp rename to Cesium3DTilesContent/src/B3dmToGltfConverter.cpp index c4c9bf5c7..bd9b9f433 100644 --- a/Cesium3DTilesSelection/src/B3dmToGltfConverter.cpp +++ b/Cesium3DTilesContent/src/B3dmToGltfConverter.cpp @@ -1,8 +1,7 @@ -#include "B3dmToGltfConverter.h" - #include "BatchTableToGltfStructuralMetadata.h" -#include "BinaryToGltfConverter.h" +#include +#include #include #include diff --git a/Cesium3DTilesSelection/src/BatchTableHierarchyPropertyValues.cpp b/Cesium3DTilesContent/src/BatchTableHierarchyPropertyValues.cpp similarity index 100% rename from Cesium3DTilesSelection/src/BatchTableHierarchyPropertyValues.cpp rename to Cesium3DTilesContent/src/BatchTableHierarchyPropertyValues.cpp diff --git a/Cesium3DTilesSelection/src/BatchTableHierarchyPropertyValues.h b/Cesium3DTilesContent/src/BatchTableHierarchyPropertyValues.h similarity index 100% rename from Cesium3DTilesSelection/src/BatchTableHierarchyPropertyValues.h rename to Cesium3DTilesContent/src/BatchTableHierarchyPropertyValues.h diff --git a/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp b/Cesium3DTilesContent/src/BatchTableToGltfStructuralMetadata.cpp similarity index 99% rename from Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp rename to Cesium3DTilesContent/src/BatchTableToGltfStructuralMetadata.cpp index 4ff3abc25..754fc7444 100644 --- a/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.cpp +++ b/Cesium3DTilesContent/src/BatchTableToGltfStructuralMetadata.cpp @@ -1,7 +1,6 @@ #include "BatchTableToGltfStructuralMetadata.h" #include "BatchTableHierarchyPropertyValues.h" -#include "Cesium3DTilesSelection/spdlog-cesium.h" #include #include @@ -20,6 +19,7 @@ using namespace CesiumGltf; using namespace Cesium3DTilesSelection::CesiumImpl; +using namespace CesiumUtility; namespace Cesium3DTilesSelection { namespace { diff --git a/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.h b/Cesium3DTilesContent/src/BatchTableToGltfStructuralMetadata.h similarity index 81% rename from Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.h rename to Cesium3DTilesContent/src/BatchTableToGltfStructuralMetadata.h index 05517786b..ff51e822b 100644 --- a/Cesium3DTilesSelection/src/BatchTableToGltfStructuralMetadata.h +++ b/Cesium3DTilesContent/src/BatchTableToGltfStructuralMetadata.h @@ -1,7 +1,7 @@ #pragma once -#include #include +#include #include #include @@ -10,13 +10,13 @@ namespace Cesium3DTilesSelection { struct BatchTableToGltfStructuralMetadata { - static ErrorList convertFromB3dm( + static CesiumUtility::ErrorList convertFromB3dm( const rapidjson::Document& featureTableJson, const rapidjson::Document& batchTableJson, const gsl::span& batchTableBinaryData, CesiumGltf::Model& gltf); - static ErrorList convertFromPnts( + static CesiumUtility::ErrorList convertFromPnts( const rapidjson::Document& featureTableJson, const rapidjson::Document& batchTableJson, const gsl::span& batchTableBinaryData, diff --git a/Cesium3DTilesSelection/src/BinaryToGltfConverter.cpp b/Cesium3DTilesContent/src/BinaryToGltfConverter.cpp similarity index 91% rename from Cesium3DTilesSelection/src/BinaryToGltfConverter.cpp rename to Cesium3DTilesContent/src/BinaryToGltfConverter.cpp index cfa4e62df..64c0bab91 100644 --- a/Cesium3DTilesSelection/src/BinaryToGltfConverter.cpp +++ b/Cesium3DTilesContent/src/BinaryToGltfConverter.cpp @@ -1,4 +1,4 @@ -#include "BinaryToGltfConverter.h" +#include namespace Cesium3DTilesSelection { CesiumGltfReader::GltfReader BinaryToGltfConverter::_gltfReader; diff --git a/Cesium3DTilesSelection/src/PntsToGltfConverter.cpp b/Cesium3DTilesContent/src/PntsToGltfConverter.cpp similarity index 99% rename from Cesium3DTilesSelection/src/PntsToGltfConverter.cpp rename to Cesium3DTilesContent/src/PntsToGltfConverter.cpp index d732fc7da..8525e3951 100644 --- a/Cesium3DTilesSelection/src/PntsToGltfConverter.cpp +++ b/Cesium3DTilesContent/src/PntsToGltfConverter.cpp @@ -1,7 +1,6 @@ -#include "PntsToGltfConverter.h" - #include "BatchTableToGltfStructuralMetadata.h" +#include #include #include #include @@ -228,7 +227,7 @@ struct PntsContent { std::map dracoMetadataSemantics; std::vector dracoBatchTableBinary; - Cesium3DTilesSelection::ErrorList errors; + ErrorList errors; bool dracoMetadataHasErrors = false; }; diff --git a/Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp b/Cesium3DTilesContent/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp similarity index 100% rename from Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp rename to Cesium3DTilesContent/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp diff --git a/Cesium3DTilesSelection/CMakeLists.txt b/Cesium3DTilesSelection/CMakeLists.txt index e0736a313..c07cd2c1d 100644 --- a/Cesium3DTilesSelection/CMakeLists.txt +++ b/Cesium3DTilesSelection/CMakeLists.txt @@ -44,6 +44,7 @@ target_include_directories( target_link_libraries(Cesium3DTilesSelection PUBLIC Cesium3DTiles + Cesium3DTilesContent Cesium3DTilesReader CesiumAsync CesiumGeospatial diff --git a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/GltfConverters.h b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/GltfConverters.h index f027da24c..c41c4dfb3 100644 --- a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/GltfConverters.h +++ b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/GltfConverters.h @@ -2,7 +2,7 @@ #include "Library.h" -#include +#include #include #include diff --git a/Cesium3DTilesSelection/src/CmptToGltfConverter.h b/Cesium3DTilesSelection/src/CmptToGltfConverter.h index 20b845456..b95f3a66f 100644 --- a/Cesium3DTilesSelection/src/CmptToGltfConverter.h +++ b/Cesium3DTilesSelection/src/CmptToGltfConverter.h @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #include diff --git a/Cesium3DTilesSelection/src/GltfConverters.cpp b/Cesium3DTilesSelection/src/GltfConverters.cpp index a1bf24b5a..2680da4ac 100644 --- a/Cesium3DTilesSelection/src/GltfConverters.cpp +++ b/Cesium3DTilesSelection/src/GltfConverters.cpp @@ -2,6 +2,8 @@ #include +using namespace CesiumUtility; + namespace Cesium3DTilesSelection { std::unordered_map GltfConverters::_loadersByMagic; diff --git a/Cesium3DTilesSelection/src/QuantizedMeshLoader.h b/Cesium3DTilesSelection/src/QuantizedMeshLoader.h index ab32df66b..241cd30bd 100644 --- a/Cesium3DTilesSelection/src/QuantizedMeshLoader.h +++ b/Cesium3DTilesSelection/src/QuantizedMeshLoader.h @@ -1,11 +1,11 @@ #pragma once #include -#include #include #include #include #include +#include #include @@ -52,12 +52,12 @@ struct QuantizedMeshLoadResult { */ std::shared_ptr pRequest; - ErrorList errors; + CesiumUtility::ErrorList errors; }; struct QuantizedMeshMetadataResult { std::vector availability; - ErrorList errors; + CesiumUtility::ErrorList errors; }; /** diff --git a/Cesium3DTilesSelection/src/TilesetContentLoaderResult.h b/Cesium3DTilesSelection/src/TilesetContentLoaderResult.h index 144e0f6e0..662eef218 100644 --- a/Cesium3DTilesSelection/src/TilesetContentLoaderResult.h +++ b/Cesium3DTilesSelection/src/TilesetContentLoaderResult.h @@ -1,9 +1,9 @@ #pragma once -#include #include #include #include +#include #include #include @@ -24,7 +24,7 @@ template struct TilesetContentLoaderResult { std::unique_ptr&& pRootTile_, std::vector&& credits_, std::vector&& requestHeaders_, - ErrorList&& errors_) + CesiumUtility::ErrorList&& errors_) : pLoader{std::move(pLoader_)}, pRootTile{std::move(pRootTile_)}, credits{std::move(credits_)}, @@ -85,7 +85,7 @@ template struct TilesetContentLoaderResult { std::vector requestHeaders; - ErrorList errors; + CesiumUtility::ErrorList errors; uint16_t statusCode{200}; }; diff --git a/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp b/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp index e6433575d..11e4b1d4a 100644 --- a/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp +++ b/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp @@ -24,6 +24,8 @@ #include +using namespace CesiumUtility; + namespace Cesium3DTilesSelection { namespace { struct ExternalContentInitializer { diff --git a/Cesium3DTilesSelection/src/logTileLoadResult.cpp b/Cesium3DTilesSelection/src/logTileLoadResult.cpp index 4db7a4549..68918e7a0 100644 --- a/Cesium3DTilesSelection/src/logTileLoadResult.cpp +++ b/Cesium3DTilesSelection/src/logTileLoadResult.cpp @@ -1,5 +1,7 @@ #include "logTileLoadResult.h" +using namespace CesiumUtility; + namespace Cesium3DTilesSelection { void logTileLoadResult( const std::shared_ptr& pLogger, diff --git a/Cesium3DTilesSelection/src/logTileLoadResult.h b/Cesium3DTilesSelection/src/logTileLoadResult.h index 3747a07a4..d735eb00e 100644 --- a/Cesium3DTilesSelection/src/logTileLoadResult.h +++ b/Cesium3DTilesSelection/src/logTileLoadResult.h @@ -1,6 +1,6 @@ #pragma once -#include +#include #include @@ -11,5 +11,5 @@ namespace Cesium3DTilesSelection { void logTileLoadResult( const std::shared_ptr& pLogger, const std::string& url, - const ErrorList& errorLists); + const CesiumUtility::ErrorList& errorLists); } diff --git a/Cesium3DTilesSelection/src/registerAllTileContentTypes.cpp b/Cesium3DTilesSelection/src/registerAllTileContentTypes.cpp index ce9a98020..cf7e741dc 100644 --- a/Cesium3DTilesSelection/src/registerAllTileContentTypes.cpp +++ b/Cesium3DTilesSelection/src/registerAllTileContentTypes.cpp @@ -1,8 +1,8 @@ -#include "B3dmToGltfConverter.h" -#include "BinaryToGltfConverter.h" #include "CmptToGltfConverter.h" -#include "PntsToGltfConverter.h" +#include +#include +#include #include #include diff --git a/Cesium3DTilesSelection/test/ConvertTileToGltf.h b/Cesium3DTilesSelection/test/ConvertTileToGltf.h index 2011ca5cb..76665a184 100644 --- a/Cesium3DTilesSelection/test/ConvertTileToGltf.h +++ b/Cesium3DTilesSelection/test/ConvertTileToGltf.h @@ -1,9 +1,10 @@ #pragma once -#include "B3dmToGltfConverter.h" -#include "PntsToGltfConverter.h" #include "readFile.h" +#include +#include + #include namespace Cesium3DTilesSelection { diff --git a/CesiumUtility/CMakeLists.txt b/CesiumUtility/CMakeLists.txt index bb704b8d0..1859e2703 100644 --- a/CesiumUtility/CMakeLists.txt +++ b/CesiumUtility/CMakeLists.txt @@ -47,6 +47,7 @@ target_link_libraries_system( PUBLIC glm uriparser + spdlog ) target_link_libraries(CesiumUtility diff --git a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/ErrorList.h b/CesiumUtility/include/CesiumUtility/ErrorList.h similarity index 94% rename from Cesium3DTilesSelection/include/Cesium3DTilesSelection/ErrorList.h rename to CesiumUtility/include/CesiumUtility/ErrorList.h index 2ebd1a43e..3626368e6 100644 --- a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/ErrorList.h +++ b/CesiumUtility/include/CesiumUtility/ErrorList.h @@ -1,20 +1,20 @@ #pragma once #include "Library.h" - -#include +#include "joinToString.h" #include #include #include -namespace Cesium3DTilesSelection { +namespace CesiumUtility { + /** * @brief The container to store the error and warning list when loading a tile * or glTF content */ -struct CESIUM3DTILESSELECTION_API ErrorList { +struct CESIUMUTILITY_API ErrorList { /** * @brief Merge the errors and warnings from other ErrorList together * @@ -107,4 +107,5 @@ struct CESIUM3DTILESSELECTION_API ErrorList { */ std::vector warnings; }; -} // namespace Cesium3DTilesSelection + +} // namespace CesiumUtility diff --git a/Cesium3DTilesSelection/src/ErrorList.cpp b/CesiumUtility/src/ErrorList.cpp similarity index 86% rename from Cesium3DTilesSelection/src/ErrorList.cpp rename to CesiumUtility/src/ErrorList.cpp index 4ad3cc84c..4e157c1fd 100644 --- a/Cesium3DTilesSelection/src/ErrorList.cpp +++ b/CesiumUtility/src/ErrorList.cpp @@ -1,6 +1,7 @@ -#include +#include + +namespace CesiumUtility { -namespace Cesium3DTilesSelection { void ErrorList::merge(const ErrorList& errorList) { errors.insert(errors.end(), errorList.errors.begin(), errorList.errors.end()); warnings.insert( @@ -24,4 +25,5 @@ void ErrorList::merge(ErrorList&& errorList) { bool ErrorList::hasErrors() const noexcept { return !errors.empty(); } ErrorList::operator bool() const noexcept { return hasErrors(); } -} // namespace Cesium3DTilesSelection + +} // namespace CesiumUtility From c561b6407c4ef20914417fb5fa86c4f819dc527f Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Thu, 2 Nov 2023 19:48:57 +1100 Subject: [PATCH 318/421] Move more functionality to Cesium3DTilesContent. --- Cesium3DTilesContent/CMakeLists.txt | 2 + .../CmptToGltfConverter.h | 0 .../Cesium3DTilesContent}/GltfConverters.h | 2 +- .../Cesium3DTilesContent/GltfUtilities.h | 96 ++++++++++ .../include/Cesium3DTilesContent/Library.h | 2 +- .../QuantizedMeshLoader.h | 18 +- .../Cesium3DTilesContent}/SkirtMeshMetadata.h | 0 .../SubtreeAvailability.h | 1 - .../registerAllTileContentTypes.h | 2 +- .../upsampleGltfForRasterOverlays.h | 0 .../src/CmptToGltfConverter.cpp | 5 +- .../src/GltfConverters.cpp | 2 +- Cesium3DTilesContent/src/GltfUtilities.cpp | 153 ++++++++++++++++ .../src/QuantizedMeshLoader.cpp | 40 +---- .../src/SkirtMeshMetadata.cpp | 3 +- .../src/SubtreeAvailability.cpp | 3 +- .../src/registerAllTileContentTypes.cpp | 7 +- .../src/upsampleGltfForRasterOverlays.cpp | 6 +- .../test/ConvertTileToGltf.h | 3 +- .../test/TestPntsToGltfConverter.cpp | 0 .../test/TestQuantizedMeshContent.cpp | 6 +- .../test/TestSkirtMeshMetadata.cpp | 0 .../test/TestSubtreeAvailability.cpp | 0 .../Cesium3DTilesSelection/CreditSystem.h | 3 +- .../Cesium3DTilesSelection/GltfUtilities.h | 139 --------------- .../RasterOverlayUtilities.h | 66 +++++++ Cesium3DTilesSelection/src/CreditSystem.cpp | 5 +- .../src/ImplicitOctreeLoader.cpp | 2 +- .../src/ImplicitOctreeLoader.h | 3 +- .../src/ImplicitQuadtreeLoader.cpp | 2 +- .../src/ImplicitQuadtreeLoader.h | 3 +- .../src/LayerJsonTerrainLoader.cpp | 28 +-- .../src/RasterOverlayUpsampler.cpp | 3 +- ...ilities.cpp => RasterOverlayUtilities.cpp} | 166 ++---------------- .../src/TilesetContentManager.cpp | 38 ++-- .../src/TilesetJsonLoader.cpp | 2 +- .../test/SimplePrepareRendererResource.h | 2 + .../test/TestImplicitOctreeLoader.cpp | 10 +- .../test/TestImplicitQuadtreeLoader.cpp | 10 +- .../test/TestLayerJsonTerrainLoader.cpp | 10 +- .../TestQuadtreeRasterOverlayTileProvider.cpp | 2 +- .../test/TestTilesetContentManager.cpp | 12 +- .../test/TestTilesetJsonLoader.cpp | 10 +- .../test/TestTilesetSelectionAlgorithm.cpp | 8 +- .../calcQuadtreeMaxGeometricError.h | 7 +- .../src/calcQuadtreeMaxGeometricError.cpp | 6 +- CesiumNativeTests/CMakeLists.txt | 13 +- .../CesiumNativeTests}/SimpleAssetAccessor.h | 0 .../CesiumNativeTests}/SimpleAssetRequest.h | 3 +- .../CesiumNativeTests}/SimpleAssetResponse.h | 0 .../include/CesiumNativeTests}/readFile.h | 0 .../src}/readFile.cpp | 2 +- 52 files changed, 469 insertions(+), 437 deletions(-) rename {Cesium3DTilesSelection/src => Cesium3DTilesContent/include/Cesium3DTilesContent}/CmptToGltfConverter.h (100%) rename {Cesium3DTilesSelection/include/Cesium3DTilesSelection => Cesium3DTilesContent/include/Cesium3DTilesContent}/GltfConverters.h (99%) create mode 100644 Cesium3DTilesContent/include/Cesium3DTilesContent/GltfUtilities.h rename {Cesium3DTilesSelection/src => Cesium3DTilesContent/include/Cesium3DTilesContent}/QuantizedMeshLoader.h (83%) rename {Cesium3DTilesSelection/src => Cesium3DTilesContent/include/Cesium3DTilesContent}/SkirtMeshMetadata.h (100%) rename {Cesium3DTilesSelection/src => Cesium3DTilesContent/include/Cesium3DTilesContent}/SubtreeAvailability.h (97%) rename {Cesium3DTilesSelection/include/Cesium3DTilesSelection => Cesium3DTilesContent/include/Cesium3DTilesContent}/registerAllTileContentTypes.h (85%) rename {Cesium3DTilesSelection/src => Cesium3DTilesContent/include/Cesium3DTilesContent}/upsampleGltfForRasterOverlays.h (100%) rename {Cesium3DTilesSelection => Cesium3DTilesContent}/src/CmptToGltfConverter.cpp (96%) rename {Cesium3DTilesSelection => Cesium3DTilesContent}/src/GltfConverters.cpp (98%) create mode 100644 Cesium3DTilesContent/src/GltfUtilities.cpp rename {Cesium3DTilesSelection => Cesium3DTilesContent}/src/QuantizedMeshLoader.cpp (97%) rename {Cesium3DTilesSelection => Cesium3DTilesContent}/src/SkirtMeshMetadata.cpp (98%) rename {Cesium3DTilesSelection => Cesium3DTilesContent}/src/SubtreeAvailability.cpp (99%) rename {Cesium3DTilesSelection => Cesium3DTilesContent}/src/registerAllTileContentTypes.cpp (81%) rename {Cesium3DTilesSelection => Cesium3DTilesContent}/src/upsampleGltfForRasterOverlays.cpp (99%) rename {Cesium3DTilesSelection => Cesium3DTilesContent}/test/ConvertTileToGltf.h (93%) rename {Cesium3DTilesSelection => Cesium3DTilesContent}/test/TestPntsToGltfConverter.cpp (100%) rename {Cesium3DTilesSelection => Cesium3DTilesContent}/test/TestQuantizedMeshContent.cpp (99%) rename {Cesium3DTilesSelection => Cesium3DTilesContent}/test/TestSkirtMeshMetadata.cpp (100%) rename {Cesium3DTilesSelection => Cesium3DTilesContent}/test/TestSubtreeAvailability.cpp (100%) delete mode 100644 Cesium3DTilesSelection/include/Cesium3DTilesSelection/GltfUtilities.h create mode 100644 Cesium3DTilesSelection/include/Cesium3DTilesSelection/RasterOverlayUtilities.h rename Cesium3DTilesSelection/src/{GltfUtilities.cpp => RasterOverlayUtilities.cpp} (65%) rename {Cesium3DTilesSelection/src => CesiumGeospatial/include/CesiumGeospatial}/calcQuadtreeMaxGeometricError.h (85%) rename {Cesium3DTilesSelection => CesiumGeospatial}/src/calcQuadtreeMaxGeometricError.cpp (66%) rename {Cesium3DTilesSelection/test => CesiumNativeTests/include/CesiumNativeTests}/SimpleAssetAccessor.h (100%) rename {Cesium3DTilesSelection/test => CesiumNativeTests/include/CesiumNativeTests}/SimpleAssetRequest.h (95%) rename {Cesium3DTilesSelection/test => CesiumNativeTests/include/CesiumNativeTests}/SimpleAssetResponse.h (100%) rename {Cesium3DTilesSelection/test => CesiumNativeTests/include/CesiumNativeTests}/readFile.h (100%) rename {Cesium3DTilesSelection/test => CesiumNativeTests/src}/readFile.cpp (91%) diff --git a/Cesium3DTilesContent/CMakeLists.txt b/Cesium3DTilesContent/CMakeLists.txt index ac061db7c..8493e1397 100644 --- a/Cesium3DTilesContent/CMakeLists.txt +++ b/Cesium3DTilesContent/CMakeLists.txt @@ -55,8 +55,10 @@ target_link_libraries(Cesium3DTilesContent PUBLIC CesiumAsync CesiumGeometry + CesiumGeospatial CesiumGltf CesiumGltfReader + CesiumUtility ) install(TARGETS Cesium3DTilesContent diff --git a/Cesium3DTilesSelection/src/CmptToGltfConverter.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/CmptToGltfConverter.h similarity index 100% rename from Cesium3DTilesSelection/src/CmptToGltfConverter.h rename to Cesium3DTilesContent/include/Cesium3DTilesContent/CmptToGltfConverter.h diff --git a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/GltfConverters.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverters.h similarity index 99% rename from Cesium3DTilesSelection/include/Cesium3DTilesSelection/GltfConverters.h rename to Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverters.h index c41c4dfb3..3b8f8d611 100644 --- a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/GltfConverters.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverters.h @@ -26,7 +26,7 @@ namespace Cesium3DTilesSelection { * header. Based on this header or the file extension of the network response, * the loader that will be used for processing the input can be looked up. */ -class CESIUM3DTILESSELECTION_API GltfConverters { +class CESIUM3DTILESCONTENT_API GltfConverters { public: /** * @brief A function pointer that can create a {@link GltfConverterResult} from a diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfUtilities.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfUtilities.h new file mode 100644 index 000000000..c8437616d --- /dev/null +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfUtilities.h @@ -0,0 +1,96 @@ +#pragma once + +#include "Library.h" + +#include +#include + +#include + +#include +#include + +namespace CesiumGltf { +struct Model; +} + +namespace Cesium3DTilesSelection { +/** + * A collection of utility functions that are used to process and transform a + * gltf model + */ +struct CESIUM3DTILESCONTENT_API GltfUtilities { + /** + * @brief Applies the glTF's RTC_CENTER, if any, to the given transform. + * + * If the glTF has a `CESIUM_RTC` extension, this function will multiply the + * given matrix with the (translation) matrix that is created from the + * `RTC_CENTER` in the. If the given model does not have this extension, then + * this function will return the `rootTransform` unchanged. + * + * @param model The glTF model + * @param rootTransform The matrix that will be multiplied with the transform + * @return The result of multiplying the `RTC_CENTER` with the + * `rootTransform`. + */ + static glm::dmat4x4 applyRtcCenter( + const CesiumGltf::Model& gltf, + const glm::dmat4x4& rootTransform); + + /** + * @brief Applies the glTF's `gltfUpAxis`, if any, to the given transform. + * + * By default, the up-axis of a glTF model will the the Y-axis. + * + * If the tileset that contained the model had the `asset.gltfUpAxis` string + * property, then the information about the up-axis has been stored in as a + * number property called `gltfUpAxis` in the `extras` of the given model. + * + * Depending on whether this value is `CesiumGeometry::Axis::X`, `Y`, or `Z`, + * the given matrix will be multiplied with a matrix that converts the + * respective axis to be the Z-axis, as required by the 3D Tiles standard. + * + * @param model The glTF model + * @param rootTransform The matrix that will be multiplied with the transform + * @return The result of multiplying the `rootTransform` with the + * `gltfUpAxis`. + */ + static glm::dmat4x4 applyGltfUpAxisTransform( + const CesiumGltf::Model& model, + const glm::dmat4x4& rootTransform); + + /** + * @brief Computes a bounding region from the vertex positions in a glTF + * model. + * + * If the glTF model spans the anti-meridian, the west and east longitude + * values will be in the usual -PI to PI range, but east will have a smaller + * value than west. + * + * If the glTF contains no geometry, the returned region's rectangle + * will be {@link GlobeRectangle::EMPTY}, its minimum height will be 1.0, and + * its maximum height will be -1.0 (the minimum will be greater than the + * maximum). + * + * @param gltf The model. + * @param transform The transform from model coordinates to ECEF coordinates. + * @return The computed bounding region. + */ + static CesiumGeospatial::BoundingRegion computeBoundingRegion( + const CesiumGltf::Model& gltf, + const glm::dmat4& transform); + + /** + * @brief Parse the copyright field of a glTF model and return the individual + * credits. + * + * Credits are read from the glTF's asset.copyright field. This method assumes + * that individual credits are separated by semicolons. + * + * @param gltf The model. + * @return The credits from the glTF. + */ + static std::vector + parseGltfCopyright(const CesiumGltf::Model& gltf); +}; +} // namespace Cesium3DTilesSelection diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/Library.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/Library.h index 1fd764600..26f187703 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/Library.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/Library.h @@ -1,7 +1,7 @@ #pragma once /** - * @brief Classes that support the 3D Tiles tile content types. + * @brief Classes that support loading and converting 3D Tiles tile content. */ namespace Cesium3DTilesContent {} diff --git a/Cesium3DTilesSelection/src/QuantizedMeshLoader.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/QuantizedMeshLoader.h similarity index 83% rename from Cesium3DTilesSelection/src/QuantizedMeshLoader.h rename to Cesium3DTilesContent/include/Cesium3DTilesContent/QuantizedMeshLoader.h index 241cd30bd..10c380974 100644 --- a/Cesium3DTilesSelection/src/QuantizedMeshLoader.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/QuantizedMeshLoader.h @@ -1,12 +1,14 @@ #pragma once -#include -#include -#include +#include "Library.h" + +#include #include +#include #include #include +#include #include #include @@ -31,12 +33,12 @@ struct QuantizedMeshLoadResult { std::optional model; /** - * @brief An improved bounding volume for this tile. + * @brief An improved bounding region for this tile. * * If this is available, then it is more accurate than the one the tile used * originally. */ - std::optional updatedBoundingVolume{}; + std::optional updatedBoundingVolume{}; /** * @brief Available quadtree tiles discovered as a result of loading this @@ -63,7 +65,7 @@ struct QuantizedMeshMetadataResult { /** * @brief Loads `quantized-mesh-1.0` terrain data. */ -class CESIUM3DTILESSELECTION_API QuantizedMeshLoader final { +class CESIUM3DTILESCONTENT_API QuantizedMeshLoader final { public: /** * @brief Create a {@link QuantizedMeshLoadResult} from the given data. @@ -75,8 +77,8 @@ class CESIUM3DTILESSELECTION_API QuantizedMeshLoader final { * @return The {@link QuantizedMeshLoadResult} */ static QuantizedMeshLoadResult load( - const TileID& tileID, - const BoundingVolume& tileBoundingVolume, + const CesiumGeometry::QuadtreeTileID& tileID, + const CesiumGeospatial::BoundingRegion& tileBoundingVolume, const std::string& url, const gsl::span& data, bool enableWaterMask); diff --git a/Cesium3DTilesSelection/src/SkirtMeshMetadata.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/SkirtMeshMetadata.h similarity index 100% rename from Cesium3DTilesSelection/src/SkirtMeshMetadata.h rename to Cesium3DTilesContent/include/Cesium3DTilesContent/SkirtMeshMetadata.h diff --git a/Cesium3DTilesSelection/src/SubtreeAvailability.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/SubtreeAvailability.h similarity index 97% rename from Cesium3DTilesSelection/src/SubtreeAvailability.h rename to Cesium3DTilesContent/include/Cesium3DTilesContent/SubtreeAvailability.h index 3e5957974..51b2a75d9 100644 --- a/Cesium3DTilesSelection/src/SubtreeAvailability.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/SubtreeAvailability.h @@ -1,6 +1,5 @@ #pragma once -#include #include #include diff --git a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/registerAllTileContentTypes.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/registerAllTileContentTypes.h similarity index 85% rename from Cesium3DTilesSelection/include/Cesium3DTilesSelection/registerAllTileContentTypes.h rename to Cesium3DTilesContent/include/Cesium3DTilesContent/registerAllTileContentTypes.h index ac0ef9ee6..bd00bc181 100644 --- a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/registerAllTileContentTypes.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/registerAllTileContentTypes.h @@ -9,6 +9,6 @@ namespace Cesium3DTilesSelection { * any {@link Tileset} is loaded. It will register loaders for the * different types of tiles that can be encountered. */ -CESIUM3DTILESSELECTION_API void registerAllTileContentTypes(); +CESIUM3DTILESCONTENT_API void registerAllTileContentTypes(); } // namespace Cesium3DTilesSelection diff --git a/Cesium3DTilesSelection/src/upsampleGltfForRasterOverlays.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/upsampleGltfForRasterOverlays.h similarity index 100% rename from Cesium3DTilesSelection/src/upsampleGltfForRasterOverlays.h rename to Cesium3DTilesContent/include/Cesium3DTilesContent/upsampleGltfForRasterOverlays.h diff --git a/Cesium3DTilesSelection/src/CmptToGltfConverter.cpp b/Cesium3DTilesContent/src/CmptToGltfConverter.cpp similarity index 96% rename from Cesium3DTilesSelection/src/CmptToGltfConverter.cpp rename to Cesium3DTilesContent/src/CmptToGltfConverter.cpp index 534dc95d1..e6b571e57 100644 --- a/Cesium3DTilesSelection/src/CmptToGltfConverter.cpp +++ b/Cesium3DTilesContent/src/CmptToGltfConverter.cpp @@ -1,6 +1,5 @@ -#include "CmptToGltfConverter.h" - -#include +#include +#include #include diff --git a/Cesium3DTilesSelection/src/GltfConverters.cpp b/Cesium3DTilesContent/src/GltfConverters.cpp similarity index 98% rename from Cesium3DTilesSelection/src/GltfConverters.cpp rename to Cesium3DTilesContent/src/GltfConverters.cpp index 2680da4ac..67713120d 100644 --- a/Cesium3DTilesSelection/src/GltfConverters.cpp +++ b/Cesium3DTilesContent/src/GltfConverters.cpp @@ -1,4 +1,4 @@ -#include +#include #include diff --git a/Cesium3DTilesContent/src/GltfUtilities.cpp b/Cesium3DTilesContent/src/GltfUtilities.cpp new file mode 100644 index 000000000..d21616897 --- /dev/null +++ b/Cesium3DTilesContent/src/GltfUtilities.cpp @@ -0,0 +1,153 @@ +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace Cesium3DTilesSelection { +/*static*/ glm::dmat4x4 GltfUtilities::applyRtcCenter( + const CesiumGltf::Model& gltf, + const glm::dmat4x4& rootTransform) { + const CesiumGltf::ExtensionCesiumRTC* cesiumRTC = + gltf.getExtension(); + if (cesiumRTC == nullptr) { + return rootTransform; + } + const std::vector& rtcCenter = cesiumRTC->center; + if (rtcCenter.size() != 3) { + return rootTransform; + } + const double x = rtcCenter[0]; + const double y = rtcCenter[1]; + const double z = rtcCenter[2]; + const glm::dmat4x4 rtcTransform( + glm::dvec4(1.0, 0.0, 0.0, 0.0), + glm::dvec4(0.0, 1.0, 0.0, 0.0), + glm::dvec4(0.0, 0.0, 1.0, 0.0), + glm::dvec4(x, y, z, 1.0)); + return rootTransform * rtcTransform; +} + +/*static*/ glm::dmat4x4 GltfUtilities::applyGltfUpAxisTransform( + const CesiumGltf::Model& model, + const glm::dmat4x4& rootTransform) { + auto gltfUpAxisIt = model.extras.find("gltfUpAxis"); + if (gltfUpAxisIt == model.extras.end()) { + // The default up-axis of glTF is the Y-axis, and no other + // up-axis was specified. Transform the Y-axis to the Z-axis, + // to match the 3D Tiles specification + return rootTransform * CesiumGeometry::Transforms::Y_UP_TO_Z_UP; + } + const CesiumUtility::JsonValue& gltfUpAxis = gltfUpAxisIt->second; + int gltfUpAxisValue = static_cast(gltfUpAxis.getSafeNumberOrDefault(1)); + if (gltfUpAxisValue == static_cast(CesiumGeometry::Axis::X)) { + return rootTransform * CesiumGeometry::Transforms::X_UP_TO_Z_UP; + } else if (gltfUpAxisValue == static_cast(CesiumGeometry::Axis::Y)) { + return rootTransform * CesiumGeometry::Transforms::Y_UP_TO_Z_UP; + } else if (gltfUpAxisValue == static_cast(CesiumGeometry::Axis::Z)) { + // No transform required + } + return rootTransform; +} + +/*static*/ CesiumGeospatial::BoundingRegion +GltfUtilities::computeBoundingRegion( + const CesiumGltf::Model& gltf, + const glm::dmat4& transform) { + glm::dmat4 rootTransform = transform; + rootTransform = applyRtcCenter(gltf, rootTransform); + rootTransform = applyGltfUpAxisTransform(gltf, rootTransform); + + // When computing the tile's bounds, ignore tiles that are less than 1/1000th + // of a tile width from the North or South pole. Longitudes cannot be trusted + // at such extreme latitudes. + CesiumGeospatial::BoundingRegionBuilder computedBounds; + + gltf.forEachPrimitiveInScene( + -1, + [&rootTransform, &computedBounds]( + const CesiumGltf::Model& gltf_, + const CesiumGltf::Node& /*node*/, + const CesiumGltf::Mesh& /*mesh*/, + const CesiumGltf::MeshPrimitive& primitive, + const glm::dmat4& nodeTransform) { + auto positionIt = primitive.attributes.find("POSITION"); + if (positionIt == primitive.attributes.end()) { + return; + } + + const int positionAccessorIndex = positionIt->second; + if (positionAccessorIndex < 0 || + positionAccessorIndex >= static_cast(gltf_.accessors.size())) { + return; + } + + const glm::dmat4 fullTransform = rootTransform * nodeTransform; + + const CesiumGltf::AccessorView positionView( + gltf_, + positionAccessorIndex); + if (positionView.status() != CesiumGltf::AccessorViewStatus::Valid) { + return; + } + + std::optional skirtMeshMetadata = + SkirtMeshMetadata::parseFromGltfExtras(primitive.extras); + int64_t vertexBegin, vertexEnd; + if (skirtMeshMetadata.has_value()) { + vertexBegin = skirtMeshMetadata->noSkirtVerticesBegin; + vertexEnd = skirtMeshMetadata->noSkirtVerticesBegin + + skirtMeshMetadata->noSkirtVerticesCount; + } else { + vertexBegin = 0; + vertexEnd = positionView.size(); + } + + for (int64_t i = vertexBegin; i < vertexEnd; ++i) { + // Get the ECEF position + const glm::vec3 position = positionView[i]; + const glm::dvec3 positionEcef = + glm::dvec3(fullTransform * glm::dvec4(position, 1.0)); + + // Convert it to cartographic + std::optional cartographic = + CesiumGeospatial::Ellipsoid::WGS84.cartesianToCartographic( + positionEcef); + if (!cartographic) { + continue; + } + + computedBounds.expandToIncludePosition(*cartographic); + } + }); + + return computedBounds.toRegion(); +} + +std::vector +GltfUtilities::parseGltfCopyright(const CesiumGltf::Model& gltf) { + std::vector result; + if (gltf.asset.copyright) { + const std::string_view copyright = *gltf.asset.copyright; + if (copyright.size() > 0) { + size_t start = 0; + size_t end; + size_t ltrim; + size_t rtrim; + do { + ltrim = copyright.find_first_not_of(" \t", start); + end = copyright.find(';', ltrim); + rtrim = copyright.find_last_not_of(" \t", end - 1); + result.emplace_back(copyright.substr(ltrim, rtrim - ltrim + 1)); + start = end + 1; + } while (end != std::string::npos); + } + } + + return result; +} +} // namespace Cesium3DTilesSelection diff --git a/Cesium3DTilesSelection/src/QuantizedMeshLoader.cpp b/Cesium3DTilesContent/src/QuantizedMeshLoader.cpp similarity index 97% rename from Cesium3DTilesSelection/src/QuantizedMeshLoader.cpp rename to Cesium3DTilesContent/src/QuantizedMeshLoader.cpp index 326acbc11..059038f33 100644 --- a/Cesium3DTilesSelection/src/QuantizedMeshLoader.cpp +++ b/Cesium3DTilesContent/src/QuantizedMeshLoader.cpp @@ -1,14 +1,9 @@ -#include "QuantizedMeshLoader.h" - -#include "Cesium3DTilesSelection/Tile.h" -#include "Cesium3DTilesSelection/Tileset.h" -#include "Cesium3DTilesSelection/spdlog-cesium.h" -#include "SkirtMeshMetadata.h" -#include "calcQuadtreeMaxGeometricError.h" - +#include +#include #include #include #include +#include #include #include #include @@ -19,6 +14,7 @@ #include #include +#include #include #include @@ -662,16 +658,14 @@ static std::vector generateNormals( } /*static*/ QuantizedMeshLoadResult QuantizedMeshLoader::load( - const TileID& tileID, - const BoundingVolume& tileBoundingVolume, + const QuadtreeTileID& tileID, + const BoundingRegion& tileBoundingVolume, const std::string& url, const gsl::span& data, bool enableWaterMask) { CESIUM_TRACE("Cesium3DTilesSelection::QuantizedMeshLoader::load"); - const QuadtreeTileID& id = std::get(tileID); - QuantizedMeshLoadResult result; std::optional meshView = @@ -681,23 +675,6 @@ static std::vector generateNormals( return result; } - const BoundingRegion* pRegion = - std::get_if(&tileBoundingVolume); - if (!pRegion) { - const BoundingRegionWithLooseFittingHeights* pLooseRegion = - std::get_if(&tileBoundingVolume); - if (pLooseRegion) { - pRegion = &pLooseRegion->getBoundingRegion(); - } - } - - if (!pRegion) { - result.errors.emplaceError( - "Unable to create quantized-mesh-1.0 tile because the tile's bounding " - "volume is not a bounding region."); - return result; - } - // get vertex count for this mesh const QuantizedMeshHeader* pHeader = meshView->header; const uint32_t vertexCount = pHeader->vertexCount; @@ -735,7 +712,8 @@ static std::vector generateNormals( double maxZ = std::numeric_limits::lowest(); const Ellipsoid& ellipsoid = Ellipsoid::WGS84; - const CesiumGeospatial::GlobeRectangle& rectangle = pRegion->getRectangle(); + const CesiumGeospatial::GlobeRectangle& rectangle = + tileBoundingVolume.getRectangle(); const double west = rectangle.getWest(); const double south = rectangle.getSouth(); const double east = rectangle.getEast(); @@ -797,7 +775,7 @@ static std::vector generateNormals( // decode metadata if (meshView->metadataJsonLength > 0) { QuantizedMeshMetadataResult metadata = - processMetadata(id, meshView->metadataJsonBuffer); + processMetadata(tileID, meshView->metadataJsonBuffer); result.availableTileRectangles = std::move(metadata.availability); result.errors.merge(std::move(metadata.errors)); } diff --git a/Cesium3DTilesSelection/src/SkirtMeshMetadata.cpp b/Cesium3DTilesContent/src/SkirtMeshMetadata.cpp similarity index 98% rename from Cesium3DTilesSelection/src/SkirtMeshMetadata.cpp rename to Cesium3DTilesContent/src/SkirtMeshMetadata.cpp index 29a7d6028..76a1332ed 100644 --- a/Cesium3DTilesSelection/src/SkirtMeshMetadata.cpp +++ b/Cesium3DTilesContent/src/SkirtMeshMetadata.cpp @@ -1,5 +1,4 @@ -#include "SkirtMeshMetadata.h" - +#include #include #include diff --git a/Cesium3DTilesSelection/src/SubtreeAvailability.cpp b/Cesium3DTilesContent/src/SubtreeAvailability.cpp similarity index 99% rename from Cesium3DTilesSelection/src/SubtreeAvailability.cpp rename to Cesium3DTilesContent/src/SubtreeAvailability.cpp index 515f59e6e..12ae841a9 100644 --- a/Cesium3DTilesSelection/src/SubtreeAvailability.cpp +++ b/Cesium3DTilesContent/src/SubtreeAvailability.cpp @@ -1,5 +1,4 @@ -#include "SubtreeAvailability.h" - +#include #include #include diff --git a/Cesium3DTilesSelection/src/registerAllTileContentTypes.cpp b/Cesium3DTilesContent/src/registerAllTileContentTypes.cpp similarity index 81% rename from Cesium3DTilesSelection/src/registerAllTileContentTypes.cpp rename to Cesium3DTilesContent/src/registerAllTileContentTypes.cpp index cf7e741dc..ead2c7b75 100644 --- a/Cesium3DTilesSelection/src/registerAllTileContentTypes.cpp +++ b/Cesium3DTilesContent/src/registerAllTileContentTypes.cpp @@ -1,10 +1,9 @@ -#include "CmptToGltfConverter.h" - #include #include +#include +#include #include -#include -#include +#include namespace Cesium3DTilesSelection { diff --git a/Cesium3DTilesSelection/src/upsampleGltfForRasterOverlays.cpp b/Cesium3DTilesContent/src/upsampleGltfForRasterOverlays.cpp similarity index 99% rename from Cesium3DTilesSelection/src/upsampleGltfForRasterOverlays.cpp rename to Cesium3DTilesContent/src/upsampleGltfForRasterOverlays.cpp index e198912e9..2dc969d07 100644 --- a/Cesium3DTilesSelection/src/upsampleGltfForRasterOverlays.cpp +++ b/Cesium3DTilesContent/src/upsampleGltfForRasterOverlays.cpp @@ -1,7 +1,5 @@ -#include "upsampleGltfForRasterOverlays.h" - -#include "SkirtMeshMetadata.h" - +#include +#include #include #include #include diff --git a/Cesium3DTilesSelection/test/ConvertTileToGltf.h b/Cesium3DTilesContent/test/ConvertTileToGltf.h similarity index 93% rename from Cesium3DTilesSelection/test/ConvertTileToGltf.h rename to Cesium3DTilesContent/test/ConvertTileToGltf.h index 76665a184..eabb7237e 100644 --- a/Cesium3DTilesSelection/test/ConvertTileToGltf.h +++ b/Cesium3DTilesContent/test/ConvertTileToGltf.h @@ -1,9 +1,8 @@ #pragma once -#include "readFile.h" - #include #include +#include #include diff --git a/Cesium3DTilesSelection/test/TestPntsToGltfConverter.cpp b/Cesium3DTilesContent/test/TestPntsToGltfConverter.cpp similarity index 100% rename from Cesium3DTilesSelection/test/TestPntsToGltfConverter.cpp rename to Cesium3DTilesContent/test/TestPntsToGltfConverter.cpp diff --git a/Cesium3DTilesSelection/test/TestQuantizedMeshContent.cpp b/Cesium3DTilesContent/test/TestQuantizedMeshContent.cpp similarity index 99% rename from Cesium3DTilesSelection/test/TestQuantizedMeshContent.cpp rename to Cesium3DTilesContent/test/TestQuantizedMeshContent.cpp index 7ecfbf384..0204c70bf 100644 --- a/Cesium3DTilesSelection/test/TestQuantizedMeshContent.cpp +++ b/Cesium3DTilesContent/test/TestQuantizedMeshContent.cpp @@ -1,7 +1,5 @@ -#include "Cesium3DTilesSelection/registerAllTileContentTypes.h" -#include "Cesium3DTilesSelection/spdlog-cesium.h" -#include "QuantizedMeshLoader.h" - +#include +#include #include #include #include diff --git a/Cesium3DTilesSelection/test/TestSkirtMeshMetadata.cpp b/Cesium3DTilesContent/test/TestSkirtMeshMetadata.cpp similarity index 100% rename from Cesium3DTilesSelection/test/TestSkirtMeshMetadata.cpp rename to Cesium3DTilesContent/test/TestSkirtMeshMetadata.cpp diff --git a/Cesium3DTilesSelection/test/TestSubtreeAvailability.cpp b/Cesium3DTilesContent/test/TestSubtreeAvailability.cpp similarity index 100% rename from Cesium3DTilesSelection/test/TestSubtreeAvailability.cpp rename to Cesium3DTilesContent/test/TestSubtreeAvailability.cpp diff --git a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/CreditSystem.h b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/CreditSystem.h index 90bef6897..50ea31dd9 100644 --- a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/CreditSystem.h +++ b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/CreditSystem.h @@ -3,6 +3,7 @@ #include "Library.h" #include +#include #include #include #include @@ -44,7 +45,7 @@ class CESIUM3DTILESSELECTION_API CreditSystem final { * @return If this string already exists, returns a Credit handle to the * existing entry. Otherwise returns a Credit handle to a new entry. */ - Credit createCredit(const std::string& html, bool showOnScreen = false); + Credit createCredit(const std::string_view& html, bool showOnScreen = false); /** * @brief Gets whether or not the credit should be shown on screen. diff --git a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/GltfUtilities.h b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/GltfUtilities.h deleted file mode 100644 index 73e7a98ed..000000000 --- a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/GltfUtilities.h +++ /dev/null @@ -1,139 +0,0 @@ -#pragma once - -#include "Library.h" - -#include -#include -#include -#include -#include - -#include - -#include - -namespace Cesium3DTilesSelection { -/** - * A collection of utility functions that are used to process and transform a - * gltf model - */ -struct CESIUM3DTILESSELECTION_API GltfUtilities { - /** - * @brief Applies the glTF's RTC_CENTER, if any, to the given transform. - * - * If the glTF has a `CESIUM_RTC` extension, this function will multiply the - * given matrix with the (translation) matrix that is created from the - * `RTC_CENTER` in the. If the given model does not have this extension, then - * this function will return the `rootTransform` unchanged. - * - * @param model The glTF model - * @param rootTransform The matrix that will be multiplied with the transform - * @return The result of multiplying the `RTC_CENTER` with the - * `rootTransform`. - */ - static glm::dmat4x4 applyRtcCenter( - const CesiumGltf::Model& gltf, - const glm::dmat4x4& rootTransform); - - /** - * @brief Applies the glTF's `gltfUpAxis`, if any, to the given transform. - * - * By default, the up-axis of a glTF model will the the Y-axis. - * - * If the tileset that contained the model had the `asset.gltfUpAxis` string - * property, then the information about the up-axis has been stored in as a - * number property called `gltfUpAxis` in the `extras` of the given model. - * - * Depending on whether this value is `CesiumGeometry::Axis::X`, `Y`, or `Z`, - * the given matrix will be multiplied with a matrix that converts the - * respective axis to be the Z-axis, as required by the 3D Tiles standard. - * - * @param model The glTF model - * @param rootTransform The matrix that will be multiplied with the transform - * @return The result of multiplying the `rootTransform` with the - * `gltfUpAxis`. - */ - static glm::dmat4x4 applyGltfUpAxisTransform( - const CesiumGltf::Model& model, - const glm::dmat4x4& rootTransform); - - /** - * @brief Creates texture coordinates for mapping {@link RasterOverlay} tiles - * to {@link Tileset} tiles. - * - * Generates new texture coordinates for the `gltf` using the given - * `projections`. The first new texture coordinate (`u` or `s`) will be 0.0 at - * the `minimumX` of the given `rectangle` and 1.0 at the `maximumX`. The - * second texture coordinate (`v` or `t`) will be 0.0 at the `minimumY` of - * the given `rectangle` and 1.0 at the `maximumY`. - * - * Coordinate values for vertices in between these extremes are determined by - * projecting the vertex position with the `projection` and then computing the - * fractional distance of that projected position between the minimum and - * maximum. - * - * Projected positions that fall outside the `globeRectangle` will be clamped - * to the edges, so the coordinate values will never be less then 0.0 or - * greater than 1.0. - * - * These texture coordinates are stored in the provided glTF, and a new - * primitive attribute named `_CESIUMOVERLAY_n` is added to each primitive, - * where `n` starts with the `firstTextureCoordinateID` passed to this - * function and increases with each projection. - * - * @param gltf The glTF model. - * @param modelToEcefTransform The transformation of this glTF to ECEF - * coordinates. - * @param firstTextureCoordinateID The texture coordinate ID of the first - * projection. - * @param globeRectangle The rectangle that all projected vertex positions are - * expected to lie within. If this parameter is std::nullopt, it is computed - * from the vertices. - * @param projections The projections for which to generate texture - * coordinates. There is a linear relationship between the coordinates of this - * projection and the generated texture coordinates. - * @return The details of the generated texture coordinates. - */ - static std::optional - createRasterOverlayTextureCoordinates( - CesiumGltf::Model& gltf, - const glm::dmat4& modelToEcefTransform, - int32_t firstTextureCoordinateID, - const std::optional& globeRectangle, - std::vector&& projections); - - /** - * @brief Computes a bounding region from the vertex positions in a glTF - * model. - * - * If the glTF model spans the anti-meridian, the west and east longitude - * values will be in the usual -PI to PI range, but east will have a smaller - * value than west. - * - * If the glTF contains no geometry, the returned region's rectangle - * will be {@link GlobeRectangle::EMPTY}, its minimum height will be 1.0, and - * its maximum height will be -1.0 (the minimum will be greater than the - * maximum). - * - * @param gltf The model. - * @param transform The transform from model coordinates to ECEF coordinates. - * @return The computed bounding region. - */ - static CesiumGeospatial::BoundingRegion computeBoundingRegion( - const CesiumGltf::Model& gltf, - const glm::dmat4& transform); - - /** - * @brief Parse the copyright field of a gltf model and create credits for it. - * - * @param creditSystems The {@link Cesium3DTilesSelection::CreditSystem} that is used to credits for the gltf's copyright - * @param gltf The model. - * @return showOnScreen The option to indicate if the created credits should - * be shown on the screen. - */ - static std::vector parseGltfCopyright( - CreditSystem& creditSystems, - const CesiumGltf::Model& gltf, - bool showOnScreen); -}; -} // namespace Cesium3DTilesSelection diff --git a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/RasterOverlayUtilities.h b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/RasterOverlayUtilities.h new file mode 100644 index 000000000..9b2aac33d --- /dev/null +++ b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/RasterOverlayUtilities.h @@ -0,0 +1,66 @@ +#pragma once + +#include "Library.h" +#include "RasterOverlayDetails.h" + +#include + +#include + +#include +#include + +namespace CesiumGltf { +struct Model; +} + +namespace Cesium3DTilesSelection { + +struct CESIUM3DTILESSELECTION_API RasterOverlayUtilities { + /** + * @brief Creates texture coordinates for mapping {@link RasterOverlay} tiles + * to {@link Tileset} tiles. + * + * Generates new texture coordinates for the `gltf` using the given + * `projections`. The first new texture coordinate (`u` or `s`) will be 0.0 at + * the `minimumX` of the given `rectangle` and 1.0 at the `maximumX`. The + * second texture coordinate (`v` or `t`) will be 0.0 at the `minimumY` of + * the given `rectangle` and 1.0 at the `maximumY`. + * + * Coordinate values for vertices in between these extremes are determined by + * projecting the vertex position with the `projection` and then computing the + * fractional distance of that projected position between the minimum and + * maximum. + * + * Projected positions that fall outside the `globeRectangle` will be clamped + * to the edges, so the coordinate values will never be less then 0.0 or + * greater than 1.0. + * + * These texture coordinates are stored in the provided glTF, and a new + * primitive attribute named `_CESIUMOVERLAY_n` is added to each primitive, + * where `n` starts with the `firstTextureCoordinateID` passed to this + * function and increases with each projection. + * + * @param gltf The glTF model. + * @param modelToEcefTransform The transformation of this glTF to ECEF + * coordinates. + * @param firstTextureCoordinateID The texture coordinate ID of the first + * projection. + * @param globeRectangle The rectangle that all projected vertex positions are + * expected to lie within. If this parameter is std::nullopt, it is computed + * from the vertices. + * @param projections The projections for which to generate texture + * coordinates. There is a linear relationship between the coordinates of this + * projection and the generated texture coordinates. + * @return The details of the generated texture coordinates. + */ + static std::optional + createRasterOverlayTextureCoordinates( + CesiumGltf::Model& gltf, + const glm::dmat4& modelToEcefTransform, + int32_t firstTextureCoordinateID, + const std::optional& globeRectangle, + std::vector&& projections); +}; + +} // namespace Cesium3DTilesSelection diff --git a/Cesium3DTilesSelection/src/CreditSystem.cpp b/Cesium3DTilesSelection/src/CreditSystem.cpp index d2af7d75c..336f74434 100644 --- a/Cesium3DTilesSelection/src/CreditSystem.cpp +++ b/Cesium3DTilesSelection/src/CreditSystem.cpp @@ -4,7 +4,8 @@ namespace Cesium3DTilesSelection { -Credit CreditSystem::createCredit(const std::string& html, bool showOnScreen) { +Credit +CreditSystem::createCredit(const std::string_view& html, bool showOnScreen) { // if this credit already exists, return a Credit handle to it for (size_t id = 0; id < _credits.size(); ++id) { if (_credits[id].html == html) { @@ -14,7 +15,7 @@ Credit CreditSystem::createCredit(const std::string& html, bool showOnScreen) { } } - _credits.push_back({html, showOnScreen, -1, 0}); + _credits.push_back({std::string(html), showOnScreen, -1, 0}); return Credit(_credits.size() - 1); } diff --git a/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp b/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp index 85b6b6862..17f33858c 100644 --- a/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp +++ b/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp @@ -2,7 +2,7 @@ #include "logTileLoadResult.h" -#include +#include #include #include #include diff --git a/Cesium3DTilesSelection/src/ImplicitOctreeLoader.h b/Cesium3DTilesSelection/src/ImplicitOctreeLoader.h index ca2bd35d5..df8d97cf9 100644 --- a/Cesium3DTilesSelection/src/ImplicitOctreeLoader.h +++ b/Cesium3DTilesSelection/src/ImplicitOctreeLoader.h @@ -1,7 +1,6 @@ #pragma once -#include "SubtreeAvailability.h" - +#include #include #include #include diff --git a/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp b/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp index 8b174e52e..212abb94c 100644 --- a/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp +++ b/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp @@ -2,7 +2,7 @@ #include "logTileLoadResult.h" -#include +#include #include #include #include diff --git a/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.h b/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.h index bc81f3339..330390e83 100644 --- a/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.h +++ b/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.h @@ -1,7 +1,6 @@ #pragma once -#include "SubtreeAvailability.h" - +#include #include #include #include diff --git a/Cesium3DTilesSelection/src/LayerJsonTerrainLoader.cpp b/Cesium3DTilesSelection/src/LayerJsonTerrainLoader.cpp index 46b0aa3c8..fb6c3f175 100644 --- a/Cesium3DTilesSelection/src/LayerJsonTerrainLoader.cpp +++ b/Cesium3DTilesSelection/src/LayerJsonTerrainLoader.cpp @@ -1,11 +1,11 @@ #include "LayerJsonTerrainLoader.h" -#include "QuantizedMeshLoader.h" -#include "calcQuadtreeMaxGeometricError.h" -#include "upsampleGltfForRasterOverlays.h" - -#include +#include +#include +#include +#include #include +#include #include #include @@ -210,7 +210,7 @@ void generateRasterOverlayUVs( std::get_if(&result.contentKind); if (pModel) { result.rasterOverlayDetails = - GltfUtilities::createRasterOverlayTextureCoordinates( + RasterOverlayUtilities::createRasterOverlayTextureCoordinates( *pModel, tileTransform, 0, @@ -636,14 +636,14 @@ Future requestTileContent( const AsyncSystem& asyncSystem, const std::shared_ptr& pAssetAccessor, const QuadtreeTileID& tileID, - const BoundingVolume& boundingVolume, + const BoundingRegion& boundingRegion, const LayerJsonTerrainLoader::Layer& layer, const std::vector& requestHeaders, bool enableWaterMask) { std::string url = resolveTileUrl(tileID, layer); return pAssetAccessor->get(asyncSystem, url, requestHeaders) .thenInWorkerThread( - [asyncSystem, pLogger, tileID, boundingVolume, enableWaterMask]( + [asyncSystem, pLogger, tileID, boundingRegion, enableWaterMask]( std::shared_ptr&& pRequest) { const IAssetResponse* pResponse = pRequest->response(); if (!pResponse) { @@ -669,7 +669,7 @@ Future requestTileContent( return QuantizedMeshLoader::load( tileID, - boundingVolume, + boundingRegion, pRequest->url(), pResponse->data(), enableWaterMask); @@ -780,6 +780,14 @@ LayerJsonTerrainLoader::loadTileContent(const TileLoadInput& loadInput) { ++it; } + const BoundingRegion* pRegion = + getBoundingRegionFromBoundingVolume(tile.getBoundingVolume()); + if (!pRegion) { + // This tile does not have the required bounding volume type. + return asyncSystem.createResolvedFuture( + TileLoadResult::createFailedResult(nullptr)); + } + // Start the actual content request. auto& currentLayer = *firstAvailableIt; Future futureQuantizedMesh = requestTileContent( @@ -787,7 +795,7 @@ LayerJsonTerrainLoader::loadTileContent(const TileLoadInput& loadInput) { asyncSystem, pAssetAccessor, *pQuadtreeTileID, - tile.getBoundingVolume(), + *pRegion, currentLayer, requestHeaders, contentOptions.enableWaterMask); diff --git a/Cesium3DTilesSelection/src/RasterOverlayUpsampler.cpp b/Cesium3DTilesSelection/src/RasterOverlayUpsampler.cpp index ea3d1f0ef..419ed22b9 100644 --- a/Cesium3DTilesSelection/src/RasterOverlayUpsampler.cpp +++ b/Cesium3DTilesSelection/src/RasterOverlayUpsampler.cpp @@ -1,7 +1,6 @@ #include "RasterOverlayUpsampler.h" -#include "upsampleGltfForRasterOverlays.h" - +#include #include #include #include diff --git a/Cesium3DTilesSelection/src/GltfUtilities.cpp b/Cesium3DTilesSelection/src/RasterOverlayUtilities.cpp similarity index 65% rename from Cesium3DTilesSelection/src/GltfUtilities.cpp rename to Cesium3DTilesSelection/src/RasterOverlayUtilities.cpp index 3ceacdd0b..028a1b769 100644 --- a/Cesium3DTilesSelection/src/GltfUtilities.cpp +++ b/Cesium3DTilesSelection/src/RasterOverlayUtilities.cpp @@ -1,63 +1,16 @@ -#include "SkirtMeshMetadata.h" - -#include -#include -#include +#include +#include +#include #include -#include #include -#include +#include -#include +using namespace Cesium3DTilesContent; namespace Cesium3DTilesSelection { -/*static*/ glm::dmat4x4 GltfUtilities::applyRtcCenter( - const CesiumGltf::Model& gltf, - const glm::dmat4x4& rootTransform) { - const CesiumGltf::ExtensionCesiumRTC* cesiumRTC = - gltf.getExtension(); - if (cesiumRTC == nullptr) { - return rootTransform; - } - const std::vector& rtcCenter = cesiumRTC->center; - if (rtcCenter.size() != 3) { - return rootTransform; - } - const double x = rtcCenter[0]; - const double y = rtcCenter[1]; - const double z = rtcCenter[2]; - const glm::dmat4x4 rtcTransform( - glm::dvec4(1.0, 0.0, 0.0, 0.0), - glm::dvec4(0.0, 1.0, 0.0, 0.0), - glm::dvec4(0.0, 0.0, 1.0, 0.0), - glm::dvec4(x, y, z, 1.0)); - return rootTransform * rtcTransform; -} - -/*static*/ glm::dmat4x4 GltfUtilities::applyGltfUpAxisTransform( - const CesiumGltf::Model& model, - const glm::dmat4x4& rootTransform) { - auto gltfUpAxisIt = model.extras.find("gltfUpAxis"); - if (gltfUpAxisIt == model.extras.end()) { - // The default up-axis of glTF is the Y-axis, and no other - // up-axis was specified. Transform the Y-axis to the Z-axis, - // to match the 3D Tiles specification - return rootTransform * CesiumGeometry::Transforms::Y_UP_TO_Z_UP; - } - const CesiumUtility::JsonValue& gltfUpAxis = gltfUpAxisIt->second; - int gltfUpAxisValue = static_cast(gltfUpAxis.getSafeNumberOrDefault(1)); - if (gltfUpAxisValue == static_cast(CesiumGeometry::Axis::X)) { - return rootTransform * CesiumGeometry::Transforms::X_UP_TO_Z_UP; - } else if (gltfUpAxisValue == static_cast(CesiumGeometry::Axis::Y)) { - return rootTransform * CesiumGeometry::Transforms::Y_UP_TO_Z_UP; - } else if (gltfUpAxisValue == static_cast(CesiumGeometry::Axis::Z)) { - // No transform required - } - return rootTransform; -} /*static*/ std::optional -GltfUtilities::createRasterOverlayTextureCoordinates( +RasterOverlayUtilities::createRasterOverlayTextureCoordinates( CesiumGltf::Model& model, const glm::dmat4& modelToEcefTransform, int32_t firstTextureCoordinateID, @@ -71,7 +24,8 @@ GltfUtilities::createRasterOverlayTextureCoordinates( CesiumGeospatial::GlobeRectangle bounds = globeRectangle ? *globeRectangle - : computeBoundingRegion(model, modelToEcefTransform).getRectangle(); + : GltfUtilities::computeBoundingRegion(model, modelToEcefTransform) + .getRectangle(); // Currently, a Longitude/Latitude Rectangle maps perfectly to all possible // projection types, because the only possible projection types are @@ -84,8 +38,8 @@ GltfUtilities::createRasterOverlayTextureCoordinates( } glm::dmat4 rootTransform = modelToEcefTransform; - rootTransform = applyRtcCenter(model, rootTransform); - rootTransform = applyGltfUpAxisTransform(model, rootTransform); + rootTransform = GltfUtilities::applyRtcCenter(model, rootTransform); + rootTransform = GltfUtilities::applyGltfUpAxisTransform(model, rootTransform); std::vector positionAccessorsToTextureCoordinateAccessor; positionAccessorsToTextureCoordinateAccessor.resize( @@ -311,104 +265,4 @@ GltfUtilities::createRasterOverlayTextureCoordinates( computedBounds.toRegion()}; } -/*static*/ CesiumGeospatial::BoundingRegion -GltfUtilities::computeBoundingRegion( - const CesiumGltf::Model& gltf, - const glm::dmat4& transform) { - glm::dmat4 rootTransform = transform; - rootTransform = applyRtcCenter(gltf, rootTransform); - rootTransform = applyGltfUpAxisTransform(gltf, rootTransform); - - // When computing the tile's bounds, ignore tiles that are less than 1/1000th - // of a tile width from the North or South pole. Longitudes cannot be trusted - // at such extreme latitudes. - CesiumGeospatial::BoundingRegionBuilder computedBounds; - - gltf.forEachPrimitiveInScene( - -1, - [&rootTransform, &computedBounds]( - const CesiumGltf::Model& gltf_, - const CesiumGltf::Node& /*node*/, - const CesiumGltf::Mesh& /*mesh*/, - const CesiumGltf::MeshPrimitive& primitive, - const glm::dmat4& nodeTransform) { - auto positionIt = primitive.attributes.find("POSITION"); - if (positionIt == primitive.attributes.end()) { - return; - } - - const int positionAccessorIndex = positionIt->second; - if (positionAccessorIndex < 0 || - positionAccessorIndex >= static_cast(gltf_.accessors.size())) { - return; - } - - const glm::dmat4 fullTransform = rootTransform * nodeTransform; - - const CesiumGltf::AccessorView positionView( - gltf_, - positionAccessorIndex); - if (positionView.status() != CesiumGltf::AccessorViewStatus::Valid) { - return; - } - - std::optional skirtMeshMetadata = - SkirtMeshMetadata::parseFromGltfExtras(primitive.extras); - int64_t vertexBegin, vertexEnd; - if (skirtMeshMetadata.has_value()) { - vertexBegin = skirtMeshMetadata->noSkirtVerticesBegin; - vertexEnd = skirtMeshMetadata->noSkirtVerticesBegin + - skirtMeshMetadata->noSkirtVerticesCount; - } else { - vertexBegin = 0; - vertexEnd = positionView.size(); - } - - for (int64_t i = vertexBegin; i < vertexEnd; ++i) { - // Get the ECEF position - const glm::vec3 position = positionView[i]; - const glm::dvec3 positionEcef = - glm::dvec3(fullTransform * glm::dvec4(position, 1.0)); - - // Convert it to cartographic - std::optional cartographic = - CesiumGeospatial::Ellipsoid::WGS84.cartesianToCartographic( - positionEcef); - if (!cartographic) { - continue; - } - - computedBounds.expandToIncludePosition(*cartographic); - } - }); - - return computedBounds.toRegion(); -} - -std::vector GltfUtilities::parseGltfCopyright( - CreditSystem& creditSystem, - const CesiumGltf::Model& gltf, - bool showOnScreen) { - std::vector result; - if (gltf.asset.copyright) { - const std::string& copyright = *gltf.asset.copyright; - if (copyright.size() > 0) { - size_t start = 0; - size_t end; - size_t ltrim; - size_t rtrim; - do { - ltrim = copyright.find_first_not_of(" \t", start); - end = copyright.find(';', ltrim); - rtrim = copyright.find_last_not_of(" \t", end - 1); - result.push_back(creditSystem.createCredit( - copyright.substr(ltrim, rtrim - ltrim + 1), - showOnScreen)); - start = end + 1; - } while (end != std::string::npos); - } - } - - return result; -} } // namespace Cesium3DTilesSelection diff --git a/Cesium3DTilesSelection/src/TilesetContentManager.cpp b/Cesium3DTilesSelection/src/TilesetContentManager.cpp index 2d18a6278..f81a103a6 100644 --- a/Cesium3DTilesSelection/src/TilesetContentManager.cpp +++ b/Cesium3DTilesSelection/src/TilesetContentManager.cpp @@ -5,11 +5,12 @@ #include "TileContentLoadInfo.h" #include "TilesetJsonLoader.h" -#include +#include #include #include #include #include +#include #include #include #include @@ -373,12 +374,13 @@ void calcRasterOverlayDetailsInWorkerThread( // generate the overlay details from the rest of projections and merge it with // the existing one - auto overlayDetails = GltfUtilities::createRasterOverlayTextureCoordinates( - model, - tileLoadInfo.tileTransform, - firstRasterOverlayTexCoord, - pRegion ? std::make_optional(pRegion->getRectangle()) : std::nullopt, - std::move(projections)); + auto overlayDetails = + RasterOverlayUtilities::createRasterOverlayTextureCoordinates( + model, + tileLoadInfo.tileTransform, + firstRasterOverlayTexCoord, + pRegion ? std::make_optional(pRegion->getRectangle()) : std::nullopt, + std::move(projections)); if (pRegion && overlayDetails) { // If the original bounding region was wrong, report it. @@ -1169,6 +1171,8 @@ bool TilesetContentManager::tileNeedsMainThreadLoading( tile.isRenderContent(); } +namespace {} + void TilesetContentManager::finishLoading( Tile& tile, const TilesetOptions& tilesetOptions) { @@ -1181,10 +1185,22 @@ void TilesetContentManager::finishLoading( assert(pRenderContent != nullptr); // add copyright - pRenderContent->setCredits(GltfUtilities::parseGltfCopyright( - *this->_externals.pCreditSystem, - pRenderContent->getModel(), - tilesetOptions.showCreditsOnScreen)); + CreditSystem* pCreditSystem = this->_externals.pCreditSystem.get(); + if (pCreditSystem) { + std::vector creditStrings = + GltfUtilities::parseGltfCopyright(pRenderContent->getModel()); + + std::vector credits; + credits.reserve(creditStrings.size()); + + for (const std::string_view& creditString : creditStrings) { + credits.emplace_back(pCreditSystem->createCredit( + creditString, + tilesetOptions.showCreditsOnScreen)); + } + + pRenderContent->setCredits(credits); + } void* pWorkerRenderResources = pRenderContent->getRenderResources(); void* pMainThreadRenderResources = diff --git a/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp b/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp index 11e4b1d4a..2382f105d 100644 --- a/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp +++ b/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp @@ -4,10 +4,10 @@ #include "ImplicitQuadtreeLoader.h" #include "logTileLoadResult.h" +#include #include #include #include -#include #include #include #include diff --git a/Cesium3DTilesSelection/test/SimplePrepareRendererResource.h b/Cesium3DTilesSelection/test/SimplePrepareRendererResource.h index 82d028771..59417ce53 100644 --- a/Cesium3DTilesSelection/test/SimplePrepareRendererResource.h +++ b/Cesium3DTilesSelection/test/SimplePrepareRendererResource.h @@ -4,6 +4,8 @@ #include "Cesium3DTilesSelection/RasterOverlayTile.h" #include "Cesium3DTilesSelection/Tile.h" +#include + #include namespace Cesium3DTilesSelection { diff --git a/Cesium3DTilesSelection/test/TestImplicitOctreeLoader.cpp b/Cesium3DTilesSelection/test/TestImplicitOctreeLoader.cpp index 99975f5f2..84c7acb3e 100644 --- a/Cesium3DTilesSelection/test/TestImplicitOctreeLoader.cpp +++ b/Cesium3DTilesSelection/test/TestImplicitOctreeLoader.cpp @@ -1,15 +1,15 @@ #include "ImplicitOctreeLoader.h" -#include "SimpleAssetAccessor.h" -#include "SimpleAssetRequest.h" -#include "SimpleAssetResponse.h" #include "SimpleTaskProcessor.h" -#include "readFile.h" +#include #include -#include #include #include #include +#include +#include +#include +#include #include #include diff --git a/Cesium3DTilesSelection/test/TestImplicitQuadtreeLoader.cpp b/Cesium3DTilesSelection/test/TestImplicitQuadtreeLoader.cpp index 58d936ea4..f7725e5cc 100644 --- a/Cesium3DTilesSelection/test/TestImplicitQuadtreeLoader.cpp +++ b/Cesium3DTilesSelection/test/TestImplicitQuadtreeLoader.cpp @@ -1,15 +1,15 @@ #include "ImplicitQuadtreeLoader.h" -#include "SimpleAssetAccessor.h" -#include "SimpleAssetRequest.h" -#include "SimpleAssetResponse.h" #include "SimpleTaskProcessor.h" -#include "readFile.h" +#include #include -#include #include #include #include +#include +#include +#include +#include #include #include diff --git a/Cesium3DTilesSelection/test/TestLayerJsonTerrainLoader.cpp b/Cesium3DTilesSelection/test/TestLayerJsonTerrainLoader.cpp index 69635a9ca..667879fb6 100644 --- a/Cesium3DTilesSelection/test/TestLayerJsonTerrainLoader.cpp +++ b/Cesium3DTilesSelection/test/TestLayerJsonTerrainLoader.cpp @@ -1,15 +1,15 @@ #include "LayerJsonTerrainLoader.h" #include "MockTilesetContentManager.h" -#include "SimpleAssetAccessor.h" -#include "SimpleAssetRequest.h" -#include "SimpleAssetResponse.h" #include "SimplePrepareRendererResource.h" #include "SimpleTaskProcessor.h" -#include "readFile.h" -#include +#include #include #include +#include +#include +#include +#include #include #include diff --git a/Cesium3DTilesSelection/test/TestQuadtreeRasterOverlayTileProvider.cpp b/Cesium3DTilesSelection/test/TestQuadtreeRasterOverlayTileProvider.cpp index c5366b91d..b57af928c 100644 --- a/Cesium3DTilesSelection/test/TestQuadtreeRasterOverlayTileProvider.cpp +++ b/Cesium3DTilesSelection/test/TestQuadtreeRasterOverlayTileProvider.cpp @@ -1,8 +1,8 @@ #include "Cesium3DTilesSelection/QuadtreeRasterOverlayTileProvider.h" #include "Cesium3DTilesSelection/RasterOverlay.h" -#include "SimpleAssetAccessor.h" #include +#include #include diff --git a/Cesium3DTilesSelection/test/TestTilesetContentManager.cpp b/Cesium3DTilesSelection/test/TestTilesetContentManager.cpp index b563f4046..712cf1537 100644 --- a/Cesium3DTilesSelection/test/TestTilesetContentManager.cpp +++ b/Cesium3DTilesSelection/test/TestTilesetContentManager.cpp @@ -1,20 +1,20 @@ -#include "SimpleAssetAccessor.h" -#include "SimpleAssetRequest.h" -#include "SimpleAssetResponse.h" #include "SimplePrepareRendererResource.h" #include "SimpleTaskProcessor.h" #include "TilesetContentManager.h" -#include "readFile.h" +#include +#include #include -#include #include #include -#include #include #include #include #include +#include +#include +#include +#include #include #include diff --git a/Cesium3DTilesSelection/test/TestTilesetJsonLoader.cpp b/Cesium3DTilesSelection/test/TestTilesetJsonLoader.cpp index 64f370bdf..ab1e7fd7e 100644 --- a/Cesium3DTilesSelection/test/TestTilesetJsonLoader.cpp +++ b/Cesium3DTilesSelection/test/TestTilesetJsonLoader.cpp @@ -1,13 +1,13 @@ #include "ImplicitQuadtreeLoader.h" -#include "SimpleAssetAccessor.h" -#include "SimpleAssetRequest.h" -#include "SimpleAssetResponse.h" #include "SimplePrepareRendererResource.h" #include "SimpleTaskProcessor.h" #include "TilesetJsonLoader.h" -#include "readFile.h" -#include +#include +#include +#include +#include +#include #include diff --git a/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp b/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp index f8784addc..be9b0ef78 100644 --- a/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp +++ b/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp @@ -1,14 +1,14 @@ +#include "Cesium3DTilesContent/registerAllTileContentTypes.h" #include "Cesium3DTilesSelection/Tileset.h" #include "Cesium3DTilesSelection/ViewState.h" -#include "Cesium3DTilesSelection/registerAllTileContentTypes.h" -#include "SimpleAssetAccessor.h" -#include "SimpleAssetRequest.h" -#include "SimpleAssetResponse.h" #include "SimplePrepareRendererResource.h" #include "SimpleTaskProcessor.h" #include #include +#include +#include +#include #include #include diff --git a/Cesium3DTilesSelection/src/calcQuadtreeMaxGeometricError.h b/CesiumGeospatial/include/CesiumGeospatial/calcQuadtreeMaxGeometricError.h similarity index 85% rename from Cesium3DTilesSelection/src/calcQuadtreeMaxGeometricError.h rename to CesiumGeospatial/include/CesiumGeospatial/calcQuadtreeMaxGeometricError.h index 03dad3d74..1134b2678 100644 --- a/Cesium3DTilesSelection/src/calcQuadtreeMaxGeometricError.h +++ b/CesiumGeospatial/include/CesiumGeospatial/calcQuadtreeMaxGeometricError.h @@ -1,9 +1,10 @@ #pragma once +#include "Ellipsoid.h" + #include -#include -namespace Cesium3DTilesSelection { +namespace CesiumGeospatial { /** * @brief Computes the maximum geometric error per radian of a quadtree with * certain assumptions. @@ -20,4 +21,4 @@ namespace Cesium3DTilesSelection { */ double calcQuadtreeMaxGeometricError( const CesiumGeospatial::Ellipsoid& ellipsoid) noexcept; -} // namespace Cesium3DTilesSelection +} // namespace CesiumGeospatial diff --git a/Cesium3DTilesSelection/src/calcQuadtreeMaxGeometricError.cpp b/CesiumGeospatial/src/calcQuadtreeMaxGeometricError.cpp similarity index 66% rename from Cesium3DTilesSelection/src/calcQuadtreeMaxGeometricError.cpp rename to CesiumGeospatial/src/calcQuadtreeMaxGeometricError.cpp index 90c22ec35..6d6a54ccc 100644 --- a/Cesium3DTilesSelection/src/calcQuadtreeMaxGeometricError.cpp +++ b/CesiumGeospatial/src/calcQuadtreeMaxGeometricError.cpp @@ -1,10 +1,10 @@ -#include "calcQuadtreeMaxGeometricError.h" +#include -namespace Cesium3DTilesSelection { +namespace CesiumGeospatial { double calcQuadtreeMaxGeometricError( const CesiumGeospatial::Ellipsoid& ellipsoid) noexcept { static const double mapQuality = 0.25; static const uint32_t mapWidth = 65; return ellipsoid.getMaximumRadius() * mapQuality / mapWidth; } -} // namespace Cesium3DTilesSelection +} // namespace CesiumGeospatial diff --git a/CesiumNativeTests/CMakeLists.txt b/CesiumNativeTests/CMakeLists.txt index 89604034c..f2c5bba27 100644 --- a/CesiumNativeTests/CMakeLists.txt +++ b/CesiumNativeTests/CMakeLists.txt @@ -5,6 +5,7 @@ configure_cesium_library(cesium-native-tests) # properties. set(cesium_native_targets Cesium3DTiles + Cesium3DTilesContent Cesium3DTilesReader Cesium3DTilesWriter Cesium3DTilesSelection @@ -18,10 +19,14 @@ set(cesium_native_targets CesiumUtility ) -set(test_sources "") -set(test_headers "") -set(test_include_directories "") - +cesium_glob_files(test_sources + ${CMAKE_CURRENT_LIST_DIR}/src/*.cpp +) +cesium_glob_files(test_headers + ${CMAKE_CURRENT_LIST_DIR}/include/CesiumNativeTests/*.h + ${CMAKE_CURRENT_LIST_DIR}/generated/include/CesiumNativeTests/*.h +) +set(test_include_directories ${CMAKE_CURRENT_LIST_DIR}/include) # Iterate through all targets, extracting their private sources and test sources / test headers foreach(target ${cesium_native_targets}) diff --git a/Cesium3DTilesSelection/test/SimpleAssetAccessor.h b/CesiumNativeTests/include/CesiumNativeTests/SimpleAssetAccessor.h similarity index 100% rename from Cesium3DTilesSelection/test/SimpleAssetAccessor.h rename to CesiumNativeTests/include/CesiumNativeTests/SimpleAssetAccessor.h diff --git a/Cesium3DTilesSelection/test/SimpleAssetRequest.h b/CesiumNativeTests/include/CesiumNativeTests/SimpleAssetRequest.h similarity index 95% rename from Cesium3DTilesSelection/test/SimpleAssetRequest.h rename to CesiumNativeTests/include/CesiumNativeTests/SimpleAssetRequest.h index f6755ce00..4599f967c 100644 --- a/Cesium3DTilesSelection/test/SimpleAssetRequest.h +++ b/CesiumNativeTests/include/CesiumNativeTests/SimpleAssetRequest.h @@ -1,8 +1,7 @@ #pragma once -#include "SimpleAssetResponse.h" - #include +#include #include diff --git a/Cesium3DTilesSelection/test/SimpleAssetResponse.h b/CesiumNativeTests/include/CesiumNativeTests/SimpleAssetResponse.h similarity index 100% rename from Cesium3DTilesSelection/test/SimpleAssetResponse.h rename to CesiumNativeTests/include/CesiumNativeTests/SimpleAssetResponse.h diff --git a/Cesium3DTilesSelection/test/readFile.h b/CesiumNativeTests/include/CesiumNativeTests/readFile.h similarity index 100% rename from Cesium3DTilesSelection/test/readFile.h rename to CesiumNativeTests/include/CesiumNativeTests/readFile.h diff --git a/Cesium3DTilesSelection/test/readFile.cpp b/CesiumNativeTests/src/readFile.cpp similarity index 91% rename from Cesium3DTilesSelection/test/readFile.cpp rename to CesiumNativeTests/src/readFile.cpp index 2be1f2a0c..411f298ff 100644 --- a/Cesium3DTilesSelection/test/readFile.cpp +++ b/CesiumNativeTests/src/readFile.cpp @@ -1,4 +1,4 @@ -#include "readFile.h" +#include #include From f3bdf698e27c9d9d6412c597141b9d721114a5bf Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Thu, 2 Nov 2023 22:05:00 +1100 Subject: [PATCH 319/421] Change namespace of Cesium3DTilesContent types. --- .vscode/settings.json | 5 ++-- .../B3dmToGltfConverter.h | 4 ++-- .../BinaryToGltfConverter.h | 4 ++-- .../CmptToGltfConverter.h | 4 ++-- .../GltfConverterResult.h | 4 ++-- .../Cesium3DTilesContent/GltfConverters.h | 4 ++-- .../Cesium3DTilesContent/GltfUtilities.h | 4 ++-- .../PntsToGltfConverter.h | 4 ++-- .../QuantizedMeshLoader.h | 4 ++-- .../Cesium3DTilesContent/SkirtMeshMetadata.h | 4 ++-- .../SubtreeAvailability.h | 4 ++-- .../registerAllTileContentTypes.h | 4 ++-- .../upsampleGltfForRasterOverlays.h | 2 +- .../src/B3dmToGltfConverter.cpp | 4 ++-- .../src/BatchTableHierarchyPropertyValues.cpp | 2 +- .../src/BatchTableHierarchyPropertyValues.h | 4 ++-- .../BatchTableToGltfStructuralMetadata.cpp | 6 ++--- .../src/BatchTableToGltfStructuralMetadata.h | 4 ++-- .../src/BinaryToGltfConverter.cpp | 4 ++-- .../src/CmptToGltfConverter.cpp | 4 ++-- Cesium3DTilesContent/src/GltfConverters.cpp | 4 ++-- Cesium3DTilesContent/src/GltfUtilities.cpp | 4 ++-- .../src/PntsToGltfConverter.cpp | 4 ++-- .../src/QuantizedMeshLoader.cpp | 2 +- .../src/SkirtMeshMetadata.cpp | 4 ++-- .../src/SubtreeAvailability.cpp | 4 ++-- .../src/registerAllTileContentTypes.cpp | 4 ++-- .../src/upsampleGltfForRasterOverlays.cpp | 4 ++-- Cesium3DTilesContent/test/ConvertTileToGltf.h | 4 ++-- .../test/TestPntsToGltfConverter.cpp | 2 +- .../test/TestQuantizedMeshContent.cpp | 2 +- .../test/TestSkirtMeshMetadata.cpp | 5 ++-- .../test/TestSubtreeAvailability.cpp | 10 ++++---- ...gradeBatchTableToExtStructuralMetadata.cpp | 2 +- .../test/TestUpsampleGltfForRasterOverlay.cpp | 7 +++--- .../src/ImplicitOctreeLoader.cpp | 2 ++ .../src/ImplicitOctreeLoader.h | 5 ++-- .../src/ImplicitQuadtreeLoader.cpp | 2 ++ .../src/ImplicitQuadtreeLoader.h | 5 ++-- .../src/LayerJsonTerrainLoader.cpp | 1 + .../src/RasterOverlayUpsampler.cpp | 2 ++ .../src/TilesetContentManager.cpp | 2 ++ .../src/TilesetJsonLoader.cpp | 1 + .../test/TestImplicitOctreeLoader.cpp | 8 ++++--- .../test/TestImplicitQuadtreeLoader.cpp | 8 ++++--- .../test/TestLayerJsonTerrainLoader.cpp | 9 ++++---- .../TestQuadtreeRasterOverlayTileProvider.cpp | 1 + .../test/TestTilesetContentManager.cpp | 9 ++++---- .../test/TestTilesetJsonLoader.cpp | 8 ++++--- .../test/TestTilesetSelectionAlgorithm.cpp | 23 ++++++++++--------- .../CesiumNativeTests/SimpleAssetAccessor.h | 4 ++-- .../CesiumNativeTests/SimpleAssetRequest.h | 4 ++-- .../CesiumNativeTests/SimpleAssetResponse.h | 4 ++-- .../CesiumNativeTests}/SimpleTaskProcessor.h | 4 ++-- 54 files changed, 132 insertions(+), 111 deletions(-) rename {Cesium3DTilesSelection => Cesium3DTilesContent}/test/TestUpsampleGltfForRasterOverlay.cpp (99%) rename {Cesium3DTilesSelection/test => CesiumNativeTests/include/CesiumNativeTests}/SimpleTaskProcessor.h (73%) diff --git a/.vscode/settings.json b/.vscode/settings.json index b9550a3de..f92e06db9 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -114,8 +114,9 @@ "ktxint.h": "c", "texture.h": "c", "gl_format.h": "c", - "complex": "cpp" + "complex": "cpp", + "expected": "cpp" }, "C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools", "cmake.configureOnOpen": true -} \ No newline at end of file +} diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/B3dmToGltfConverter.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/B3dmToGltfConverter.h index c5291b1bc..a13e643c4 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/B3dmToGltfConverter.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/B3dmToGltfConverter.h @@ -9,10 +9,10 @@ #include -namespace Cesium3DTilesSelection { +namespace Cesium3DTilesContent { struct B3dmToGltfConverter { static GltfConverterResult convert( const gsl::span& b3dmBinary, const CesiumGltfReader::GltfReaderOptions& options); }; -} // namespace Cesium3DTilesSelection +} // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/BinaryToGltfConverter.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/BinaryToGltfConverter.h index a3364835f..910f3c305 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/BinaryToGltfConverter.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/BinaryToGltfConverter.h @@ -7,7 +7,7 @@ #include -namespace Cesium3DTilesSelection { +namespace Cesium3DTilesContent { struct BinaryToGltfConverter { public: static GltfConverterResult convert( @@ -17,4 +17,4 @@ struct BinaryToGltfConverter { private: static CesiumGltfReader::GltfReader _gltfReader; }; -} // namespace Cesium3DTilesSelection +} // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/CmptToGltfConverter.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/CmptToGltfConverter.h index b95f3a66f..ad7280a32 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/CmptToGltfConverter.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/CmptToGltfConverter.h @@ -7,10 +7,10 @@ #include -namespace Cesium3DTilesSelection { +namespace Cesium3DTilesContent { struct CmptToGltfConverter { static GltfConverterResult convert( const gsl::span& cmptBinary, const CesiumGltfReader::GltfReaderOptions& options); }; -} // namespace Cesium3DTilesSelection +} // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverterResult.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverterResult.h index 02d433b51..54fd31114 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverterResult.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverterResult.h @@ -7,7 +7,7 @@ #include -namespace Cesium3DTilesSelection { +namespace Cesium3DTilesContent { /** * @brief The result of converting a binary content to gltf model. * @@ -28,4 +28,4 @@ struct CESIUM3DTILESCONTENT_API GltfConverterResult { */ CesiumUtility::ErrorList errors; }; -} // namespace Cesium3DTilesSelection +} // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverters.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverters.h index 3b8f8d611..bc07748d1 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverters.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfConverters.h @@ -11,7 +11,7 @@ #include #include -namespace Cesium3DTilesSelection { +namespace Cesium3DTilesContent { /** * @brief Creates {@link GltfConverterResult} objects from a * a binary content. @@ -166,4 +166,4 @@ class CESIUM3DTILESCONTENT_API GltfConverters { static std::unordered_map _loadersByFileExtension; }; -} // namespace Cesium3DTilesSelection +} // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfUtilities.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfUtilities.h index c8437616d..5bb0c1a63 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfUtilities.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfUtilities.h @@ -14,7 +14,7 @@ namespace CesiumGltf { struct Model; } -namespace Cesium3DTilesSelection { +namespace Cesium3DTilesContent { /** * A collection of utility functions that are used to process and transform a * gltf model @@ -93,4 +93,4 @@ struct CESIUM3DTILESCONTENT_API GltfUtilities { static std::vector parseGltfCopyright(const CesiumGltf::Model& gltf); }; -} // namespace Cesium3DTilesSelection +} // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/PntsToGltfConverter.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/PntsToGltfConverter.h index a30ddf0a8..48593ca00 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/PntsToGltfConverter.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/PntsToGltfConverter.h @@ -9,10 +9,10 @@ #include -namespace Cesium3DTilesSelection { +namespace Cesium3DTilesContent { struct PntsToGltfConverter { static GltfConverterResult convert( const gsl::span& pntsBinary, const CesiumGltfReader::GltfReaderOptions& options); }; -} // namespace Cesium3DTilesSelection +} // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/QuantizedMeshLoader.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/QuantizedMeshLoader.h index 10c380974..5ffe02940 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/QuantizedMeshLoader.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/QuantizedMeshLoader.h @@ -20,7 +20,7 @@ namespace CesiumAsync { class IAssetRequest; } -namespace Cesium3DTilesSelection { +namespace Cesium3DTilesContent { struct QuantizedMeshLoadResult { /** @@ -92,4 +92,4 @@ class CESIUM3DTILESCONTENT_API QuantizedMeshLoader final { uint32_t startingLevel); }; -} // namespace Cesium3DTilesSelection +} // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/SkirtMeshMetadata.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/SkirtMeshMetadata.h index 543d11160..45759a44e 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/SkirtMeshMetadata.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/SkirtMeshMetadata.h @@ -4,7 +4,7 @@ #include -namespace Cesium3DTilesSelection { +namespace Cesium3DTilesContent { struct SkirtMeshMetadata { SkirtMeshMetadata() noexcept : noSkirtIndicesBegin{0}, @@ -33,4 +33,4 @@ struct SkirtMeshMetadata { double skirtEastHeight; double skirtNorthHeight; }; -} // namespace Cesium3DTilesSelection +} // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/SubtreeAvailability.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/SubtreeAvailability.h index 51b2a75d9..29812234a 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/SubtreeAvailability.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/SubtreeAvailability.h @@ -5,7 +5,7 @@ #include -namespace Cesium3DTilesSelection { +namespace Cesium3DTilesContent { struct SubtreeConstantAvailability { bool constant; }; @@ -63,4 +63,4 @@ class SubtreeAvailability { std::vector _contentAvailability; std::vector> _buffers; }; -} // namespace Cesium3DTilesSelection +} // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/registerAllTileContentTypes.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/registerAllTileContentTypes.h index bd00bc181..1f4a1fcee 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/registerAllTileContentTypes.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/registerAllTileContentTypes.h @@ -1,6 +1,6 @@ #include "Library.h" -namespace Cesium3DTilesSelection { +namespace Cesium3DTilesContent { /** * @brief Register all {@link Tile} content types that can be loaded. @@ -11,4 +11,4 @@ namespace Cesium3DTilesSelection { */ CESIUM3DTILESCONTENT_API void registerAllTileContentTypes(); -} // namespace Cesium3DTilesSelection +} // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/upsampleGltfForRasterOverlays.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/upsampleGltfForRasterOverlays.h index b347b1788..52e4bdea2 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/upsampleGltfForRasterOverlays.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/upsampleGltfForRasterOverlays.h @@ -3,7 +3,7 @@ #include #include -namespace Cesium3DTilesSelection { +namespace Cesium3DTilesContent { std::optional upsampleGltfForRasterOverlays( const CesiumGltf::Model& parentModel, diff --git a/Cesium3DTilesContent/src/B3dmToGltfConverter.cpp b/Cesium3DTilesContent/src/B3dmToGltfConverter.cpp index bd9b9f433..629679633 100644 --- a/Cesium3DTilesContent/src/B3dmToGltfConverter.cpp +++ b/Cesium3DTilesContent/src/B3dmToGltfConverter.cpp @@ -7,7 +7,7 @@ #include #include -namespace Cesium3DTilesSelection { +namespace Cesium3DTilesContent { namespace { struct B3dmHeader { unsigned char magic[4]; @@ -253,4 +253,4 @@ GltfConverterResult B3dmToGltfConverter::convert( return result; } -} // namespace Cesium3DTilesSelection +} // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/src/BatchTableHierarchyPropertyValues.cpp b/Cesium3DTilesContent/src/BatchTableHierarchyPropertyValues.cpp index e504760a3..de06d88c0 100644 --- a/Cesium3DTilesContent/src/BatchTableHierarchyPropertyValues.cpp +++ b/Cesium3DTilesContent/src/BatchTableHierarchyPropertyValues.cpp @@ -2,7 +2,7 @@ #include -using namespace Cesium3DTilesSelection::CesiumImpl; +using namespace Cesium3DTilesContent::CesiumImpl; namespace { diff --git a/Cesium3DTilesContent/src/BatchTableHierarchyPropertyValues.h b/Cesium3DTilesContent/src/BatchTableHierarchyPropertyValues.h index 8e124434f..a5347d429 100644 --- a/Cesium3DTilesContent/src/BatchTableHierarchyPropertyValues.h +++ b/Cesium3DTilesContent/src/BatchTableHierarchyPropertyValues.h @@ -5,7 +5,7 @@ #include #include -namespace Cesium3DTilesSelection { +namespace Cesium3DTilesContent { namespace CesiumImpl { /** @@ -104,4 +104,4 @@ class BatchTableHierarchyPropertyValues { }; } // namespace CesiumImpl -} // namespace Cesium3DTilesSelection +} // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/src/BatchTableToGltfStructuralMetadata.cpp b/Cesium3DTilesContent/src/BatchTableToGltfStructuralMetadata.cpp index 754fc7444..245a2cfe6 100644 --- a/Cesium3DTilesContent/src/BatchTableToGltfStructuralMetadata.cpp +++ b/Cesium3DTilesContent/src/BatchTableToGltfStructuralMetadata.cpp @@ -18,10 +18,10 @@ #include using namespace CesiumGltf; -using namespace Cesium3DTilesSelection::CesiumImpl; +using namespace Cesium3DTilesContent::CesiumImpl; using namespace CesiumUtility; -namespace Cesium3DTilesSelection { +namespace Cesium3DTilesContent { namespace { /** * Indicates how a JSON value can be interpreted as a primitive type. Does not @@ -1932,4 +1932,4 @@ ErrorList BatchTableToGltfStructuralMetadata::convertFromPnts( return result; } -} // namespace Cesium3DTilesSelection +} // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/src/BatchTableToGltfStructuralMetadata.h b/Cesium3DTilesContent/src/BatchTableToGltfStructuralMetadata.h index ff51e822b..d52281e7b 100644 --- a/Cesium3DTilesContent/src/BatchTableToGltfStructuralMetadata.h +++ b/Cesium3DTilesContent/src/BatchTableToGltfStructuralMetadata.h @@ -8,7 +8,7 @@ #include -namespace Cesium3DTilesSelection { +namespace Cesium3DTilesContent { struct BatchTableToGltfStructuralMetadata { static CesiumUtility::ErrorList convertFromB3dm( const rapidjson::Document& featureTableJson, @@ -22,4 +22,4 @@ struct BatchTableToGltfStructuralMetadata { const gsl::span& batchTableBinaryData, CesiumGltf::Model& gltf); }; -} // namespace Cesium3DTilesSelection +} // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/src/BinaryToGltfConverter.cpp b/Cesium3DTilesContent/src/BinaryToGltfConverter.cpp index 64c0bab91..d6420c89b 100644 --- a/Cesium3DTilesContent/src/BinaryToGltfConverter.cpp +++ b/Cesium3DTilesContent/src/BinaryToGltfConverter.cpp @@ -1,6 +1,6 @@ #include -namespace Cesium3DTilesSelection { +namespace Cesium3DTilesContent { CesiumGltfReader::GltfReader BinaryToGltfConverter::_gltfReader; GltfConverterResult BinaryToGltfConverter::convert( @@ -15,4 +15,4 @@ GltfConverterResult BinaryToGltfConverter::convert( result.errors.warnings = std::move(loadedGltf.warnings); return result; } -} // namespace Cesium3DTilesSelection +} // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/src/CmptToGltfConverter.cpp b/Cesium3DTilesContent/src/CmptToGltfConverter.cpp index e6b571e57..5676e42e4 100644 --- a/Cesium3DTilesContent/src/CmptToGltfConverter.cpp +++ b/Cesium3DTilesContent/src/CmptToGltfConverter.cpp @@ -3,7 +3,7 @@ #include -namespace Cesium3DTilesSelection { +namespace Cesium3DTilesContent { namespace { struct CmptHeader { char magic[4]; @@ -110,4 +110,4 @@ GltfConverterResult CmptToGltfConverter::convert( return result; } -} // namespace Cesium3DTilesSelection +} // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/src/GltfConverters.cpp b/Cesium3DTilesContent/src/GltfConverters.cpp index 67713120d..bd0b9b9cb 100644 --- a/Cesium3DTilesContent/src/GltfConverters.cpp +++ b/Cesium3DTilesContent/src/GltfConverters.cpp @@ -4,7 +4,7 @@ using namespace CesiumUtility; -namespace Cesium3DTilesSelection { +namespace Cesium3DTilesContent { std::unordered_map GltfConverters::_loadersByMagic; @@ -128,4 +128,4 @@ GltfConverters::ConverterFunction GltfConverters::getConverterByMagic( return nullptr; } -} // namespace Cesium3DTilesSelection +} // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/src/GltfUtilities.cpp b/Cesium3DTilesContent/src/GltfUtilities.cpp index d21616897..2d19cffbb 100644 --- a/Cesium3DTilesContent/src/GltfUtilities.cpp +++ b/Cesium3DTilesContent/src/GltfUtilities.cpp @@ -8,7 +8,7 @@ #include -namespace Cesium3DTilesSelection { +namespace Cesium3DTilesContent { /*static*/ glm::dmat4x4 GltfUtilities::applyRtcCenter( const CesiumGltf::Model& gltf, const glm::dmat4x4& rootTransform) { @@ -150,4 +150,4 @@ GltfUtilities::parseGltfCopyright(const CesiumGltf::Model& gltf) { return result; } -} // namespace Cesium3DTilesSelection +} // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/src/PntsToGltfConverter.cpp b/Cesium3DTilesContent/src/PntsToGltfConverter.cpp index 8525e3951..3b2e50868 100644 --- a/Cesium3DTilesContent/src/PntsToGltfConverter.cpp +++ b/Cesium3DTilesContent/src/PntsToGltfConverter.cpp @@ -29,7 +29,7 @@ using namespace CesiumGltf; using namespace CesiumUtility; -namespace Cesium3DTilesSelection { +namespace Cesium3DTilesContent { namespace { struct PntsHeader { unsigned char magic[4]; @@ -1631,4 +1631,4 @@ GltfConverterResult PntsToGltfConverter::convert( convertPntsContentToGltf(pntsBinary, header, headerLength, result); return result; } -} // namespace Cesium3DTilesSelection +} // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/src/QuantizedMeshLoader.cpp b/Cesium3DTilesContent/src/QuantizedMeshLoader.cpp index 059038f33..0258ab120 100644 --- a/Cesium3DTilesContent/src/QuantizedMeshLoader.cpp +++ b/Cesium3DTilesContent/src/QuantizedMeshLoader.cpp @@ -18,7 +18,7 @@ #include #include -using namespace Cesium3DTilesSelection; +using namespace Cesium3DTilesContent; using namespace CesiumUtility; using namespace CesiumGeospatial; using namespace CesiumGeometry; diff --git a/Cesium3DTilesContent/src/SkirtMeshMetadata.cpp b/Cesium3DTilesContent/src/SkirtMeshMetadata.cpp index 76a1332ed..5d5985e76 100644 --- a/Cesium3DTilesContent/src/SkirtMeshMetadata.cpp +++ b/Cesium3DTilesContent/src/SkirtMeshMetadata.cpp @@ -6,7 +6,7 @@ using namespace CesiumUtility; -namespace Cesium3DTilesSelection { +namespace Cesium3DTilesContent { std::optional SkirtMeshMetadata::parseFromGltfExtras(const JsonValue::Object& extras) { auto skirtIt = extras.find("skirtMeshMetadata"); @@ -112,4 +112,4 @@ JsonValue::Object SkirtMeshMetadata::createGltfExtras( {"skirtEastHeight", skirtMeshMetadata.skirtEastHeight}, {"skirtNorthHeight", skirtMeshMetadata.skirtNorthHeight}}}}; } -} // namespace Cesium3DTilesSelection +} // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/src/SubtreeAvailability.cpp b/Cesium3DTilesContent/src/SubtreeAvailability.cpp index 12ae841a9..6a89bfb2a 100644 --- a/Cesium3DTilesContent/src/SubtreeAvailability.cpp +++ b/Cesium3DTilesContent/src/SubtreeAvailability.cpp @@ -8,7 +8,7 @@ #include #include -namespace Cesium3DTilesSelection { +namespace Cesium3DTilesContent { namespace { constexpr const char* const SUBTREE_MAGIC = "subt"; @@ -582,4 +582,4 @@ bool SubtreeAvailability::isAvailableUsingBufferView( return bitValue == 1; } -} // namespace Cesium3DTilesSelection +} // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/src/registerAllTileContentTypes.cpp b/Cesium3DTilesContent/src/registerAllTileContentTypes.cpp index ead2c7b75..11d232f6b 100644 --- a/Cesium3DTilesContent/src/registerAllTileContentTypes.cpp +++ b/Cesium3DTilesContent/src/registerAllTileContentTypes.cpp @@ -5,7 +5,7 @@ #include #include -namespace Cesium3DTilesSelection { +namespace Cesium3DTilesContent { void registerAllTileContentTypes() { GltfConverters::registerMagic("glTF", BinaryToGltfConverter::convert); @@ -19,4 +19,4 @@ void registerAllTileContentTypes() { GltfConverters::registerFileExtension(".glb", BinaryToGltfConverter::convert); } -} // namespace Cesium3DTilesSelection +} // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/src/upsampleGltfForRasterOverlays.cpp b/Cesium3DTilesContent/src/upsampleGltfForRasterOverlays.cpp index 2dc969d07..d1c996e8e 100644 --- a/Cesium3DTilesContent/src/upsampleGltfForRasterOverlays.cpp +++ b/Cesium3DTilesContent/src/upsampleGltfForRasterOverlays.cpp @@ -15,7 +15,7 @@ using namespace CesiumGltf; -namespace Cesium3DTilesSelection { +namespace Cesium3DTilesContent { struct EdgeVertex { uint32_t index; glm::vec2 uv; @@ -1261,4 +1261,4 @@ static void copyMetadataTables(const Model& parentModel, Model& result) { } } -} // namespace Cesium3DTilesSelection +} // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/test/ConvertTileToGltf.h b/Cesium3DTilesContent/test/ConvertTileToGltf.h index eabb7237e..2d343b59f 100644 --- a/Cesium3DTilesContent/test/ConvertTileToGltf.h +++ b/Cesium3DTilesContent/test/ConvertTileToGltf.h @@ -6,7 +6,7 @@ #include -namespace Cesium3DTilesSelection { +namespace Cesium3DTilesContent { class ConvertTileToGltf { public: @@ -18,4 +18,4 @@ class ConvertTileToGltf { return PntsToGltfConverter::convert(readFile(filePath), {}); } }; -} // namespace Cesium3DTilesSelection +} // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/test/TestPntsToGltfConverter.cpp b/Cesium3DTilesContent/test/TestPntsToGltfConverter.cpp index 8cce744a9..05f2646a9 100644 --- a/Cesium3DTilesContent/test/TestPntsToGltfConverter.cpp +++ b/Cesium3DTilesContent/test/TestPntsToGltfConverter.cpp @@ -17,7 +17,7 @@ #include using namespace CesiumGltf; -using namespace Cesium3DTilesSelection; +using namespace Cesium3DTilesContent; using namespace CesiumUtility; template diff --git a/Cesium3DTilesContent/test/TestQuantizedMeshContent.cpp b/Cesium3DTilesContent/test/TestQuantizedMeshContent.cpp index 0204c70bf..921bcb067 100644 --- a/Cesium3DTilesContent/test/TestQuantizedMeshContent.cpp +++ b/Cesium3DTilesContent/test/TestQuantizedMeshContent.cpp @@ -12,7 +12,7 @@ #include -using namespace Cesium3DTilesSelection; +using namespace Cesium3DTilesContent; using namespace CesiumGeometry; using namespace CesiumGeospatial; using namespace CesiumGltf; diff --git a/Cesium3DTilesContent/test/TestSkirtMeshMetadata.cpp b/Cesium3DTilesContent/test/TestSkirtMeshMetadata.cpp index 8a68ecf2b..d85e2c135 100644 --- a/Cesium3DTilesContent/test/TestSkirtMeshMetadata.cpp +++ b/Cesium3DTilesContent/test/TestSkirtMeshMetadata.cpp @@ -1,10 +1,9 @@ -#include "SkirtMeshMetadata.h" - +#include #include #include -using namespace Cesium3DTilesSelection; +using namespace Cesium3DTilesContent; using namespace CesiumUtility; TEST_CASE("Test converting skirt mesh metadata to gltf extras") { diff --git a/Cesium3DTilesContent/test/TestSubtreeAvailability.cpp b/Cesium3DTilesContent/test/TestSubtreeAvailability.cpp index 48cf6a8a2..75923d819 100644 --- a/Cesium3DTilesContent/test/TestSubtreeAvailability.cpp +++ b/Cesium3DTilesContent/test/TestSubtreeAvailability.cpp @@ -1,9 +1,8 @@ -#include "SimpleAssetAccessor.h" -#include "SimpleTaskProcessor.h" -#include "SubtreeAvailability.h" - +#include #include #include +#include +#include #include #include @@ -14,7 +13,8 @@ #include #include -using namespace Cesium3DTilesSelection; +using namespace Cesium3DTilesContent; +using namespace CesiumNativeTests; namespace { struct SubtreeHeader { diff --git a/Cesium3DTilesContent/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp b/Cesium3DTilesContent/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp index 93151e148..4e8018e2e 100644 --- a/Cesium3DTilesContent/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp +++ b/Cesium3DTilesContent/test/TestUpgradeBatchTableToExtStructuralMetadata.cpp @@ -18,7 +18,7 @@ #include using namespace CesiumGltf; -using namespace Cesium3DTilesSelection; +using namespace Cesium3DTilesContent; using namespace CesiumUtility; template diff --git a/Cesium3DTilesSelection/test/TestUpsampleGltfForRasterOverlay.cpp b/Cesium3DTilesContent/test/TestUpsampleGltfForRasterOverlay.cpp similarity index 99% rename from Cesium3DTilesSelection/test/TestUpsampleGltfForRasterOverlay.cpp rename to Cesium3DTilesContent/test/TestUpsampleGltfForRasterOverlay.cpp index baebb6cbb..9a70c1c5b 100644 --- a/Cesium3DTilesSelection/test/TestUpsampleGltfForRasterOverlay.cpp +++ b/Cesium3DTilesContent/test/TestUpsampleGltfForRasterOverlay.cpp @@ -1,6 +1,5 @@ -#include "SkirtMeshMetadata.h" -#include "upsampleGltfForRasterOverlays.h" - +#include +#include #include #include #include @@ -12,7 +11,7 @@ #include #include -using namespace Cesium3DTilesSelection; +using namespace Cesium3DTilesContent; using namespace CesiumUtility; using namespace CesiumGeospatial; using namespace CesiumGltf; diff --git a/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp b/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp index 17f33858c..10e3c3080 100644 --- a/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp +++ b/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp @@ -10,6 +10,8 @@ #include #include +using namespace Cesium3DTilesContent; + namespace Cesium3DTilesSelection { namespace { struct BoundingVolumeSubdivision { diff --git a/Cesium3DTilesSelection/src/ImplicitOctreeLoader.h b/Cesium3DTilesSelection/src/ImplicitOctreeLoader.h index df8d97cf9..bb6a88bc0 100644 --- a/Cesium3DTilesSelection/src/ImplicitOctreeLoader.h +++ b/Cesium3DTilesSelection/src/ImplicitOctreeLoader.h @@ -50,7 +50,7 @@ class ImplicitOctreeLoader : public TilesetContentLoader { void addSubtreeAvailability( const CesiumGeometry::OctreeTileID& subtreeID, - SubtreeAvailability&& subtreeAvailability); + Cesium3DTilesContent::SubtreeAvailability&& subtreeAvailability); private: static std::string resolveUrl( @@ -64,7 +64,8 @@ class ImplicitOctreeLoader : public TilesetContentLoader { uint32_t _subtreeLevels; uint32_t _availableLevels; ImplicitOctreeBoundingVolume _boundingVolume; - std::vector> + std::vector< + std::unordered_map> _loadedSubtrees; }; } // namespace Cesium3DTilesSelection diff --git a/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp b/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp index 212abb94c..557596e00 100644 --- a/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp +++ b/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp @@ -14,6 +14,8 @@ #include #include +using namespace Cesium3DTilesContent; + namespace Cesium3DTilesSelection { namespace { struct BoundingVolumeSubdivision { diff --git a/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.h b/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.h index 330390e83..60c962042 100644 --- a/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.h +++ b/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.h @@ -52,7 +52,7 @@ class ImplicitQuadtreeLoader : public TilesetContentLoader { void addSubtreeAvailability( const CesiumGeometry::QuadtreeTileID& subtreeID, - SubtreeAvailability&& subtreeAvailability); + Cesium3DTilesContent::SubtreeAvailability&& subtreeAvailability); private: static std::string resolveUrl( @@ -66,7 +66,8 @@ class ImplicitQuadtreeLoader : public TilesetContentLoader { uint32_t _subtreeLevels; uint32_t _availableLevels; ImplicitQuadtreeBoundingVolume _boundingVolume; - std::vector> + std::vector< + std::unordered_map> _loadedSubtrees; }; } // namespace Cesium3DTilesSelection diff --git a/Cesium3DTilesSelection/src/LayerJsonTerrainLoader.cpp b/Cesium3DTilesSelection/src/LayerJsonTerrainLoader.cpp index fb6c3f175..8f44570c3 100644 --- a/Cesium3DTilesSelection/src/LayerJsonTerrainLoader.cpp +++ b/Cesium3DTilesSelection/src/LayerJsonTerrainLoader.cpp @@ -13,6 +13,7 @@ #include using namespace CesiumAsync; +using namespace Cesium3DTilesContent; using namespace Cesium3DTilesSelection; using namespace CesiumGeometry; using namespace CesiumGeospatial; diff --git a/Cesium3DTilesSelection/src/RasterOverlayUpsampler.cpp b/Cesium3DTilesSelection/src/RasterOverlayUpsampler.cpp index 419ed22b9..5d58929a3 100644 --- a/Cesium3DTilesSelection/src/RasterOverlayUpsampler.cpp +++ b/Cesium3DTilesSelection/src/RasterOverlayUpsampler.cpp @@ -11,6 +11,8 @@ #include #include +using namespace Cesium3DTilesContent; + namespace Cesium3DTilesSelection { CesiumAsync::Future RasterOverlayUpsampler::loadTileContent(const TileLoadInput& loadInput) { diff --git a/Cesium3DTilesSelection/src/TilesetContentManager.cpp b/Cesium3DTilesSelection/src/TilesetContentManager.cpp index f81a103a6..52ab7eb03 100644 --- a/Cesium3DTilesSelection/src/TilesetContentManager.cpp +++ b/Cesium3DTilesSelection/src/TilesetContentManager.cpp @@ -22,6 +22,8 @@ #include +using namespace Cesium3DTilesContent; + namespace Cesium3DTilesSelection { namespace { struct RegionAndCenter { diff --git a/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp b/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp index 2382f105d..d2f6fde2c 100644 --- a/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp +++ b/Cesium3DTilesSelection/src/TilesetJsonLoader.cpp @@ -25,6 +25,7 @@ #include using namespace CesiumUtility; +using namespace Cesium3DTilesContent; namespace Cesium3DTilesSelection { namespace { diff --git a/Cesium3DTilesSelection/test/TestImplicitOctreeLoader.cpp b/Cesium3DTilesSelection/test/TestImplicitOctreeLoader.cpp index 84c7acb3e..c881e8119 100644 --- a/Cesium3DTilesSelection/test/TestImplicitOctreeLoader.cpp +++ b/Cesium3DTilesSelection/test/TestImplicitOctreeLoader.cpp @@ -1,5 +1,4 @@ #include "ImplicitOctreeLoader.h" -#include "SimpleTaskProcessor.h" #include #include @@ -9,6 +8,7 @@ #include #include #include +#include #include #include @@ -16,17 +16,19 @@ #include +using namespace Cesium3DTilesContent; using namespace Cesium3DTilesSelection; using namespace CesiumGeometry; using namespace CesiumGeospatial; using namespace CesiumUtility; +using namespace CesiumNativeTests; namespace { std::filesystem::path testDataPath = Cesium3DTilesSelection_TEST_DATA_DIR; } TEST_CASE("Test implicit octree loader") { - Cesium3DTilesSelection::registerAllTileContentTypes(); + Cesium3DTilesContent::registerAllTileContentTypes(); auto pMockedAssetAccessor = std::make_shared( std::map>{}); @@ -197,7 +199,7 @@ TEST_CASE("Test implicit octree loader") { } TEST_CASE("Test tile subdivision for implicit octree loader") { - Cesium3DTilesSelection::registerAllTileContentTypes(); + Cesium3DTilesContent::registerAllTileContentTypes(); auto pMockedAssetAccessor = std::make_shared( std::map>{}); diff --git a/Cesium3DTilesSelection/test/TestImplicitQuadtreeLoader.cpp b/Cesium3DTilesSelection/test/TestImplicitQuadtreeLoader.cpp index f7725e5cc..00209e323 100644 --- a/Cesium3DTilesSelection/test/TestImplicitQuadtreeLoader.cpp +++ b/Cesium3DTilesSelection/test/TestImplicitQuadtreeLoader.cpp @@ -1,5 +1,4 @@ #include "ImplicitQuadtreeLoader.h" -#include "SimpleTaskProcessor.h" #include #include @@ -9,6 +8,7 @@ #include #include #include +#include #include #include @@ -16,17 +16,19 @@ #include +using namespace Cesium3DTilesContent; using namespace Cesium3DTilesSelection; using namespace CesiumGeometry; using namespace CesiumGeospatial; using namespace CesiumUtility; +using namespace CesiumNativeTests; namespace { std::filesystem::path testDataPath = Cesium3DTilesSelection_TEST_DATA_DIR; } TEST_CASE("Test implicit quadtree loader") { - Cesium3DTilesSelection::registerAllTileContentTypes(); + Cesium3DTilesContent::registerAllTileContentTypes(); auto pMockedAssetAccessor = std::make_shared( std::map>{}); @@ -197,7 +199,7 @@ TEST_CASE("Test implicit quadtree loader") { } TEST_CASE("Test tile subdivision for implicit quadtree loader") { - Cesium3DTilesSelection::registerAllTileContentTypes(); + Cesium3DTilesContent::registerAllTileContentTypes(); auto pMockedAssetAccessor = std::make_shared( std::map>{}); diff --git a/Cesium3DTilesSelection/test/TestLayerJsonTerrainLoader.cpp b/Cesium3DTilesSelection/test/TestLayerJsonTerrainLoader.cpp index 667879fb6..6cd3753a8 100644 --- a/Cesium3DTilesSelection/test/TestLayerJsonTerrainLoader.cpp +++ b/Cesium3DTilesSelection/test/TestLayerJsonTerrainLoader.cpp @@ -1,7 +1,6 @@ #include "LayerJsonTerrainLoader.h" #include "MockTilesetContentManager.h" #include "SimplePrepareRendererResource.h" -#include "SimpleTaskProcessor.h" #include #include @@ -9,6 +8,7 @@ #include #include #include +#include #include #include @@ -21,6 +21,7 @@ using namespace CesiumGeospatial; using namespace CesiumGeometry; using namespace CesiumAsync; using namespace CesiumUtility; +using namespace CesiumNativeTests; namespace { std::filesystem::path testDataPath = Cesium3DTilesSelection_TEST_DATA_DIR; @@ -70,7 +71,7 @@ Future loadTile( } // namespace TEST_CASE("Test create layer json terrain loader") { - Cesium3DTilesSelection::registerAllTileContentTypes(); + Cesium3DTilesContent::registerAllTileContentTypes(); auto pMockedAssetAccessor = std::make_shared( std::map>{}); @@ -393,7 +394,7 @@ TEST_CASE("Test create layer json terrain loader") { } TEST_CASE("Test load layer json tile content") { - Cesium3DTilesSelection::registerAllTileContentTypes(); + Cesium3DTilesContent::registerAllTileContentTypes(); auto pMockedAssetAccessor = std::make_shared( std::map>{}); @@ -683,7 +684,7 @@ TEST_CASE("Test load layer json tile content") { } TEST_CASE("Test creating tile children for layer json") { - Cesium3DTilesSelection::registerAllTileContentTypes(); + Cesium3DTilesContent::registerAllTileContentTypes(); auto pMockedAssetAccessor = std::make_shared( std::map>{}); diff --git a/Cesium3DTilesSelection/test/TestQuadtreeRasterOverlayTileProvider.cpp b/Cesium3DTilesSelection/test/TestQuadtreeRasterOverlayTileProvider.cpp index b57af928c..a04fe2fe2 100644 --- a/Cesium3DTilesSelection/test/TestQuadtreeRasterOverlayTileProvider.cpp +++ b/Cesium3DTilesSelection/test/TestQuadtreeRasterOverlayTileProvider.cpp @@ -12,6 +12,7 @@ using namespace CesiumGeometry; using namespace CesiumGeospatial; using namespace CesiumGltf; using namespace CesiumUtility; +using namespace CesiumNativeTests; namespace { diff --git a/Cesium3DTilesSelection/test/TestTilesetContentManager.cpp b/Cesium3DTilesSelection/test/TestTilesetContentManager.cpp index 712cf1537..af75480df 100644 --- a/Cesium3DTilesSelection/test/TestTilesetContentManager.cpp +++ b/Cesium3DTilesSelection/test/TestTilesetContentManager.cpp @@ -1,5 +1,4 @@ #include "SimplePrepareRendererResource.h" -#include "SimpleTaskProcessor.h" #include "TilesetContentManager.h" #include @@ -14,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -28,6 +28,7 @@ using namespace Cesium3DTilesSelection; using namespace CesiumGeospatial; using namespace CesiumGeometry; using namespace CesiumUtility; +using namespace CesiumNativeTests; namespace { std::filesystem::path testDataPath = Cesium3DTilesSelection_TEST_DATA_DIR; @@ -181,7 +182,7 @@ CesiumGltf::Model createGlobeGrid( } // namespace TEST_CASE("Test the manager can be initialized with correct loaders") { - Cesium3DTilesSelection::registerAllTileContentTypes(); + Cesium3DTilesContent::registerAllTileContentTypes(); // create mock tileset externals auto pMockedAssetAccessor = std::make_shared( @@ -294,7 +295,7 @@ TEST_CASE("Test the manager can be initialized with correct loaders") { } TEST_CASE("Test tile state machine") { - Cesium3DTilesSelection::registerAllTileContentTypes(); + Cesium3DTilesContent::registerAllTileContentTypes(); // create mock tileset externals auto pMockedAssetAccessor = std::make_shared( @@ -708,7 +709,7 @@ TEST_CASE("Test tile state machine") { } TEST_CASE("Test the tileset content manager's post processing for gltf") { - Cesium3DTilesSelection::registerAllTileContentTypes(); + Cesium3DTilesContent::registerAllTileContentTypes(); // create mock tileset externals auto pMockedAssetAccessor = std::make_shared( diff --git a/Cesium3DTilesSelection/test/TestTilesetJsonLoader.cpp b/Cesium3DTilesSelection/test/TestTilesetJsonLoader.cpp index ab1e7fd7e..ee5d273b3 100644 --- a/Cesium3DTilesSelection/test/TestTilesetJsonLoader.cpp +++ b/Cesium3DTilesSelection/test/TestTilesetJsonLoader.cpp @@ -1,12 +1,12 @@ #include "ImplicitQuadtreeLoader.h" #include "SimplePrepareRendererResource.h" -#include "SimpleTaskProcessor.h" #include "TilesetJsonLoader.h" #include #include #include #include +#include #include #include @@ -17,6 +17,8 @@ using namespace CesiumAsync; using namespace Cesium3DTilesSelection; +using namespace CesiumNativeTests; +using namespace CesiumNativeTests; namespace { std::filesystem::path testDataPath = Cesium3DTilesSelection_TEST_DATA_DIR; @@ -109,7 +111,7 @@ TileLoadResult loadTileContent( } // namespace TEST_CASE("Test creating tileset json loader") { - Cesium3DTilesSelection::registerAllTileContentTypes(); + Cesium3DTilesContent::registerAllTileContentTypes(); SECTION("Create valid tileset json with REPLACE refinement") { auto loaderResult = @@ -420,7 +422,7 @@ TEST_CASE("Test creating tileset json loader") { } TEST_CASE("Test loading individual tile of tileset json") { - Cesium3DTilesSelection::registerAllTileContentTypes(); + Cesium3DTilesContent::registerAllTileContentTypes(); SECTION("Load tile that has render content") { auto loaderResult = diff --git a/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp b/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp index be9b0ef78..d3bb3dc62 100644 --- a/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp +++ b/Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp @@ -2,13 +2,13 @@ #include "Cesium3DTilesSelection/Tileset.h" #include "Cesium3DTilesSelection/ViewState.h" #include "SimplePrepareRendererResource.h" -#include "SimpleTaskProcessor.h" #include #include #include #include #include +#include #include #include @@ -23,6 +23,7 @@ using namespace CesiumAsync; using namespace Cesium3DTilesSelection; using namespace CesiumGeospatial; using namespace CesiumUtility; +using namespace CesiumNativeTests; static std::vector readFile(const std::filesystem::path& fileName) { std::ifstream file(fileName, std::ios::binary | std::ios::ate); @@ -117,7 +118,7 @@ static ViewState zoomToTileset(const Tileset& tileset) { } TEST_CASE("Test replace refinement for render") { - Cesium3DTilesSelection::registerAllTileContentTypes(); + Cesium3DTilesContent::registerAllTileContentTypes(); // initialize REPLACE tileset // @@ -511,7 +512,7 @@ TEST_CASE("Test replace refinement for render") { } TEST_CASE("Test additive refinement") { - Cesium3DTilesSelection::registerAllTileContentTypes(); + Cesium3DTilesContent::registerAllTileContentTypes(); std::filesystem::path testDataPath = Cesium3DTilesSelection_TEST_DATA_DIR; testDataPath = testDataPath / "AddTileset"; @@ -668,7 +669,7 @@ TEST_CASE("Test additive refinement") { TEST_CASE("Render any tiles even when one of children can't be rendered for " "additive refinement") { - Cesium3DTilesSelection::registerAllTileContentTypes(); + Cesium3DTilesContent::registerAllTileContentTypes(); std::filesystem::path testDataPath = Cesium3DTilesSelection_TEST_DATA_DIR; testDataPath = testDataPath / "ErrorChildrenAddTileset"; @@ -763,7 +764,7 @@ TEST_CASE("Render any tiles even when one of children can't be rendered for " } TEST_CASE("Test multiple frustums") { - Cesium3DTilesSelection::registerAllTileContentTypes(); + Cesium3DTilesContent::registerAllTileContentTypes(); std::filesystem::path testDataPath = Cesium3DTilesSelection_TEST_DATA_DIR; testDataPath = testDataPath / "ReplaceTileset"; @@ -1093,7 +1094,7 @@ TEST_CASE("Can load example tileset.json from 3DTILES_bounding_volume_S2 " } TEST_CASE("Makes metadata available once root tile is loaded") { - Cesium3DTilesSelection::registerAllTileContentTypes(); + Cesium3DTilesContent::registerAllTileContentTypes(); std::filesystem::path testDataPath = Cesium3DTilesSelection_TEST_DATA_DIR; testDataPath = testDataPath / "WithMetadata"; @@ -1149,7 +1150,7 @@ TEST_CASE("Makes metadata available once root tile is loaded") { } TEST_CASE("Makes metadata available on external tilesets") { - Cesium3DTilesSelection::registerAllTileContentTypes(); + Cesium3DTilesContent::registerAllTileContentTypes(); std::filesystem::path testDataPath = Cesium3DTilesSelection_TEST_DATA_DIR; testDataPath = testDataPath / "WithMetadata"; @@ -1217,7 +1218,7 @@ TEST_CASE("Makes metadata available on external tilesets") { } TEST_CASE("Allows access to material variants") { - Cesium3DTilesSelection::registerAllTileContentTypes(); + Cesium3DTilesContent::registerAllTileContentTypes(); std::filesystem::path testDataPath = Cesium3DTilesSelection_TEST_DATA_DIR; testDataPath = testDataPath / "MaterialVariants"; @@ -1301,7 +1302,7 @@ TEST_CASE("Allows access to material variants") { } TEST_CASE("Allows access to material variants in an external schema") { - Cesium3DTilesSelection::registerAllTileContentTypes(); + Cesium3DTilesContent::registerAllTileContentTypes(); std::filesystem::path testDataPath = Cesium3DTilesSelection_TEST_DATA_DIR; testDataPath = testDataPath / "MaterialVariants"; @@ -1397,7 +1398,7 @@ TEST_CASE("Allows access to material variants in an external schema") { } TEST_CASE("Future from loadSchema rejects if schemaUri can't be loaded") { - Cesium3DTilesSelection::registerAllTileContentTypes(); + Cesium3DTilesContent::registerAllTileContentTypes(); std::filesystem::path testDataPath = Cesium3DTilesSelection_TEST_DATA_DIR; testDataPath = testDataPath / "MaterialVariants"; @@ -1611,7 +1612,7 @@ TEST_CASE("An unconditionally-refined tile is not rendered") { } TEST_CASE("Additive-refined tiles are added to the tilesFadingOut array") { - Cesium3DTilesSelection::registerAllTileContentTypes(); + Cesium3DTilesContent::registerAllTileContentTypes(); std::filesystem::path testDataPath = Cesium3DTilesSelection_TEST_DATA_DIR; testDataPath = testDataPath / "AdditiveThreeLevels"; diff --git a/CesiumNativeTests/include/CesiumNativeTests/SimpleAssetAccessor.h b/CesiumNativeTests/include/CesiumNativeTests/SimpleAssetAccessor.h index 23c13983b..4c0943a50 100644 --- a/CesiumNativeTests/include/CesiumNativeTests/SimpleAssetAccessor.h +++ b/CesiumNativeTests/include/CesiumNativeTests/SimpleAssetAccessor.h @@ -12,7 +12,7 @@ #include #include -namespace Cesium3DTilesSelection { +namespace CesiumNativeTests { class SimpleAssetAccessor : public CesiumAsync::IAssetAccessor { public: SimpleAssetAccessor( @@ -51,4 +51,4 @@ class SimpleAssetAccessor : public CesiumAsync::IAssetAccessor { std::map> mockCompletedRequests; }; -} // namespace Cesium3DTilesSelection +} // namespace CesiumNativeTests diff --git a/CesiumNativeTests/include/CesiumNativeTests/SimpleAssetRequest.h b/CesiumNativeTests/include/CesiumNativeTests/SimpleAssetRequest.h index 4599f967c..3726f0e29 100644 --- a/CesiumNativeTests/include/CesiumNativeTests/SimpleAssetRequest.h +++ b/CesiumNativeTests/include/CesiumNativeTests/SimpleAssetRequest.h @@ -5,7 +5,7 @@ #include -namespace Cesium3DTilesSelection { +namespace CesiumNativeTests { class SimpleAssetRequest : public CesiumAsync::IAssetRequest { public: SimpleAssetRequest( @@ -37,4 +37,4 @@ class SimpleAssetRequest : public CesiumAsync::IAssetRequest { CesiumAsync::HttpHeaders requestHeaders; std::unique_ptr pResponse; }; -} // namespace Cesium3DTilesSelection +} // namespace CesiumNativeTests diff --git a/CesiumNativeTests/include/CesiumNativeTests/SimpleAssetResponse.h b/CesiumNativeTests/include/CesiumNativeTests/SimpleAssetResponse.h index 77de26aa9..8248d9642 100644 --- a/CesiumNativeTests/include/CesiumNativeTests/SimpleAssetResponse.h +++ b/CesiumNativeTests/include/CesiumNativeTests/SimpleAssetResponse.h @@ -5,7 +5,7 @@ #include #include -namespace Cesium3DTilesSelection { +namespace CesiumNativeTests { class SimpleAssetResponse : public CesiumAsync::IAssetResponse { public: SimpleAssetResponse( @@ -37,4 +37,4 @@ class SimpleAssetResponse : public CesiumAsync::IAssetResponse { CesiumAsync::HttpHeaders mockHeaders; std::vector mockData; }; -} // namespace Cesium3DTilesSelection +} // namespace CesiumNativeTests diff --git a/Cesium3DTilesSelection/test/SimpleTaskProcessor.h b/CesiumNativeTests/include/CesiumNativeTests/SimpleTaskProcessor.h similarity index 73% rename from Cesium3DTilesSelection/test/SimpleTaskProcessor.h rename to CesiumNativeTests/include/CesiumNativeTests/SimpleTaskProcessor.h index 028ff219d..a9f169593 100644 --- a/Cesium3DTilesSelection/test/SimpleTaskProcessor.h +++ b/CesiumNativeTests/include/CesiumNativeTests/SimpleTaskProcessor.h @@ -2,9 +2,9 @@ #include -namespace Cesium3DTilesSelection { +namespace CesiumNativeTests { class SimpleTaskProcessor : public CesiumAsync::ITaskProcessor { public: virtual void startTask(std::function f) override { f(); } }; -} // namespace Cesium3DTilesSelection +} // namespace CesiumNativeTests From 58f1b7f703819f341cc56e5403fbacdb09522dab Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Thu, 2 Nov 2023 22:57:21 +1100 Subject: [PATCH 320/421] Clean up CreditSystem changes, update CHANGES.md. --- CHANGES.md | 7 +++++++ .../include/Cesium3DTilesSelection/CreditSystem.h | 11 +++++++++-- Cesium3DTilesSelection/src/CreditSystem.cpp | 9 ++++++--- Cesium3DTilesSelection/src/TilesetContentManager.cpp | 2 +- 4 files changed, 23 insertions(+), 6 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index ab402e045..9245143d7 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,6 +5,13 @@ ##### Breaking Changes :mega: - Moved `ErrorList` from `Cesium3DTilesSelection` to `CesiumUtility`. +- Moved `GltfUtilities` from `Cesium3DTilesSelection` to `Cesium3DTilesContent`. +- Moved `createRasterOverlayTextureCoordinates` method from `GltfUtilities` to a new `RasterOverlayUtilities` class in the `Cesium3DTilesSelection` library. +- `GltfUtilities::parseGltfCopyright` now returns the credits as a vector of string_views. Previously it took a `CreditSystem` and created credits directly. + +##### Additions :tada: + +- Added `Cesium3DTilesContent` library and namespace. It has classes for loading, converting, and manipulating 3D Tiles tile content. ### v0.29.0 - 2023-11-01 diff --git a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/CreditSystem.h b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/CreditSystem.h index 50ea31dd9..41612ca5c 100644 --- a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/CreditSystem.h +++ b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/CreditSystem.h @@ -3,7 +3,6 @@ #include "Library.h" #include -#include #include #include #include @@ -45,7 +44,15 @@ class CESIUM3DTILESSELECTION_API CreditSystem final { * @return If this string already exists, returns a Credit handle to the * existing entry. Otherwise returns a Credit handle to a new entry. */ - Credit createCredit(const std::string_view& html, bool showOnScreen = false); + Credit createCredit(std::string&& html, bool showOnScreen = false); + + /** + * @brief Inserts a credit string + * + * @return If this string already exists, returns a Credit handle to the + * existing entry. Otherwise returns a Credit handle to a new entry. + */ + Credit createCredit(const std::string& html, bool showOnScreen = false); /** * @brief Gets whether or not the credit should be shown on screen. diff --git a/Cesium3DTilesSelection/src/CreditSystem.cpp b/Cesium3DTilesSelection/src/CreditSystem.cpp index 336f74434..2f8537121 100644 --- a/Cesium3DTilesSelection/src/CreditSystem.cpp +++ b/Cesium3DTilesSelection/src/CreditSystem.cpp @@ -4,8 +4,11 @@ namespace Cesium3DTilesSelection { -Credit -CreditSystem::createCredit(const std::string_view& html, bool showOnScreen) { +Credit CreditSystem::createCredit(const std::string& html, bool showOnScreen) { + return this->createCredit(std::string(html), showOnScreen); +} + +Credit CreditSystem::createCredit(std::string&& html, bool showOnScreen) { // if this credit already exists, return a Credit handle to it for (size_t id = 0; id < _credits.size(); ++id) { if (_credits[id].html == html) { @@ -15,7 +18,7 @@ CreditSystem::createCredit(const std::string_view& html, bool showOnScreen) { } } - _credits.push_back({std::string(html), showOnScreen, -1, 0}); + _credits.push_back({std::move(html), showOnScreen, -1, 0}); return Credit(_credits.size() - 1); } diff --git a/Cesium3DTilesSelection/src/TilesetContentManager.cpp b/Cesium3DTilesSelection/src/TilesetContentManager.cpp index 52ab7eb03..8d4e46d7c 100644 --- a/Cesium3DTilesSelection/src/TilesetContentManager.cpp +++ b/Cesium3DTilesSelection/src/TilesetContentManager.cpp @@ -1197,7 +1197,7 @@ void TilesetContentManager::finishLoading( for (const std::string_view& creditString : creditStrings) { credits.emplace_back(pCreditSystem->createCredit( - creditString, + std::string(creditString), tilesetOptions.showCreditsOnScreen)); } From ec02c89a6b2dfec000a408452e222197fa032e87 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Mon, 6 Nov 2023 18:48:23 +1100 Subject: [PATCH 321/421] Add TileBoundingVolumes class, other BV tweaks. --- Cesium3DTilesContent/CMakeLists.txt | 1 + .../TileBoundingVolumes.h | 70 ++++++++++++ .../src/TileBoundingVolumes.cpp | 57 ++++++++++ .../test/TestTileBoundingVolumes.cpp | 103 ++++++++++++++++++ Cesium3DTilesSelection/src/BoundingVolume.cpp | 21 +--- .../include/CesiumGeometry/BoundingSphere.h | 13 +++ .../CesiumGeometry/OrientedBoundingBox.h | 21 ++++ CesiumGeometry/src/BoundingSphere.cpp | 15 +++ CesiumGeometry/src/OrientedBoundingBox.cpp | 24 ++++ CesiumGeometry/test/TestBoundingSphere.cpp | 44 ++++++++ .../test/TestOrientedBoundingBox.cpp | 51 +++++++++ 11 files changed, 402 insertions(+), 18 deletions(-) create mode 100644 Cesium3DTilesContent/include/Cesium3DTilesContent/TileBoundingVolumes.h create mode 100644 Cesium3DTilesContent/src/TileBoundingVolumes.cpp create mode 100644 Cesium3DTilesContent/test/TestTileBoundingVolumes.cpp diff --git a/Cesium3DTilesContent/CMakeLists.txt b/Cesium3DTilesContent/CMakeLists.txt index 8493e1397..649b0c327 100644 --- a/Cesium3DTilesContent/CMakeLists.txt +++ b/Cesium3DTilesContent/CMakeLists.txt @@ -53,6 +53,7 @@ target_include_directories( target_link_libraries(Cesium3DTilesContent PUBLIC + Cesium3DTiles CesiumAsync CesiumGeometry CesiumGeospatial diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/TileBoundingVolumes.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/TileBoundingVolumes.h new file mode 100644 index 000000000..0d76aa83e --- /dev/null +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/TileBoundingVolumes.h @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include + +#include + +namespace Cesium3DTiles { +struct BoundingVolume; +} + +namespace Cesium3DTilesContent { + +/** + * @brief Provides functions for extracting bounding volumes types from the + * vectors stored in {@link Cesium3DTiles::BoundingVolume}. + */ +class TileBoundingVolumes { +public: + /** + * @brief Gets the bounding box defined in a + * {@link Cesium3DTiles::BoundingVolume}, if any. + * + * @param boundingVolume The bounding volume from which to get the box. + * @return The box, or `std::nullopt` if the bounding volume does not + * define a box. The box is defined in the tile's coordinate system. + */ + static std::optional + getOrientedBoundingBox(const Cesium3DTiles::BoundingVolume& boundingVolume); + + /** + * @brief Gets the bounding region defined in a + * {@link Cesium3DTiles::BoundingVolume}, if any. + * + * @param boundingVolume The bounding volume from which to get the region. + * @return The region, or `std::nullopt` if the bounding volume does not + * define a region. The box is defined in geographic coordinates. + */ + static std::optional + getBoundingRegion(const Cesium3DTiles::BoundingVolume& boundingVolume); + + /** + * @brief Gets the bounding sphere defined in a + * {@link Cesium3DTiles::BoundingVolume}, if any. + * + * @param boundingVolume The bounding volume from which to get the sphere. + * @return The sphere, or `std::nullopt` if the bounding volume does not + * define a sphere. The sphere is defined in the tile's coordinate system. + */ + static std::optional + getBoundingSphere(const Cesium3DTiles::BoundingVolume& boundingVolume); + + /** + * @brief Gets the S2 cell bounding volume defined in the + * `3DTILES_bounding_volume_S2` extension of a + * {@link Cesium3DTiles::BoundingVolume}, if any. + * + * @param boundingVolume The bounding volume from which to get the S2 cell + * bounding volume. + * @return The S2 cell bounding volume, or `std::nullopt` if the bounding + * volume does not define an S2 cell bounding volume. The S2 cell bounding + * volume is defined in geographic coordinates. + */ + static std::optional + getS2CellBoundingVolume(const Cesium3DTiles::BoundingVolume& boundingVolume); +}; + +} // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/src/TileBoundingVolumes.cpp b/Cesium3DTilesContent/src/TileBoundingVolumes.cpp new file mode 100644 index 000000000..023e9d1ca --- /dev/null +++ b/Cesium3DTilesContent/src/TileBoundingVolumes.cpp @@ -0,0 +1,57 @@ +#include +#include +#include + +using namespace Cesium3DTiles; +using namespace CesiumGeometry; +using namespace CesiumGeospatial; + +namespace Cesium3DTilesContent { + +std::optional TileBoundingVolumes::getOrientedBoundingBox( + const BoundingVolume& boundingVolume) { + if (boundingVolume.box.size() < 12) + return std::nullopt; + + const std::vector& a = boundingVolume.box; + return CesiumGeometry::OrientedBoundingBox( + glm::dvec3(a[0], a[1], a[2]), + glm::dmat3(a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11])); +} + +std::optional +TileBoundingVolumes::getBoundingRegion(const BoundingVolume& boundingVolume) { + if (boundingVolume.region.size() < 6) + return std::nullopt; + + const std::vector& a = boundingVolume.region; + return CesiumGeospatial::BoundingRegion( + CesiumGeospatial::GlobeRectangle(a[0], a[1], a[2], a[3]), + a[4], + a[5]); +} + +std::optional +TileBoundingVolumes::getBoundingSphere(const BoundingVolume& boundingVolume) { + if (boundingVolume.sphere.size() < 4) + return std::nullopt; + + const std::vector& a = boundingVolume.sphere; + return CesiumGeometry::BoundingSphere(glm::dvec3(a[0], a[1], a[2]), a[3]); +} + +std::optional +TileBoundingVolumes::getS2CellBoundingVolume( + const BoundingVolume& boundingVolume) { + const Extension3dTilesBoundingVolumeS2* pExtension = + boundingVolume.getExtension(); + if (!pExtension) + return std::nullopt; + + return CesiumGeospatial::S2CellBoundingVolume( + CesiumGeospatial::S2CellID::fromToken(pExtension->token), + pExtension->minimumHeight, + pExtension->maximumHeight); +} + +} // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/test/TestTileBoundingVolumes.cpp b/Cesium3DTilesContent/test/TestTileBoundingVolumes.cpp new file mode 100644 index 000000000..ec6f0c168 --- /dev/null +++ b/Cesium3DTilesContent/test/TestTileBoundingVolumes.cpp @@ -0,0 +1,103 @@ +#include +#include +#include + +#include + +using namespace Cesium3DTiles; +using namespace Cesium3DTilesContent; +using namespace CesiumGeometry; +using namespace CesiumGeospatial; + +TEST_CASE("TileBoundingVolumes") { + SECTION("box") { + BoundingVolume bv{}; + + // Example bounding box from the 3D Tiles spec + // clang-format off + bv.box = { + 0.0, 0.0, 10.0, + 100.0, 0.0, 0.0, + 0.0, 100.0, 0.0, + 0.0, 0.0, 10.0}; + // clang-format on + + std::optional box = + TileBoundingVolumes::getOrientedBoundingBox(bv); + REQUIRE(box); + + CHECK(box->getCenter().x == Approx(0.0)); + CHECK(box->getCenter().y == Approx(0.0)); + CHECK(box->getCenter().z == Approx(10.0)); + CHECK(glm::length(box->getHalfAxes()[0]) == Approx(100.0)); + CHECK(glm::length(box->getHalfAxes()[1]) == Approx(100.0)); + CHECK(glm::length(box->getHalfAxes()[2]) == Approx(10.0)); + } + + SECTION("sphere") { + BoundingVolume bv{}; + + // Example bounding sphere from the 3D Tiles spec + bv.sphere = {0.0, 0.0, 10.0, 141.4214}; + + std::optional sphere = + TileBoundingVolumes::getBoundingSphere(bv); + REQUIRE(sphere); + + CHECK(sphere->getCenter().x == Approx(0.0)); + CHECK(sphere->getCenter().y == Approx(0.0)); + CHECK(sphere->getCenter().z == Approx(10.0)); + CHECK(sphere->getRadius() == Approx(141.4214)); + } + + SECTION("region") { + BoundingVolume bv{}; + + // Example bounding region from the 3D Tiles spec + bv.region = { + -1.3197004795898053, + 0.6988582109, + -1.3196595204101946, + 0.6988897891, + 0.0, + 20.0}; + + std::optional region = + TileBoundingVolumes::getBoundingRegion(bv); + REQUIRE(region); + + CHECK(region->getRectangle().getWest() == Approx(-1.3197004795898053)); + CHECK(region->getRectangle().getSouth() == Approx(0.6988582109)); + CHECK(region->getRectangle().getEast() == Approx(-1.3196595204101946)); + CHECK(region->getRectangle().getNorth() == Approx(0.6988897891)); + CHECK(region->getMinimumHeight() == Approx(0.0)); + CHECK(region->getMaximumHeight() == Approx(20.0)); + } + + SECTION("S2") { + BoundingVolume bv{}; + + // Example from 3DTILES_bounding_volume_S2 spec + Extension3dTilesBoundingVolumeS2& extension = + bv.addExtension(); + extension.token = "89c6c7"; + extension.minimumHeight = 0.0; + extension.maximumHeight = 1000.0; + + std::optional s2 = + TileBoundingVolumes::getS2CellBoundingVolume(bv); + REQUIRE(s2); + + CHECK(s2->getCellID().getID() == S2CellID::fromToken("89c6c7").getID()); + CHECK(s2->getMinimumHeight() == Approx(0.0)); + CHECK(s2->getMaximumHeight() == Approx(1000.0)); + } + + SECTION("invalid") { + BoundingVolume bv{}; + CHECK(!TileBoundingVolumes::getOrientedBoundingBox(bv).has_value()); + CHECK(!TileBoundingVolumes::getBoundingSphere(bv).has_value()); + CHECK(!TileBoundingVolumes::getBoundingRegion(bv).has_value()); + CHECK(!TileBoundingVolumes::getS2CellBoundingVolume(bv).has_value()); + } +} diff --git a/Cesium3DTilesSelection/src/BoundingVolume.cpp b/Cesium3DTilesSelection/src/BoundingVolume.cpp index db9084985..790dee8fe 100644 --- a/Cesium3DTilesSelection/src/BoundingVolume.cpp +++ b/Cesium3DTilesSelection/src/BoundingVolume.cpp @@ -17,11 +17,7 @@ BoundingVolume transformBoundingVolume( const glm::dmat4x4& transform; BoundingVolume operator()(const OrientedBoundingBox& boundingBox) { - const glm::dvec3 center = - glm::dvec3(transform * glm::dvec4(boundingBox.getCenter(), 1.0)); - const glm::dmat3 halfAxes = - glm::dmat3(transform) * boundingBox.getHalfAxes(); - return OrientedBoundingBox(center, halfAxes); + return boundingBox.transform(transform); } BoundingVolume operator()(const BoundingRegion& boundingRegion) noexcept { @@ -30,16 +26,7 @@ BoundingVolume transformBoundingVolume( } BoundingVolume operator()(const BoundingSphere& boundingSphere) { - const glm::dvec3 center = - glm::dvec3(transform * glm::dvec4(boundingSphere.getCenter(), 1.0)); - - const double uniformScale = glm::max( - glm::max( - glm::length(glm::dvec3(transform[0])), - glm::length(glm::dvec3(transform[1]))), - glm::length(glm::dvec3(transform[2]))); - - return BoundingSphere(center, boundingSphere.getRadius() * uniformScale); + return boundingSphere.transform(transform); } BoundingVolume operator()( @@ -214,9 +201,7 @@ OrientedBoundingBox getOrientedBoundingBoxFromBoundingVolume(const BoundingVolume& boundingVolume) { struct Operation { OrientedBoundingBox operator()(const BoundingSphere& sphere) const { - glm::dvec3 center = sphere.getCenter(); - glm::dmat3 halfAxes = glm::dmat3(sphere.getRadius()); - return OrientedBoundingBox(center, halfAxes); + return OrientedBoundingBox::fromSphere(sphere); } OrientedBoundingBox diff --git a/CesiumGeometry/include/CesiumGeometry/BoundingSphere.h b/CesiumGeometry/include/CesiumGeometry/BoundingSphere.h index f65e49f35..c910a2057 100644 --- a/CesiumGeometry/include/CesiumGeometry/BoundingSphere.h +++ b/CesiumGeometry/include/CesiumGeometry/BoundingSphere.h @@ -3,6 +3,7 @@ #include "CullingResult.h" #include "Library.h" +#include #include namespace CesiumGeometry { @@ -61,6 +62,18 @@ class CESIUMGEOMETRY_API BoundingSphere final { double computeDistanceSquaredToPosition(const glm::dvec3& position) const noexcept; + /** + * @brief Transforms this bounding sphere to another coordinate system using a + * 4x4 matrix. + * + * If the transformation has non-uniform scale, the bounding sphere's radius + * is scaled by the scale of the transformation's axis with the largest scale. + * + * @param transformation The transformation. + * @return The bounding sphere in the new coordinate system. + */ + BoundingSphere transform(const glm::dmat4& transformation) const noexcept; + private: glm::dvec3 _center; double _radius; diff --git a/CesiumGeometry/include/CesiumGeometry/OrientedBoundingBox.h b/CesiumGeometry/include/CesiumGeometry/OrientedBoundingBox.h index 80b16a7c2..0d50bb88a 100644 --- a/CesiumGeometry/include/CesiumGeometry/OrientedBoundingBox.h +++ b/CesiumGeometry/include/CesiumGeometry/OrientedBoundingBox.h @@ -1,6 +1,7 @@ #pragma once #include "AxisAlignedBox.h" +#include "BoundingSphere.h" #include "CullingResult.h" #include "Library.h" @@ -117,8 +118,28 @@ class CESIUMGEOMETRY_API OrientedBoundingBox final { OrientedBoundingBox transform(const glm::dmat4& transformation) const noexcept; + /** + * @brief Converts this oriented bounding box to an axis-aligned bounding box. + */ AxisAlignedBox toAxisAligned() const noexcept; + /** + * @brief Converts this oriented bounding box to a bounding sphere. + */ + BoundingSphere toSphere() const noexcept; + + /** + * @brief Creates an oriented bounding box from the given axis-aligned + * bounding box. + */ + static OrientedBoundingBox + fromAxisAligned(const AxisAlignedBox& axisAligned) noexcept; + + /** + * @brief Creates an oriented bounding box from the given bounding sphere. + */ + static OrientedBoundingBox fromSphere(const BoundingSphere& sphere) noexcept; + private: glm::dvec3 _center; glm::dmat3 _halfAxes; diff --git a/CesiumGeometry/src/BoundingSphere.cpp b/CesiumGeometry/src/BoundingSphere.cpp index 88ca2839c..2fb3c71f8 100644 --- a/CesiumGeometry/src/BoundingSphere.cpp +++ b/CesiumGeometry/src/BoundingSphere.cpp @@ -3,6 +3,7 @@ #include "CesiumGeometry/Plane.h" #include +#include namespace CesiumGeometry { @@ -34,4 +35,18 @@ double BoundingSphere::computeDistanceSquaredToPosition( return distance * distance; } +BoundingSphere +BoundingSphere::transform(const glm::dmat4& transformation) const noexcept { + const glm::dvec3 center = + glm::dvec3(transformation * glm::dvec4(this->getCenter(), 1.0)); + + const double uniformScale = glm::max( + glm::max( + glm::length(glm::dvec3(transformation[0])), + glm::length(glm::dvec3(transformation[1]))), + glm::length(glm::dvec3(transformation[2]))); + + return BoundingSphere(center, this->getRadius() * uniformScale); +} + } // namespace CesiumGeometry diff --git a/CesiumGeometry/src/OrientedBoundingBox.cpp b/CesiumGeometry/src/OrientedBoundingBox.cpp index c950e2f41..d524b9239 100644 --- a/CesiumGeometry/src/OrientedBoundingBox.cpp +++ b/CesiumGeometry/src/OrientedBoundingBox.cpp @@ -124,4 +124,28 @@ AxisAlignedBox OrientedBoundingBox::toAxisAligned() const noexcept { return AxisAlignedBox(ll.x, ll.y, ll.z, ur.x, ur.y, ur.z); } +BoundingSphere OrientedBoundingBox::toSphere() const noexcept { + const glm::dmat3& halfAxes = this->_halfAxes; + glm::dvec3 corner = halfAxes[0] + halfAxes[1] + halfAxes[2]; + double sphereRadius = glm::length(corner); + return BoundingSphere(this->_center, sphereRadius); +} + +/*static*/ OrientedBoundingBox OrientedBoundingBox::fromAxisAligned( + const AxisAlignedBox& axisAligned) noexcept { + return OrientedBoundingBox( + axisAligned.center, + glm::dmat3( + glm::dvec3(axisAligned.lengthX * 0.5, 0.0, 0.0), + glm::dvec3(0.0, axisAligned.lengthY * 0.5, 0.0), + glm::dvec3(0.0, 0.0, axisAligned.lengthZ * 0.5))); +} + +/*static*/ OrientedBoundingBox +OrientedBoundingBox::fromSphere(const BoundingSphere& sphere) noexcept { + glm::dvec3 center = sphere.getCenter(); + glm::dmat3 halfAxes = glm::dmat3(sphere.getRadius()); + return OrientedBoundingBox(center, halfAxes); +} + } // namespace CesiumGeometry diff --git a/CesiumGeometry/test/TestBoundingSphere.cpp b/CesiumGeometry/test/TestBoundingSphere.cpp index d17867e4c..52ba6e49b 100644 --- a/CesiumGeometry/test/TestBoundingSphere.cpp +++ b/CesiumGeometry/test/TestBoundingSphere.cpp @@ -3,9 +3,12 @@ #include "CesiumUtility/Math.h" #include +#include +#include #include using namespace CesiumGeometry; +using namespace CesiumUtility; TEST_CASE("BoundingSphere::intersectPlane") { struct TestCase { @@ -54,6 +57,47 @@ TEST_CASE( CHECK(bs.computeDistanceSquaredToPosition(position) == 0); } +TEST_CASE("BoundingSphere::transform") { + BoundingSphere sphere(glm::dvec3(1.0, 2.0, 3.0), 45.0); + + SECTION("translating moves the center only") { + glm::dmat4 transformation = glm::translate( + glm::identity(), + glm::dvec3(10.0, 20.0, 30.0)); + BoundingSphere transformed = sphere.transform(transformation); + CHECK(transformed.getRadius() == Approx(sphere.getRadius())); + CHECK(transformed.getCenter().x == Approx(sphere.getCenter().x + 10.0)); + CHECK(transformed.getCenter().y == Approx(sphere.getCenter().y + 20.0)); + CHECK(transformed.getCenter().z == Approx(sphere.getCenter().z + 30.0)); + } + + SECTION("rotating moves the center only") { + double fortyFiveDegrees = Math::OnePi / 4.0; + glm::dmat4 transformation = glm::eulerAngleY(fortyFiveDegrees); + BoundingSphere transformed = sphere.transform(transformation); + CHECK(transformed.getRadius() == Approx(sphere.getRadius())); + + glm::dvec3 rotatedCenter = glm::dmat3(transformation) * sphere.getCenter(); + CHECK(transformed.getCenter().x == Approx(rotatedCenter.x)); + CHECK(transformed.getCenter().y == Approx(rotatedCenter.y)); + CHECK(transformed.getCenter().z == Approx(rotatedCenter.z)); + } + + SECTION( + "scaling moves the center, and scales the radius by the max component") { + glm::dmat4 transformation = + glm::scale(glm::identity(), glm::dvec3(2.0, 3.0, 4.0)); + BoundingSphere transformed = sphere.transform(transformation); + + glm::dvec3 scaledCenter = glm::dmat3(transformation) * sphere.getCenter(); + CHECK(transformed.getCenter().x == Approx(scaledCenter.x)); + CHECK(transformed.getCenter().y == Approx(scaledCenter.y)); + CHECK(transformed.getCenter().z == Approx(scaledCenter.z)); + + CHECK(transformed.getRadius() == Approx(45.0 * 4.0)); + } +} + TEST_CASE("BoundingSphere::computeDistanceSquaredToPosition example") { auto anyOldSphereArray = []() { return std::vector{ diff --git a/CesiumGeometry/test/TestOrientedBoundingBox.cpp b/CesiumGeometry/test/TestOrientedBoundingBox.cpp index 45013b1e0..36863955b 100644 --- a/CesiumGeometry/test/TestOrientedBoundingBox.cpp +++ b/CesiumGeometry/test/TestOrientedBoundingBox.cpp @@ -389,3 +389,54 @@ TEST_CASE("OrientedBoundingBox::toAxisAligned") { CHECK(Math::equalsEpsilon(aabb.maximumZ, 3.0 + glm::sqrt(2.0), 0.0, 1e-14)); } } + +TEST_CASE("OrientedBoundingBox::toSphere") { + SECTION("axis-aligned box with identity half-axes") { + OrientedBoundingBox obb( + glm::dvec3(1.0, 2.0, 3.0), + glm::dmat3( + glm::dvec3(1.0, 0.0, 0.0), + glm::dvec3(0.0, 1.0, 0.0), + glm::dvec3(0.0, 0.0, 1.0))); + + BoundingSphere sphere = obb.toSphere(); + CHECK(sphere.getCenter().x == Approx(1.0)); + CHECK(sphere.getCenter().y == Approx(2.0)); + CHECK(sphere.getCenter().z == Approx(3.0)); + + CHECK(sphere.getRadius() == Approx(glm::sqrt(3.0))); + } + + SECTION("rotating the box does not change the bounding sphere") { + // Rotate the OBB 45 degrees around the Y-axis. + // This shouldn't change the bounding sphere at all. + double fortyFiveDegrees = Math::OnePi / 4.0; + glm::dmat3 rotation = glm::dmat3(glm::eulerAngleY(fortyFiveDegrees)); + OrientedBoundingBox obb(glm::dvec3(1.0, 2.0, 3.0), rotation); + + BoundingSphere sphere = obb.toSphere(); + CHECK(sphere.getCenter().x == Approx(1.0)); + CHECK(sphere.getCenter().y == Approx(2.0)); + CHECK(sphere.getCenter().z == Approx(3.0)); + + CHECK(sphere.getRadius() == Approx(glm::sqrt(3.0))); + } + + SECTION("a scaled axis-aligned box") { + OrientedBoundingBox obb( + glm::dvec3(1.0, 2.0, 3.0), + glm::dmat3( + glm::dvec3(10.0, 0.0, 0.0), + glm::dvec3(0.0, 20.0, 0.0), + glm::dvec3(0.0, 0.0, 30.0))); + + BoundingSphere sphere = obb.toSphere(); + CHECK(sphere.getCenter().x == Approx(1.0)); + CHECK(sphere.getCenter().y == Approx(2.0)); + CHECK(sphere.getCenter().z == Approx(3.0)); + + CHECK( + sphere.getRadius() == + Approx(glm::sqrt(10.0 * 10.0 + 20.0 * 20.0 + 30.0 * 30.0))); + } +} From 5e860d6d10a7421885718b34c2367b01f3aa882a Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Mon, 6 Nov 2023 19:19:12 +1100 Subject: [PATCH 322/421] Fix OrientedBoundingBox::contains. --- CHANGES.md | 7 ++++ CesiumGeometry/src/OrientedBoundingBox.cpp | 4 +- .../test/TestOrientedBoundingBox.cpp | 39 +++++++++++++++++++ 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 9245143d7..bdfa907d6 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -12,6 +12,13 @@ ##### Additions :tada: - Added `Cesium3DTilesContent` library and namespace. It has classes for loading, converting, and manipulating 3D Tiles tile content. +- Added `TileBoundingVolumes` class to `Cesium3DTilesContent`, making it easier to create the rich bounding volume types in `CesiumGeometry` and `CesiumGeospatial` from the simple vector representations in `Cesium3DTiles`. +- Added `transform` method to `CesiumGeometry::BoundingSphere`. +- Added `toSphere`, `fromSphere`, and `fromAxisAligned` methods to `CesiumGeometry::OrientedBoundingBox`. + +##### Fixes :wrench: + +- Fixed a bug in `OrientedBoundingBox::contains` where it didn't account for the bounding box's center. ### v0.29.0 - 2023-11-01 diff --git a/CesiumGeometry/src/OrientedBoundingBox.cpp b/CesiumGeometry/src/OrientedBoundingBox.cpp index d524b9239..6ee147b70 100644 --- a/CesiumGeometry/src/OrientedBoundingBox.cpp +++ b/CesiumGeometry/src/OrientedBoundingBox.cpp @@ -100,9 +100,9 @@ double OrientedBoundingBox::computeDistanceSquaredToPosition( return distanceSquared; } -// TODO: add test for this bool OrientedBoundingBox::contains(const glm::dvec3& position) const noexcept { - glm::dvec3 localPosition = this->_inverseHalfAxes * position; + glm::dvec3 localPosition = position - this->_center; + localPosition = this->_inverseHalfAxes * localPosition; return glm::abs(localPosition.x) <= 1.0 && glm::abs(localPosition.y) <= 1.0 && glm::abs(localPosition.z) <= 1.0; } diff --git a/CesiumGeometry/test/TestOrientedBoundingBox.cpp b/CesiumGeometry/test/TestOrientedBoundingBox.cpp index 36863955b..6b0dc7823 100644 --- a/CesiumGeometry/test/TestOrientedBoundingBox.cpp +++ b/CesiumGeometry/test/TestOrientedBoundingBox.cpp @@ -440,3 +440,42 @@ TEST_CASE("OrientedBoundingBox::toSphere") { Approx(glm::sqrt(10.0 * 10.0 + 20.0 * 20.0 + 30.0 * 30.0))); } } + +TEST_CASE("OrientedBoundingBox::contains") { + SECTION("axis-aligned") { + OrientedBoundingBox obb( + glm::dvec3(10.0, 20.0, 30.0), + glm::dmat3( + glm::dvec3(2.0, 0.0, 0.0), + glm::dvec3(0.0, 3.0, 0.0), + glm::dvec3(0.0, 0.0, 4.0))); + CHECK(!obb.contains(glm::dvec3(0.0, 0.0, 0.0))); + CHECK(obb.contains(glm::dvec3(10.0, 20.0, 30.0))); + CHECK(obb.contains(glm::dvec3(12.0, 23.0, 34.0))); + CHECK(obb.contains(glm::dvec3(8.0, 17.0, 26.0))); + CHECK(!obb.contains(glm::dvec3(13.0, 20.0, 30.0))); + CHECK(!obb.contains(glm::dvec3(10.0, 24.0, 30.0))); + CHECK(!obb.contains(glm::dvec3(10.0, 20.0, 35.0))); + } + + SECTION("rotated") { + // Rotate the OBB 45 degrees around the Y-axis + double fortyFiveDegrees = Math::OnePi / 4.0; + glm::dmat3 halfAngles( + glm::dvec3(2.0, 0.0, 0.0), + glm::dvec3(0.0, 3.0, 0.0), + glm::dvec3(0.0, 0.0, 4.0)); + glm::dmat3 rotation = glm::dmat3(glm::eulerAngleY(fortyFiveDegrees)); + glm::dmat3 transformed = rotation * halfAngles; + glm::dvec3 center(10.0, 20.0, 30.0); + OrientedBoundingBox obb(center, transformed); + + CHECK(!obb.contains(glm::dvec3(0.0, 0.0, 0.0))); + CHECK(obb.contains(center)); + CHECK(obb.contains(center + rotation * glm::dvec3(2.0, 3.0, 4.0))); + CHECK(obb.contains(center + rotation * glm::dvec3(-2.0, -3.0, -4.0))); + CHECK(!obb.contains(center + rotation * glm::dvec3(3.0, 0.0, 0.0))); + CHECK(!obb.contains(center + rotation * glm::dvec3(0.0, 4.0, 0.0))); + CHECK(!obb.contains(center + rotation * glm::dvec3(0.0, 0.0, 5.0))); + } +} From a1b37258160b77a61971df780679f1a314854a57 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Mon, 6 Nov 2023 22:24:26 +1100 Subject: [PATCH 323/421] Add TileTransform class. --- CHANGES.md | 1 + .../Cesium3DTilesContent/TileTransform.h | 19 +++++++++++++++++++ Cesium3DTilesContent/src/TileTransform.cpp | 19 +++++++++++++++++++ 3 files changed, 39 insertions(+) create mode 100644 Cesium3DTilesContent/include/Cesium3DTilesContent/TileTransform.h create mode 100644 Cesium3DTilesContent/src/TileTransform.cpp diff --git a/CHANGES.md b/CHANGES.md index bdfa907d6..106d62e3e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -15,6 +15,7 @@ - Added `TileBoundingVolumes` class to `Cesium3DTilesContent`, making it easier to create the rich bounding volume types in `CesiumGeometry` and `CesiumGeospatial` from the simple vector representations in `Cesium3DTiles`. - Added `transform` method to `CesiumGeometry::BoundingSphere`. - Added `toSphere`, `fromSphere`, and `fromAxisAligned` methods to `CesiumGeometry::OrientedBoundingBox`. +- Added `TileTransform` class to `Cesium3DTilesContent`, making it easier to create a `glm::dmat4` from the `transform` property of a `Cesium3DTiles::Tile`. ##### Fixes :wrench: diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/TileTransform.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/TileTransform.h new file mode 100644 index 000000000..16b079fea --- /dev/null +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/TileTransform.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +#include + +namespace Cesium3DTiles { +struct Tile; +} + +namespace Cesium3DTilesContent { + +class TileTransform { +public: + static std::optional + getTransform(const Cesium3DTiles::Tile& tile); +}; + +} // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/src/TileTransform.cpp b/Cesium3DTilesContent/src/TileTransform.cpp new file mode 100644 index 000000000..7e4b77ddf --- /dev/null +++ b/Cesium3DTilesContent/src/TileTransform.cpp @@ -0,0 +1,19 @@ +#include +#include + +namespace Cesium3DTilesContent { + +std::optional +TileTransform::getTransform(const Cesium3DTiles::Tile& tile) { + if (tile.transform.size() < 16) + return std::nullopt; + + const std::vector& a = tile.transform; + return glm::dmat4( + glm::dvec4(a[0], a[1], a[2], a[3]), + glm::dvec4(a[4], a[5], a[6], a[7]), + glm::dvec4(a[8], a[9], a[10], a[11]), + glm::dvec4(a[12], a[13], a[14], a[15])); +} + +} // namespace Cesium3DTilesContent From 8d9b1a6a2cbdbda109479bc624a73c7eeb76825f Mon Sep 17 00:00:00 2001 From: Leaf <1216657464@qq.com> Date: Tue, 7 Nov 2023 09:00:40 +0800 Subject: [PATCH 324/421] Fix logical errors in byteStride calculations --- CesiumGltf/src/Accessor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CesiumGltf/src/Accessor.cpp b/CesiumGltf/src/Accessor.cpp index 3db10ce84..8c87d616a 100644 --- a/CesiumGltf/src/Accessor.cpp +++ b/CesiumGltf/src/Accessor.cpp @@ -69,7 +69,7 @@ Accessor::computeByteStride(const CesiumGltf::Model& model) const noexcept { return 0; } - if (pBufferView->byteStride) { + if (pBufferView->byteStride && pBufferView->byteStride.value() != 0) { return pBufferView->byteStride.value(); } return computeNumberOfComponents(this->type) * From bb9caf138b4c5e9a5690992e750e88b0a551293b Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Tue, 7 Nov 2023 17:42:18 +1100 Subject: [PATCH 325/421] Doc improvements. --- .../QuantizedMeshLoader.h | 30 +++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/QuantizedMeshLoader.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/QuantizedMeshLoader.h index 5ffe02940..2574243e3 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/QuantizedMeshLoader.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/QuantizedMeshLoader.h @@ -70,10 +70,10 @@ class CESIUM3DTILESCONTENT_API QuantizedMeshLoader final { /** * @brief Create a {@link QuantizedMeshLoadResult} from the given data. * - * @param tileID The tile ID - * @param tileBoundingVoume The tile bounding volume - * @param url The URL - * @param data The actual input data + * @param tileID The tile ID. + * @param tileBoundingVoume The tile bounding volume. + * @param url The URL from which the data was loaded. + * @param data The actual tile data. * @return The {@link QuantizedMeshLoadResult} */ static QuantizedMeshLoadResult load( @@ -83,12 +83,32 @@ class CESIUM3DTILESCONTENT_API QuantizedMeshLoader final { const gsl::span& data, bool enableWaterMask); + /** + * @brief Parses the metadata (tile availability) from the given + * quantized-mesh terrain tile data. + * + * @param data The actual tile data. + * @param tileID The tile ID. + * @return The parsed metadata. + */ static QuantizedMeshMetadataResult loadMetadata( const gsl::span& data, const CesiumGeometry::QuadtreeTileID& tileID); + /** + * @brief Extracts tile availability information from a parsed layer.json + * or tile metadata extension. + * + * The actual availability information will be found in a property called + * `available`. + * + * @param layerJson The RapidJSON document containing the layer.json. + * @param startingLevel The first tile level number to which the availability + * information applies. + * @return The availability. + */ static QuantizedMeshMetadataResult loadAvailabilityRectangles( - const rapidjson::Document& metadata, + const rapidjson::Document& layerJson, uint32_t startingLevel); }; From fe01b5c0227c6c2676e25bc1a304f8a23efd821e Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Tue, 7 Nov 2023 17:45:05 +1100 Subject: [PATCH 326/421] Update CHANGES.md. --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index fa604c59d..f4a5fe1b3 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -11,6 +11,7 @@ - Added `getClass` to `PropertyTableView`, `PropertyTextureView`, and `PropertyAttributeView`. This can be used to retrieve the metadata `Class` associated with the view. - Added `PropertyViewStatus::EmptyPropertyWithDefault` to indicate when a property contains no data, but has a valid default value. +- A glTF `bufferView` with a `byteStride` of zero is now treated as if the `byteStride` is not defined at all. Such a glTF technically violates the spec (the minimum value is 4), but the new behavior is sensible enough and consistent with CesiumJS. ##### Fixes :wrench: From 22b447e9acd0eac10fa3c078ae7725f51f0ee453 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Tue, 7 Nov 2023 18:36:58 +1100 Subject: [PATCH 327/421] Add ImplicitTiling class. --- .../Cesium3DTilesContent/ImplicitTiling.h | 48 ++++++++++++++++ Cesium3DTilesContent/src/ImplicitTiling.cpp | 57 +++++++++++++++++++ .../src/ImplicitOctreeLoader.cpp | 39 +++---------- .../src/ImplicitOctreeLoader.h | 5 -- .../src/ImplicitQuadtreeLoader.cpp | 36 +++--------- .../src/ImplicitQuadtreeLoader.h | 5 -- 6 files changed, 123 insertions(+), 67 deletions(-) create mode 100644 Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTiling.h create mode 100644 Cesium3DTilesContent/src/ImplicitTiling.cpp diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTiling.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTiling.h new file mode 100644 index 000000000..6591844a7 --- /dev/null +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTiling.h @@ -0,0 +1,48 @@ +#pragma once + +#include + +namespace CesiumGeometry { +struct QuadtreeTileID; +struct OctreeTileID; +} // namespace CesiumGeometry + +namespace Cesium3DTilesContent { + +/** + * @brief Helper functions for working with 3D Tiles implicit tiling. + */ +class ImplicitTiling { +public: + /** + * @brief Resolves a templatized implicit tiling URL with a quadtree tile ID. + * + * @param baseUrl The base URL that is used to resolve the urlTemplate if it + * is a relative path. + * @param urlTemplate The templatized URL. + * @param quadtreeID The quadtree ID to use in resolving the parameters in the + * URL template. + * @return The resolved URL. + */ + static std::string resolveUrl( + const std::string& baseUrl, + const std::string& urlTemplate, + const CesiumGeometry::QuadtreeTileID& quadtreeID); + + /** + * @brief Resolves a templatized implicit tiling URL with an octree tile ID. + * + * @param baseUrl The base URL that is used to resolve the urlTemplate if it + * is a relative path. + * @param urlTemplate The templatized URL. + * @param octreeID The octree ID to use in resolving the parameters in the + * URL template. + * @return The resolved URL. + */ + static std::string resolveUrl( + const std::string& baseUrl, + const std::string& urlTemplate, + const CesiumGeometry::OctreeTileID& octreeID); +}; + +} // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/src/ImplicitTiling.cpp b/Cesium3DTilesContent/src/ImplicitTiling.cpp new file mode 100644 index 000000000..12db8f42a --- /dev/null +++ b/Cesium3DTilesContent/src/ImplicitTiling.cpp @@ -0,0 +1,57 @@ +#include +#include +#include +#include + +namespace Cesium3DTilesContent { + +/*static*/ std::string ImplicitTiling::resolveUrl( + const std::string& baseUrl, + const std::string& urlTemplate, + const CesiumGeometry::QuadtreeTileID& quadtreeID) { + std::string url = CesiumUtility::Uri::substituteTemplateParameters( + urlTemplate, + [&quadtreeID](const std::string& placeholder) { + if (placeholder == "level") { + return std::to_string(quadtreeID.level); + } + if (placeholder == "x") { + return std::to_string(quadtreeID.x); + } + if (placeholder == "y") { + return std::to_string(quadtreeID.y); + } + + return placeholder; + }); + + return CesiumUtility::Uri::resolve(baseUrl, url); +} + +/*static*/ std::string ImplicitTiling::resolveUrl( + const std::string& baseUrl, + const std::string& urlTemplate, + const CesiumGeometry::OctreeTileID& octreeID) { + std::string url = CesiumUtility::Uri::substituteTemplateParameters( + urlTemplate, + [&octreeID](const std::string& placeholder) { + if (placeholder == "level") { + return std::to_string(octreeID.level); + } + if (placeholder == "x") { + return std::to_string(octreeID.x); + } + if (placeholder == "y") { + return std::to_string(octreeID.y); + } + if (placeholder == "z") { + return std::to_string(octreeID.z); + } + + return placeholder; + }); + + return CesiumUtility::Uri::resolve(baseUrl, url); +} + +} // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp b/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp index 10e3c3080..34cbf13ef 100644 --- a/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp +++ b/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp @@ -3,6 +3,7 @@ #include "logTileLoadResult.h" #include +#include #include #include #include @@ -274,8 +275,10 @@ ImplicitOctreeLoader::loadTileContent(const TileLoadInput& loadInput) { this->_loadedSubtrees[subtreeLevelIdx].find(subtreeMortonIdx); if (subtreeIt == this->_loadedSubtrees[subtreeLevelIdx].end()) { // subtree is not loaded, so load it now. - std::string subtreeUrl = - resolveUrl(this->_baseUrl, this->_subtreeUrlTemplate, subtreeID); + std::string subtreeUrl = ImplicitTiling::resolveUrl( + this->_baseUrl, + this->_subtreeUrlTemplate, + subtreeID); return SubtreeAvailability::loadSubtree( 3, asyncSystem, @@ -311,8 +314,10 @@ ImplicitOctreeLoader::loadTileContent(const TileLoadInput& loadInput) { TileLoadResultState::Success}); } - std::string tileUrl = - resolveUrl(this->_baseUrl, this->_contentUrlTemplate, *pOctreeID); + std::string tileUrl = ImplicitTiling::resolveUrl( + this->_baseUrl, + this->_contentUrlTemplate, + *pOctreeID); return requestTileContent( pLogger, asyncSystem, @@ -389,30 +394,4 @@ void ImplicitOctreeLoader::addSubtreeAvailability( subtreeMortonID, std::move(subtreeAvailability)); } - -std::string ImplicitOctreeLoader::resolveUrl( - const std::string& baseUrl, - const std::string& urlTemplate, - const CesiumGeometry::OctreeTileID& octreeID) { - std::string url = CesiumUtility::Uri::substituteTemplateParameters( - urlTemplate, - [&octreeID](const std::string& placeholder) { - if (placeholder == "level") { - return std::to_string(octreeID.level); - } - if (placeholder == "x") { - return std::to_string(octreeID.x); - } - if (placeholder == "y") { - return std::to_string(octreeID.y); - } - if (placeholder == "z") { - return std::to_string(octreeID.z); - } - - return placeholder; - }); - - return CesiumUtility::Uri::resolve(baseUrl, url); -} } // namespace Cesium3DTilesSelection diff --git a/Cesium3DTilesSelection/src/ImplicitOctreeLoader.h b/Cesium3DTilesSelection/src/ImplicitOctreeLoader.h index bb6a88bc0..c49c59f95 100644 --- a/Cesium3DTilesSelection/src/ImplicitOctreeLoader.h +++ b/Cesium3DTilesSelection/src/ImplicitOctreeLoader.h @@ -53,11 +53,6 @@ class ImplicitOctreeLoader : public TilesetContentLoader { Cesium3DTilesContent::SubtreeAvailability&& subtreeAvailability); private: - static std::string resolveUrl( - const std::string& baseUrl, - const std::string& urlTemplate, - const CesiumGeometry::OctreeTileID& octreeID); - std::string _baseUrl; std::string _contentUrlTemplate; std::string _subtreeUrlTemplate; diff --git a/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp b/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp index 557596e00..db6365769 100644 --- a/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp +++ b/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp @@ -3,6 +3,7 @@ #include "logTileLoadResult.h" #include +#include #include #include #include @@ -297,8 +298,10 @@ ImplicitQuadtreeLoader::loadTileContent(const TileLoadInput& loadInput) { this->_loadedSubtrees[subtreeLevelIdx].find(subtreeMortonIdx); if (subtreeIt == this->_loadedSubtrees[subtreeLevelIdx].end()) { // subtree is not loaded, so load it now. - std::string subtreeUrl = - resolveUrl(this->_baseUrl, this->_subtreeUrlTemplate, subtreeID); + std::string subtreeUrl = ImplicitTiling::resolveUrl( + this->_baseUrl, + this->_subtreeUrlTemplate, + subtreeID); return SubtreeAvailability::loadSubtree( 2, asyncSystem, @@ -334,8 +337,10 @@ ImplicitQuadtreeLoader::loadTileContent(const TileLoadInput& loadInput) { TileLoadResultState::Success}); } - std::string tileUrl = - resolveUrl(this->_baseUrl, this->_contentUrlTemplate, *pQuadtreeID); + std::string tileUrl = ImplicitTiling::resolveUrl( + this->_baseUrl, + this->_contentUrlTemplate, + *pQuadtreeID); return requestTileContent( pLogger, asyncSystem, @@ -410,27 +415,4 @@ void ImplicitQuadtreeLoader::addSubtreeAvailability( subtreeMortonID, std::move(subtreeAvailability)); } - -std::string ImplicitQuadtreeLoader::resolveUrl( - const std::string& baseUrl, - const std::string& urlTemplate, - const CesiumGeometry::QuadtreeTileID& quadtreeID) { - std::string url = CesiumUtility::Uri::substituteTemplateParameters( - urlTemplate, - [&quadtreeID](const std::string& placeholder) { - if (placeholder == "level") { - return std::to_string(quadtreeID.level); - } - if (placeholder == "x") { - return std::to_string(quadtreeID.x); - } - if (placeholder == "y") { - return std::to_string(quadtreeID.y); - } - - return placeholder; - }); - - return CesiumUtility::Uri::resolve(baseUrl, url); -} } // namespace Cesium3DTilesSelection diff --git a/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.h b/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.h index 60c962042..80c3dd36a 100644 --- a/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.h +++ b/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.h @@ -55,11 +55,6 @@ class ImplicitQuadtreeLoader : public TilesetContentLoader { Cesium3DTilesContent::SubtreeAvailability&& subtreeAvailability); private: - static std::string resolveUrl( - const std::string& baseUrl, - const std::string& urlTemplate, - const CesiumGeometry::QuadtreeTileID& quadtreeID); - std::string _baseUrl; std::string _contentUrlTemplate; std::string _subtreeUrlTemplate; From 48c37e84bdf550d445820e51e9ff1ee416c9b60b Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Tue, 7 Nov 2023 22:54:15 +1100 Subject: [PATCH 328/421] More implicit tiling support. --- Cesium3DTilesContent/CMakeLists.txt | 2 + .../Cesium3DTilesContent/ImplicitTiling.h | 48 ---- .../ImplicitTilingUtilities.h | 160 ++++++++++++ .../SubtreeAvailability.h | 23 ++ Cesium3DTilesContent/src/ImplicitTiling.cpp | 57 ----- .../src/ImplicitTilingUtilities.cpp | 236 ++++++++++++++++++ .../src/SubtreeAvailability.cpp | 47 ++++ .../test/TestImplicitTilingUtilities.cpp | 104 ++++++++ .../src/ImplicitOctreeLoader.cpp | 23 +- .../src/ImplicitQuadtreeLoader.cpp | 22 +- 10 files changed, 580 insertions(+), 142 deletions(-) delete mode 100644 Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTiling.h create mode 100644 Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTilingUtilities.h delete mode 100644 Cesium3DTilesContent/src/ImplicitTiling.cpp create mode 100644 Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp create mode 100644 Cesium3DTilesContent/test/TestImplicitTilingUtilities.cpp diff --git a/Cesium3DTilesContent/CMakeLists.txt b/Cesium3DTilesContent/CMakeLists.txt index 649b0c327..854e36c96 100644 --- a/Cesium3DTilesContent/CMakeLists.txt +++ b/Cesium3DTilesContent/CMakeLists.txt @@ -60,6 +60,8 @@ target_link_libraries(Cesium3DTilesContent CesiumGltf CesiumGltfReader CesiumUtility + PRIVATE + libmorton ) install(TARGETS Cesium3DTilesContent diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTiling.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTiling.h deleted file mode 100644 index 6591844a7..000000000 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTiling.h +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once - -#include - -namespace CesiumGeometry { -struct QuadtreeTileID; -struct OctreeTileID; -} // namespace CesiumGeometry - -namespace Cesium3DTilesContent { - -/** - * @brief Helper functions for working with 3D Tiles implicit tiling. - */ -class ImplicitTiling { -public: - /** - * @brief Resolves a templatized implicit tiling URL with a quadtree tile ID. - * - * @param baseUrl The base URL that is used to resolve the urlTemplate if it - * is a relative path. - * @param urlTemplate The templatized URL. - * @param quadtreeID The quadtree ID to use in resolving the parameters in the - * URL template. - * @return The resolved URL. - */ - static std::string resolveUrl( - const std::string& baseUrl, - const std::string& urlTemplate, - const CesiumGeometry::QuadtreeTileID& quadtreeID); - - /** - * @brief Resolves a templatized implicit tiling URL with an octree tile ID. - * - * @param baseUrl The base URL that is used to resolve the urlTemplate if it - * is a relative path. - * @param urlTemplate The templatized URL. - * @param octreeID The octree ID to use in resolving the parameters in the - * URL template. - * @return The resolved URL. - */ - static std::string resolveUrl( - const std::string& baseUrl, - const std::string& urlTemplate, - const CesiumGeometry::OctreeTileID& octreeID); -}; - -} // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTilingUtilities.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTilingUtilities.h new file mode 100644 index 000000000..808241dc1 --- /dev/null +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTilingUtilities.h @@ -0,0 +1,160 @@ +#pragma once + +#include +#include + +#include +#include + +namespace CesiumGeometry { +struct QuadtreeTileID; +struct OctreeTileID; +} // namespace CesiumGeometry + +namespace Cesium3DTilesContent { + +class QuadtreeChildIterator { +public: + using iterator_category = std::forward_iterator_tag; + using value_type = CesiumGeometry::QuadtreeTileID; + using pointer = CesiumGeometry::QuadtreeTileID*; + using reference = CesiumGeometry::QuadtreeTileID&; + + explicit QuadtreeChildIterator( + const CesiumGeometry::QuadtreeTileID& parentTileID, + bool isFirst) noexcept; + + const CesiumGeometry::QuadtreeTileID& operator*() const { + return this->_current; + } + const CesiumGeometry::QuadtreeTileID* operator->() const { + return &this->_current; + } + QuadtreeChildIterator& operator++(); + QuadtreeChildIterator operator++(int); + + bool operator==(const QuadtreeChildIterator& rhs) const noexcept; + bool operator!=(const QuadtreeChildIterator& rhs) const noexcept; + +private: + CesiumGeometry::QuadtreeTileID _current; +}; + +class OctreeChildIterator { +public: + using iterator_category = std::forward_iterator_tag; + using value_type = CesiumGeometry::OctreeTileID; + using pointer = CesiumGeometry::OctreeTileID*; + using reference = CesiumGeometry::OctreeTileID&; + + explicit OctreeChildIterator( + const CesiumGeometry::OctreeTileID& parentTileID, + bool isFirst) noexcept; + + const CesiumGeometry::OctreeTileID& operator*() const { + return this->_current; + } + const CesiumGeometry::OctreeTileID* operator->() const { + return &this->_current; + } + OctreeChildIterator& operator++(); + OctreeChildIterator operator++(int); + + bool operator==(const OctreeChildIterator& rhs) const noexcept; + bool operator!=(const OctreeChildIterator& rhs) const noexcept; + +private: + CesiumGeometry::OctreeTileID _current; +}; + +/** + * @brief Helper functions for working with 3D Tiles implicit tiling. + */ +class ImplicitTilingUtilities { +public: + /** + * @brief Resolves a templatized implicit tiling URL with a quadtree tile ID. + * + * @param baseUrl The base URL that is used to resolve the urlTemplate if it + * is a relative path. + * @param urlTemplate The templatized URL. + * @param quadtreeID The quadtree ID to use in resolving the parameters in the + * URL template. + * @return The resolved URL. + */ + static std::string resolveUrl( + const std::string& baseUrl, + const std::string& urlTemplate, + const CesiumGeometry::QuadtreeTileID& quadtreeID); + + /** + * @brief Resolves a templatized implicit tiling URL with an octree tile ID. + * + * @param baseUrl The base URL that is used to resolve the urlTemplate if it + * is a relative path. + * @param urlTemplate The templatized URL. + * @param octreeID The octree ID to use in resolving the parameters in the + * URL template. + * @return The resolved URL. + */ + static std::string resolveUrl( + const std::string& baseUrl, + const std::string& urlTemplate, + const CesiumGeometry::OctreeTileID& octreeID); + + /** + * @brief Computes the relative Morton index for a given quadtree tile within + * a subtree with the given quadtree ID. + * + * @param subtreeID The ID of the subtree the contains the tile. + * @param tileID The ID of the tile. + * @return The relative Morton index. + */ + static uint64_t computeRelativeMortonIndex( + const CesiumGeometry::QuadtreeTileID& subtreeID, + const CesiumGeometry::QuadtreeTileID& tileID); + + /** + * @brief Computes the relative Morton index for a given octree tile within + * a subtree with the given octree ID. + * + * @param subtreeID The ID of the subtree the contains the tile. + * @param tileID The ID of the tile. + * @return The relative Morton index. + */ + static uint64_t computeRelativeMortonIndex( + const CesiumGeometry::OctreeTileID& subtreeID, + const CesiumGeometry::OctreeTileID& tileID); + + static QuadtreeChildIterator + childrenBegin(const CesiumGeometry::QuadtreeTileID& tileID) noexcept; + static QuadtreeChildIterator + childrenEnd(const CesiumGeometry::QuadtreeTileID& tileID) noexcept; + + static OctreeChildIterator + childrenBegin(const CesiumGeometry::OctreeTileID& tileID) noexcept; + static OctreeChildIterator + childrenEnd(const CesiumGeometry::OctreeTileID& tileID) noexcept; + + /** + * @brief Gets the quadtree tile IDs of the four children of a given quadtree + * tile. + * + * @param parentTileID The ID of the parent tile. + * @return The IDs of the four children. + */ + static std::array + getChildTileIDs(const CesiumGeometry::QuadtreeTileID& parentTileID); + + /** + * @brief Gets the octree tile IDs of the eight children of a given octree + * tile. + * + * @param parentTileID The ID of the parent tile. + * @return The IDs of the four children. + */ + static std::array + getChildTileIDs(const CesiumGeometry::OctreeTileID& parentTileID); +}; + +} // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/SubtreeAvailability.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/SubtreeAvailability.h index 29812234a..334b71d6b 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/SubtreeAvailability.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/SubtreeAvailability.h @@ -5,6 +5,11 @@ #include +namespace CesiumGeometry { +struct QuadtreeTileID; +struct OctreeTileID; +} // namespace CesiumGeometry + namespace Cesium3DTilesContent { struct SubtreeConstantAvailability { bool constant; @@ -26,10 +31,28 @@ class SubtreeAvailability { std::vector&& contentAvailability, std::vector>&& buffers); + bool isTileAvailable( + const CesiumGeometry::QuadtreeTileID& subtreeID, + const CesiumGeometry::QuadtreeTileID& tileID) const noexcept; + + bool isTileAvailable( + const CesiumGeometry::OctreeTileID& subtreeID, + const CesiumGeometry::OctreeTileID& tileID) const noexcept; + bool isTileAvailable( uint32_t relativeTileLevel, uint64_t relativeTileMortonId) const noexcept; + bool isContentAvailable( + const CesiumGeometry::QuadtreeTileID& subtreeID, + const CesiumGeometry::QuadtreeTileID& tileID, + uint64_t contentId) const noexcept; + + bool isContentAvailable( + const CesiumGeometry::OctreeTileID& subtreeID, + const CesiumGeometry::OctreeTileID& tileID, + uint64_t contentId) const noexcept; + bool isContentAvailable( uint32_t relativeTileLevel, uint64_t relativeTileMortonId, diff --git a/Cesium3DTilesContent/src/ImplicitTiling.cpp b/Cesium3DTilesContent/src/ImplicitTiling.cpp deleted file mode 100644 index 12db8f42a..000000000 --- a/Cesium3DTilesContent/src/ImplicitTiling.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include -#include -#include -#include - -namespace Cesium3DTilesContent { - -/*static*/ std::string ImplicitTiling::resolveUrl( - const std::string& baseUrl, - const std::string& urlTemplate, - const CesiumGeometry::QuadtreeTileID& quadtreeID) { - std::string url = CesiumUtility::Uri::substituteTemplateParameters( - urlTemplate, - [&quadtreeID](const std::string& placeholder) { - if (placeholder == "level") { - return std::to_string(quadtreeID.level); - } - if (placeholder == "x") { - return std::to_string(quadtreeID.x); - } - if (placeholder == "y") { - return std::to_string(quadtreeID.y); - } - - return placeholder; - }); - - return CesiumUtility::Uri::resolve(baseUrl, url); -} - -/*static*/ std::string ImplicitTiling::resolveUrl( - const std::string& baseUrl, - const std::string& urlTemplate, - const CesiumGeometry::OctreeTileID& octreeID) { - std::string url = CesiumUtility::Uri::substituteTemplateParameters( - urlTemplate, - [&octreeID](const std::string& placeholder) { - if (placeholder == "level") { - return std::to_string(octreeID.level); - } - if (placeholder == "x") { - return std::to_string(octreeID.x); - } - if (placeholder == "y") { - return std::to_string(octreeID.y); - } - if (placeholder == "z") { - return std::to_string(octreeID.z); - } - - return placeholder; - }); - - return CesiumUtility::Uri::resolve(baseUrl, url); -} - -} // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp b/Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp new file mode 100644 index 000000000..e8fa46825 --- /dev/null +++ b/Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp @@ -0,0 +1,236 @@ +#include +#include +#include +#include + +#include + +using namespace CesiumGeometry; + +namespace Cesium3DTilesContent { + +/*static*/ std::string ImplicitTilingUtilities::resolveUrl( + const std::string& baseUrl, + const std::string& urlTemplate, + const QuadtreeTileID& quadtreeID) { + std::string url = CesiumUtility::Uri::substituteTemplateParameters( + urlTemplate, + [&quadtreeID](const std::string& placeholder) { + if (placeholder == "level") { + return std::to_string(quadtreeID.level); + } + if (placeholder == "x") { + return std::to_string(quadtreeID.x); + } + if (placeholder == "y") { + return std::to_string(quadtreeID.y); + } + + return placeholder; + }); + + return CesiumUtility::Uri::resolve(baseUrl, url); +} + +/*static*/ std::string ImplicitTilingUtilities::resolveUrl( + const std::string& baseUrl, + const std::string& urlTemplate, + const OctreeTileID& octreeID) { + std::string url = CesiumUtility::Uri::substituteTemplateParameters( + urlTemplate, + [&octreeID](const std::string& placeholder) { + if (placeholder == "level") { + return std::to_string(octreeID.level); + } + if (placeholder == "x") { + return std::to_string(octreeID.x); + } + if (placeholder == "y") { + return std::to_string(octreeID.y); + } + if (placeholder == "z") { + return std::to_string(octreeID.z); + } + + return placeholder; + }); + + return CesiumUtility::Uri::resolve(baseUrl, url); +} + +/*static*/ uint64_t ImplicitTilingUtilities::computeRelativeMortonIndex( + const QuadtreeTileID& subtreeID, + const QuadtreeTileID& tileID) { + uint32_t relativeTileLevel = tileID.level - subtreeID.level; + return libmorton::morton2D_64_encode( + tileID.x - (subtreeID.x << relativeTileLevel), + tileID.y - (subtreeID.y << relativeTileLevel)); +} + +/*static*/ uint64_t ImplicitTilingUtilities::computeRelativeMortonIndex( + const OctreeTileID& subtreeID, + const OctreeTileID& tileID) { + uint32_t relativeTileLevel = tileID.level - subtreeID.level; + return libmorton::morton3D_64_encode( + tileID.x - (subtreeID.x << relativeTileLevel), + tileID.y - (subtreeID.y << relativeTileLevel), + tileID.z - (subtreeID.z << relativeTileLevel)); +} + +QuadtreeChildIterator::QuadtreeChildIterator( + const CesiumGeometry::QuadtreeTileID& parentTileID, + bool isEnd) noexcept + : _current( + parentTileID.level + 1, + parentTileID.x << 1, + parentTileID.y << 1) { + if (isEnd) { + this->_current.y += 2; + } +} + +QuadtreeChildIterator& QuadtreeChildIterator::operator++() { + // Put an indication of the child in the two low bits of `value`. + // Bit 0 indicates left child (0) or right child (1). + // Bit 1 indicates front child (0) or back child (1). + uint32_t value = ((this->_current.y & 1) << 1) | (this->_current.x & 1); + + // Add one to the current value to get the value for the next child. + // Bit cascade from addition gives us exactly what we need. + ++value; + + // Set the tile coordinates based on the new value. + // Bit 0 in value replaces bit 0 of the X coordinate. + this->_current.x = (this->_current.x & ~1) | (value & 1); + + // Value is then shifted right one bit, so its value will be 0, 1, or 2. + // 0 and 1 are the bottom or top children, while 2 indicates "end" (one past + // the last child). So we just clear the low bit of the current Y coordinate + // and add this shifted value to produce the new Y coordinate. + this->_current.y = (this->_current.y & ~1) + (value >> 1); + + return *this; +} + +QuadtreeChildIterator QuadtreeChildIterator::operator++(int) { + QuadtreeChildIterator copy = *this; + ++copy; + return copy; +} + +bool QuadtreeChildIterator::operator==( + const QuadtreeChildIterator& rhs) const noexcept { + return this->_current == rhs._current; +} + +bool QuadtreeChildIterator::operator!=( + const QuadtreeChildIterator& rhs) const noexcept { + return this->_current != rhs._current; +} + +OctreeChildIterator::OctreeChildIterator( + const CesiumGeometry::OctreeTileID& parentTileID, + bool isEnd) noexcept + : _current( + parentTileID.level + 1, + parentTileID.x << 1, + parentTileID.y << 1, + parentTileID.z << 1) { + if (isEnd) { + this->_current.z += 2; + } +} + +OctreeChildIterator& OctreeChildIterator::operator++() { + // Put an indication of the child in the three low bits of `value`. + // Bit 0 indicates left child (0) or right child (1). + // Bit 1 indicates front child (0) or back child (1). + // Bit 2 indicates bottom child (0) or top child (1). + uint32_t value = ((this->_current.z & 1) << 2) | + ((this->_current.y & 1) << 1) | (this->_current.x & 1); + + // Add one to the current value to get the value for the next child. + // Bit cascade from addition gives us exactly what we need. + ++value; + + // Set the tile coordinates based on the new value. + // Bit 0 in value replaces bit 0 of the X coordinate. + // Bit 1 in the value replaces bit 0 of the Y coordinate. + this->_current.x = (this->_current.x & ~1) | (value & 1); + this->_current.y = (this->_current.y & ~1) | ((value >> 1) & 1); + + // Value is then shifted right one bit, so its value will be 0, 1, or 2. + // 0 and 1 are the bottom or top children, while 2 indicates "end" (one past + // the last child). So we just clear the low bit of the current Y coordinate + // and add this shifted value to produce the new Y coordinate. + this->_current.z = (this->_current.z & ~1) + (value >> 2); + + return *this; +} + +OctreeChildIterator OctreeChildIterator::operator++(int) { + OctreeChildIterator copy = *this; + ++copy; + return copy; +} + +bool OctreeChildIterator::operator==( + const OctreeChildIterator& rhs) const noexcept { + return this->_current == rhs._current; +} + +bool OctreeChildIterator::operator!=( + const OctreeChildIterator& rhs) const noexcept { + return this->_current != rhs._current; +} + +QuadtreeChildIterator ImplicitTilingUtilities::childrenBegin( + const CesiumGeometry::QuadtreeTileID& tileID) noexcept { + return QuadtreeChildIterator(tileID, false); +} + +QuadtreeChildIterator ImplicitTilingUtilities::childrenEnd( + const CesiumGeometry::QuadtreeTileID& tileID) noexcept { + return QuadtreeChildIterator(tileID, true); +} + +OctreeChildIterator ImplicitTilingUtilities::childrenBegin( + const CesiumGeometry::OctreeTileID& tileID) noexcept { + return OctreeChildIterator(tileID, false); +} + +OctreeChildIterator ImplicitTilingUtilities::childrenEnd( + const CesiumGeometry::OctreeTileID& tileID) noexcept { + return OctreeChildIterator(tileID, true); +} + +std::array +ImplicitTilingUtilities::getChildTileIDs(const QuadtreeTileID& parentTileID) { + uint32_t level = parentTileID.level + 1; + uint32_t x = parentTileID.x << 1; + uint32_t y = parentTileID.y << 1; + return std::array{ + QuadtreeTileID(level, x, y), + QuadtreeTileID(level, x + 1, y), + QuadtreeTileID(level, x, y + 1), + QuadtreeTileID(level, x + 1, y + 1)}; +} + +std::array +ImplicitTilingUtilities::getChildTileIDs(const OctreeTileID& parentTileID) { + uint32_t level = parentTileID.level + 1; + uint32_t x = parentTileID.x << 1; + uint32_t y = parentTileID.y << 1; + uint32_t z = parentTileID.z << 1; + return std::array{ + OctreeTileID(level, x, y, z), + OctreeTileID(level, x + 1, y, z), + OctreeTileID(level, x, y + 1, z), + OctreeTileID(level, x + 1, y + 1, z), + OctreeTileID(level, x, y, z + 1), + OctreeTileID(level, x + 1, y, z + 1), + OctreeTileID(level, x, y + 1, z + 1), + OctreeTileID(level, x + 1, y + 1, z + 1)}; +} + +} // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/src/SubtreeAvailability.cpp b/Cesium3DTilesContent/src/SubtreeAvailability.cpp index 6a89bfb2a..fadbc70aa 100644 --- a/Cesium3DTilesContent/src/SubtreeAvailability.cpp +++ b/Cesium3DTilesContent/src/SubtreeAvailability.cpp @@ -1,5 +1,8 @@ +#include #include #include +#include +#include #include #include @@ -462,6 +465,26 @@ SubtreeAvailability::SubtreeAvailability( "Only support quadtree and octree"); } +bool SubtreeAvailability::isTileAvailable( + const CesiumGeometry::QuadtreeTileID& subtreeID, + const CesiumGeometry::QuadtreeTileID& tileID) const noexcept { + uint64_t relativeTileMortonIdx = + ImplicitTilingUtilities::computeRelativeMortonIndex(subtreeID, tileID); + return this->isTileAvailable( + tileID.level - subtreeID.level, + relativeTileMortonIdx); +} + +bool SubtreeAvailability::isTileAvailable( + const CesiumGeometry::OctreeTileID& subtreeID, + const CesiumGeometry::OctreeTileID& tileID) const noexcept { + uint64_t relativeTileMortonIdx = + ImplicitTilingUtilities::computeRelativeMortonIndex(subtreeID, tileID); + return this->isTileAvailable( + tileID.level - subtreeID.level, + relativeTileMortonIdx); +} + bool SubtreeAvailability::isTileAvailable( uint32_t relativeTileLevel, uint64_t relativeTileMortonId) const noexcept { @@ -471,6 +494,30 @@ bool SubtreeAvailability::isTileAvailable( this->_tileAvailability); } +bool SubtreeAvailability::isContentAvailable( + const CesiumGeometry::QuadtreeTileID& subtreeID, + const CesiumGeometry::QuadtreeTileID& tileID, + uint64_t contentId) const noexcept { + uint64_t relativeTileMortonIdx = + ImplicitTilingUtilities::computeRelativeMortonIndex(subtreeID, tileID); + return this->isContentAvailable( + tileID.level - subtreeID.level, + relativeTileMortonIdx, + contentId); +} + +bool SubtreeAvailability::isContentAvailable( + const CesiumGeometry::OctreeTileID& subtreeID, + const CesiumGeometry::OctreeTileID& tileID, + uint64_t contentId) const noexcept { + uint64_t relativeTileMortonIdx = + ImplicitTilingUtilities::computeRelativeMortonIndex(subtreeID, tileID); + return this->isContentAvailable( + tileID.level - subtreeID.level, + relativeTileMortonIdx, + contentId); +} + bool SubtreeAvailability::isContentAvailable( uint32_t relativeTileLevel, uint64_t relativeTileMortonId, diff --git a/Cesium3DTilesContent/test/TestImplicitTilingUtilities.cpp b/Cesium3DTilesContent/test/TestImplicitTilingUtilities.cpp new file mode 100644 index 000000000..a25d79a06 --- /dev/null +++ b/Cesium3DTilesContent/test/TestImplicitTilingUtilities.cpp @@ -0,0 +1,104 @@ +#include + +#include + +using namespace CesiumGeometry; +using namespace Cesium3DTilesContent; + +TEST_CASE("ImplicitTilingUtilities child tile iteration") { + SECTION("QuadtreeTileID") { + QuadtreeTileID parent(1, 2, 3); + + QuadtreeChildIterator it = ImplicitTilingUtilities::childrenBegin(parent); + + REQUIRE(it != ImplicitTilingUtilities::childrenEnd(parent)); + CHECK(it->level == 2); + CHECK(it->x == 4); + CHECK(it->y == 6); + + ++it; + REQUIRE(it != ImplicitTilingUtilities::childrenEnd(parent)); + CHECK(it->level == 2); + CHECK(it->x == 5); + CHECK(it->y == 6); + + ++it; + REQUIRE(it != ImplicitTilingUtilities::childrenEnd(parent)); + CHECK(it->level == 2); + CHECK(it->x == 4); + CHECK(it->y == 7); + + ++it; + REQUIRE(it != ImplicitTilingUtilities::childrenEnd(parent)); + CHECK(it->level == 2); + CHECK(it->x == 5); + CHECK(it->y == 7); + + ++it; + CHECK(it == ImplicitTilingUtilities::childrenEnd(parent)); + } + + SECTION("OctreeTileID") { + OctreeTileID parent(1, 2, 3, 4); + + OctreeChildIterator it = ImplicitTilingUtilities::childrenBegin(parent); + + REQUIRE(it != ImplicitTilingUtilities::childrenEnd(parent)); + CHECK(it->level == 2); + CHECK(it->x == 4); + CHECK(it->y == 6); + CHECK(it->z == 8); + + ++it; + REQUIRE(it != ImplicitTilingUtilities::childrenEnd(parent)); + CHECK(it->level == 2); + CHECK(it->x == 5); + CHECK(it->y == 6); + CHECK(it->z == 8); + + ++it; + REQUIRE(it != ImplicitTilingUtilities::childrenEnd(parent)); + CHECK(it->level == 2); + CHECK(it->x == 4); + CHECK(it->y == 7); + CHECK(it->z == 8); + + ++it; + REQUIRE(it != ImplicitTilingUtilities::childrenEnd(parent)); + CHECK(it->level == 2); + CHECK(it->x == 5); + CHECK(it->y == 7); + CHECK(it->z == 8); + + ++it; + REQUIRE(it != ImplicitTilingUtilities::childrenEnd(parent)); + CHECK(it->level == 2); + CHECK(it->x == 4); + CHECK(it->y == 6); + CHECK(it->z == 9); + + ++it; + REQUIRE(it != ImplicitTilingUtilities::childrenEnd(parent)); + CHECK(it->level == 2); + CHECK(it->x == 5); + CHECK(it->y == 6); + CHECK(it->z == 9); + + ++it; + REQUIRE(it != ImplicitTilingUtilities::childrenEnd(parent)); + CHECK(it->level == 2); + CHECK(it->x == 4); + CHECK(it->y == 7); + CHECK(it->z == 9); + + ++it; + REQUIRE(it != ImplicitTilingUtilities::childrenEnd(parent)); + CHECK(it->level == 2); + CHECK(it->x == 5); + CHECK(it->y == 7); + CHECK(it->z == 9); + + ++it; + CHECK(it == ImplicitTilingUtilities::childrenEnd(parent)); + } +} diff --git a/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp b/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp index 34cbf13ef..abef3651b 100644 --- a/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp +++ b/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp @@ -3,7 +3,7 @@ #include "logTileLoadResult.h" #include -#include +#include #include #include #include @@ -147,21 +147,6 @@ std::vector populateSubtree( return children; } -bool isTileContentAvailable( - const CesiumGeometry::OctreeTileID& subtreeID, - const CesiumGeometry::OctreeTileID& octreeID, - const SubtreeAvailability& subtreeAvailability) { - uint32_t relativeTileLevel = octreeID.level - subtreeID.level; - uint64_t relativeTileMortonIdx = libmorton::morton3D_64_encode( - octreeID.x - (subtreeID.x << relativeTileLevel), - octreeID.y - (subtreeID.y << relativeTileLevel), - octreeID.z - (subtreeID.z << relativeTileLevel)); - return subtreeAvailability.isContentAvailable( - relativeTileLevel, - relativeTileMortonIdx, - 0); -} - CesiumAsync::Future requestTileContent( const std::shared_ptr& pLogger, const CesiumAsync::AsyncSystem& asyncSystem, @@ -275,7 +260,7 @@ ImplicitOctreeLoader::loadTileContent(const TileLoadInput& loadInput) { this->_loadedSubtrees[subtreeLevelIdx].find(subtreeMortonIdx); if (subtreeIt == this->_loadedSubtrees[subtreeLevelIdx].end()) { // subtree is not loaded, so load it now. - std::string subtreeUrl = ImplicitTiling::resolveUrl( + std::string subtreeUrl = ImplicitTilingUtilities::resolveUrl( this->_baseUrl, this->_subtreeUrlTemplate, subtreeID); @@ -301,7 +286,7 @@ ImplicitOctreeLoader::loadTileContent(const TileLoadInput& loadInput) { // subtree is available, so check if tile has content or not. If it has, then // request it - if (!isTileContentAvailable(subtreeID, *pOctreeID, subtreeIt->second)) { + if (!subtreeIt->second.isContentAvailable(subtreeID, *pOctreeID, 0)) { // check if tile has empty content return asyncSystem.createResolvedFuture(TileLoadResult{ TileEmptyContent{}, @@ -314,7 +299,7 @@ ImplicitOctreeLoader::loadTileContent(const TileLoadInput& loadInput) { TileLoadResultState::Success}); } - std::string tileUrl = ImplicitTiling::resolveUrl( + std::string tileUrl = ImplicitTilingUtilities::resolveUrl( this->_baseUrl, this->_contentUrlTemplate, *pOctreeID); diff --git a/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp b/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp index db6365769..de99e3f7e 100644 --- a/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp +++ b/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp @@ -3,7 +3,7 @@ #include "logTileLoadResult.h" #include -#include +#include #include #include #include @@ -148,20 +148,6 @@ std::vector populateSubtree( return children; } -bool isTileContentAvailable( - const CesiumGeometry::QuadtreeTileID& subtreeID, - const CesiumGeometry::QuadtreeTileID& quadtreeID, - const SubtreeAvailability& subtreeAvailability) { - uint32_t relativeTileLevel = quadtreeID.level - subtreeID.level; - uint64_t relativeTileMortonIdx = libmorton::morton2D_64_encode( - quadtreeID.x - (subtreeID.x << relativeTileLevel), - quadtreeID.y - (subtreeID.y << relativeTileLevel)); - return subtreeAvailability.isContentAvailable( - relativeTileLevel, - relativeTileMortonIdx, - 0); -} - CesiumAsync::Future requestTileContent( const std::shared_ptr& pLogger, const CesiumAsync::AsyncSystem& asyncSystem, @@ -298,7 +284,7 @@ ImplicitQuadtreeLoader::loadTileContent(const TileLoadInput& loadInput) { this->_loadedSubtrees[subtreeLevelIdx].find(subtreeMortonIdx); if (subtreeIt == this->_loadedSubtrees[subtreeLevelIdx].end()) { // subtree is not loaded, so load it now. - std::string subtreeUrl = ImplicitTiling::resolveUrl( + std::string subtreeUrl = ImplicitTilingUtilities::resolveUrl( this->_baseUrl, this->_subtreeUrlTemplate, subtreeID); @@ -324,7 +310,7 @@ ImplicitQuadtreeLoader::loadTileContent(const TileLoadInput& loadInput) { // subtree is available, so check if tile has content or not. If it has, then // request it - if (!isTileContentAvailable(subtreeID, *pQuadtreeID, subtreeIt->second)) { + if (!subtreeIt->second.isContentAvailable(subtreeID, *pQuadtreeID, 0)) { // check if tile has empty content return asyncSystem.createResolvedFuture(TileLoadResult{ TileEmptyContent{}, @@ -337,7 +323,7 @@ ImplicitQuadtreeLoader::loadTileContent(const TileLoadInput& loadInput) { TileLoadResultState::Success}); } - std::string tileUrl = ImplicitTiling::resolveUrl( + std::string tileUrl = ImplicitTilingUtilities::resolveUrl( this->_baseUrl, this->_contentUrlTemplate, *pQuadtreeID); From fdf1084887566b724eab67287c3b8f5e27f29025 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 8 Nov 2023 16:49:40 +1100 Subject: [PATCH 329/421] Move quadtree computations into ImplicitTilingUtilities. --- .../ImplicitTilingUtilities.h | 232 +++++++++++++----- .../src/ImplicitTilingUtilities.cpp | 154 ++++++------ .../test/TestImplicitTilingUtilities.cpp | 145 +++++------ .../src/ImplicitQuadtreeLoader.cpp | 126 +++++----- 4 files changed, 376 insertions(+), 281 deletions(-) diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTilingUtilities.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTilingUtilities.h index 808241dc1..6bf604f32 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTilingUtilities.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTilingUtilities.h @@ -4,6 +4,7 @@ #include #include +#include #include namespace CesiumGeometry { @@ -13,58 +14,96 @@ struct OctreeTileID; namespace Cesium3DTilesContent { -class QuadtreeChildIterator { +/** + * @brief A lightweight virtual container enumerating the quadtree IDs of the + * children of a given quadtree tile. + */ +class QuadtreeChildren { public: - using iterator_category = std::forward_iterator_tag; - using value_type = CesiumGeometry::QuadtreeTileID; - using pointer = CesiumGeometry::QuadtreeTileID*; - using reference = CesiumGeometry::QuadtreeTileID&; + class iterator { + public: + using iterator_category = std::forward_iterator_tag; + using value_type = CesiumGeometry::QuadtreeTileID; + using difference_type = void; + using pointer = CesiumGeometry::QuadtreeTileID*; + using reference = CesiumGeometry::QuadtreeTileID&; - explicit QuadtreeChildIterator( - const CesiumGeometry::QuadtreeTileID& parentTileID, - bool isFirst) noexcept; + explicit iterator( + const CesiumGeometry::QuadtreeTileID& parentTileID, + bool isFirst) noexcept; - const CesiumGeometry::QuadtreeTileID& operator*() const { - return this->_current; - } - const CesiumGeometry::QuadtreeTileID* operator->() const { - return &this->_current; - } - QuadtreeChildIterator& operator++(); - QuadtreeChildIterator operator++(int); + const CesiumGeometry::QuadtreeTileID& operator*() const { + return this->_current; + } + const CesiumGeometry::QuadtreeTileID* operator->() const { + return &this->_current; + } + iterator& operator++(); + iterator operator++(int); + + bool operator==(const iterator& rhs) const noexcept; + bool operator!=(const iterator& rhs) const noexcept; + + private: + CesiumGeometry::QuadtreeTileID _current; + }; - bool operator==(const QuadtreeChildIterator& rhs) const noexcept; - bool operator!=(const QuadtreeChildIterator& rhs) const noexcept; + using const_iterator = iterator; + + QuadtreeChildren(const CesiumGeometry::QuadtreeTileID& tileID) noexcept + : _tileID(tileID) {} + iterator begin() const noexcept; + iterator end() const noexcept; + constexpr int64_t size() const noexcept { return 4; } private: - CesiumGeometry::QuadtreeTileID _current; + CesiumGeometry::QuadtreeTileID _tileID; }; -class OctreeChildIterator { +/** + * @brief A lightweight virtual container enumerating the octree IDs of the + * children of a given octree tile. + */ +class OctreeChildren { public: - using iterator_category = std::forward_iterator_tag; - using value_type = CesiumGeometry::OctreeTileID; - using pointer = CesiumGeometry::OctreeTileID*; - using reference = CesiumGeometry::OctreeTileID&; + class iterator { + public: + using iterator_category = std::forward_iterator_tag; + using value_type = CesiumGeometry::OctreeTileID; + using difference_type = void; + using pointer = CesiumGeometry::OctreeTileID*; + using reference = CesiumGeometry::OctreeTileID&; - explicit OctreeChildIterator( - const CesiumGeometry::OctreeTileID& parentTileID, - bool isFirst) noexcept; + explicit iterator( + const CesiumGeometry::OctreeTileID& parentTileID, + bool isFirst) noexcept; - const CesiumGeometry::OctreeTileID& operator*() const { - return this->_current; - } - const CesiumGeometry::OctreeTileID* operator->() const { - return &this->_current; - } - OctreeChildIterator& operator++(); - OctreeChildIterator operator++(int); + const CesiumGeometry::OctreeTileID& operator*() const { + return this->_current; + } + const CesiumGeometry::OctreeTileID* operator->() const { + return &this->_current; + } + iterator& operator++(); + iterator operator++(int); - bool operator==(const OctreeChildIterator& rhs) const noexcept; - bool operator!=(const OctreeChildIterator& rhs) const noexcept; + bool operator==(const iterator& rhs) const noexcept; + bool operator!=(const iterator& rhs) const noexcept; + + private: + CesiumGeometry::OctreeTileID _current; + }; + + using const_iterator = iterator; + + OctreeChildren(const CesiumGeometry::OctreeTileID& tileID) noexcept + : _tileID(tileID) {} + iterator begin() const noexcept; + iterator end() const noexcept; + constexpr int64_t size() const noexcept { return 8; } private: - CesiumGeometry::OctreeTileID _current; + CesiumGeometry::OctreeTileID _tileID; }; /** @@ -102,9 +141,28 @@ class ImplicitTilingUtilities { const std::string& urlTemplate, const CesiumGeometry::OctreeTileID& octreeID); + /** + * @brief Computes the Morton index for a given quadtree tile within its + * level. + * + * @param tileID The ID of the tile. + * @return The Morton index. + */ + static uint64_t + computeMortonIndex(const CesiumGeometry::QuadtreeTileID& tileID); + + /** + * @brief Computes the Morton index for a given octree tile within its level. + * + * @param tileID The ID of the tile. + * @return The Morton index. + */ + static uint64_t + computeMortonIndex(const CesiumGeometry::OctreeTileID& tileID); + /** * @brief Computes the relative Morton index for a given quadtree tile within - * a subtree with the given quadtree ID. + * its level of a subtree root at the tile with the given quadtree ID. * * @param subtreeID The ID of the subtree the contains the tile. * @param tileID The ID of the tile. @@ -116,45 +174,101 @@ class ImplicitTilingUtilities { /** * @brief Computes the relative Morton index for a given octree tile within - * a subtree with the given octree ID. + * its level of a subtree rooted at the tile with the given octree ID. * * @param subtreeID The ID of the subtree the contains the tile. * @param tileID The ID of the tile. * @return The relative Morton index. */ static uint64_t computeRelativeMortonIndex( - const CesiumGeometry::OctreeTileID& subtreeID, + const CesiumGeometry::OctreeTileID& subtreeRootID, const CesiumGeometry::OctreeTileID& tileID); - static QuadtreeChildIterator - childrenBegin(const CesiumGeometry::QuadtreeTileID& tileID) noexcept; - static QuadtreeChildIterator - childrenEnd(const CesiumGeometry::QuadtreeTileID& tileID) noexcept; + /** + * @brief Gets the ID of the root tile of the subtree that contains a given + * tile. + * + * @param subtreeLevels The number of levels in each sub-tree. For example, if + * this parameter is 4, then the first subtree starts at level 0 and + * contains tiles in levels 0 through 3, and the next subtree starts at + * level 4 and contains tiles in levels 4 through 7. + * @param tileID The tile ID for each to find the subtree root. + * @return The ID of the root tile of the subtree. + */ + static CesiumGeometry::QuadtreeTileID getSubtreeRootID( + uint32_t subtreeLevels, + const CesiumGeometry::QuadtreeTileID& tileID) noexcept; - static OctreeChildIterator - childrenBegin(const CesiumGeometry::OctreeTileID& tileID) noexcept; - static OctreeChildIterator - childrenEnd(const CesiumGeometry::OctreeTileID& tileID) noexcept; + /** + * @brief Gets the ID of the root tile of the subtree that contains a given + * tile. + * + * @param subtreeLevels The number of levels in each sub-tree. For example, if + * this parameter is 4, then the first subtree starts at level 0 and + * contains tiles in levels 0 through 3, and the next subtree starts at + * level 4 and contains tiles in levels 4 through 7. + * @param tileID The tile ID for each to find the subtree root. + * @return The ID of the root tile of the subtree. + */ + static CesiumGeometry::OctreeTileID getSubtreeRootID( + uint32_t subtreeLevels, + const CesiumGeometry::OctreeTileID& tileID) noexcept; /** - * @brief Gets the quadtree tile IDs of the four children of a given quadtree + * @brief Converts an absolute tile ID to a tile ID relative to a given root * tile. * - * @param parentTileID The ID of the parent tile. - * @return The IDs of the four children. + * For example, if `rootID` and `tileID` are the same, this method returns + * `QuadtreeTileID(0, 0, 0)`. + * + * @param rootID The ID of the root tile that the returned ID should be + * relative to. + * @param tileID The absolute ID of the tile to compute a relative ID for. + * @return The relative tile ID. */ - static std::array - getChildTileIDs(const CesiumGeometry::QuadtreeTileID& parentTileID); + static CesiumGeometry::QuadtreeTileID absoluteTileIDToRelative( + const CesiumGeometry::QuadtreeTileID& rootID, + const CesiumGeometry::QuadtreeTileID& tileID) noexcept; /** - * @brief Gets the octree tile IDs of the eight children of a given octree + * @brief Converts an absolute tile ID to a tile ID relative to a given root * tile. * - * @param parentTileID The ID of the parent tile. - * @return The IDs of the four children. + * For example, if `rootID` and `tileID` are the same, this method returns + * `OctreeTileID(0, 0, 0, 0)`. + * + * @param rootID The ID of the root tile that the returned ID should be + * relative to. + * @param tileID The absolute ID of the tile to compute a relative ID for. + * @return The relative tile ID. + */ + static CesiumGeometry::OctreeTileID absoluteTileIDToRelative( + const CesiumGeometry::OctreeTileID& rootID, + const CesiumGeometry::OctreeTileID& tileID) noexcept; + + /** + * @brief Gets a lightweight virtual container for enumerating the quadtree + * IDs of the children of a given quadtree tile. + * + * @param tileID The tile ID of the parent tile for which to get children. + * @return The children. */ - static std::array - getChildTileIDs(const CesiumGeometry::OctreeTileID& parentTileID); + static QuadtreeChildren + getChildren(const CesiumGeometry::QuadtreeTileID& tileID) noexcept { + return QuadtreeChildren{tileID}; + } + + /** + * @brief Gets a lightweight virtual container for enumerating the octree + * IDs of the children of a given octree tile. + * + * @param tileID The tile ID of the parent tile for which to get children. + * @return The children. + */ + static OctreeChildren + getChildren(const CesiumGeometry::OctreeTileID& tileID) noexcept { + return OctreeChildren{tileID}; + } }; } // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp b/Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp index e8fa46825..da9c65340 100644 --- a/Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp +++ b/Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp @@ -9,7 +9,7 @@ using namespace CesiumGeometry; namespace Cesium3DTilesContent { -/*static*/ std::string ImplicitTilingUtilities::resolveUrl( +std::string ImplicitTilingUtilities::resolveUrl( const std::string& baseUrl, const std::string& urlTemplate, const QuadtreeTileID& quadtreeID) { @@ -32,7 +32,7 @@ namespace Cesium3DTilesContent { return CesiumUtility::Uri::resolve(baseUrl, url); } -/*static*/ std::string ImplicitTilingUtilities::resolveUrl( +std::string ImplicitTilingUtilities::resolveUrl( const std::string& baseUrl, const std::string& urlTemplate, const OctreeTileID& octreeID) { @@ -58,26 +58,73 @@ namespace Cesium3DTilesContent { return CesiumUtility::Uri::resolve(baseUrl, url); } -/*static*/ uint64_t ImplicitTilingUtilities::computeRelativeMortonIndex( +uint64_t ImplicitTilingUtilities::computeMortonIndex( + const CesiumGeometry::QuadtreeTileID& tileID) { + return libmorton::morton2D_64_encode(tileID.x, tileID.y); +} + +uint64_t ImplicitTilingUtilities::computeMortonIndex( + const CesiumGeometry::OctreeTileID& tileID) { + return libmorton::morton3D_64_encode(tileID.x, tileID.y, tileID.z); +} + +uint64_t ImplicitTilingUtilities::computeRelativeMortonIndex( const QuadtreeTileID& subtreeID, const QuadtreeTileID& tileID) { - uint32_t relativeTileLevel = tileID.level - subtreeID.level; - return libmorton::morton2D_64_encode( - tileID.x - (subtreeID.x << relativeTileLevel), - tileID.y - (subtreeID.y << relativeTileLevel)); + return computeMortonIndex(absoluteTileIDToRelative(subtreeID, tileID)); } -/*static*/ uint64_t ImplicitTilingUtilities::computeRelativeMortonIndex( +uint64_t ImplicitTilingUtilities::computeRelativeMortonIndex( const OctreeTileID& subtreeID, const OctreeTileID& tileID) { - uint32_t relativeTileLevel = tileID.level - subtreeID.level; - return libmorton::morton3D_64_encode( - tileID.x - (subtreeID.x << relativeTileLevel), - tileID.y - (subtreeID.y << relativeTileLevel), - tileID.z - (subtreeID.z << relativeTileLevel)); + return computeMortonIndex(absoluteTileIDToRelative(subtreeID, tileID)); +} + +CesiumGeometry::QuadtreeTileID ImplicitTilingUtilities::getSubtreeRootID( + uint32_t subtreeLevels, + const CesiumGeometry::QuadtreeTileID& tileID) noexcept { + uint32_t subtreeLevel = tileID.level / subtreeLevels; + uint32_t levelsLeft = tileID.level % subtreeLevels; + return QuadtreeTileID( + subtreeLevel, + tileID.x >> levelsLeft, + tileID.y >> levelsLeft); } -QuadtreeChildIterator::QuadtreeChildIterator( +CesiumGeometry::OctreeTileID ImplicitTilingUtilities::getSubtreeRootID( + uint32_t subtreeLevels, + const CesiumGeometry::OctreeTileID& tileID) noexcept { + uint32_t subtreeLevel = tileID.level / subtreeLevels; + uint32_t levelsLeft = tileID.level % subtreeLevels; + return OctreeTileID( + subtreeLevel, + tileID.x >> levelsLeft, + tileID.y >> levelsLeft, + tileID.z >> levelsLeft); +} + +QuadtreeTileID ImplicitTilingUtilities::absoluteTileIDToRelative( + const QuadtreeTileID& rootID, + const QuadtreeTileID& tileID) noexcept { + uint32_t relativeTileLevel = tileID.level - rootID.level; + return QuadtreeTileID( + relativeTileLevel, + tileID.x - (rootID.x << relativeTileLevel), + tileID.y - (rootID.y << relativeTileLevel)); +} + +OctreeTileID ImplicitTilingUtilities::absoluteTileIDToRelative( + const OctreeTileID& rootID, + const OctreeTileID& tileID) noexcept { + uint32_t relativeTileLevel = tileID.level - rootID.level; + return OctreeTileID( + relativeTileLevel, + tileID.x - (rootID.x << relativeTileLevel), + tileID.y - (rootID.y << relativeTileLevel), + tileID.z - (rootID.z << relativeTileLevel)); +} + +QuadtreeChildren::iterator::iterator( const CesiumGeometry::QuadtreeTileID& parentTileID, bool isEnd) noexcept : _current( @@ -89,7 +136,7 @@ QuadtreeChildIterator::QuadtreeChildIterator( } } -QuadtreeChildIterator& QuadtreeChildIterator::operator++() { +QuadtreeChildren::iterator& QuadtreeChildren::iterator::operator++() { // Put an indication of the child in the two low bits of `value`. // Bit 0 indicates left child (0) or right child (1). // Bit 1 indicates front child (0) or back child (1). @@ -112,23 +159,23 @@ QuadtreeChildIterator& QuadtreeChildIterator::operator++() { return *this; } -QuadtreeChildIterator QuadtreeChildIterator::operator++(int) { - QuadtreeChildIterator copy = *this; +QuadtreeChildren::iterator QuadtreeChildren::iterator::operator++(int) { + iterator copy = *this; ++copy; return copy; } -bool QuadtreeChildIterator::operator==( - const QuadtreeChildIterator& rhs) const noexcept { +bool QuadtreeChildren::iterator::operator==( + const iterator& rhs) const noexcept { return this->_current == rhs._current; } -bool QuadtreeChildIterator::operator!=( - const QuadtreeChildIterator& rhs) const noexcept { +bool QuadtreeChildren::iterator::operator!=( + const iterator& rhs) const noexcept { return this->_current != rhs._current; } -OctreeChildIterator::OctreeChildIterator( +OctreeChildren::iterator::iterator( const CesiumGeometry::OctreeTileID& parentTileID, bool isEnd) noexcept : _current( @@ -141,7 +188,7 @@ OctreeChildIterator::OctreeChildIterator( } } -OctreeChildIterator& OctreeChildIterator::operator++() { +OctreeChildren::iterator& OctreeChildren::iterator::operator++() { // Put an indication of the child in the three low bits of `value`. // Bit 0 indicates left child (0) or right child (1). // Bit 1 indicates front child (0) or back child (1). @@ -168,69 +215,36 @@ OctreeChildIterator& OctreeChildIterator::operator++() { return *this; } -OctreeChildIterator OctreeChildIterator::operator++(int) { - OctreeChildIterator copy = *this; +OctreeChildren::iterator OctreeChildren::iterator::operator++(int) { + iterator copy = *this; ++copy; return copy; } -bool OctreeChildIterator::operator==( - const OctreeChildIterator& rhs) const noexcept { +bool OctreeChildren::iterator::operator==(const iterator& rhs) const noexcept { return this->_current == rhs._current; } -bool OctreeChildIterator::operator!=( - const OctreeChildIterator& rhs) const noexcept { +bool OctreeChildren::iterator::operator!=(const iterator& rhs) const noexcept { return this->_current != rhs._current; } -QuadtreeChildIterator ImplicitTilingUtilities::childrenBegin( - const CesiumGeometry::QuadtreeTileID& tileID) noexcept { - return QuadtreeChildIterator(tileID, false); +QuadtreeChildren::const_iterator +Cesium3DTilesContent::QuadtreeChildren::begin() const noexcept { + return const_iterator(this->_tileID, false); } -QuadtreeChildIterator ImplicitTilingUtilities::childrenEnd( - const CesiumGeometry::QuadtreeTileID& tileID) noexcept { - return QuadtreeChildIterator(tileID, true); +QuadtreeChildren::const_iterator QuadtreeChildren::end() const noexcept { + return const_iterator(this->_tileID, true); } -OctreeChildIterator ImplicitTilingUtilities::childrenBegin( - const CesiumGeometry::OctreeTileID& tileID) noexcept { - return OctreeChildIterator(tileID, false); +OctreeChildren::const_iterator +Cesium3DTilesContent::OctreeChildren::begin() const noexcept { + return const_iterator(this->_tileID, false); } -OctreeChildIterator ImplicitTilingUtilities::childrenEnd( - const CesiumGeometry::OctreeTileID& tileID) noexcept { - return OctreeChildIterator(tileID, true); -} - -std::array -ImplicitTilingUtilities::getChildTileIDs(const QuadtreeTileID& parentTileID) { - uint32_t level = parentTileID.level + 1; - uint32_t x = parentTileID.x << 1; - uint32_t y = parentTileID.y << 1; - return std::array{ - QuadtreeTileID(level, x, y), - QuadtreeTileID(level, x + 1, y), - QuadtreeTileID(level, x, y + 1), - QuadtreeTileID(level, x + 1, y + 1)}; -} - -std::array -ImplicitTilingUtilities::getChildTileIDs(const OctreeTileID& parentTileID) { - uint32_t level = parentTileID.level + 1; - uint32_t x = parentTileID.x << 1; - uint32_t y = parentTileID.y << 1; - uint32_t z = parentTileID.z << 1; - return std::array{ - OctreeTileID(level, x, y, z), - OctreeTileID(level, x + 1, y, z), - OctreeTileID(level, x, y + 1, z), - OctreeTileID(level, x + 1, y + 1, z), - OctreeTileID(level, x, y, z + 1), - OctreeTileID(level, x + 1, y, z + 1), - OctreeTileID(level, x, y + 1, z + 1), - OctreeTileID(level, x + 1, y + 1, z + 1)}; +OctreeChildren::const_iterator OctreeChildren::end() const noexcept { + return const_iterator(this->_tileID, true); } } // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/test/TestImplicitTilingUtilities.cpp b/Cesium3DTilesContent/test/TestImplicitTilingUtilities.cpp index a25d79a06..f81926c25 100644 --- a/Cesium3DTilesContent/test/TestImplicitTilingUtilities.cpp +++ b/Cesium3DTilesContent/test/TestImplicitTilingUtilities.cpp @@ -2,6 +2,8 @@ #include +#include + using namespace CesiumGeometry; using namespace Cesium3DTilesContent; @@ -9,96 +11,67 @@ TEST_CASE("ImplicitTilingUtilities child tile iteration") { SECTION("QuadtreeTileID") { QuadtreeTileID parent(1, 2, 3); - QuadtreeChildIterator it = ImplicitTilingUtilities::childrenBegin(parent); - - REQUIRE(it != ImplicitTilingUtilities::childrenEnd(parent)); - CHECK(it->level == 2); - CHECK(it->x == 4); - CHECK(it->y == 6); - - ++it; - REQUIRE(it != ImplicitTilingUtilities::childrenEnd(parent)); - CHECK(it->level == 2); - CHECK(it->x == 5); - CHECK(it->y == 6); - - ++it; - REQUIRE(it != ImplicitTilingUtilities::childrenEnd(parent)); - CHECK(it->level == 2); - CHECK(it->x == 4); - CHECK(it->y == 7); - - ++it; - REQUIRE(it != ImplicitTilingUtilities::childrenEnd(parent)); - CHECK(it->level == 2); - CHECK(it->x == 5); - CHECK(it->y == 7); - - ++it; - CHECK(it == ImplicitTilingUtilities::childrenEnd(parent)); + QuadtreeChildren children = ImplicitTilingUtilities::getChildren(parent); + + // Check we can enumerate the children with a range-based for loop. + int count = 0; + for (const QuadtreeTileID& tileID : children) { + CHECK(tileID.level == 2); + CHECK((tileID.x == 4 || tileID.x == 5)); + CHECK((tileID.y == 6 || tileID.y == 7)); + ++count; + } + + CHECK(count == 4); + + // Check we have exactly the right children. + std::vector expected{ + QuadtreeTileID(2, 4, 6), + QuadtreeTileID(2, 5, 6), + QuadtreeTileID(2, 4, 7), + QuadtreeTileID(2, 5, 7)}; + auto mismatch = std::mismatch( + children.begin(), + children.end(), + expected.begin(), + expected.end()); + CHECK(mismatch.first == children.end()); + CHECK(mismatch.second == expected.end()); } SECTION("OctreeTileID") { OctreeTileID parent(1, 2, 3, 4); - OctreeChildIterator it = ImplicitTilingUtilities::childrenBegin(parent); - - REQUIRE(it != ImplicitTilingUtilities::childrenEnd(parent)); - CHECK(it->level == 2); - CHECK(it->x == 4); - CHECK(it->y == 6); - CHECK(it->z == 8); - - ++it; - REQUIRE(it != ImplicitTilingUtilities::childrenEnd(parent)); - CHECK(it->level == 2); - CHECK(it->x == 5); - CHECK(it->y == 6); - CHECK(it->z == 8); - - ++it; - REQUIRE(it != ImplicitTilingUtilities::childrenEnd(parent)); - CHECK(it->level == 2); - CHECK(it->x == 4); - CHECK(it->y == 7); - CHECK(it->z == 8); - - ++it; - REQUIRE(it != ImplicitTilingUtilities::childrenEnd(parent)); - CHECK(it->level == 2); - CHECK(it->x == 5); - CHECK(it->y == 7); - CHECK(it->z == 8); - - ++it; - REQUIRE(it != ImplicitTilingUtilities::childrenEnd(parent)); - CHECK(it->level == 2); - CHECK(it->x == 4); - CHECK(it->y == 6); - CHECK(it->z == 9); - - ++it; - REQUIRE(it != ImplicitTilingUtilities::childrenEnd(parent)); - CHECK(it->level == 2); - CHECK(it->x == 5); - CHECK(it->y == 6); - CHECK(it->z == 9); - - ++it; - REQUIRE(it != ImplicitTilingUtilities::childrenEnd(parent)); - CHECK(it->level == 2); - CHECK(it->x == 4); - CHECK(it->y == 7); - CHECK(it->z == 9); - - ++it; - REQUIRE(it != ImplicitTilingUtilities::childrenEnd(parent)); - CHECK(it->level == 2); - CHECK(it->x == 5); - CHECK(it->y == 7); - CHECK(it->z == 9); - - ++it; - CHECK(it == ImplicitTilingUtilities::childrenEnd(parent)); + OctreeChildren children = ImplicitTilingUtilities::getChildren(parent); + + // Check we can enumerate the children with a range-based for loop. + int count = 0; + for (const OctreeTileID& tileID : children) { + CHECK(tileID.level == 2); + CHECK((tileID.x == 4 || tileID.x == 5)); + CHECK((tileID.y == 6 || tileID.y == 7)); + CHECK((tileID.z == 8 || tileID.z == 9)); + ++count; + } + + CHECK(count == 8); + + // Check we have exactly the right children. + std::vector expected{ + OctreeTileID(2, 4, 6, 8), + OctreeTileID(2, 5, 6, 8), + OctreeTileID(2, 4, 7, 8), + OctreeTileID(2, 5, 7, 8), + OctreeTileID(2, 4, 6, 9), + OctreeTileID(2, 5, 6, 9), + OctreeTileID(2, 4, 7, 9), + OctreeTileID(2, 5, 7, 9)}; + auto mismatch = std::mismatch( + children.begin(), + children.end(), + expected.begin(), + expected.end()); + CHECK(mismatch.first == children.end()); + CHECK(mismatch.second == expected.end()); } } diff --git a/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp b/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp index de99e3f7e..a9f95c568 100644 --- a/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp +++ b/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp @@ -84,63 +84,59 @@ BoundingVolume subdivideBoundingVolume( std::vector populateSubtree( const SubtreeAvailability& subtreeAvailability, uint32_t subtreeLevels, - uint32_t relativeTileLevel, - uint64_t relativeTileMortonID, + const CesiumGeometry::QuadtreeTileID& subtreeRootID, const Tile& tile, ImplicitQuadtreeLoader& loader) { + const CesiumGeometry::QuadtreeTileID& quadtreeID = + std::get(tile.getTileID()); + + uint32_t relativeTileLevel = quadtreeID.level - subtreeRootID.level; if (relativeTileLevel >= subtreeLevels) { return {}; } - const CesiumGeometry::QuadtreeTileID& quadtreeID = - std::get(tile.getTileID()); + QuadtreeChildren childIDs = ImplicitTilingUtilities::getChildren(quadtreeID); std::vector children; - children.reserve(4); - for (uint16_t y = 0; y < 2; ++y) { - uint32_t childY = (quadtreeID.y << 1) | y; - for (uint16_t x = 0; x < 2; ++x) { - uint32_t childX = (quadtreeID.x << 1) | x; - CesiumGeometry::QuadtreeTileID childID{ - quadtreeID.level + 1, - childX, - childY}; - - uint32_t childIndex = - static_cast(libmorton::morton2D_32_encode(x, y)); - uint64_t relativeChildMortonID = relativeTileMortonID << 2 | childIndex; - uint32_t relativeChildLevel = relativeTileLevel + 1; - if (relativeChildLevel == subtreeLevels) { - if (subtreeAvailability.isSubtreeAvailable(relativeChildMortonID)) { - Tile& child = children.emplace_back(&loader); - child.setTransform(tile.getTransform()); - child.setBoundingVolume( - subdivideBoundingVolume(childID, loader.getBoundingVolume())); - child.setGeometricError(tile.getGeometricError() * 0.5); - child.setRefine(tile.getRefine()); - child.setTileID(childID); - } - } else { - if (subtreeAvailability.isTileAvailable( + children.reserve(childIDs.size()); + + for (const CesiumGeometry::QuadtreeTileID& childID : childIDs) { + uint64_t relativeChildMortonID = + ImplicitTilingUtilities::computeRelativeMortonIndex( + subtreeRootID, + childID); + + uint32_t relativeChildLevel = relativeTileLevel + 1; + if (relativeChildLevel == subtreeLevels) { + if (subtreeAvailability.isSubtreeAvailable(relativeChildMortonID)) { + Tile& child = children.emplace_back(&loader); + child.setTransform(tile.getTransform()); + child.setBoundingVolume( + subdivideBoundingVolume(childID, loader.getBoundingVolume())); + child.setGeometricError(tile.getGeometricError() * 0.5); + child.setRefine(tile.getRefine()); + child.setTileID(childID); + } + } else { + if (subtreeAvailability.isTileAvailable( + relativeChildLevel, + relativeChildMortonID)) { + if (subtreeAvailability.isContentAvailable( relativeChildLevel, - relativeChildMortonID)) { - if (subtreeAvailability.isContentAvailable( - relativeChildLevel, - relativeChildMortonID, - 0)) { - children.emplace_back(&loader); - } else { - children.emplace_back(&loader, TileEmptyContent{}); - } - - Tile& child = children.back(); - child.setTransform(tile.getTransform()); - child.setBoundingVolume( - subdivideBoundingVolume(childID, loader.getBoundingVolume())); - child.setGeometricError(tile.getGeometricError() * 0.5); - child.setRefine(tile.getRefine()); - child.setTileID(childID); + relativeChildMortonID, + 0)) { + children.emplace_back(&loader); + } else { + children.emplace_back(&loader, TileEmptyContent{}); } + + Tile& child = children.back(); + child.setTransform(tile.getTransform()); + child.setBoundingVolume( + subdivideBoundingVolume(childID, loader.getBoundingVolume())); + child.setGeometricError(tile.getGeometricError() * 0.5); + child.setRefine(tile.getRefine()); + child.setTileID(childID); } } } @@ -256,18 +252,16 @@ ImplicitQuadtreeLoader::loadTileContent(const TileLoadInput& loadInput) { } // find the subtree ID - uint32_t subtreeLevelIdx = pQuadtreeID->level / this->_subtreeLevels; + CesiumGeometry::QuadtreeTileID subtreeID = + ImplicitTilingUtilities::getSubtreeRootID( + this->_subtreeLevels, + *pQuadtreeID); + uint32_t subtreeLevelIdx = subtreeID.level / this->_subtreeLevels; if (subtreeLevelIdx >= _loadedSubtrees.size()) { return asyncSystem.createResolvedFuture( TileLoadResult::createFailedResult(nullptr)); } - uint64_t levelLeft = pQuadtreeID->level % this->_subtreeLevels; - uint32_t subtreeLevel = this->_subtreeLevels * subtreeLevelIdx; - uint32_t subtreeX = pQuadtreeID->x >> levelLeft; - uint32_t subtreeY = pQuadtreeID->y >> levelLeft; - CesiumGeometry::QuadtreeTileID subtreeID{subtreeLevel, subtreeX, subtreeY}; - // the below morton index hash to the subtree assumes that tileID's components // x and y never exceed 32-bit. In other words, the max levels this loader can // support is 33 which will have 4^32 tiles in the level 32th. The 64-bit @@ -279,7 +273,8 @@ ImplicitQuadtreeLoader::loadTileContent(const TileLoadInput& loadInput) { // loader will serve up to 33 levels with the level 0 being relative to the // parent loader. The solution isn't implemented at the moment, as implicit // tilesets that exceeds 33 levels are expected to be very rare - uint64_t subtreeMortonIdx = libmorton::morton2D_64_encode(subtreeX, subtreeY); + uint64_t subtreeMortonIdx = + ImplicitTilingUtilities::computeMortonIndex(subtreeID); auto subtreeIt = this->_loadedSubtrees[subtreeLevelIdx].find(subtreeMortonIdx); if (subtreeIt == this->_loadedSubtrees[subtreeLevelIdx].end()) { @@ -343,27 +338,26 @@ ImplicitQuadtreeLoader::createTileChildren(const Tile& tile) { assert(pQuadtreeID != nullptr && "This loader only serves quadtree tile"); // find the subtree ID - uint32_t subtreeLevelIdx = pQuadtreeID->level / this->_subtreeLevels; + CesiumGeometry::QuadtreeTileID subtreeID = + ImplicitTilingUtilities::getSubtreeRootID( + this->_subtreeLevels, + *pQuadtreeID); + + uint32_t subtreeLevelIdx = subtreeID.level / this->_subtreeLevels; if (subtreeLevelIdx >= this->_loadedSubtrees.size()) { return {{}, TileLoadResultState::Failed}; } - uint64_t levelLeft = pQuadtreeID->level % this->_subtreeLevels; - uint32_t subtreeX = pQuadtreeID->x >> levelLeft; - uint32_t subtreeY = pQuadtreeID->y >> levelLeft; + uint64_t subtreeMortonIdx = + ImplicitTilingUtilities::computeMortonIndex(subtreeID); - uint64_t subtreeMortonIdx = libmorton::morton2D_64_encode(subtreeX, subtreeY); auto subtreeIt = this->_loadedSubtrees[subtreeLevelIdx].find(subtreeMortonIdx); if (subtreeIt != this->_loadedSubtrees[subtreeLevelIdx].end()) { - uint64_t relativeTileMortonIdx = libmorton::morton2D_64_encode( - pQuadtreeID->x - (subtreeX << levelLeft), - pQuadtreeID->y - (subtreeY << levelLeft)); auto children = populateSubtree( subtreeIt->second, this->_subtreeLevels, - static_cast(levelLeft), - relativeTileMortonIdx, + subtreeID, tile, *this); @@ -395,7 +389,7 @@ void ImplicitQuadtreeLoader::addSubtreeAvailability( } uint64_t subtreeMortonID = - libmorton::morton2D_64_encode(subtreeID.x, subtreeID.y); + ImplicitTilingUtilities::computeMortonIndex(subtreeID); this->_loadedSubtrees[levelIndex].insert_or_assign( subtreeMortonID, From d263ffd49ade5405b22bfd97e9b3aa6e834b6fef Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 8 Nov 2023 17:39:04 +1100 Subject: [PATCH 330/421] Make octree loader use ImplicitTilingUtilities. --- .../src/ImplicitOctreeLoader.cpp | 144 +++++++-------- .../src/ImplicitQuadtreeLoader.cpp | 2 +- .../test/TestImplicitOctreeLoader.cpp | 174 +++++++++--------- 3 files changed, 155 insertions(+), 165 deletions(-) diff --git a/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp b/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp index abef3651b..40ed21695 100644 --- a/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp +++ b/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp @@ -78,68 +78,59 @@ BoundingVolume subdivideBoundingVolume( std::vector populateSubtree( const SubtreeAvailability& subtreeAvailability, uint32_t subtreeLevels, - uint32_t relativeTileLevel, - uint64_t relativeTileMortonID, + const CesiumGeometry::OctreeTileID& subtreeRootID, const Tile& tile, ImplicitOctreeLoader& loader) { + const CesiumGeometry::OctreeTileID& octreeID = + std::get(tile.getTileID()); + + uint32_t relativeTileLevel = octreeID.level - subtreeRootID.level; if (relativeTileLevel >= subtreeLevels) { return {}; } - const CesiumGeometry::OctreeTileID& octreeID = - std::get(tile.getTileID()); + OctreeChildren childIDs = ImplicitTilingUtilities::getChildren(octreeID); std::vector children; - children.reserve(8); - for (uint16_t y = 0; y < 2; ++y) { - uint32_t childY = (octreeID.y << 1) | y; - for (uint16_t z = 0; z < 2; ++z) { - uint32_t childZ = (octreeID.z << 1) | z; - for (uint16_t x = 0; x < 2; ++x) { - uint32_t childX = (octreeID.x << 1) | x; - - CesiumGeometry::OctreeTileID childID{ - octreeID.level + 1, - childX, - childY, - childZ}; - - uint32_t childIndex = - static_cast(libmorton::morton3D_32_encode(x, y, z)); - uint64_t relativeChildMortonID = relativeTileMortonID << 3 | childIndex; - uint32_t relativeChildLevel = relativeTileLevel + 1; - if (relativeChildLevel == subtreeLevels) { - if (subtreeAvailability.isSubtreeAvailable(relativeChildMortonID)) { - Tile& child = children.emplace_back(&loader); - child.setTransform(tile.getTransform()); - child.setBoundingVolume( - subdivideBoundingVolume(childID, loader.getBoundingVolume())); - child.setGeometricError(tile.getGeometricError() * 0.5); - child.setRefine(tile.getRefine()); - child.setTileID(childID); - } + children.reserve(childIDs.size()); + + for (const CesiumGeometry::OctreeTileID& childID : childIDs) { + uint64_t relativeChildMortonID = + ImplicitTilingUtilities::computeRelativeMortonIndex( + subtreeRootID, + childID); + + uint32_t relativeChildLevel = relativeTileLevel + 1; + if (relativeChildLevel == subtreeLevels) { + if (subtreeAvailability.isSubtreeAvailable(relativeChildMortonID)) { + Tile& child = children.emplace_back(&loader); + child.setTransform(tile.getTransform()); + child.setBoundingVolume( + subdivideBoundingVolume(childID, loader.getBoundingVolume())); + child.setGeometricError(tile.getGeometricError() * 0.5); + child.setRefine(tile.getRefine()); + child.setTileID(childID); + } + } else { + if (subtreeAvailability.isTileAvailable( + relativeChildLevel, + relativeChildMortonID)) { + if (subtreeAvailability.isContentAvailable( + relativeChildLevel, + relativeChildMortonID, + 0)) { + children.emplace_back(&loader); } else { - if (subtreeAvailability.isTileAvailable( - relativeChildLevel, - relativeChildMortonID)) { - if (subtreeAvailability.isContentAvailable( - relativeChildLevel, - relativeChildMortonID, - 0)) { - children.emplace_back(&loader); - } else { - children.emplace_back(&loader, TileEmptyContent{}); - } - - Tile& child = children.back(); - child.setTransform(tile.getTransform()); - child.setBoundingVolume( - subdivideBoundingVolume(childID, loader.getBoundingVolume())); - child.setGeometricError(tile.getGeometricError() * 0.5); - child.setRefine(tile.getRefine()); - child.setTileID(childID); - } + children.emplace_back(&loader, TileEmptyContent{}); } + + Tile& child = children.back(); + child.setTransform(tile.getTransform()); + child.setBoundingVolume( + subdivideBoundingVolume(childID, loader.getBoundingVolume())); + child.setGeometricError(tile.getGeometricError() * 0.5); + child.setRefine(tile.getRefine()); + child.setTileID(childID); } } } @@ -237,25 +228,18 @@ ImplicitOctreeLoader::loadTileContent(const TileLoadInput& loadInput) { } // find the subtree ID - uint32_t subtreeLevelIdx = pOctreeID->level / this->_subtreeLevels; - if (subtreeLevelIdx >= this->_loadedSubtrees.size()) { - return asyncSystem.createResolvedFuture( + CesiumGeometry::OctreeTileID subtreeID = + ImplicitTilingUtilities::getSubtreeRootID( + this->_subtreeLevels, + *pOctreeID); + uint32_t subtreeLevelIdx = subtreeID.level / this->_subtreeLevels; + if (subtreeLevelIdx >= _loadedSubtrees.size()) { + return asyncSystem.createResolvedFuture( TileLoadResult::createFailedResult(nullptr)); } - uint64_t levelLeft = pOctreeID->level % this->_subtreeLevels; - uint32_t subtreeLevel = this->_subtreeLevels * subtreeLevelIdx; - uint32_t subtreeX = pOctreeID->x >> levelLeft; - uint32_t subtreeY = pOctreeID->y >> levelLeft; - uint32_t subtreeZ = pOctreeID->z >> levelLeft; - CesiumGeometry::OctreeTileID subtreeID{ - subtreeLevel, - subtreeX, - subtreeY, - subtreeZ}; - uint64_t subtreeMortonIdx = - libmorton::morton3D_64_encode(subtreeX, subtreeY, subtreeZ); + ImplicitTilingUtilities::computeMortonIndex(subtreeID); auto subtreeIt = this->_loadedSubtrees[subtreeLevelIdx].find(subtreeMortonIdx); if (subtreeIt == this->_loadedSubtrees[subtreeLevelIdx].end()) { @@ -315,33 +299,29 @@ ImplicitOctreeLoader::loadTileContent(const TileLoadInput& loadInput) { TileChildrenResult ImplicitOctreeLoader::createTileChildren(const Tile& tile) { const CesiumGeometry::OctreeTileID* pOctreeID = std::get_if(&tile.getTileID()); - assert(pOctreeID != nullptr && "This loader only serves quadtree tile"); + assert(pOctreeID != nullptr && "This loader only serves octree tile"); // find the subtree ID - uint32_t subtreeLevelIdx = pOctreeID->level / this->_subtreeLevels; + CesiumGeometry::OctreeTileID subtreeID = + ImplicitTilingUtilities::getSubtreeRootID( + this->_subtreeLevels, + *pOctreeID); + + uint32_t subtreeLevelIdx = subtreeID.level / this->_subtreeLevels; if (subtreeLevelIdx >= this->_loadedSubtrees.size()) { return {{}, TileLoadResultState::Failed}; } - uint64_t levelLeft = pOctreeID->level % this->_subtreeLevels; - uint32_t subtreeX = pOctreeID->x >> levelLeft; - uint32_t subtreeY = pOctreeID->y >> levelLeft; - uint32_t subtreeZ = pOctreeID->z >> levelLeft; - uint64_t subtreeMortonIdx = - libmorton::morton3D_64_encode(subtreeX, subtreeY, subtreeZ); + ImplicitTilingUtilities::computeMortonIndex(subtreeID); + auto subtreeIt = this->_loadedSubtrees[subtreeLevelIdx].find(subtreeMortonIdx); if (subtreeIt != this->_loadedSubtrees[subtreeLevelIdx].end()) { - uint64_t relativeTileMortonIdx = libmorton::morton3D_64_encode( - pOctreeID->x - (subtreeX << levelLeft), - pOctreeID->y - (subtreeY << levelLeft), - pOctreeID->z - (subtreeZ << levelLeft)); auto children = populateSubtree( subtreeIt->second, this->_subtreeLevels, - static_cast(levelLeft), - relativeTileMortonIdx, + subtreeID, tile, *this); @@ -373,7 +353,7 @@ void ImplicitOctreeLoader::addSubtreeAvailability( } uint64_t subtreeMortonID = - libmorton::morton3D_64_encode(subtreeID.x, subtreeID.y, subtreeID.z); + ImplicitTilingUtilities::computeMortonIndex(subtreeID); this->_loadedSubtrees[levelIndex].insert_or_assign( subtreeMortonID, diff --git a/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp b/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp index a9f95c568..f13a42d90 100644 --- a/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp +++ b/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp @@ -13,7 +13,7 @@ #include #include -#include +#include using namespace Cesium3DTilesContent; diff --git a/Cesium3DTilesSelection/test/TestImplicitOctreeLoader.cpp b/Cesium3DTilesSelection/test/TestImplicitOctreeLoader.cpp index c881e8119..f7f49045a 100644 --- a/Cesium3DTilesSelection/test/TestImplicitOctreeLoader.cpp +++ b/Cesium3DTilesSelection/test/TestImplicitOctreeLoader.cpp @@ -198,6 +198,30 @@ TEST_CASE("Test implicit octree loader") { } } +namespace { + +const Tile& +findTile(const gsl::span& children, const OctreeTileID& tileID) { + auto it = std::find_if( + children.begin(), + children.end(), + [tileID](const Tile& tile) { + const OctreeTileID* pID = std::get_if(&tile.getTileID()); + if (!pID) + return false; + return *pID == tileID; + }); + REQUIRE(it != children.end()); + return *it; +} + +const Tile& +findTile(const std::vector& children, const OctreeTileID& tileID) { + return findTile(gsl::span(children), tileID); +} + +} // namespace + TEST_CASE("Test tile subdivision for implicit octree loader") { Cesium3DTilesContent::registerAllTileContentTypes(); @@ -238,10 +262,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { const auto& tileChildren = tileChildrenResult.children; CHECK(tileChildren.size() == 8); - const auto& tile_1_0_0_0 = tileChildren[0]; - CHECK( - std::get(tile_1_0_0_0.getTileID()) == - OctreeTileID(1, 0, 0, 0)); + const auto& tile_1_0_0_0 = + findTile(tileChildren, OctreeTileID(1, 0, 0, 0)); const auto& box_1_0_0_0 = std::get(tile_1_0_0_0.getBoundingVolume()); CHECK(box_1_0_0_0.getCenter() == glm::dvec3(-10.0, -10.0, -10.0)); @@ -249,10 +271,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { CHECK(box_1_0_0_0.getHalfAxes()[1] == glm::dvec3(0.0, 10.0, 0.0)); CHECK(box_1_0_0_0.getHalfAxes()[2] == glm::dvec3(0.0, 0.0, 10.0)); - const auto& tile_1_1_0_0 = tileChildren[1]; - CHECK( - std::get(tile_1_1_0_0.getTileID()) == - OctreeTileID(1, 1, 0, 0)); + const auto& tile_1_1_0_0 = + findTile(tileChildren, OctreeTileID(1, 1, 0, 0)); const auto& box_1_1_0_0 = std::get(tile_1_1_0_0.getBoundingVolume()); CHECK(box_1_1_0_0.getCenter() == glm::dvec3(10.0, -10.0, -10.0)); @@ -260,10 +280,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { CHECK(box_1_1_0_0.getHalfAxes()[1] == glm::dvec3(0.0, 10.0, 0.0)); CHECK(box_1_1_0_0.getHalfAxes()[2] == glm::dvec3(0.0, 0.0, 10.0)); - const auto& tile_1_0_0_1 = tileChildren[2]; - CHECK( - std::get(tile_1_0_0_1.getTileID()) == - OctreeTileID(1, 0, 0, 1)); + const auto& tile_1_0_0_1 = + findTile(tileChildren, OctreeTileID(1, 0, 0, 1)); const auto& box_1_0_0_1 = std::get(tile_1_0_0_1.getBoundingVolume()); CHECK(box_1_0_0_1.getCenter() == glm::dvec3(-10.0, -10.0, 10.0)); @@ -271,10 +289,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { CHECK(box_1_0_0_1.getHalfAxes()[1] == glm::dvec3(0.0, 10.0, 0.0)); CHECK(box_1_0_0_1.getHalfAxes()[2] == glm::dvec3(0.0, 0.0, 10.0)); - const auto& tile_1_1_0_1 = tileChildren[3]; - CHECK( - std::get(tile_1_1_0_1.getTileID()) == - OctreeTileID(1, 1, 0, 1)); + const auto& tile_1_1_0_1 = + findTile(tileChildren, OctreeTileID(1, 1, 0, 1)); const auto& box_1_1_0_1 = std::get(tile_1_1_0_1.getBoundingVolume()); CHECK(box_1_1_0_1.getCenter() == glm::dvec3(10.0, -10.0, 10.0)); @@ -282,10 +298,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { CHECK(box_1_1_0_1.getHalfAxes()[1] == glm::dvec3(0.0, 10.0, 0.0)); CHECK(box_1_1_0_1.getHalfAxes()[2] == glm::dvec3(0.0, 0.0, 10.0)); - const auto& tile_1_0_1_0 = tileChildren[4]; - CHECK( - std::get(tile_1_0_1_0.getTileID()) == - OctreeTileID(1, 0, 1, 0)); + const auto& tile_1_0_1_0 = + findTile(tileChildren, OctreeTileID(1, 0, 1, 0)); const auto& box_1_0_1_0 = std::get(tile_1_0_1_0.getBoundingVolume()); CHECK(box_1_0_1_0.getCenter() == glm::dvec3(-10.0, 10.0, -10.0)); @@ -293,10 +307,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { CHECK(box_1_0_1_0.getHalfAxes()[1] == glm::dvec3(0.0, 10.0, 0.0)); CHECK(box_1_0_1_0.getHalfAxes()[2] == glm::dvec3(0.0, 0.0, 10.0)); - const auto& tile_1_1_1_0 = tileChildren[5]; - CHECK( - std::get(tile_1_1_1_0.getTileID()) == - OctreeTileID(1, 1, 1, 0)); + const auto& tile_1_1_1_0 = + findTile(tileChildren, OctreeTileID(1, 1, 1, 0)); const auto& box_1_1_1_0 = std::get(tile_1_1_1_0.getBoundingVolume()); CHECK(box_1_1_1_0.getCenter() == glm::dvec3(10.0, 10.0, -10.0)); @@ -304,10 +316,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { CHECK(box_1_1_1_0.getHalfAxes()[1] == glm::dvec3(0.0, 10.0, 0.0)); CHECK(box_1_1_1_0.getHalfAxes()[2] == glm::dvec3(0.0, 0.0, 10.0)); - const auto& tile_1_0_1_1 = tileChildren[6]; - CHECK( - std::get(tile_1_0_1_1.getTileID()) == - OctreeTileID(1, 0, 1, 1)); + const auto& tile_1_0_1_1 = + findTile(tileChildren, OctreeTileID(1, 0, 1, 1)); const auto& box_1_0_1_1 = std::get(tile_1_0_1_1.getBoundingVolume()); CHECK(box_1_0_1_1.getCenter() == glm::dvec3(-10.0, 10.0, 10.0)); @@ -315,10 +325,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { CHECK(box_1_0_1_1.getHalfAxes()[1] == glm::dvec3(0.0, 10.0, 0.0)); CHECK(box_1_0_1_1.getHalfAxes()[2] == glm::dvec3(0.0, 0.0, 10.0)); - const auto& tile_1_1_1_1 = tileChildren[7]; - CHECK( - std::get(tile_1_1_1_1.getTileID()) == - OctreeTileID(1, 1, 1, 1)); + const auto& tile_1_1_1_1 = + findTile(tileChildren, OctreeTileID(1, 1, 1, 1)); const auto& box_1_1_1_1 = std::get(tile_1_1_1_1.getBoundingVolume()); CHECK(box_1_1_1_1.getCenter() == glm::dvec3(10.0, 10.0, 10.0)); @@ -331,7 +339,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { // check subdivide one of the root children { - auto& tile_1_1_0_0 = tile.getChildren()[1]; + const auto& tile_1_1_0_0 = + findTile(tile.getChildren(), OctreeTileID(1, 1, 0, 0)); auto tileChildrenResult = loader.createTileChildren(tile_1_1_0_0); CHECK(tileChildrenResult.state == TileLoadResultState::Success); @@ -339,10 +348,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { const auto& tileChildren = tileChildrenResult.children; CHECK(tileChildren.size() == 8); - const auto& tile_2_2_0_0 = tileChildren[0]; - CHECK( - std::get(tile_2_2_0_0.getTileID()) == - OctreeTileID(2, 2, 0, 0)); + const auto& tile_2_2_0_0 = + findTile(tileChildren, OctreeTileID(2, 2, 0, 0)); const auto& box_2_2_0_0 = std::get(tile_2_2_0_0.getBoundingVolume()); CHECK(box_2_2_0_0.getCenter() == glm::dvec3(5.0, -15.0, -15.0)); @@ -350,10 +357,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { CHECK(box_2_2_0_0.getHalfAxes()[1] == glm::dvec3(0.0, 5.0, 0.0)); CHECK(box_2_2_0_0.getHalfAxes()[2] == glm::dvec3(0.0, 0.0, 5.0)); - const auto& tile_2_3_0_0 = tileChildren[1]; - CHECK( - std::get(tile_2_3_0_0.getTileID()) == - OctreeTileID(2, 3, 0, 0)); + const auto& tile_2_3_0_0 = + findTile(tileChildren, OctreeTileID(2, 3, 0, 0)); const auto& box_2_3_0_0 = std::get(tile_2_3_0_0.getBoundingVolume()); CHECK(box_2_3_0_0.getCenter() == glm::dvec3(15.0, -15.0, -15.0)); @@ -361,10 +366,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { CHECK(box_2_3_0_0.getHalfAxes()[1] == glm::dvec3(0.0, 5.0, 0.0)); CHECK(box_2_3_0_0.getHalfAxes()[2] == glm::dvec3(0.0, 0.0, 5.0)); - const auto& tile_2_2_0_1 = tileChildren[2]; - CHECK( - std::get(tile_2_2_0_1.getTileID()) == - OctreeTileID(2, 2, 0, 1)); + const auto& tile_2_2_0_1 = + findTile(tileChildren, OctreeTileID(2, 2, 0, 1)); const auto& box_2_2_0_1 = std::get(tile_2_2_0_1.getBoundingVolume()); CHECK(box_2_2_0_1.getCenter() == glm::dvec3(5.0, -15.0, -5.0)); @@ -372,10 +375,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { CHECK(box_2_2_0_1.getHalfAxes()[1] == glm::dvec3(0.0, 5.0, 0.0)); CHECK(box_2_2_0_1.getHalfAxes()[2] == glm::dvec3(0.0, 0.0, 5.0)); - const auto& tile_2_3_0_1 = tileChildren[3]; - CHECK( - std::get(tile_2_3_0_1.getTileID()) == - OctreeTileID(2, 3, 0, 1)); + const auto& tile_2_3_0_1 = + findTile(tileChildren, OctreeTileID(2, 3, 0, 1)); const auto& box_2_3_0_1 = std::get(tile_2_3_0_1.getBoundingVolume()); CHECK(box_2_3_0_1.getCenter() == glm::dvec3(15.0, -15.0, -5.0)); @@ -383,10 +384,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { CHECK(box_2_3_0_1.getHalfAxes()[1] == glm::dvec3(0.0, 5.0, 0.0)); CHECK(box_2_3_0_1.getHalfAxes()[2] == glm::dvec3(0.0, 0.0, 5.0)); - const auto& tile_2_2_1_0 = tileChildren[4]; - CHECK( - std::get(tile_2_2_1_0.getTileID()) == - OctreeTileID(2, 2, 1, 0)); + const auto& tile_2_2_1_0 = + findTile(tileChildren, OctreeTileID(2, 2, 1, 0)); const auto& box_2_2_1_0 = std::get(tile_2_2_1_0.getBoundingVolume()); CHECK(box_2_2_1_0.getCenter() == glm::dvec3(5.0, -5.0, -15.0)); @@ -394,10 +393,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { CHECK(box_2_2_1_0.getHalfAxes()[1] == glm::dvec3(0.0, 5.0, 0.0)); CHECK(box_2_2_1_0.getHalfAxes()[2] == glm::dvec3(0.0, 0.0, 5.0)); - const auto& tile_2_3_1_0 = tileChildren[5]; - CHECK( - std::get(tile_2_3_1_0.getTileID()) == - OctreeTileID(2, 3, 1, 0)); + const auto& tile_2_3_1_0 = + findTile(tileChildren, OctreeTileID(2, 3, 1, 0)); const auto& box_2_3_1_0 = std::get(tile_2_3_1_0.getBoundingVolume()); CHECK(box_2_3_1_0.getCenter() == glm::dvec3(15.0, -5.0, -15.0)); @@ -405,10 +402,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { CHECK(box_2_3_1_0.getHalfAxes()[1] == glm::dvec3(0.0, 5.0, 0.0)); CHECK(box_2_3_1_0.getHalfAxes()[2] == glm::dvec3(0.0, 0.0, 5.0)); - const auto& tile_2_2_1_1 = tileChildren[6]; - CHECK( - std::get(tile_2_2_1_1.getTileID()) == - OctreeTileID(2, 2, 1, 1)); + const auto& tile_2_2_1_1 = + findTile(tileChildren, OctreeTileID(2, 2, 1, 1)); const auto& box_2_2_1_1 = std::get(tile_2_2_1_1.getBoundingVolume()); CHECK(box_2_2_1_1.getCenter() == glm::dvec3(5.0, -5.0, -5.0)); @@ -416,10 +411,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { CHECK(box_2_2_1_1.getHalfAxes()[1] == glm::dvec3(0.0, 5.0, 0.0)); CHECK(box_2_2_1_1.getHalfAxes()[2] == glm::dvec3(0.0, 0.0, 5.0)); - const auto& tile_2_3_1_1 = tileChildren[7]; - CHECK( - std::get(tile_2_3_1_1.getTileID()) == - OctreeTileID(2, 3, 1, 1)); + const auto& tile_2_3_1_1 = + findTile(tileChildren, OctreeTileID(2, 3, 1, 1)); const auto& box_2_3_1_1 = std::get(tile_2_3_1_1.getBoundingVolume()); CHECK(box_2_3_1_1.getCenter() == glm::dvec3(15.0, -5.0, -5.0)); @@ -469,7 +462,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { const auto& tileChildren = tileChildrenResult.children; CHECK(tileChildren.size() == 8); - const auto& tile_1_0_0_0 = tileChildren[0]; + const auto& tile_1_0_0_0 = + findTile(tileChildren, OctreeTileID(1, 0, 0, 0)); const auto& region_1_0_0_0 = std::get(tile_1_0_0_0.getBoundingVolume()); CHECK(region_1_0_0_0.getRectangle().getWest() == Approx(-Math::OnePi)); @@ -480,7 +474,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { CHECK(region_1_0_0_0.getMinimumHeight() == Approx(0.0)); CHECK(region_1_0_0_0.getMaximumHeight() == Approx(50.0)); - const auto& tile_1_1_0_0 = tileChildren[1]; + const auto& tile_1_1_0_0 = + findTile(tileChildren, OctreeTileID(1, 1, 0, 0)); const auto& region_1_1_0_0 = std::get(tile_1_1_0_0.getBoundingVolume()); CHECK(region_1_1_0_0.getRectangle().getWest() == Approx(0.0)); @@ -491,7 +486,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { CHECK(region_1_1_0_0.getMinimumHeight() == Approx(0.0)); CHECK(region_1_1_0_0.getMaximumHeight() == Approx(50.0)); - const auto& tile_1_0_0_1 = tileChildren[2]; + const auto& tile_1_0_0_1 = + findTile(tileChildren, OctreeTileID(1, 0, 0, 1)); const auto& region_1_0_0_1 = std::get(tile_1_0_0_1.getBoundingVolume()); CHECK(region_1_0_0_0.getRectangle().getWest() == Approx(-Math::OnePi)); @@ -502,7 +498,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { CHECK(region_1_0_0_1.getMinimumHeight() == Approx(50.0)); CHECK(region_1_0_0_1.getMaximumHeight() == Approx(100.0)); - const auto& tile_1_1_0_1 = tileChildren[3]; + const auto& tile_1_1_0_1 = + findTile(tileChildren, OctreeTileID(1, 1, 0, 1)); const auto& region_1_1_0_1 = std::get(tile_1_1_0_1.getBoundingVolume()); CHECK(region_1_1_0_0.getRectangle().getWest() == Approx(0.0)); @@ -513,7 +510,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { CHECK(region_1_1_0_1.getMinimumHeight() == Approx(50.0)); CHECK(region_1_1_0_1.getMaximumHeight() == Approx(100.0)); - const auto& tile_1_0_1_0 = tileChildren[4]; + const auto& tile_1_0_1_0 = + findTile(tileChildren, OctreeTileID(1, 0, 1, 0)); const auto& region_1_0_1_0 = std::get(tile_1_0_1_0.getBoundingVolume()); CHECK(region_1_0_1_0.getRectangle().getWest() == Approx(-Math::OnePi)); @@ -524,7 +522,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { CHECK(region_1_0_1_0.getMinimumHeight() == Approx(0.0)); CHECK(region_1_0_1_0.getMaximumHeight() == Approx(50.0)); - const auto& tile_1_1_1_0 = tileChildren[5]; + const auto& tile_1_1_1_0 = + findTile(tileChildren, OctreeTileID(1, 1, 1, 0)); const auto& region_1_1_1_0 = std::get(tile_1_1_1_0.getBoundingVolume()); CHECK(region_1_1_1_0.getRectangle().getWest() == Approx(0.0)); @@ -535,7 +534,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { CHECK(region_1_1_1_0.getMinimumHeight() == Approx(0.0)); CHECK(region_1_1_1_0.getMaximumHeight() == Approx(50.0)); - const auto& tile_1_0_1_1 = tileChildren[6]; + const auto& tile_1_0_1_1 = + findTile(tileChildren, OctreeTileID(1, 0, 1, 1)); const auto& region_1_0_1_1 = std::get(tile_1_0_1_1.getBoundingVolume()); CHECK(region_1_0_1_1.getRectangle().getWest() == Approx(-Math::OnePi)); @@ -546,7 +546,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { CHECK(region_1_0_1_1.getMinimumHeight() == Approx(50.0)); CHECK(region_1_0_1_1.getMaximumHeight() == Approx(100.0)); - const auto& tile_1_1_1_1 = tileChildren[7]; + const auto& tile_1_1_1_1 = + findTile(tileChildren, OctreeTileID(1, 1, 1, 1)); const auto& region_1_1_1_1 = std::get(tile_1_1_1_1.getBoundingVolume()); CHECK(region_1_1_1_1.getRectangle().getWest() == Approx(0.0)); @@ -562,14 +563,16 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { // check subdivide one of the root children { - auto& tile_1_1_0_0 = tile.getChildren()[1]; + auto& tile_1_1_0_0 = + findTile(tile.getChildren(), OctreeTileID(1, 1, 0, 0)); auto tileChildrenResult = loader.createTileChildren(tile_1_1_0_0); CHECK(tileChildrenResult.state == TileLoadResultState::Success); const auto& tileChildren = tileChildrenResult.children; CHECK(tileChildren.size() == 8); - const auto& tile_2_2_0_0 = tileChildren[0]; + const auto& tile_2_2_0_0 = + findTile(tileChildren, OctreeTileID(2, 2, 0, 0)); const auto& region_2_2_0_0 = std::get(tile_2_2_0_0.getBoundingVolume()); CHECK(region_2_2_0_0.getRectangle().getWest() == Approx(0.0)); @@ -582,7 +585,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { CHECK(region_2_2_0_0.getMinimumHeight() == Approx(0.0)); CHECK(region_2_2_0_0.getMaximumHeight() == Approx(25.0)); - const auto& tile_2_3_0_0 = tileChildren[1]; + const auto& tile_2_3_0_0 = + findTile(tileChildren, OctreeTileID(2, 3, 0, 0)); const auto& region_2_3_0_0 = std::get(tile_2_3_0_0.getBoundingVolume()); CHECK(region_2_3_0_0.getRectangle().getWest() == Approx(Math::PiOverTwo)); @@ -595,7 +599,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { CHECK(region_2_3_0_0.getMinimumHeight() == Approx(0.0)); CHECK(region_2_3_0_0.getMaximumHeight() == Approx(25.0)); - const auto& tile_2_2_0_1 = tileChildren[2]; + const auto& tile_2_2_0_1 = + findTile(tileChildren, OctreeTileID(2, 2, 0, 1)); const auto& region_2_2_0_1 = std::get(tile_2_2_0_1.getBoundingVolume()); CHECK(region_2_2_0_1.getRectangle().getWest() == Approx(0.0)); @@ -608,7 +613,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { CHECK(region_2_2_0_1.getMinimumHeight() == Approx(25.0)); CHECK(region_2_2_0_1.getMaximumHeight() == Approx(50.0)); - const auto& tile_2_3_0_1 = tileChildren[3]; + const auto& tile_2_3_0_1 = + findTile(tileChildren, OctreeTileID(2, 3, 0, 1)); const auto& region_2_3_0_1 = std::get(tile_2_3_0_1.getBoundingVolume()); CHECK(region_2_3_0_1.getRectangle().getWest() == Approx(Math::PiOverTwo)); @@ -621,7 +627,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { CHECK(region_2_3_0_1.getMinimumHeight() == Approx(25.0)); CHECK(region_2_3_0_1.getMaximumHeight() == Approx(50.0)); - const auto& tile_2_2_1_0 = tileChildren[4]; + const auto& tile_2_2_1_0 = + findTile(tileChildren, OctreeTileID(2, 2, 1, 0)); const auto& region_2_2_1_0 = std::get(tile_2_2_1_0.getBoundingVolume()); CHECK(region_2_2_1_0.getRectangle().getWest() == Approx(0.0)); @@ -634,7 +641,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { CHECK(region_2_2_1_0.getMinimumHeight() == Approx(0.0)); CHECK(region_2_2_1_0.getMaximumHeight() == Approx(25.0)); - const auto& tile_2_3_1_0 = tileChildren[5]; + const auto& tile_2_3_1_0 = + findTile(tileChildren, OctreeTileID(2, 3, 1, 0)); const auto& region_2_3_1_0 = std::get(tile_2_3_1_0.getBoundingVolume()); CHECK(region_2_3_1_0.getRectangle().getWest() == Approx(Math::PiOverTwo)); @@ -646,7 +654,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { CHECK(region_2_3_1_0.getMinimumHeight() == Approx(0.0)); CHECK(region_2_3_1_0.getMaximumHeight() == Approx(25.0)); - const auto& tile_2_2_1_1 = tileChildren[6]; + const auto& tile_2_2_1_1 = + findTile(tileChildren, OctreeTileID(2, 2, 1, 1)); const auto& region_2_2_1_1 = std::get(tile_2_2_1_1.getBoundingVolume()); CHECK(region_2_2_1_1.getRectangle().getWest() == Approx(0.0)); @@ -659,7 +668,8 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { CHECK(region_2_2_1_1.getMinimumHeight() == Approx(25.0)); CHECK(region_2_2_1_1.getMaximumHeight() == Approx(50.0)); - const auto& tile_2_3_1_1 = tileChildren[7]; + const auto& tile_2_3_1_1 = + findTile(tileChildren, OctreeTileID(2, 3, 1, 1)); const auto& region_2_3_1_1 = std::get(tile_2_3_1_1.getBoundingVolume()); CHECK(region_2_3_1_1.getRectangle().getWest() == Approx(Math::PiOverTwo)); From 75f545bb719f6a1ba2e50b0519b9e68c120d8ae1 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 8 Nov 2023 18:04:23 +1100 Subject: [PATCH 331/421] Move implicit BV computations to ImplicitTilingUtilities. --- .../ImplicitTilingUtilities.h | 65 ++++++++- .../src/ImplicitTilingUtilities.cpp | 129 +++++++++++++++++- .../src/ImplicitOctreeLoader.cpp | 48 +------ .../src/ImplicitQuadtreeLoader.cpp | 48 +------ 4 files changed, 197 insertions(+), 93 deletions(-) diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTilingUtilities.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTilingUtilities.h index 6bf604f32..760ecfaf8 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTilingUtilities.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTilingUtilities.h @@ -7,10 +7,14 @@ #include #include +namespace CesiumGeospatial { +class BoundingRegion; +class S2CellBoundingVolume; +} // namespace CesiumGeospatial + namespace CesiumGeometry { -struct QuadtreeTileID; -struct OctreeTileID; -} // namespace CesiumGeometry +class OrientedBoundingBox; +} namespace Cesium3DTilesContent { @@ -269,6 +273,61 @@ class ImplicitTilingUtilities { getChildren(const CesiumGeometry::OctreeTileID& tileID) noexcept { return OctreeChildren{tileID}; } + + /** + * @brief Computes the bounding volume for an implicit tile with the given ID. + * + * @param rootBoundingVolume The bounding volume of the root tile. + * @param tileID The tile ID for each to compute the bounding volume. + * @return The bounding volume for the given implicit tile. + */ + static CesiumGeospatial::BoundingRegion computeBoundingVolume( + const CesiumGeospatial::BoundingRegion& rootBoundingVolume, + const CesiumGeometry::QuadtreeTileID& tileID) noexcept; + + /** + * @brief Computes the bounding volume for an implicit tile with the given ID. + * + * @param rootBoundingVolume The bounding volume of the root tile. + * @param tileID The tile ID for each to compute the bounding volume. + * @return The bounding volume for the given implicit tile. + */ + static CesiumGeospatial::BoundingRegion computeBoundingVolume( + const CesiumGeospatial::BoundingRegion& rootBoundingVolume, + const CesiumGeometry::OctreeTileID& tileID) noexcept; + + /** + * @brief Computes the bounding volume for an implicit tile with the given ID. + * + * @param rootBoundingVolume The bounding volume of the root tile. + * @param tileID The tile ID for each to compute the bounding volume. + * @return The bounding volume for the given implicit tile. + */ + static CesiumGeometry::OrientedBoundingBox computeBoundingVolume( + const CesiumGeometry::OrientedBoundingBox& rootBoundingVolume, + const CesiumGeometry::QuadtreeTileID& tileID) noexcept; + + /** + * @brief Computes the bounding volume for an implicit tile with the given ID. + * + * @param rootBoundingVolume The bounding volume of the root tile. + * @param tileID The tile ID for each to compute the bounding volume. + * @return The bounding volume for the given implicit tile. + */ + static CesiumGeometry::OrientedBoundingBox computeBoundingVolume( + const CesiumGeometry::OrientedBoundingBox& rootBoundingVolume, + const CesiumGeometry::OctreeTileID& tileID) noexcept; + + /** + * @brief Computes the bounding volume for an implicit tile with the given ID. + * + * @param rootBoundingVolume The bounding volume of the root tile. + * @param tileID The tile ID for each to compute the bounding volume. + * @return The bounding volume for the given implicit tile. + */ + static CesiumGeospatial::S2CellBoundingVolume computeBoundingVolume( + const CesiumGeospatial::S2CellBoundingVolume& rootBoundingVolume, + const CesiumGeometry::QuadtreeTileID& tileID) noexcept; }; } // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp b/Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp index da9c65340..95257a97e 100644 --- a/Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp +++ b/Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp @@ -1,6 +1,9 @@ #include #include +#include #include +#include +#include #include #include @@ -124,6 +127,124 @@ OctreeTileID ImplicitTilingUtilities::absoluteTileIDToRelative( tileID.z - (rootID.z << relativeTileLevel)); } +namespace { + +CesiumGeospatial::GlobeRectangle subdivideRectangle( + const CesiumGeospatial::GlobeRectangle& rootRectangle, + const CesiumGeometry::QuadtreeTileID& tileID, + double denominator) { + double latSize = + (rootRectangle.getNorth() - rootRectangle.getSouth()) / denominator; + double longSize = + (rootRectangle.getEast() - rootRectangle.getWest()) / denominator; + + double childWest = rootRectangle.getWest() + longSize * tileID.x; + double childEast = rootRectangle.getWest() + longSize * (tileID.x + 1); + + double childSouth = rootRectangle.getSouth() + latSize * tileID.y; + double childNorth = rootRectangle.getSouth() + latSize * (tileID.y + 1); + + return CesiumGeospatial::GlobeRectangle( + childWest, + childSouth, + childEast, + childNorth); +} + +} // namespace + +CesiumGeospatial::BoundingRegion ImplicitTilingUtilities::computeBoundingVolume( + const CesiumGeospatial::BoundingRegion& rootBoundingVolume, + const CesiumGeometry::QuadtreeTileID& tileID) noexcept { + double denominator = static_cast(1 << tileID.level); + return CesiumGeospatial::BoundingRegion{ + subdivideRectangle( + rootBoundingVolume.getRectangle(), + tileID, + denominator), + rootBoundingVolume.getMinimumHeight(), + rootBoundingVolume.getMaximumHeight()}; +} + +CesiumGeospatial::BoundingRegion ImplicitTilingUtilities::computeBoundingVolume( + const CesiumGeospatial::BoundingRegion& rootBoundingVolume, + const CesiumGeometry::OctreeTileID& tileID) noexcept { + double denominator = static_cast(1 << tileID.level); + double heightSize = (rootBoundingVolume.getMaximumHeight() - + rootBoundingVolume.getMinimumHeight()) / + denominator; + + double childMinHeight = + rootBoundingVolume.getMinimumHeight() + heightSize * tileID.z; + double childMaxHeight = + rootBoundingVolume.getMinimumHeight() + heightSize * (tileID.z + 1); + + return CesiumGeospatial::BoundingRegion{ + subdivideRectangle( + rootBoundingVolume.getRectangle(), + QuadtreeTileID(tileID.level, tileID.x, tileID.y), + denominator), + childMinHeight, + childMaxHeight}; +} + +CesiumGeometry::OrientedBoundingBox +ImplicitTilingUtilities::computeBoundingVolume( + const CesiumGeometry::OrientedBoundingBox& rootBoundingVolume, + const CesiumGeometry::QuadtreeTileID& tileID) noexcept { + const glm::dmat3& halfAxes = rootBoundingVolume.getHalfAxes(); + const glm::dvec3& center = rootBoundingVolume.getCenter(); + + double denominator = static_cast(1 << tileID.level); + glm::dvec3 min = center - halfAxes[0] - halfAxes[1] - halfAxes[2]; + + glm::dvec3 xDim = halfAxes[0] * 2.0 / denominator; + glm::dvec3 yDim = halfAxes[1] * 2.0 / denominator; + glm::dvec3 childMin = min + xDim * double(tileID.x) + yDim * double(tileID.y); + glm::dvec3 childMax = min + xDim * double(tileID.x + 1) + + yDim * double(tileID.y + 1) + halfAxes[2] * 2.0; + + return CesiumGeometry::OrientedBoundingBox( + (childMin + childMax) / 2.0, + glm::dmat3{xDim / 2.0, yDim / 2.0, halfAxes[2]}); +} + +CesiumGeometry::OrientedBoundingBox +ImplicitTilingUtilities::computeBoundingVolume( + const CesiumGeometry::OrientedBoundingBox& rootBoundingVolume, + const CesiumGeometry::OctreeTileID& tileID) noexcept { + const glm::dmat3& halfAxes = rootBoundingVolume.getHalfAxes(); + const glm::dvec3& center = rootBoundingVolume.getCenter(); + + double denominator = static_cast(1 << tileID.level); + glm::dvec3 min = center - halfAxes[0] - halfAxes[1] - halfAxes[2]; + + glm::dvec3 xDim = halfAxes[0] * 2.0 / denominator; + glm::dvec3 yDim = halfAxes[1] * 2.0 / denominator; + glm::dvec3 zDim = halfAxes[2] * 2.0 / denominator; + glm::dvec3 childMin = min + xDim * double(tileID.x) + + yDim * double(tileID.y) + zDim * double(tileID.z); + glm::dvec3 childMax = min + xDim * double(tileID.x + 1) + + yDim * double(tileID.y + 1) + + zDim * double(tileID.z + 1); + + return CesiumGeometry::OrientedBoundingBox( + (childMin + childMax) / 2.0, + glm::dmat3{xDim / 2.0, yDim / 2.0, zDim / 2.0}); +} + +CesiumGeospatial::S2CellBoundingVolume +ImplicitTilingUtilities::computeBoundingVolume( + const CesiumGeospatial::S2CellBoundingVolume& rootBoundingVolume, + const CesiumGeometry::QuadtreeTileID& tileID) noexcept { + return CesiumGeospatial::S2CellBoundingVolume( + CesiumGeospatial::S2CellID::fromQuadtreeTileID( + rootBoundingVolume.getCellID().getFace(), + tileID), + rootBoundingVolume.getMinimumHeight(), + rootBoundingVolume.getMaximumHeight()); +} + QuadtreeChildren::iterator::iterator( const CesiumGeometry::QuadtreeTileID& parentTileID, bool isEnd) noexcept @@ -150,7 +271,7 @@ QuadtreeChildren::iterator& QuadtreeChildren::iterator::operator++() { // Bit 0 in value replaces bit 0 of the X coordinate. this->_current.x = (this->_current.x & ~1) | (value & 1); - // Value is then shifted right one bit, so its value will be 0, 1, or 2. + // Value is then shifted right one bit, so it will be 0, 1, or 2. // 0 and 1 are the bottom or top children, while 2 indicates "end" (one past // the last child). So we just clear the low bit of the current Y coordinate // and add this shifted value to produce the new Y coordinate. @@ -206,10 +327,10 @@ OctreeChildren::iterator& OctreeChildren::iterator::operator++() { this->_current.x = (this->_current.x & ~1) | (value & 1); this->_current.y = (this->_current.y & ~1) | ((value >> 1) & 1); - // Value is then shifted right one bit, so its value will be 0, 1, or 2. + // Value is then shifted right one bit, so it will be 0, 1, or 2. // 0 and 1 are the bottom or top children, while 2 indicates "end" (one past - // the last child). So we just clear the low bit of the current Y coordinate - // and add this shifted value to produce the new Y coordinate. + // the last child). So we just clear the low bit of the current Z coordinate + // and add this shifted value to produce the new Z coordinate. this->_current.z = (this->_current.z & ~1) + (value >> 2); return *this; diff --git a/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp b/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp index 40ed21695..203654df1 100644 --- a/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp +++ b/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp @@ -11,59 +11,19 @@ #include #include +#include + using namespace Cesium3DTilesContent; namespace Cesium3DTilesSelection { namespace { struct BoundingVolumeSubdivision { BoundingVolume operator()(const CesiumGeospatial::BoundingRegion& region) { - const CesiumGeospatial::GlobeRectangle& globeRect = region.getRectangle(); - double denominator = static_cast(1 << tileID.level); - double latSize = - (globeRect.getNorth() - globeRect.getSouth()) / denominator; - double longSize = (globeRect.getEast() - globeRect.getWest()) / denominator; - double heightSize = - (region.getMaximumHeight() - region.getMinimumHeight()) / denominator; - - double childWest = globeRect.getWest() + longSize * tileID.x; - double childEast = globeRect.getWest() + longSize * (tileID.x + 1); - - double childSouth = globeRect.getSouth() + latSize * tileID.y; - double childNorth = globeRect.getSouth() + latSize * (tileID.y + 1); - - double childMinHeight = region.getMinimumHeight() + heightSize * tileID.z; - double childMaxHeight = - region.getMinimumHeight() + heightSize * (tileID.z + 1); - - return CesiumGeospatial::BoundingRegion{ - CesiumGeospatial::GlobeRectangle( - childWest, - childSouth, - childEast, - childNorth), - childMinHeight, - childMaxHeight}; + return ImplicitTilingUtilities::computeBoundingVolume(region, this->tileID); } BoundingVolume operator()(const CesiumGeometry::OrientedBoundingBox& obb) { - const glm::dmat3& halfAxes = obb.getHalfAxes(); - const glm::dvec3& center = obb.getCenter(); - - double denominator = static_cast(1 << tileID.level); - glm::dvec3 min = center - halfAxes[0] - halfAxes[1] - halfAxes[2]; - - glm::dvec3 xDim = halfAxes[0] * 2.0 / denominator; - glm::dvec3 yDim = halfAxes[1] * 2.0 / denominator; - glm::dvec3 zDim = halfAxes[2] * 2.0 / denominator; - glm::dvec3 childMin = min + xDim * double(tileID.x) + - yDim * double(tileID.y) + zDim * double(tileID.z); - glm::dvec3 childMax = min + xDim * double(tileID.x + 1) + - yDim * double(tileID.y + 1) + - zDim * double(tileID.z + 1); - - return CesiumGeometry::OrientedBoundingBox( - (childMin + childMax) / 2.0, - glm::dmat3{xDim / 2.0, yDim / 2.0, zDim / 2.0}); + return ImplicitTilingUtilities::computeBoundingVolume(obb, this->tileID); } const CesiumGeometry::OctreeTileID& tileID; diff --git a/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp b/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp index f13a42d90..22b7a80b3 100644 --- a/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp +++ b/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp @@ -14,6 +14,7 @@ #include #include +#include using namespace Cesium3DTilesContent; @@ -21,55 +22,18 @@ namespace Cesium3DTilesSelection { namespace { struct BoundingVolumeSubdivision { BoundingVolume operator()(const CesiumGeospatial::BoundingRegion& region) { - const CesiumGeospatial::GlobeRectangle& globeRect = region.getRectangle(); - double denominator = static_cast(1 << tileID.level); - double latSize = - (globeRect.getNorth() - globeRect.getSouth()) / denominator; - double longSize = (globeRect.getEast() - globeRect.getWest()) / denominator; - - double childWest = globeRect.getWest() + longSize * tileID.x; - double childEast = globeRect.getWest() + longSize * (tileID.x + 1); - - double childSouth = globeRect.getSouth() + latSize * tileID.y; - double childNorth = globeRect.getSouth() + latSize * (tileID.y + 1); - - return CesiumGeospatial::BoundingRegion{ - CesiumGeospatial::GlobeRectangle( - childWest, - childSouth, - childEast, - childNorth), - region.getMinimumHeight(), - region.getMaximumHeight()}; + return ImplicitTilingUtilities::computeBoundingVolume(region, this->tileID); } BoundingVolume operator()(const CesiumGeospatial::S2CellBoundingVolume& s2Volume) { - return CesiumGeospatial::S2CellBoundingVolume( - CesiumGeospatial::S2CellID::fromQuadtreeTileID( - s2Volume.getCellID().getFace(), - tileID), - s2Volume.getMinimumHeight(), - s2Volume.getMaximumHeight()); + return ImplicitTilingUtilities::computeBoundingVolume( + s2Volume, + this->tileID); } BoundingVolume operator()(const CesiumGeometry::OrientedBoundingBox& obb) { - const glm::dmat3& halfAxes = obb.getHalfAxes(); - const glm::dvec3& center = obb.getCenter(); - - double denominator = static_cast(1 << tileID.level); - glm::dvec3 min = center - halfAxes[0] - halfAxes[1] - halfAxes[2]; - - glm::dvec3 xDim = halfAxes[0] * 2.0 / denominator; - glm::dvec3 yDim = halfAxes[1] * 2.0 / denominator; - glm::dvec3 childMin = - min + xDim * double(tileID.x) + yDim * double(tileID.y); - glm::dvec3 childMax = min + xDim * double(tileID.x + 1) + - yDim * double(tileID.y + 1) + halfAxes[2] * 2.0; - - return CesiumGeometry::OrientedBoundingBox( - (childMin + childMax) / 2.0, - glm::dmat3{xDim / 2.0, yDim / 2.0, halfAxes[2]}); + return ImplicitTilingUtilities::computeBoundingVolume(obb, this->tileID); } const CesiumGeometry::QuadtreeTileID& tileID; From 5def156fdeab6d59df1c04ba0c32cf8f7abc0b85 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 8 Nov 2023 18:10:51 +1100 Subject: [PATCH 332/421] Add computeLevelDenominator. --- .../Cesium3DTilesContent/ImplicitTilingUtilities.h | 13 +++++++++++++ .../src/ImplicitTilingUtilities.cpp | 13 +++++++++---- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTilingUtilities.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTilingUtilities.h index 760ecfaf8..61c8eda29 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTilingUtilities.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTilingUtilities.h @@ -328,6 +328,19 @@ class ImplicitTilingUtilities { static CesiumGeospatial::S2CellBoundingVolume computeBoundingVolume( const CesiumGeospatial::S2CellBoundingVolume& rootBoundingVolume, const CesiumGeometry::QuadtreeTileID& tileID) noexcept; + + /** + * @brief Computes the denominator for a given implicit tile level. + * + * Divide the root tile's geometric error by this value to get the standard + * geometric error for tiles on the level. Or divide each component of a + * bounding volume by this factor to get the size of the bounding volume along + * that axis for tiles of this level. + * + * @param level The tile level. + * @return The denominator for the level. + */ + static double computeLevelDenominator(uint32_t level) noexcept; }; } // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp b/Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp index 95257a97e..cce56d434 100644 --- a/Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp +++ b/Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp @@ -156,7 +156,7 @@ CesiumGeospatial::GlobeRectangle subdivideRectangle( CesiumGeospatial::BoundingRegion ImplicitTilingUtilities::computeBoundingVolume( const CesiumGeospatial::BoundingRegion& rootBoundingVolume, const CesiumGeometry::QuadtreeTileID& tileID) noexcept { - double denominator = static_cast(1 << tileID.level); + double denominator = computeLevelDenominator(tileID.level); return CesiumGeospatial::BoundingRegion{ subdivideRectangle( rootBoundingVolume.getRectangle(), @@ -169,7 +169,7 @@ CesiumGeospatial::BoundingRegion ImplicitTilingUtilities::computeBoundingVolume( CesiumGeospatial::BoundingRegion ImplicitTilingUtilities::computeBoundingVolume( const CesiumGeospatial::BoundingRegion& rootBoundingVolume, const CesiumGeometry::OctreeTileID& tileID) noexcept { - double denominator = static_cast(1 << tileID.level); + double denominator = computeLevelDenominator(tileID.level); double heightSize = (rootBoundingVolume.getMaximumHeight() - rootBoundingVolume.getMinimumHeight()) / denominator; @@ -195,7 +195,7 @@ ImplicitTilingUtilities::computeBoundingVolume( const glm::dmat3& halfAxes = rootBoundingVolume.getHalfAxes(); const glm::dvec3& center = rootBoundingVolume.getCenter(); - double denominator = static_cast(1 << tileID.level); + double denominator = computeLevelDenominator(tileID.level); glm::dvec3 min = center - halfAxes[0] - halfAxes[1] - halfAxes[2]; glm::dvec3 xDim = halfAxes[0] * 2.0 / denominator; @@ -216,7 +216,7 @@ ImplicitTilingUtilities::computeBoundingVolume( const glm::dmat3& halfAxes = rootBoundingVolume.getHalfAxes(); const glm::dvec3& center = rootBoundingVolume.getCenter(); - double denominator = static_cast(1 << tileID.level); + double denominator = computeLevelDenominator(tileID.level); glm::dvec3 min = center - halfAxes[0] - halfAxes[1] - halfAxes[2]; glm::dvec3 xDim = halfAxes[0] * 2.0 / denominator; @@ -245,6 +245,11 @@ ImplicitTilingUtilities::computeBoundingVolume( rootBoundingVolume.getMaximumHeight()); } +double +ImplicitTilingUtilities::computeLevelDenominator(uint32_t level) noexcept { + return static_cast(1 << level); +} + QuadtreeChildren::iterator::iterator( const CesiumGeometry::QuadtreeTileID& parentTileID, bool isEnd) noexcept From f004b778645a0fe2c05dd752e64fdae464326dad Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 8 Nov 2023 19:20:09 +1100 Subject: [PATCH 333/421] Add some tests, fix some bugs. --- .../ImplicitTilingUtilities.h | 26 ++-- .../src/ImplicitTilingUtilities.cpp | 4 +- .../test/TestImplicitTilingUtilities.cpp | 147 ++++++++++++++++-- 3 files changed, 146 insertions(+), 31 deletions(-) diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTilingUtilities.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTilingUtilities.h index 61c8eda29..a71f3e385 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTilingUtilities.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/ImplicitTilingUtilities.h @@ -145,6 +145,19 @@ class ImplicitTilingUtilities { const std::string& urlTemplate, const CesiumGeometry::OctreeTileID& octreeID); + /** + * @brief Computes the denominator for a given implicit tile level. + * + * Divide the root tile's geometric error by this value to get the standard + * geometric error for tiles on the level. Or divide each component of a + * bounding volume by this factor to get the size of the bounding volume along + * that axis for tiles of this level. + * + * @param level The tile level. + * @return The denominator for the level. + */ + static double computeLevelDenominator(uint32_t level) noexcept; + /** * @brief Computes the Morton index for a given quadtree tile within its * level. @@ -328,19 +341,6 @@ class ImplicitTilingUtilities { static CesiumGeospatial::S2CellBoundingVolume computeBoundingVolume( const CesiumGeospatial::S2CellBoundingVolume& rootBoundingVolume, const CesiumGeometry::QuadtreeTileID& tileID) noexcept; - - /** - * @brief Computes the denominator for a given implicit tile level. - * - * Divide the root tile's geometric error by this value to get the standard - * geometric error for tiles on the level. Or divide each component of a - * bounding volume by this factor to get the size of the bounding volume along - * that axis for tiles of this level. - * - * @param level The tile level. - * @return The denominator for the level. - */ - static double computeLevelDenominator(uint32_t level) noexcept; }; } // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp b/Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp index cce56d434..e8dfec1ad 100644 --- a/Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp +++ b/Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp @@ -89,7 +89,7 @@ CesiumGeometry::QuadtreeTileID ImplicitTilingUtilities::getSubtreeRootID( uint32_t subtreeLevel = tileID.level / subtreeLevels; uint32_t levelsLeft = tileID.level % subtreeLevels; return QuadtreeTileID( - subtreeLevel, + subtreeLevel * subtreeLevels, tileID.x >> levelsLeft, tileID.y >> levelsLeft); } @@ -100,7 +100,7 @@ CesiumGeometry::OctreeTileID ImplicitTilingUtilities::getSubtreeRootID( uint32_t subtreeLevel = tileID.level / subtreeLevels; uint32_t levelsLeft = tileID.level % subtreeLevels; return OctreeTileID( - subtreeLevel, + subtreeLevel * subtreeLevels, tileID.x >> levelsLeft, tileID.y >> levelsLeft, tileID.z >> levelsLeft); diff --git a/Cesium3DTilesContent/test/TestImplicitTilingUtilities.cpp b/Cesium3DTilesContent/test/TestImplicitTilingUtilities.cpp index f81926c25..f93121f44 100644 --- a/Cesium3DTilesContent/test/TestImplicitTilingUtilities.cpp +++ b/Cesium3DTilesContent/test/TestImplicitTilingUtilities.cpp @@ -1,6 +1,7 @@ #include #include +#include #include @@ -9,14 +10,14 @@ using namespace Cesium3DTilesContent; TEST_CASE("ImplicitTilingUtilities child tile iteration") { SECTION("QuadtreeTileID") { - QuadtreeTileID parent(1, 2, 3); + QuadtreeTileID parent(11, 2, 3); QuadtreeChildren children = ImplicitTilingUtilities::getChildren(parent); // Check we can enumerate the children with a range-based for loop. int count = 0; for (const QuadtreeTileID& tileID : children) { - CHECK(tileID.level == 2); + CHECK(tileID.level == 12); CHECK((tileID.x == 4 || tileID.x == 5)); CHECK((tileID.y == 6 || tileID.y == 7)); ++count; @@ -26,10 +27,10 @@ TEST_CASE("ImplicitTilingUtilities child tile iteration") { // Check we have exactly the right children. std::vector expected{ - QuadtreeTileID(2, 4, 6), - QuadtreeTileID(2, 5, 6), - QuadtreeTileID(2, 4, 7), - QuadtreeTileID(2, 5, 7)}; + QuadtreeTileID(12, 4, 6), + QuadtreeTileID(12, 5, 6), + QuadtreeTileID(12, 4, 7), + QuadtreeTileID(12, 5, 7)}; auto mismatch = std::mismatch( children.begin(), children.end(), @@ -40,14 +41,14 @@ TEST_CASE("ImplicitTilingUtilities child tile iteration") { } SECTION("OctreeTileID") { - OctreeTileID parent(1, 2, 3, 4); + OctreeTileID parent(11, 2, 3, 4); OctreeChildren children = ImplicitTilingUtilities::getChildren(parent); // Check we can enumerate the children with a range-based for loop. int count = 0; for (const OctreeTileID& tileID : children) { - CHECK(tileID.level == 2); + CHECK(tileID.level == 12); CHECK((tileID.x == 4 || tileID.x == 5)); CHECK((tileID.y == 6 || tileID.y == 7)); CHECK((tileID.z == 8 || tileID.z == 9)); @@ -58,14 +59,14 @@ TEST_CASE("ImplicitTilingUtilities child tile iteration") { // Check we have exactly the right children. std::vector expected{ - OctreeTileID(2, 4, 6, 8), - OctreeTileID(2, 5, 6, 8), - OctreeTileID(2, 4, 7, 8), - OctreeTileID(2, 5, 7, 8), - OctreeTileID(2, 4, 6, 9), - OctreeTileID(2, 5, 6, 9), - OctreeTileID(2, 4, 7, 9), - OctreeTileID(2, 5, 7, 9)}; + OctreeTileID(12, 4, 6, 8), + OctreeTileID(12, 5, 6, 8), + OctreeTileID(12, 4, 7, 8), + OctreeTileID(12, 5, 7, 8), + OctreeTileID(12, 4, 6, 9), + OctreeTileID(12, 5, 6, 9), + OctreeTileID(12, 4, 7, 9), + OctreeTileID(12, 5, 7, 9)}; auto mismatch = std::mismatch( children.begin(), children.end(), @@ -75,3 +76,117 @@ TEST_CASE("ImplicitTilingUtilities child tile iteration") { CHECK(mismatch.second == expected.end()); } } + +TEST_CASE("ImplicitTilingUtilities::resolveUrl") { + SECTION("quadtree") { + QuadtreeTileID tileID(11, 2, 3); + std::string url = ImplicitTilingUtilities::resolveUrl( + "https://example.com", + "tiles/{level}/{x}/{y}", + tileID); + CHECK(url == "https://example.com/tiles/11/2/3"); + } + + SECTION("octree") { + OctreeTileID tileID(11, 2, 3, 4); + std::string url = ImplicitTilingUtilities::resolveUrl( + "https://example.com", + "tiles/{level}/{x}/{y}/{z}", + tileID); + CHECK(url == "https://example.com/tiles/11/2/3/4"); + } +} + +TEST_CASE("ImplicitTilingUtilities::computeMortonIndex") { + SECTION("quadtree") { + QuadtreeTileID tileID(11, 2, 3); + CHECK( + ImplicitTilingUtilities::computeMortonIndex(tileID) == + libmorton::morton2D_64_encode(2, 3)); + } + + SECTION("quadtree") { + OctreeTileID tileID(11, 2, 3, 4); + CHECK( + ImplicitTilingUtilities::computeMortonIndex(tileID) == + libmorton::morton3D_64_encode(2, 3, 4)); + } +} + +TEST_CASE("ImplicitTilingUtilities::computeRelativeMortonIndex") { + SECTION("quadtree") { + QuadtreeTileID rootID(11, 2, 3); + QuadtreeTileID tileID(12, 5, 6); + CHECK( + ImplicitTilingUtilities::computeRelativeMortonIndex(rootID, tileID) == + 1); + } + + SECTION("octree") { + OctreeTileID rootID(11, 2, 3, 4); + OctreeTileID tileID(12, 5, 6, 8); + CHECK( + ImplicitTilingUtilities::computeRelativeMortonIndex(rootID, tileID) == + 1); + } +} + +TEST_CASE("ImplicitTilingUtilities::getSubtreeRootID") { + SECTION("quadtree") { + QuadtreeTileID tileID(10, 2, 3); + CHECK( + ImplicitTilingUtilities::getSubtreeRootID(5, tileID) == + QuadtreeTileID(10, 2, 3)); + CHECK( + ImplicitTilingUtilities::getSubtreeRootID(4, tileID) == + QuadtreeTileID(8, 0, 0)); + } + + SECTION("octree") { + OctreeTileID tileID(10, 2, 3, 4); + CHECK( + ImplicitTilingUtilities::getSubtreeRootID(5, tileID) == + OctreeTileID(10, 2, 3, 4)); + CHECK( + ImplicitTilingUtilities::getSubtreeRootID(4, tileID) == + OctreeTileID(8, 0, 0, 1)); + } +} + +TEST_CASE("ImplicitTilingUtilities::absoluteTileIDToRelative") { + SECTION("quadtree") { + CHECK( + ImplicitTilingUtilities::absoluteTileIDToRelative( + QuadtreeTileID(0, 0, 0), + QuadtreeTileID(11, 2, 3)) == QuadtreeTileID(11, 2, 3)); + CHECK( + ImplicitTilingUtilities::absoluteTileIDToRelative( + QuadtreeTileID(11, 2, 3), + QuadtreeTileID(11, 2, 3)) == QuadtreeTileID(0, 0, 0)); + CHECK( + ImplicitTilingUtilities::absoluteTileIDToRelative( + QuadtreeTileID(11, 2, 3), + QuadtreeTileID(12, 5, 7)) == QuadtreeTileID(1, 1, 1)); + } + + SECTION("octree") { + CHECK( + ImplicitTilingUtilities::absoluteTileIDToRelative( + OctreeTileID(0, 0, 0, 0), + OctreeTileID(11, 2, 3, 4)) == OctreeTileID(11, 2, 3, 4)); + CHECK( + ImplicitTilingUtilities::absoluteTileIDToRelative( + OctreeTileID(11, 2, 3, 4), + OctreeTileID(11, 2, 3, 4)) == OctreeTileID(0, 0, 0, 0)); + CHECK( + ImplicitTilingUtilities::absoluteTileIDToRelative( + OctreeTileID(11, 2, 3, 4), + OctreeTileID(12, 5, 7, 9)) == OctreeTileID(1, 1, 1, 1)); + } +} + +TEST_CASE("ImplicitTilingUtilities::computeLevelDenominator") { + CHECK(ImplicitTilingUtilities::computeLevelDenominator(0) == 1.0); + CHECK(ImplicitTilingUtilities::computeLevelDenominator(1) == 2.0); + CHECK(ImplicitTilingUtilities::computeLevelDenominator(2) == 4.0); +} From a950b38b1fbc5e47092b35a3ddb74cc9e527e15a Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 8 Nov 2023 19:54:08 +1100 Subject: [PATCH 334/421] Add bounding volume tests. --- .../test/TestImplicitTilingUtilities.cpp | 187 ++++++++++++++++++ 1 file changed, 187 insertions(+) diff --git a/Cesium3DTilesContent/test/TestImplicitTilingUtilities.cpp b/Cesium3DTilesContent/test/TestImplicitTilingUtilities.cpp index f93121f44..83a7085ef 100644 --- a/Cesium3DTilesContent/test/TestImplicitTilingUtilities.cpp +++ b/Cesium3DTilesContent/test/TestImplicitTilingUtilities.cpp @@ -1,4 +1,7 @@ #include +#include +#include +#include #include #include @@ -6,6 +9,7 @@ #include using namespace CesiumGeometry; +using namespace CesiumGeospatial; using namespace Cesium3DTilesContent; TEST_CASE("ImplicitTilingUtilities child tile iteration") { @@ -190,3 +194,186 @@ TEST_CASE("ImplicitTilingUtilities::computeLevelDenominator") { CHECK(ImplicitTilingUtilities::computeLevelDenominator(1) == 2.0); CHECK(ImplicitTilingUtilities::computeLevelDenominator(2) == 4.0); } + +TEST_CASE("ImplicitTilingUtilities::computeBoundingVolume") { + SECTION("OrientedBoundingBox") { + SECTION("quadtree") { + OrientedBoundingBox root(glm::dvec3(1.0, 2.0, 3.0), glm::dmat3(10.0)); + + OrientedBoundingBox l1x0y0 = + ImplicitTilingUtilities::computeBoundingVolume( + root, + QuadtreeTileID(1, 0, 0)); + CHECK(l1x0y0.getCenter() == glm::dvec3(-4.0, -3.0, 3.0)); + CHECK(l1x0y0.getLengths() == glm::dvec3(10.0, 10.0, 20.0)); + + OrientedBoundingBox l1x1y0 = + ImplicitTilingUtilities::computeBoundingVolume( + root, + QuadtreeTileID(1, 1, 0)); + CHECK(l1x1y0.getCenter() == glm::dvec3(6.0, -3.0, 3.0)); + CHECK(l1x1y0.getLengths() == glm::dvec3(10.0, 10.0, 20.0)); + + OrientedBoundingBox l1x0y1 = + ImplicitTilingUtilities::computeBoundingVolume( + root, + QuadtreeTileID(1, 0, 1)); + CHECK(l1x0y1.getCenter() == glm::dvec3(-4.0, 7.0, 3.0)); + CHECK(l1x0y1.getLengths() == glm::dvec3(10.0, 10.0, 20.0)); + } + + SECTION("octree") { + OrientedBoundingBox root(glm::dvec3(1.0, 2.0, 3.0), glm::dmat3(10.0)); + + OrientedBoundingBox l1x0y0z0 = + ImplicitTilingUtilities::computeBoundingVolume( + root, + OctreeTileID(1, 0, 0, 0)); + CHECK(l1x0y0z0.getCenter() == glm::dvec3(-4.0, -3.0, -2.0)); + CHECK(l1x0y0z0.getLengths() == glm::dvec3(10.0, 10.0, 10.0)); + + OrientedBoundingBox l1x1y0z0 = + ImplicitTilingUtilities::computeBoundingVolume( + root, + OctreeTileID(1, 1, 0, 0)); + CHECK(l1x1y0z0.getCenter() == glm::dvec3(6.0, -3.0, -2.0)); + CHECK(l1x1y0z0.getLengths() == glm::dvec3(10.0, 10.0, 10.0)); + + OrientedBoundingBox l1x0y1z0 = + ImplicitTilingUtilities::computeBoundingVolume( + root, + OctreeTileID(1, 0, 1, 0)); + CHECK(l1x0y1z0.getCenter() == glm::dvec3(-4.0, 7.0, -2.0)); + CHECK(l1x0y1z0.getLengths() == glm::dvec3(10.0, 10.0, 10.0)); + + OrientedBoundingBox l1x0y0z1 = + ImplicitTilingUtilities::computeBoundingVolume( + root, + OctreeTileID(1, 0, 0, 1)); + CHECK(l1x0y0z1.getCenter() == glm::dvec3(-4.0, -3.0, 8.0)); + CHECK(l1x0y0z1.getLengths() == glm::dvec3(10.0, 10.0, 10.0)); + } + } + + SECTION("BoundingRegion") { + SECTION("quadtree") { + BoundingRegion root(GlobeRectangle(1.0, 2.0, 3.0, 4.0), 10.0, 20.0); + + BoundingRegion l1x0y0 = ImplicitTilingUtilities::computeBoundingVolume( + root, + QuadtreeTileID(1, 0, 0)); + CHECK(l1x0y0.getRectangle().getWest() == 1.0); + CHECK(l1x0y0.getRectangle().getSouth() == 2.0); + CHECK(l1x0y0.getRectangle().getEast() == 2.0); + CHECK(l1x0y0.getRectangle().getNorth() == 3.0); + CHECK(l1x0y0.getMinimumHeight() == 10.0); + CHECK(l1x0y0.getMaximumHeight() == 20.0); + + BoundingRegion l1x1y0 = ImplicitTilingUtilities::computeBoundingVolume( + root, + QuadtreeTileID(1, 1, 0)); + CHECK(l1x1y0.getRectangle().getWest() == 2.0); + CHECK(l1x1y0.getRectangle().getSouth() == 2.0); + CHECK(l1x1y0.getRectangle().getEast() == 3.0); + CHECK(l1x1y0.getRectangle().getNorth() == 3.0); + CHECK(l1x1y0.getMinimumHeight() == 10.0); + CHECK(l1x1y0.getMaximumHeight() == 20.0); + + BoundingRegion l1x0y1 = ImplicitTilingUtilities::computeBoundingVolume( + root, + QuadtreeTileID(1, 0, 1)); + CHECK(l1x0y1.getRectangle().getWest() == 1.0); + CHECK(l1x0y1.getRectangle().getSouth() == 3.0); + CHECK(l1x0y1.getRectangle().getEast() == 2.0); + CHECK(l1x0y1.getRectangle().getNorth() == 4.0); + CHECK(l1x0y1.getMinimumHeight() == 10.0); + CHECK(l1x0y1.getMaximumHeight() == 20.0); + } + + SECTION("octree") { + BoundingRegion root(GlobeRectangle(1.0, 2.0, 3.0, 4.0), 10.0, 20.0); + + BoundingRegion l1x0y0z0 = ImplicitTilingUtilities::computeBoundingVolume( + root, + OctreeTileID(1, 0, 0, 0)); + CHECK(l1x0y0z0.getRectangle().getWest() == 1.0); + CHECK(l1x0y0z0.getRectangle().getSouth() == 2.0); + CHECK(l1x0y0z0.getRectangle().getEast() == 2.0); + CHECK(l1x0y0z0.getRectangle().getNorth() == 3.0); + CHECK(l1x0y0z0.getMinimumHeight() == 10.0); + CHECK(l1x0y0z0.getMaximumHeight() == 15.0); + + BoundingRegion l1x1y0z0 = ImplicitTilingUtilities::computeBoundingVolume( + root, + OctreeTileID(1, 1, 0, 0)); + CHECK(l1x1y0z0.getRectangle().getWest() == 2.0); + CHECK(l1x1y0z0.getRectangle().getSouth() == 2.0); + CHECK(l1x1y0z0.getRectangle().getEast() == 3.0); + CHECK(l1x1y0z0.getRectangle().getNorth() == 3.0); + CHECK(l1x1y0z0.getMinimumHeight() == 10.0); + CHECK(l1x1y0z0.getMaximumHeight() == 15.0); + + BoundingRegion l1x0y1z0 = ImplicitTilingUtilities::computeBoundingVolume( + root, + OctreeTileID(1, 0, 1, 0)); + CHECK(l1x0y1z0.getRectangle().getWest() == 1.0); + CHECK(l1x0y1z0.getRectangle().getSouth() == 3.0); + CHECK(l1x0y1z0.getRectangle().getEast() == 2.0); + CHECK(l1x0y1z0.getRectangle().getNorth() == 4.0); + CHECK(l1x0y1z0.getMinimumHeight() == 10.0); + CHECK(l1x0y1z0.getMaximumHeight() == 15.0); + + BoundingRegion l1x0y0z1 = ImplicitTilingUtilities::computeBoundingVolume( + root, + OctreeTileID(1, 0, 0, 1)); + CHECK(l1x0y0z1.getRectangle().getWest() == 1.0); + CHECK(l1x0y0z1.getRectangle().getSouth() == 2.0); + CHECK(l1x0y0z1.getRectangle().getEast() == 2.0); + CHECK(l1x0y0z1.getRectangle().getNorth() == 3.0); + CHECK(l1x0y0z1.getMinimumHeight() == 15.0); + CHECK(l1x0y0z1.getMaximumHeight() == 20.0); + } + } + + SECTION("S2") { + SECTION("quadtree") { + S2CellBoundingVolume root( + S2CellID::fromQuadtreeTileID(1, QuadtreeTileID(0, 0, 0)), + 10.0, + 20.0); + + S2CellBoundingVolume l1x0y0 = + ImplicitTilingUtilities::computeBoundingVolume( + root, + QuadtreeTileID(1, 0, 0)); + CHECK(l1x0y0.getCellID().getFace() == 1); + CHECK( + l1x0y0.getCellID().getID() == + S2CellID::fromQuadtreeTileID(1, QuadtreeTileID(1, 0, 0)).getID()); + CHECK(l1x0y0.getMinimumHeight() == 10.0); + CHECK(l1x0y0.getMaximumHeight() == 20.0); + + S2CellBoundingVolume l1x1y0 = + ImplicitTilingUtilities::computeBoundingVolume( + root, + QuadtreeTileID(1, 1, 0)); + CHECK(l1x1y0.getCellID().getFace() == 1); + CHECK( + l1x1y0.getCellID().getID() == + S2CellID::fromQuadtreeTileID(1, QuadtreeTileID(1, 1, 0)).getID()); + CHECK(l1x1y0.getMinimumHeight() == 10.0); + CHECK(l1x1y0.getMaximumHeight() == 20.0); + + S2CellBoundingVolume l1x0y1 = + ImplicitTilingUtilities::computeBoundingVolume( + root, + QuadtreeTileID(1, 0, 1)); + CHECK(l1x0y1.getCellID().getFace() == 1); + CHECK( + l1x0y1.getCellID().getID() == + S2CellID::fromQuadtreeTileID(1, QuadtreeTileID(1, 0, 1)).getID()); + CHECK(l1x0y1.getMinimumHeight() == 10.0); + CHECK(l1x0y1.getMaximumHeight() == 20.0); + } + } +} From 3a558f37f58ec188c2a699b311665623259b2e1f Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 8 Nov 2023 21:10:52 +1100 Subject: [PATCH 335/421] Make sure libmorton is treated as a system library. --- Cesium3DTilesContent/CMakeLists.txt | 4 ++++ Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp | 1 - Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp | 1 - 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Cesium3DTilesContent/CMakeLists.txt b/Cesium3DTilesContent/CMakeLists.txt index 854e36c96..1f0ec1b81 100644 --- a/Cesium3DTilesContent/CMakeLists.txt +++ b/Cesium3DTilesContent/CMakeLists.txt @@ -60,6 +60,10 @@ target_link_libraries(Cesium3DTilesContent CesiumGltf CesiumGltfReader CesiumUtility +) + +target_link_libraries_system( + Cesium3DTilesContent PRIVATE libmorton ) diff --git a/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp b/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp index 203654df1..04d0e6589 100644 --- a/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp +++ b/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp @@ -8,7 +8,6 @@ #include #include -#include #include #include diff --git a/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp b/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp index 22b7a80b3..e8dadf7df 100644 --- a/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp +++ b/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp @@ -9,7 +9,6 @@ #include #include -#include #include #include From a91979eb3c28888227e29b7ecbfaa4e64263351b Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 8 Nov 2023 21:16:11 +1100 Subject: [PATCH 336/421] Fix clang warnings. --- Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp b/Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp index e8dfec1ad..62cd74568 100644 --- a/Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp +++ b/Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp @@ -274,13 +274,13 @@ QuadtreeChildren::iterator& QuadtreeChildren::iterator::operator++() { // Set the tile coordinates based on the new value. // Bit 0 in value replaces bit 0 of the X coordinate. - this->_current.x = (this->_current.x & ~1) | (value & 1); + this->_current.x = (this->_current.x & ~1UL) | (value & 1); // Value is then shifted right one bit, so it will be 0, 1, or 2. // 0 and 1 are the bottom or top children, while 2 indicates "end" (one past // the last child). So we just clear the low bit of the current Y coordinate // and add this shifted value to produce the new Y coordinate. - this->_current.y = (this->_current.y & ~1) + (value >> 1); + this->_current.y = (this->_current.y & ~1UL) + (value >> 1); return *this; } @@ -329,14 +329,14 @@ OctreeChildren::iterator& OctreeChildren::iterator::operator++() { // Set the tile coordinates based on the new value. // Bit 0 in value replaces bit 0 of the X coordinate. // Bit 1 in the value replaces bit 0 of the Y coordinate. - this->_current.x = (this->_current.x & ~1) | (value & 1); - this->_current.y = (this->_current.y & ~1) | ((value >> 1) & 1); + this->_current.x = (this->_current.x & ~1UL) | (value & 1); + this->_current.y = (this->_current.y & ~1UL) | ((value >> 1) & 1); // Value is then shifted right one bit, so it will be 0, 1, or 2. // 0 and 1 are the bottom or top children, while 2 indicates "end" (one past // the last child). So we just clear the low bit of the current Z coordinate // and add this shifted value to produce the new Z coordinate. - this->_current.z = (this->_current.z & ~1) + (value >> 2); + this->_current.z = (this->_current.z & ~1UL) + (value >> 2); return *this; } From 885d472df7f4c26e7de6aa4c020d04e263dbef34 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 8 Nov 2023 21:19:11 +1100 Subject: [PATCH 337/421] Update CHANGES.md. --- CHANGES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 106d62e3e..317b08d9c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -16,6 +16,8 @@ - Added `transform` method to `CesiumGeometry::BoundingSphere`. - Added `toSphere`, `fromSphere`, and `fromAxisAligned` methods to `CesiumGeometry::OrientedBoundingBox`. - Added `TileTransform` class to `Cesium3DTilesContent`, making it easier to create a `glm::dmat4` from the `transform` property of a `Cesium3DTiles::Tile`. +- Added `ImplicitTilingUtilities` class to `Cesium3DTilesContent`. +- Added overloads of `isTileAvailable` and `isContentAvailable` on the `SubtreeAvailability` class that take the subtree root tile ID and the tile ID of interest, instead of a relative level and Morton index. ##### Fixes :wrench: From 6497d291bf7c328da32b93e26f6d28cafb1e473d Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 8 Nov 2023 21:34:07 +1100 Subject: [PATCH 338/421] Fix GCC warning. --- Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp b/Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp index 62cd74568..5dd19c5b8 100644 --- a/Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp +++ b/Cesium3DTilesContent/src/ImplicitTilingUtilities.cpp @@ -274,13 +274,14 @@ QuadtreeChildren::iterator& QuadtreeChildren::iterator::operator++() { // Set the tile coordinates based on the new value. // Bit 0 in value replaces bit 0 of the X coordinate. - this->_current.x = (this->_current.x & ~1UL) | (value & 1); + const uint32_t one = 1; + this->_current.x = (this->_current.x & ~one) | (value & 1); // Value is then shifted right one bit, so it will be 0, 1, or 2. // 0 and 1 are the bottom or top children, while 2 indicates "end" (one past // the last child). So we just clear the low bit of the current Y coordinate // and add this shifted value to produce the new Y coordinate. - this->_current.y = (this->_current.y & ~1UL) + (value >> 1); + this->_current.y = (this->_current.y & ~one) + (value >> 1); return *this; } @@ -329,14 +330,15 @@ OctreeChildren::iterator& OctreeChildren::iterator::operator++() { // Set the tile coordinates based on the new value. // Bit 0 in value replaces bit 0 of the X coordinate. // Bit 1 in the value replaces bit 0 of the Y coordinate. - this->_current.x = (this->_current.x & ~1UL) | (value & 1); - this->_current.y = (this->_current.y & ~1UL) | ((value >> 1) & 1); + const uint32_t one = 1; + this->_current.x = (this->_current.x & ~one) | (value & 1); + this->_current.y = (this->_current.y & ~one) | ((value >> 1) & 1); // Value is then shifted right one bit, so it will be 0, 1, or 2. // 0 and 1 are the bottom or top children, while 2 indicates "end" (one past // the last child). So we just clear the low bit of the current Z coordinate // and add this shifted value to produce the new Z coordinate. - this->_current.z = (this->_current.z & ~1UL) + (value >> 2); + this->_current.z = (this->_current.z & ~one) + (value >> 2); return *this; } From c20713c1d4143f5a7229e26b21652dd4618802d8 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Wed, 8 Nov 2023 10:03:28 -0500 Subject: [PATCH 339/421] Fixed crash when loading glTFs with data uri images --- CHANGES.md | 6 ++++++ CesiumGltfReader/src/GltfReader.cpp | 5 +++++ CesiumGltfReader/src/decodeDataUrls.cpp | 7 +++++-- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index af76ce6ce..54532ac0f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,11 @@ # Change Log +### v0.30.0 - 2023-12-01 + +##### Fixes :wrench: + +- Fixed crash when loading glTFs with data uri images. + ### v0.29.0 - 2023-11-01 ##### Breaking Changes :mega: diff --git a/CesiumGltfReader/src/GltfReader.cpp b/CesiumGltfReader/src/GltfReader.cpp index da9c5c2a6..4ddaba649 100644 --- a/CesiumGltfReader/src/GltfReader.cpp +++ b/CesiumGltfReader/src/GltfReader.cpp @@ -252,6 +252,11 @@ void postprocess( continue; } + // Image has already been decoded + if (!image.cesium.pixelData.empty()) { + continue; + } + const BufferView& bufferView = Model::getSafe(model.bufferViews, image.bufferView); const Buffer& buffer = Model::getSafe(model.buffers, bufferView.buffer); diff --git a/CesiumGltfReader/src/decodeDataUrls.cpp b/CesiumGltfReader/src/decodeDataUrls.cpp index f9bcdeed1..0775ca0c0 100644 --- a/CesiumGltfReader/src/decodeDataUrls.cpp +++ b/CesiumGltfReader/src/decodeDataUrls.cpp @@ -126,10 +126,13 @@ void decodeDataUrls( ImageReaderResult imageResult = reader.readImage(decoded.value().data, options.ktx2TranscodeTargets); - if (imageResult.image) { - image.cesium = std::move(imageResult.image.value()); + + if (!imageResult.image) { + continue; } + image.cesium = std::move(imageResult.image.value()); + if (options.clearDecodedDataUrls) { image.uri.reset(); } From ef7e8e353677c2f10bd0c1d02d609bb57b895471 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Thu, 9 Nov 2023 22:12:59 +1100 Subject: [PATCH 340/421] Start implementing SubtreeFileReader. --- .../Cesium3DTiles/{Buffer.h => BufferSpec.h} | 9 +- .../include/Cesium3DTiles/Properties.h | 4 +- .../generated/include/Cesium3DTiles/Tile.h | 2 +- Cesium3DTiles/include/Cesium3DTiles/Buffer.h | 16 ++ .../include/Cesium3DTiles/BufferCesium.h | 19 ++ Cesium3DTilesReader/CMakeLists.txt | 1 + .../Cesium3DTilesReader/SubtreeFileReader.h | 99 +++++++++ Cesium3DTilesReader/src/SubtreeFileReader.cpp | 190 ++++++++++++++++++ tools/generate-classes/3dTiles.json | 5 +- 9 files changed, 340 insertions(+), 5 deletions(-) rename Cesium3DTiles/generated/include/Cesium3DTiles/{Buffer.h => BufferSpec.h} (81%) create mode 100644 Cesium3DTiles/include/Cesium3DTiles/Buffer.h create mode 100644 Cesium3DTiles/include/Cesium3DTiles/BufferCesium.h create mode 100644 Cesium3DTilesReader/include/Cesium3DTilesReader/SubtreeFileReader.h create mode 100644 Cesium3DTilesReader/src/SubtreeFileReader.cpp diff --git a/Cesium3DTiles/generated/include/Cesium3DTiles/Buffer.h b/Cesium3DTiles/generated/include/Cesium3DTiles/BufferSpec.h similarity index 81% rename from Cesium3DTiles/generated/include/Cesium3DTiles/Buffer.h rename to Cesium3DTiles/generated/include/Cesium3DTiles/BufferSpec.h index 6645cc5b0..bc94aa084 100644 --- a/Cesium3DTiles/generated/include/Cesium3DTiles/Buffer.h +++ b/Cesium3DTiles/generated/include/Cesium3DTiles/BufferSpec.h @@ -15,7 +15,7 @@ namespace Cesium3DTiles { * @brief A buffer is a binary blob. It is either the binary chunk of the * subtree file, or an external buffer referenced by a URI. */ -struct CESIUM3DTILES_API Buffer final : public CesiumUtility::ExtensibleObject { +struct CESIUM3DTILES_API BufferSpec : public CesiumUtility::ExtensibleObject { static inline constexpr const char* TypeName = "Buffer"; /** @@ -36,5 +36,12 @@ struct CESIUM3DTILES_API Buffer final : public CesiumUtility::ExtensibleObject { * @brief The name of the buffer. */ std::optional name; + +private: + /** + * @brief This class is not meant to be instantiated directly. Use {@link Buffer} instead. + */ + BufferSpec() = default; + friend struct Buffer; }; } // namespace Cesium3DTiles diff --git a/Cesium3DTiles/generated/include/Cesium3DTiles/Properties.h b/Cesium3DTiles/generated/include/Cesium3DTiles/Properties.h index 86affa635..6fa419e5e 100644 --- a/Cesium3DTiles/generated/include/Cesium3DTiles/Properties.h +++ b/Cesium3DTiles/generated/include/Cesium3DTiles/Properties.h @@ -16,13 +16,13 @@ struct CESIUM3DTILES_API Properties final /** * @brief The maximum value of this property of all the features in the - * tileset. The maximum value shall not be larger than the minimum value. + * tileset. The maximum value shall not be smaller than the minimum value. */ double maximum = double(); /** * @brief The minimum value of this property of all the features in the - * tileset. The maximum value shall not be larger than the minimum value. + * tileset. The maximum value shall not be smaller than the minimum value. */ double minimum = double(); }; diff --git a/Cesium3DTiles/generated/include/Cesium3DTiles/Tile.h b/Cesium3DTiles/generated/include/Cesium3DTiles/Tile.h index 050830c83..15cfc1f1a 100644 --- a/Cesium3DTiles/generated/include/Cesium3DTiles/Tile.h +++ b/Cesium3DTiles/generated/include/Cesium3DTiles/Tile.h @@ -104,7 +104,7 @@ struct CESIUM3DTILES_API Tile final : public CesiumUtility::ExtensibleObject { * @brief An array of objects that define child tiles. Each child tile content * is fully enclosed by its parent tile's bounding volume and, generally, has * a geometricError less than its parent tile's geometricError. For leaf - * tiles, the length of this array is zero, and children may not be defined. + * tiles, there are no children, and this property may not be defined. */ std::vector children; }; diff --git a/Cesium3DTiles/include/Cesium3DTiles/Buffer.h b/Cesium3DTiles/include/Cesium3DTiles/Buffer.h new file mode 100644 index 000000000..42df0a3bb --- /dev/null +++ b/Cesium3DTiles/include/Cesium3DTiles/Buffer.h @@ -0,0 +1,16 @@ +#pragma once + +#include "Cesium3DTiles/BufferCesium.h" +#include "Cesium3DTiles/BufferSpec.h" +#include "Cesium3DTiles/Library.h" + +namespace Cesium3DTiles { +/** @copydoc BufferSpec */ +struct CESIUM3DTILES_API Buffer final : public BufferSpec { + /** + * @brief Holds properties that are specific to the 3D Tiles loader rather + * than part of the 3D Tiles spec. + */ + BufferCesium cesium; +}; +} // namespace Cesium3DTiles diff --git a/Cesium3DTiles/include/Cesium3DTiles/BufferCesium.h b/Cesium3DTiles/include/Cesium3DTiles/BufferCesium.h new file mode 100644 index 000000000..43c54715b --- /dev/null +++ b/Cesium3DTiles/include/Cesium3DTiles/BufferCesium.h @@ -0,0 +1,19 @@ +#pragma once + +#include "Cesium3DTiles/Library.h" + +#include +#include + +namespace Cesium3DTiles { +/** + * @brief Holds {@link Buffer} properties that are specific to the 3D Tiles loader + * rather than part of the 3D Tiles spec. + */ +struct CESIUM3DTILES_API BufferCesium final { + /** + * @brief The buffer's data. + */ + std::vector data; +}; +} // namespace Cesium3DTiles diff --git a/Cesium3DTilesReader/CMakeLists.txt b/Cesium3DTilesReader/CMakeLists.txt index 612a17e5b..a3f8b2833 100644 --- a/Cesium3DTilesReader/CMakeLists.txt +++ b/Cesium3DTilesReader/CMakeLists.txt @@ -54,6 +54,7 @@ target_include_directories( target_link_libraries(Cesium3DTilesReader PUBLIC Cesium3DTiles + CesiumAsync CesiumJsonReader GSL ) diff --git a/Cesium3DTilesReader/include/Cesium3DTilesReader/SubtreeFileReader.h b/Cesium3DTilesReader/include/Cesium3DTilesReader/SubtreeFileReader.h new file mode 100644 index 000000000..96f251d0b --- /dev/null +++ b/Cesium3DTilesReader/include/Cesium3DTilesReader/SubtreeFileReader.h @@ -0,0 +1,99 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace Cesium3DTilesReader { + +struct SubtreeReadOptions {}; + +/** + * @brief Reads 3D Tiles subtrees from a binary or JSON subtree file. + * + * While {@link SubtreeReader} can parse a {@link Subtree} from a binary buffer + * as well, `SubtreeFileReader` additionally supports: + * + * 1. Loading binary subtree files. + * 2. Loading external buffers asynchronously. + * 3. Decoding buffers from data URIs. + * + * The subtree file need not be an actual file on disk. + */ +class CESIUM3DTILESREADER_API SubtreeFileReader { +public: + /** + * @brief Constructs a new instance. + */ + SubtreeFileReader(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + CesiumJsonReader::JsonReaderOptions& getOptions(); + + /** + * @brief Gets the options controlling how the JSON is read. + */ + const CesiumJsonReader::JsonReaderOptions& getOptions() const; + + /** + * @brief Asynchronously loads a subtree from a URL. + * + * @param asyncSystem The AsyncSystem used to do asynchronous work. + * @param pAssetAccessor The accessor used to retrieve the URL and any other + * required resources. + * @param url The URL from which to get the subtree file. + * @param headers Headers to include in the request for the initial subtree + * file and any additional resources that are required. + * @return A future that resolves to the result of loading the subtree. + */ + CesiumAsync::Future> + load( + const CesiumAsync::AsyncSystem& asyncSystem, + const std::shared_ptr& pAssetAccessor, + const std::string& url, + const std::vector& headers = {}) + const noexcept; + + CesiumAsync::Future> + load( + const CesiumAsync::AsyncSystem& asyncSystem, + const std::shared_ptr& pAssetAccessor, + const std::shared_ptr& pRequest) + const noexcept; + + CesiumAsync::Future> + load( + const CesiumAsync::AsyncSystem& asyncSystem, + const std::shared_ptr& pAssetAccessor, + const std::string& url, + const gsl::span& data) const noexcept; + +private: + CesiumAsync::Future> + loadBinary( + const CesiumAsync::AsyncSystem& asyncSystem, + const std::shared_ptr& pAssetAccessor, + const std::string& url, + const gsl::span& data) const noexcept; + CesiumAsync::Future> + loadJson( + const CesiumAsync::AsyncSystem& asyncSystem, + const std::shared_ptr& pAssetAccessor, + const std::string& url, + const gsl::span& data) const noexcept; + CesiumAsync::Future> + postprocess( + const CesiumAsync::AsyncSystem& asyncSystem, + const std::shared_ptr& pAssetAccessor, + const std::string& url, + CesiumJsonReader::ReadJsonResult&& loaded) + const noexcept; + + SubtreeReader _reader; +}; + +} // namespace Cesium3DTilesReader diff --git a/Cesium3DTilesReader/src/SubtreeFileReader.cpp b/Cesium3DTilesReader/src/SubtreeFileReader.cpp new file mode 100644 index 000000000..1e68bde55 --- /dev/null +++ b/Cesium3DTilesReader/src/SubtreeFileReader.cpp @@ -0,0 +1,190 @@ +#include +#include + +using namespace Cesium3DTiles; +using namespace CesiumAsync; +using namespace CesiumJsonReader; + +namespace Cesium3DTilesReader { + +SubtreeFileReader::SubtreeFileReader() : _reader() {} + +CesiumJsonReader::JsonReaderOptions& SubtreeFileReader::getOptions() { + return this->_reader.getOptions(); +} + +const CesiumJsonReader::JsonReaderOptions& +SubtreeFileReader::getOptions() const { + return this->_reader.getOptions(); +} + +Future> SubtreeFileReader::load( + const AsyncSystem& asyncSystem, + const std::shared_ptr& pAssetAccessor, + const std::string& url, + const std::vector& headers) const noexcept { + return pAssetAccessor->get(asyncSystem, url, headers) + .thenInWorkerThread([asyncSystem, pAssetAccessor, this]( + std::shared_ptr&& pRequest) { + return this->load(asyncSystem, pAssetAccessor, pRequest); + }); +} + +Future> SubtreeFileReader::load( + const AsyncSystem& asyncSystem, + const std::shared_ptr& pAssetAccessor, + const std::shared_ptr& pRequest) const noexcept { + const IAssetResponse* pResponse = pRequest->response(); + if (pResponse == nullptr) { + ReadJsonResult result; + result.errors.emplace_back("Request failed."); + return asyncSystem.createResolvedFuture(std::move(result)); + } + + uint16_t statusCode = pResponse->statusCode(); + if (statusCode != 0 && (statusCode < 200 || statusCode >= 300)) { + CesiumJsonReader::ReadJsonResult result; + result.errors.emplace_back( + fmt::format("Request failed with status code {}", statusCode)); + return asyncSystem.createResolvedFuture(std::move(result)); + } + + return this + ->load(asyncSystem, pAssetAccessor, pRequest->url(), pResponse->data()); +} + +namespace { +constexpr const char SUBTREE_MAGIC[] = "subt"; +} + +Future> SubtreeFileReader::load( + const AsyncSystem& asyncSystem, + const std::shared_ptr& pAssetAccessor, + const std::string& url, + const gsl::span& data) const noexcept { + if (data.size() < 4) { + CesiumJsonReader::ReadJsonResult result; + result.errors.emplace_back(fmt::format( + "Subtree file has only {} bytes, which is too few to be a valid " + "subtree.", + data.size())); + return asyncSystem.createResolvedFuture(std::move(result)); + } + + bool isBinarySubtree = true; + for (std::size_t i = 0; i < 4; ++i) { + if (data[i] != static_cast(SUBTREE_MAGIC[i])) { + isBinarySubtree = false; + break; + } + } + + if (isBinarySubtree) { + return this->loadBinary(asyncSystem, pAssetAccessor, url, data); + } else { + return this->loadJson(asyncSystem, pAssetAccessor, url, data); + } +} + +namespace { + +struct SubtreeHeader { + unsigned char magic[4]; + uint32_t version; + uint64_t jsonByteLength; + uint64_t binaryByteLength; +}; + +} // namespace + +Future> SubtreeFileReader::loadBinary( + const CesiumAsync::AsyncSystem& asyncSystem, + const std::shared_ptr& pAssetAccessor, + const std::string& url, + const gsl::span& data) const noexcept { + if (data.size() < sizeof(SubtreeHeader)) { + CesiumJsonReader::ReadJsonResult result; + result.errors.emplace_back(fmt::format( + "The binary Subtree file is invalid because it is too small to include " + "a Subtree header.", + data.size())); + return asyncSystem.createResolvedFuture(std::move(result)); + } + + const SubtreeHeader* header = + reinterpret_cast(data.data()); + if (header->jsonByteLength > data.size() - sizeof(SubtreeHeader)) { + CesiumJsonReader::ReadJsonResult result; + result.errors.emplace_back(fmt::format( + "The binary Subtree file is invalid because it is too small to include " + "the jsonByteLength specified in its header.", + data.size())); + return asyncSystem.createResolvedFuture(std::move(result)); + } + + if (header->binaryByteLength > + data.size() - sizeof(SubtreeHeader) - header->jsonByteLength) { + CesiumJsonReader::ReadJsonResult result; + result.errors.emplace_back(fmt::format( + "The binary Subtree file is invalid because it is too small to include " + "the binaryByteLength specified in its header.", + data.size())); + return asyncSystem.createResolvedFuture(std::move(result)); + } + + ReadJsonResult result = this->_reader.readFromJson( + data.subspan(sizeof(SubtreeHeader), header->jsonByteLength)); + + if (result.value) { + gsl::span binaryChunk = data.subspan( + sizeof(SubtreeHeader) + header->jsonByteLength, + header->binaryByteLength); + + if (result.value->buffers.empty()) { + result.errors.emplace_back("Subtree has a binary chunk but the JSON does " + "not define any buffers."); + return asyncSystem.createResolvedFuture(std::move(result)); + } + + Buffer& buffer = result.value->buffers[0]; + if (buffer.uri) { + result.errors.emplace_back( + "Subtree has a binary chunk but the first buffer " + "in the JSON chunk also has a 'uri'."); + return asyncSystem.createResolvedFuture(std::move(result)); + } + + const int64_t binaryChunkSize = static_cast(binaryChunk.size()); + if (buffer.byteLength > binaryChunkSize || + buffer.byteLength + 3 < binaryChunkSize) { + result.errors.emplace_back("Subtree binary chunk size does not match the " + "size of the first buffer in the JSON chunk."); + return asyncSystem.createResolvedFuture(std::move(result)); + } + + buffer.cesium.data = std::vector( + binaryChunk.begin(), + binaryChunk.begin() + buffer.byteLength); + } + + return postprocess(asyncSystem, pAssetAccessor, url, std::move(result)); +} + +CesiumAsync::Future> SubtreeFileReader::loadJson( + const CesiumAsync::AsyncSystem& asyncSystem, + const std::shared_ptr& pAssetAccessor, + const std::string& url, + const gsl::span& data) const noexcept { + ReadJsonResult result = this->_reader.readFromJson(data); + return postprocess(asyncSystem, pAssetAccessor, url, std::move(result)); +} + +Future> SubtreeFileReader::postprocess( + const AsyncSystem& asyncSystem, + const std::shared_ptr& /* pAssetAccessor */, + const std::string& /* url */, + ReadJsonResult&& loaded) const noexcept { + return asyncSystem.createResolvedFuture(std::move(loaded)); +} + +} // namespace Cesium3DTilesReader diff --git a/tools/generate-classes/3dTiles.json b/tools/generate-classes/3dTiles.json index fdeef684d..b32520166 100644 --- a/tools/generate-classes/3dTiles.json +++ b/tools/generate-classes/3dTiles.json @@ -18,6 +18,9 @@ }, "Metadata Entity": { "isBaseClass": true + }, + "Buffer": { + "toBeInherited": true } }, "extensions": [ @@ -26,4 +29,4 @@ "schema": "3DTILES_bounding_volume_S2/schema/boundingVolume.3DTILES_bounding_volume_S2.schema.json" } ] -} \ No newline at end of file +} From 2bdd74a0a32ff0989abfaff2fbfcf232a1c70376 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Fri, 10 Nov 2023 23:26:42 +1100 Subject: [PATCH 341/421] Use generated classes to read subtrees. --- Cesium3DTiles/include/Cesium3DTiles/Buffer.h | 6 +- .../include/Cesium3DTiles/BufferCesium.h | 2 +- Cesium3DTilesContent/CMakeLists.txt | 1 + .../SubtreeAvailability.h | 28 +- .../src/SubtreeAvailability.cpp | 548 ++++-------------- .../test/TestSubtreeAvailability.cpp | 79 ++- .../Cesium3DTilesReader/SubtreeFileReader.h | 4 + Cesium3DTilesReader/src/SubtreeFileReader.cpp | 121 +++- .../data/ImplicitTileset/subtrees/0.0.0.json | 6 +- 9 files changed, 282 insertions(+), 513 deletions(-) diff --git a/Cesium3DTiles/include/Cesium3DTiles/Buffer.h b/Cesium3DTiles/include/Cesium3DTiles/Buffer.h index 42df0a3bb..45a292247 100644 --- a/Cesium3DTiles/include/Cesium3DTiles/Buffer.h +++ b/Cesium3DTiles/include/Cesium3DTiles/Buffer.h @@ -1,8 +1,8 @@ #pragma once -#include "Cesium3DTiles/BufferCesium.h" -#include "Cesium3DTiles/BufferSpec.h" -#include "Cesium3DTiles/Library.h" +#include +#include +#include namespace Cesium3DTiles { /** @copydoc BufferSpec */ diff --git a/Cesium3DTiles/include/Cesium3DTiles/BufferCesium.h b/Cesium3DTiles/include/Cesium3DTiles/BufferCesium.h index 43c54715b..be3e8ff6a 100644 --- a/Cesium3DTiles/include/Cesium3DTiles/BufferCesium.h +++ b/Cesium3DTiles/include/Cesium3DTiles/BufferCesium.h @@ -1,6 +1,6 @@ #pragma once -#include "Cesium3DTiles/Library.h" +#include #include #include diff --git a/Cesium3DTilesContent/CMakeLists.txt b/Cesium3DTilesContent/CMakeLists.txt index 1f0ec1b81..fd45b00a9 100644 --- a/Cesium3DTilesContent/CMakeLists.txt +++ b/Cesium3DTilesContent/CMakeLists.txt @@ -54,6 +54,7 @@ target_include_directories( target_link_libraries(Cesium3DTilesContent PUBLIC Cesium3DTiles + Cesium3DTilesReader CesiumAsync CesiumGeometry CesiumGeospatial diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/SubtreeAvailability.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/SubtreeAvailability.h index 334b71d6b..8ff4a5158 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/SubtreeAvailability.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/SubtreeAvailability.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -10,6 +11,10 @@ struct QuadtreeTileID; struct OctreeTileID; } // namespace CesiumGeometry +namespace Cesium3DTiles { +struct ImplicitTiling; +} // namespace Cesium3DTiles + namespace Cesium3DTilesContent { struct SubtreeConstantAvailability { bool constant; @@ -24,12 +29,23 @@ using AvailabilityView = class SubtreeAvailability { public: + static std::optional + fromSubtree(uint32_t powerOfTwo, Cesium3DTiles::Subtree&& subtree) noexcept; + + static CesiumAsync::Future> loadSubtree( + uint32_t powerOf2, + const CesiumAsync::AsyncSystem& asyncSystem, + const std::shared_ptr& pAssetAccessor, + const std::shared_ptr& pLogger, + const std::string& subtreeUrl, + const std::vector& requestHeaders); + SubtreeAvailability( uint32_t powerOf2, AvailabilityView tileAvailability, AvailabilityView subtreeAvailability, std::vector&& contentAvailability, - std::vector>&& buffers); + Cesium3DTiles::Subtree&& subtree); bool isTileAvailable( const CesiumGeometry::QuadtreeTileID& subtreeID, @@ -60,14 +76,6 @@ class SubtreeAvailability { bool isSubtreeAvailable(uint64_t relativeSubtreeMortonId) const noexcept; - static CesiumAsync::Future> loadSubtree( - uint32_t powerOf2, - const CesiumAsync::AsyncSystem& asyncSystem, - const std::shared_ptr& pAssetAccessor, - const std::shared_ptr& pLogger, - const std::string& subtreeUrl, - const std::vector& requestHeaders); - private: bool isAvailable( uint32_t relativeTileLevel, @@ -79,11 +87,11 @@ class SubtreeAvailability { uint64_t relativeTileMortonId, const AvailabilityView& availabilityView) const noexcept; + Cesium3DTiles::Subtree _subtree; uint32_t _childCount; uint32_t _powerOf2; AvailabilityView _tileAvailability; AvailabilityView _subtreeAvailability; std::vector _contentAvailability; - std::vector> _buffers; }; } // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/src/SubtreeAvailability.cpp b/Cesium3DTilesContent/src/SubtreeAvailability.cpp index fadbc70aa..be2e31f6c 100644 --- a/Cesium3DTilesContent/src/SubtreeAvailability.cpp +++ b/Cesium3DTilesContent/src/SubtreeAvailability.cpp @@ -1,8 +1,12 @@ +#include +#include #include #include +#include #include #include #include +#include #include #include @@ -11,93 +15,50 @@ #include #include -namespace Cesium3DTilesContent { -namespace { -constexpr const char* const SUBTREE_MAGIC = "subt"; - -struct SubtreeHeader { - unsigned char magic[4]; - uint32_t version; - uint64_t jsonByteLength; - uint64_t binaryByteLength; -}; - -struct RequestedSubtreeBuffer { - size_t idx; - std::vector data; -}; - -struct SubtreeBufferView { - size_t bufferIdx; - size_t byteOffset; - size_t byteLength; -}; - -CesiumAsync::Future requestBuffer( - const std::shared_ptr& pAssetAccessor, - const CesiumAsync::AsyncSystem& asyncSystem, - size_t bufferIdx, - std::string&& subtreeUrl, - size_t bufferLength, - const std::vector& requestHeaders) { - return pAssetAccessor->get(asyncSystem, subtreeUrl, requestHeaders) - .thenInWorkerThread( - [bufferIdx, bufferLength]( - std::shared_ptr&& pCompletedRequest) { - const CesiumAsync::IAssetResponse* pResponse = - pCompletedRequest->response(); - if (!pResponse) { - return RequestedSubtreeBuffer{bufferIdx, {}}; - } +using namespace Cesium3DTiles; +using namespace Cesium3DTilesReader; +using namespace CesiumJsonReader; - uint16_t statusCode = pResponse->statusCode(); - if (statusCode != 0 && (statusCode < 200 || statusCode >= 300)) { - return RequestedSubtreeBuffer{bufferIdx, {}}; - } - - auto data = pResponse->data(); - if (data.size() < bufferLength) { - return RequestedSubtreeBuffer{bufferIdx, {}}; - } +namespace Cesium3DTilesContent { - using vector_diff_type = - typename std::vector::difference_type; - std::vector buffer( - data.begin(), - data.begin() + static_cast(bufferLength)); - return RequestedSubtreeBuffer{bufferIdx, std::move(buffer)}; - }); -} +namespace { std::optional parseAvailabilityView( - const rapidjson::Value& availabilityJson, - const std::vector>& buffers, - const std::vector& bufferViews) { - auto constantIt = availabilityJson.FindMember("constant"); - if (constantIt != availabilityJson.MemberEnd() && - constantIt->value.IsUint()) { - return SubtreeConstantAvailability{constantIt->value.GetUint() == 1U}; + const Cesium3DTiles::Availability& availability, + const std::vector& buffers, + const std::vector& bufferViews) { + if (availability.constant) { + return SubtreeConstantAvailability{*availability.constant == 1}; } - auto bitStreamIt = availabilityJson.FindMember("bitstream"); - if (bitStreamIt == availabilityJson.MemberEnd()) { + int64_t bufferViewIndex = -1; + + if (availability.bitstream) { + bufferViewIndex = *availability.bitstream; + } else { // old version uses bufferView property instead of bitstream. Same semantic // either way - bitStreamIt = availabilityJson.FindMember("bufferView"); + auto bufferViewIt = availability.unknownProperties.find("bufferView"); + if (bufferViewIt != availability.unknownProperties.end()) { + bufferViewIndex = + bufferViewIt->second.getSafeNumberOrDefault(-1); + } } - if (bitStreamIt != availabilityJson.MemberEnd() && - bitStreamIt->value.IsUint()) { - uint32_t bufferViewIdx = bitStreamIt->value.GetUint(); - if (bufferViewIdx < bufferViews.size()) { - const SubtreeBufferView& bufferView = bufferViews[bufferViewIdx]; - - if (bufferView.bufferIdx < buffers.size()) { - const std::vector& buffer = buffers[bufferView.bufferIdx]; - if (bufferView.byteOffset + bufferView.byteLength <= buffer.size()) { - return SubtreeBufferViewAvailability{gsl::span( - buffer.data() + bufferView.byteOffset, - bufferView.byteLength)}; - } + + if (bufferViewIndex >= 0 && + bufferViewIndex < static_cast(bufferViews.size())) { + const Cesium3DTiles::BufferView& bufferView = bufferViews[bufferViewIndex]; + + if (bufferView.buffer >= 0 && + bufferView.buffer < static_cast(buffers.size())) { + const Cesium3DTiles::Buffer& buffer = buffers[bufferView.buffer]; + const std::vector& data = buffer.cesium.data; + int64_t bufferSize = + std::min(static_cast(data.size()), buffer.byteLength); + if (bufferView.byteOffset + bufferView.byteLength <= bufferSize) { + return SubtreeBufferViewAvailability{gsl::span( + data.data() + bufferView.byteOffset, + bufferView.byteLength)}; } } } @@ -105,361 +66,100 @@ std::optional parseAvailabilityView( return std::nullopt; } -std::optional createSubtreeAvailability( - uint32_t powerOf2, - const rapidjson::Document& subtreeJson, - std::vector>&& buffers) { - // make sure all the required fields exist - auto tileAvailabilityIt = subtreeJson.FindMember("tileAvailability"); - if (tileAvailabilityIt == subtreeJson.MemberEnd() || - !tileAvailabilityIt->value.IsObject()) { - return std::nullopt; - } - - auto contentAvailabilityIt = subtreeJson.FindMember("contentAvailability"); - if (contentAvailabilityIt == subtreeJson.MemberEnd() || - (!contentAvailabilityIt->value.IsArray() && - !contentAvailabilityIt->value.IsObject())) { - return std::nullopt; - } +} // namespace - auto childSubtreeAvailabilityIt = - subtreeJson.FindMember("childSubtreeAvailability"); - if (childSubtreeAvailabilityIt == subtreeJson.MemberEnd() || - !childSubtreeAvailabilityIt->value.IsObject()) { +/*static*/ std::optional SubtreeAvailability::fromSubtree( + uint32_t powerOfTwo, + Cesium3DTiles::Subtree&& subtree) noexcept { + std::optional maybeTileAvailability = parseAvailabilityView( + subtree.tileAvailability, + subtree.buffers, + subtree.bufferViews); + if (!maybeTileAvailability) return std::nullopt; - } - - std::vector bufferViews; - auto bufferViewIt = subtreeJson.FindMember("bufferViews"); - if (bufferViewIt != subtreeJson.MemberEnd() && - bufferViewIt->value.IsArray()) { - bufferViews.resize(bufferViewIt->value.Size()); - for (rapidjson::SizeType i = 0; i < bufferViewIt->value.Size(); ++i) { - const auto& bufferViewJson = bufferViewIt->value[i]; - auto bufferIdxIt = bufferViewJson.FindMember("buffer"); - auto byteOffsetIt = bufferViewJson.FindMember("byteOffset"); - auto byteLengthIt = bufferViewJson.FindMember("byteLength"); - - if (bufferIdxIt == bufferViewJson.MemberEnd() || - !bufferIdxIt->value.IsUint()) { - return std::nullopt; - } - - if (byteOffsetIt == bufferViewJson.MemberEnd() || - !byteOffsetIt->value.IsUint()) { - return std::nullopt; - } - if (byteLengthIt == bufferViewJson.MemberEnd() || - !byteLengthIt->value.IsUint()) { - return std::nullopt; - } - - bufferViews[i].bufferIdx = bufferIdxIt->value.GetUint(); - bufferViews[i].byteOffset = byteOffsetIt->value.GetUint(); - bufferViews[i].byteLength = byteLengthIt->value.GetUint(); - } - } - - auto tileAvailability = - parseAvailabilityView(tileAvailabilityIt->value, buffers, bufferViews); - if (!tileAvailability) { + std::optional maybeChildSubtreeAvailability = + parseAvailabilityView( + subtree.childSubtreeAvailability, + subtree.buffers, + subtree.bufferViews); + if (!maybeChildSubtreeAvailability) return std::nullopt; - } - auto childSubtreeAvailability = parseAvailabilityView( - childSubtreeAvailabilityIt->value, - buffers, - bufferViews); - if (!childSubtreeAvailability) { + // At least one element is required in contentAvailability. + if (subtree.contentAvailability.empty()) return std::nullopt; - } std::vector contentAvailability; - if (contentAvailabilityIt->value.IsArray()) { - contentAvailability.reserve(contentAvailabilityIt->value.Size()); - for (const auto& contentAvailabilityJson : - contentAvailabilityIt->value.GetArray()) { - auto availability = - parseAvailabilityView(contentAvailabilityJson, buffers, bufferViews); - if (!availability) { - return std::nullopt; - } - - contentAvailability.emplace_back(*availability); + contentAvailability.reserve(subtree.contentAvailability.size()); + + for (const auto& availabilityDesc : subtree.contentAvailability) { + auto maybeAvailability = parseAvailabilityView( + availabilityDesc, + subtree.buffers, + subtree.bufferViews); + if (maybeAvailability) { + contentAvailability.emplace_back(std::move(*maybeAvailability)); } - } else { - auto availability = parseAvailabilityView( - contentAvailabilityIt->value, - buffers, - bufferViews); - if (!availability) { - return std::nullopt; - } - - contentAvailability.emplace_back(*availability); } - return SubtreeAvailability{ - powerOf2, - *tileAvailability, - *childSubtreeAvailability, + return SubtreeAvailability( + powerOfTwo, + *maybeTileAvailability, + *maybeChildSubtreeAvailability, std::move(contentAvailability), - std::move(buffers)}; + std::move(subtree)); } -CesiumAsync::Future> parseJsonSubtree( +/*static*/ CesiumAsync::Future> +SubtreeAvailability::loadSubtree( uint32_t powerOf2, - CesiumAsync::AsyncSystem&& asyncSystem, - std::shared_ptr&& pAssetAccessor, - std::shared_ptr&& pLogger, - std::vector&& requestHeaders, - const std::string& baseUrl, - rapidjson::Document&& subtreeJson, - std::vector&& internalBuffer) { - // resolve all the buffers - std::vector> resolvedBuffers; - auto bufferIt = subtreeJson.FindMember("buffers"); - if (bufferIt != subtreeJson.MemberEnd() && bufferIt->value.IsArray()) { - const auto& arrayBufferJsons = bufferIt->value.GetArray(); - resolvedBuffers.resize(arrayBufferJsons.Size()); - - std::vector> requestBuffers; - for (rapidjson::SizeType i = 0; i < arrayBufferJsons.Size(); ++i) { - const auto& bufferJson = arrayBufferJsons[i]; - auto byteLengthIt = bufferJson.FindMember("byteLength"); - if (byteLengthIt == bufferJson.MemberEnd() || - !byteLengthIt->value.IsUint()) { - SPDLOG_LOGGER_ERROR( - pLogger, - "Subtree Buffer requires byteLength property."); - return asyncSystem - .createResolvedFuture>( - std::nullopt); - } - - size_t byteLength = byteLengthIt->value.GetUint(); - - auto uriIt = bufferJson.FindMember("uri"); - if (uriIt != bufferJson.MemberEnd()) { - if (!uriIt->value.IsString()) { - SPDLOG_LOGGER_ERROR( - pLogger, - "Subtree Buffer has uri field but it's not string."); - return asyncSystem - .createResolvedFuture>( - std::nullopt); - } - - std::string bufferUrl = - CesiumUtility::Uri::resolve(baseUrl, uriIt->value.GetString()); - requestBuffers.emplace_back(requestBuffer( - pAssetAccessor, - asyncSystem, - i, - std::move(bufferUrl), - byteLength, - requestHeaders)); - } else if ( - !internalBuffer.empty() && internalBuffer.size() >= byteLength) { - resolvedBuffers[i] = std::move(internalBuffer); - } - } - - // if we have buffers to request, resolve them now and then, create - // SubtreeAvailability later - if (!requestBuffers.empty()) { - return asyncSystem.all(std::move(requestBuffers)) - .thenInWorkerThread([powerOf2, - resolvedBuffers = std::move(resolvedBuffers), - subtreeJson = std::move(subtreeJson)]( - std::vector&& - completedBuffers) mutable { - for (auto& requestedBuffer : completedBuffers) { - resolvedBuffers[requestedBuffer.idx] = - std::move(requestedBuffer.data); + const CesiumAsync::AsyncSystem& asyncSystem, + const std::shared_ptr& pAssetAccessor, + const std::shared_ptr& pLogger, + const std::string& subtreeUrl, + const std::vector& requestHeaders) { + SubtreeFileReader reader; + return reader.load(asyncSystem, pAssetAccessor, subtreeUrl, requestHeaders) + .thenInMainThread( + [pLogger, subtreeUrl, powerOf2](ReadJsonResult&& subtree) + -> std::optional { + if (!subtree.value) { + if (!subtree.errors.empty()) { + SPDLOG_LOGGER_ERROR( + pLogger, + "Errors while loading subtree from {}:\n- {}", + subtreeUrl, + CesiumUtility::joinToString(subtree.errors, "\n- ")); + } + if (!subtree.warnings.empty()) { + SPDLOG_LOGGER_WARN( + pLogger, + "Warnings while loading subtree from {}:\n- {}", + subtreeUrl, + CesiumUtility::joinToString(subtree.warnings, "\n- ")); + } + return std::nullopt; } - return createSubtreeAvailability( + return SubtreeAvailability::fromSubtree( powerOf2, - subtreeJson, - std::move(resolvedBuffers)); + std::move(*subtree.value)); }); - } - } - - return asyncSystem.createResolvedFuture(createSubtreeAvailability( - powerOf2, - subtreeJson, - std::move(resolvedBuffers))); -} - -CesiumAsync::Future> parseJsonSubtreeRequest( - uint32_t powerOf2, - CesiumAsync::AsyncSystem&& asyncSystem, - std::shared_ptr&& pAssetAccessor, - std::shared_ptr&& pLogger, - std::shared_ptr&& pCompletedRequest, - std::vector&& requestHeaders) { - const auto* pResponse = pCompletedRequest->response(); - gsl::span data = pResponse->data(); - - rapidjson::Document subtreeJson; - subtreeJson.Parse(reinterpret_cast(data.data()), data.size()); - if (subtreeJson.HasParseError()) { - SPDLOG_LOGGER_ERROR( - pLogger, - "Error when parsing feature table JSON, error code {} at byte offset " - "{}", - subtreeJson.GetParseError(), - subtreeJson.GetErrorOffset()); - return asyncSystem.createResolvedFuture>( - std::nullopt); - } - - return parseJsonSubtree( - powerOf2, - std::move(asyncSystem), - std::move(pAssetAccessor), - std::move(pLogger), - std::move(requestHeaders), - pCompletedRequest->url(), - std::move(subtreeJson), - {}); -} - -CesiumAsync::Future> -parseBinarySubtreeRequest( - uint32_t powerOf2, - CesiumAsync::AsyncSystem&& asyncSystem, - std::shared_ptr&& pAssetAccessor, - std::shared_ptr&& pLogger, - std::shared_ptr&& pCompletedRequest, - std::vector&& requestHeaders) { - const auto* pResponse = pCompletedRequest->response(); - gsl::span data = pResponse->data(); - - size_t headerLength = sizeof(SubtreeHeader); - if (data.size() < headerLength) { - SPDLOG_LOGGER_ERROR( - pLogger, - "The Subtree file is invalid because it is too " - "small to include a Subtree header."); - return asyncSystem.createResolvedFuture>( - std::nullopt); - } - - const SubtreeHeader* header = - reinterpret_cast(data.data()); - if (header->jsonByteLength > data.size() - headerLength) { - SPDLOG_LOGGER_ERROR( - pLogger, - "The Subtree file is invalid because it is too " - "small to include the jsonByteLength specified in its header."); - return asyncSystem.createResolvedFuture>( - std::nullopt); - } - - if (header->binaryByteLength > - data.size() - headerLength - header->jsonByteLength) { - SPDLOG_LOGGER_ERROR( - pLogger, - "The Subtree file is invalid because it is too " - "small to include the binaryByteLength specified in its header."); - return asyncSystem.createResolvedFuture>( - std::nullopt); - } - - rapidjson::Document subtreeJson; - subtreeJson.Parse( - reinterpret_cast(data.data() + headerLength), - header->jsonByteLength); - if (subtreeJson.HasParseError()) { - SPDLOG_LOGGER_ERROR( - pLogger, - "Error when parsing feature table JSON, error code {} at byte offset " - "{}", - subtreeJson.GetParseError(), - subtreeJson.GetErrorOffset()); - return asyncSystem.createResolvedFuture>( - std::nullopt); - } - - // get the internal buffer if there is any - std::vector internalBuffer; - if (header->binaryByteLength > 0) { - using vector_diff_type = typename std::vector::difference_type; - auto begin = data.begin() + static_cast(headerLength) + - static_cast(header->jsonByteLength); - auto end = begin + static_cast(header->binaryByteLength); - internalBuffer.insert(internalBuffer.end(), begin, end); - } - - return parseJsonSubtree( - powerOf2, - std::move(asyncSystem), - std::move(pAssetAccessor), - std::move(pLogger), - std::move(requestHeaders), - pCompletedRequest->url(), - std::move(subtreeJson), - std::move(internalBuffer)); } -CesiumAsync::Future> parseSubtreeRequest( - uint32_t powerOf2, - CesiumAsync::AsyncSystem&& asyncSystem, - std::shared_ptr&& pAssetAccessor, - std::shared_ptr&& pLogger, - std::shared_ptr&& pCompletedRequest, - std::vector&& requestHeaders) { - const auto* pResponse = pCompletedRequest->response(); - gsl::span data = pResponse->data(); - - // check if this is binary subtree - bool isBinarySubtree = true; - if (data.size() >= 4) { - for (std::size_t i = 0; i < 4; ++i) { - if (data[i] != static_cast(SUBTREE_MAGIC[i])) { - isBinarySubtree = false; - break; - } - } - } - - if (isBinarySubtree) { - return parseBinarySubtreeRequest( - powerOf2, - std::move(asyncSystem), - std::move(pAssetAccessor), - std::move(pLogger), - std::move(pCompletedRequest), - std::move(requestHeaders)); - } else { - return parseJsonSubtreeRequest( - powerOf2, - std::move(asyncSystem), - std::move(pAssetAccessor), - std::move(pLogger), - std::move(pCompletedRequest), - std::move(requestHeaders)); - } -} -} // namespace - SubtreeAvailability::SubtreeAvailability( uint32_t powerOf2, AvailabilityView tileAvailability, AvailabilityView subtreeAvailability, std::vector&& contentAvailability, - std::vector>&& buffers) - : _childCount{1U << powerOf2}, + Cesium3DTiles::Subtree&& subtree) + : _subtree(std::move(subtree)), + _childCount{1U << powerOf2}, _powerOf2{powerOf2}, _tileAvailability{tileAvailability}, _subtreeAvailability{subtreeAvailability}, - _contentAvailability{std::move(contentAvailability)}, - _buffers{std::move(buffers)} { + _contentAvailability{std::move(contentAvailability)} { assert( (this->_childCount == 4 || this->_childCount == 8) && "Only support quadtree and octree"); @@ -542,46 +242,6 @@ bool SubtreeAvailability::isSubtreeAvailable( this->_subtreeAvailability); } -CesiumAsync::Future> -SubtreeAvailability::loadSubtree( - uint32_t powerOf2, - const CesiumAsync::AsyncSystem& asyncSystem, - const std::shared_ptr& pAssetAccessor, - const std::shared_ptr& pLogger, - const std::string& subtreeUrl, - const std::vector& requestHeaders) { - return pAssetAccessor->get(asyncSystem, subtreeUrl, requestHeaders) - .thenInWorkerThread([powerOf2, - asyncSystem = asyncSystem, - pAssetAccessor = pAssetAccessor, - pLogger = pLogger, - requestHeaders = requestHeaders]( - std::shared_ptr&& - pCompletedRequest) mutable { - const auto* pResponse = pCompletedRequest->response(); - if (!pResponse) { - return asyncSystem - .createResolvedFuture>( - std::nullopt); - } - - uint16_t statusCode = pResponse->statusCode(); - if (statusCode != 0 && (statusCode < 200 || statusCode >= 300)) { - return asyncSystem - .createResolvedFuture>( - std::nullopt); - } - - return parseSubtreeRequest( - powerOf2, - std::move(asyncSystem), - std::move(pAssetAccessor), - std::move(pLogger), - std::move(pCompletedRequest), - std::move(requestHeaders)); - }); -} - bool SubtreeAvailability::isAvailable( uint32_t relativeTileLevel, uint64_t relativeTileMortonId, diff --git a/Cesium3DTilesContent/test/TestSubtreeAvailability.cpp b/Cesium3DTilesContent/test/TestSubtreeAvailability.cpp index 75923d819..3546de655 100644 --- a/Cesium3DTilesContent/test/TestSubtreeAvailability.cpp +++ b/Cesium3DTilesContent/test/TestSubtreeAvailability.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -13,6 +14,7 @@ #include #include +using namespace Cesium3DTiles; using namespace Cesium3DTilesContent; using namespace CesiumNativeTests; @@ -371,9 +373,32 @@ TEST_CASE("Test SubtreeAvailability methods") { uint64_t subtreeBufferSize = static_cast( std::ceil(static_cast(maxSubtreeTiles) / 8.0)); - std::vector contentAvailabilityBuffer(bufferSize); - std::vector tileAvailabilityBuffer(bufferSize); - std::vector subtreeAvailabilityBuffer(subtreeBufferSize); + Subtree subtree; + subtree.buffers.resize(3); + subtree.bufferViews.resize(3); + + std::vector& contentAvailabilityBuffer = + subtree.buffers[0].cesium.data; + std::vector& tileAvailabilityBuffer = + subtree.buffers[1].cesium.data; + std::vector& subtreeAvailabilityBuffer = + subtree.buffers[2].cesium.data; + + subtree.bufferViews[0].buffer = 0; + subtree.bufferViews[1].buffer = 1; + subtree.bufferViews[2].buffer = 2; + + contentAvailabilityBuffer.resize(bufferSize); + tileAvailabilityBuffer.resize(bufferSize); + subtreeAvailabilityBuffer.resize(subtreeBufferSize); + + subtree.buffers[0].byteLength = subtree.bufferViews[0].byteLength = + bufferSize; + subtree.buffers[1].byteLength = subtree.bufferViews[1].byteLength = + bufferSize; + subtree.buffers[2].byteLength = subtree.bufferViews[2].byteLength = + subtreeBufferSize; + for (const auto& tileID : availableTileIDs) { markTileAvailableForQuadtree(tileID, tileAvailabilityBuffer); markTileAvailableForQuadtree(tileID, contentAvailabilityBuffer); @@ -383,22 +408,18 @@ TEST_CASE("Test SubtreeAvailability methods") { markSubtreeAvailableForQuadtree(subtreeID, subtreeAvailabilityBuffer); } - std::vector> buffers{ - std::move(tileAvailabilityBuffer), - std::move(subtreeAvailabilityBuffer), - std::move(contentAvailabilityBuffer)}; - - SubtreeBufferViewAvailability tileAvailability{buffers[0]}; - SubtreeBufferViewAvailability subtreeAvailability{buffers[1]}; + SubtreeBufferViewAvailability tileAvailability{tileAvailabilityBuffer}; + SubtreeBufferViewAvailability subtreeAvailability{ + subtreeAvailabilityBuffer}; std::vector contentAvailability{ - SubtreeBufferViewAvailability{buffers[2]}}; + SubtreeBufferViewAvailability{contentAvailabilityBuffer}}; SubtreeAvailability quadtreeAvailability( 2, tileAvailability, subtreeAvailability, std::move(contentAvailability), - std::move(buffers)); + std::move(subtree)); SECTION("isTileAvailable()") { for (const auto& tileID : availableTileIDs) { @@ -544,7 +565,7 @@ TEST_CASE("Test parsing subtree format") { asyncSystem.dispatchMainThreadTasks(); auto parsedSubtree = subtreeFuture.wait(); - CHECK(parsedSubtree != std::nullopt); + REQUIRE(parsedSubtree != std::nullopt); for (const auto& tileID : availableTileIDs) { uint64_t mortonID = libmorton::morton2D_64_encode(tileID.x, tileID.y); @@ -575,7 +596,7 @@ TEST_CASE("Test parsing subtree format") { auto parsedSubtree = mockLoadSubtreeJson(std::move(subtreeBuffers), std::move(subtreeJson)); - CHECK(parsedSubtree != std::nullopt); + REQUIRE(parsedSubtree != std::nullopt); for (const auto& tileID : availableTileIDs) { uint64_t mortonID = libmorton::morton2D_64_encode(tileID.x, tileID.y); @@ -665,35 +686,5 @@ TEST_CASE("Test parsing subtree format") { std::move(subtreeBuffers), std::move(subtreeJson)) == std::nullopt); } - - SECTION("Buffer view does not have required buffer field") { - auto bufferViewIt = subtreeJson.FindMember("bufferViews"); - auto bufferViewObj = bufferViewIt->value.GetArray().Begin(); - bufferViewObj->RemoveMember("buffer"); - CHECK( - mockLoadSubtreeJson( - std::move(subtreeBuffers), - std::move(subtreeJson)) == std::nullopt); - } - - SECTION("Buffer view does not have required byteOffset field") { - auto bufferViewIt = subtreeJson.FindMember("bufferViews"); - auto bufferViewObj = bufferViewIt->value.GetArray().Begin(); - bufferViewObj->RemoveMember("byteOffset"); - CHECK( - mockLoadSubtreeJson( - std::move(subtreeBuffers), - std::move(subtreeJson)) == std::nullopt); - } - - SECTION("Buffer view does not have required byteLength field") { - auto bufferViewIt = subtreeJson.FindMember("bufferViews"); - auto bufferViewObj = bufferViewIt->value.GetArray().Begin(); - bufferViewObj->RemoveMember("byteLength"); - CHECK( - mockLoadSubtreeJson( - std::move(subtreeBuffers), - std::move(subtreeJson)) == std::nullopt); - } } } diff --git a/Cesium3DTilesReader/include/Cesium3DTilesReader/SubtreeFileReader.h b/Cesium3DTilesReader/include/Cesium3DTilesReader/SubtreeFileReader.h index 96f251d0b..9e7b0b678 100644 --- a/Cesium3DTilesReader/include/Cesium3DTilesReader/SubtreeFileReader.h +++ b/Cesium3DTilesReader/include/Cesium3DTilesReader/SubtreeFileReader.h @@ -70,6 +70,7 @@ class CESIUM3DTILESREADER_API SubtreeFileReader { const CesiumAsync::AsyncSystem& asyncSystem, const std::shared_ptr& pAssetAccessor, const std::string& url, + const std::vector& requestHeaders, const gsl::span& data) const noexcept; private: @@ -78,18 +79,21 @@ class CESIUM3DTILESREADER_API SubtreeFileReader { const CesiumAsync::AsyncSystem& asyncSystem, const std::shared_ptr& pAssetAccessor, const std::string& url, + const std::vector& requestHeaders, const gsl::span& data) const noexcept; CesiumAsync::Future> loadJson( const CesiumAsync::AsyncSystem& asyncSystem, const std::shared_ptr& pAssetAccessor, const std::string& url, + const std::vector& requestHeaders, const gsl::span& data) const noexcept; CesiumAsync::Future> postprocess( const CesiumAsync::AsyncSystem& asyncSystem, const std::shared_ptr& pAssetAccessor, const std::string& url, + const std::vector& requestHeaders, CesiumJsonReader::ReadJsonResult&& loaded) const noexcept; diff --git a/Cesium3DTilesReader/src/SubtreeFileReader.cpp b/Cesium3DTilesReader/src/SubtreeFileReader.cpp index 1e68bde55..dbc0461b8 100644 --- a/Cesium3DTilesReader/src/SubtreeFileReader.cpp +++ b/Cesium3DTilesReader/src/SubtreeFileReader.cpp @@ -1,5 +1,6 @@ #include #include +#include using namespace Cesium3DTiles; using namespace CesiumAsync; @@ -49,8 +50,15 @@ Future> SubtreeFileReader::load( return asyncSystem.createResolvedFuture(std::move(result)); } - return this - ->load(asyncSystem, pAssetAccessor, pRequest->url(), pResponse->data()); + std::vector requestHeaders( + pRequest->headers().begin(), + pRequest->headers().end()); + return this->load( + asyncSystem, + pAssetAccessor, + pRequest->url(), + requestHeaders, + pResponse->data()); } namespace { @@ -61,6 +69,7 @@ Future> SubtreeFileReader::load( const AsyncSystem& asyncSystem, const std::shared_ptr& pAssetAccessor, const std::string& url, + const std::vector& requestHeaders, const gsl::span& data) const noexcept { if (data.size() < 4) { CesiumJsonReader::ReadJsonResult result; @@ -80,9 +89,11 @@ Future> SubtreeFileReader::load( } if (isBinarySubtree) { - return this->loadBinary(asyncSystem, pAssetAccessor, url, data); + return this + ->loadBinary(asyncSystem, pAssetAccessor, url, requestHeaders, data); } else { - return this->loadJson(asyncSystem, pAssetAccessor, url, data); + return this + ->loadJson(asyncSystem, pAssetAccessor, url, requestHeaders, data); } } @@ -101,6 +112,7 @@ Future> SubtreeFileReader::loadBinary( const CesiumAsync::AsyncSystem& asyncSystem, const std::shared_ptr& pAssetAccessor, const std::string& url, + const std::vector& requestHeaders, const gsl::span& data) const noexcept { if (data.size() < sizeof(SubtreeHeader)) { CesiumJsonReader::ReadJsonResult result; @@ -167,23 +179,116 @@ Future> SubtreeFileReader::loadBinary( binaryChunk.begin() + buffer.byteLength); } - return postprocess(asyncSystem, pAssetAccessor, url, std::move(result)); + return postprocess( + asyncSystem, + pAssetAccessor, + url, + requestHeaders, + std::move(result)); } CesiumAsync::Future> SubtreeFileReader::loadJson( const CesiumAsync::AsyncSystem& asyncSystem, const std::shared_ptr& pAssetAccessor, const std::string& url, + const std::vector& requestHeaders, const gsl::span& data) const noexcept { ReadJsonResult result = this->_reader.readFromJson(data); - return postprocess(asyncSystem, pAssetAccessor, url, std::move(result)); + return postprocess( + asyncSystem, + pAssetAccessor, + url, + requestHeaders, + std::move(result)); } +namespace { + +struct RequestedSubtreeBuffer { + size_t index; + std::vector data; +}; + +CesiumAsync::Future requestBuffer( + const std::shared_ptr& pAssetAccessor, + const CesiumAsync::AsyncSystem& asyncSystem, + size_t bufferIdx, + std::string&& subtreeUrl, + const std::vector& requestHeaders) { + return pAssetAccessor->get(asyncSystem, subtreeUrl, requestHeaders) + .thenInWorkerThread( + [bufferIdx]( + std::shared_ptr&& pCompletedRequest) { + const CesiumAsync::IAssetResponse* pResponse = + pCompletedRequest->response(); + if (!pResponse) { + return RequestedSubtreeBuffer{bufferIdx, {}}; + } + + uint16_t statusCode = pResponse->statusCode(); + if (statusCode != 0 && (statusCode < 200 || statusCode >= 300)) { + return RequestedSubtreeBuffer{bufferIdx, {}}; + } + + const gsl::span& data = pResponse->data(); + return RequestedSubtreeBuffer{ + bufferIdx, + std::vector(data.begin(), data.end())}; + }); +} + +} // namespace + Future> SubtreeFileReader::postprocess( const AsyncSystem& asyncSystem, - const std::shared_ptr& /* pAssetAccessor */, - const std::string& /* url */, + const std::shared_ptr& pAssetAccessor, + const std::string& url, + const std::vector& requestHeaders, ReadJsonResult&& loaded) const noexcept { + if (!loaded.value) { + return asyncSystem.createResolvedFuture(std::move(loaded)); + } + + // Load any external buffers + std::vector> bufferRequests; + const std::vector& buffers = loaded.value->buffers; + for (size_t i = 0; i < buffers.size(); ++i) { + const Buffer& buffer = buffers[i]; + if (buffer.uri && !buffer.uri->empty()) { + std::string bufferUrl = CesiumUtility::Uri::resolve(url, *buffer.uri); + bufferRequests.emplace_back(requestBuffer( + pAssetAccessor, + asyncSystem, + i, + std::move(bufferUrl), + requestHeaders)); + } + } + + if (!bufferRequests.empty()) { + return asyncSystem.all(std::move(bufferRequests)) + .thenInMainThread( + [loaded = std::move(loaded)](std::vector&& + completedBuffers) mutable { + for (RequestedSubtreeBuffer& completedBuffer : completedBuffers) { + Buffer& buffer = loaded.value->buffers[completedBuffer.index]; + if (buffer.byteLength > + static_cast(completedBuffer.data.size())) { + loaded.warnings.emplace_back(fmt::format( + "Buffer byteLength ({}) is greater than the size of the " + "downloaded resource ({} bytes). The byteLength will be " + "updated to match.", + buffer.byteLength, + completedBuffer.data.size())); + buffer.byteLength = completedBuffer.data.size(); + } + buffer.cesium.data = std::move(completedBuffer.data); + } + + return std::move(loaded); + }); + } + return asyncSystem.createResolvedFuture(std::move(loaded)); } diff --git a/Cesium3DTilesSelection/test/data/ImplicitTileset/subtrees/0.0.0.json b/Cesium3DTilesSelection/test/data/ImplicitTileset/subtrees/0.0.0.json index c68b89173..401dca6a1 100644 --- a/Cesium3DTilesSelection/test/data/ImplicitTileset/subtrees/0.0.0.json +++ b/Cesium3DTilesSelection/test/data/ImplicitTileset/subtrees/0.0.0.json @@ -2,10 +2,10 @@ "tileAvailability": { "constant": 1 }, - "contentAvailability": { + "contentAvailability": [{ "constant": 1 - }, + }], "childSubtreeAvailability": { "constant": 0 } -} \ No newline at end of file +} From 31afce9bef5725ff5abb0fe6fa56ba7c296de607 Mon Sep 17 00:00:00 2001 From: Janine Liu <32226860+j9liu@users.noreply.github.com> Date: Fri, 10 Nov 2023 10:57:51 -0500 Subject: [PATCH 342/421] Apply suggestions from code review --- Cesium3DTilesSelection/src/TilesetContentManager.cpp | 1 - Cesium3DTilesSelection/test/TestTilesetJsonLoader.cpp | 1 - 2 files changed, 2 deletions(-) diff --git a/Cesium3DTilesSelection/src/TilesetContentManager.cpp b/Cesium3DTilesSelection/src/TilesetContentManager.cpp index 8d4e46d7c..8ad812a41 100644 --- a/Cesium3DTilesSelection/src/TilesetContentManager.cpp +++ b/Cesium3DTilesSelection/src/TilesetContentManager.cpp @@ -1173,7 +1173,6 @@ bool TilesetContentManager::tileNeedsMainThreadLoading( tile.isRenderContent(); } -namespace {} void TilesetContentManager::finishLoading( Tile& tile, diff --git a/Cesium3DTilesSelection/test/TestTilesetJsonLoader.cpp b/Cesium3DTilesSelection/test/TestTilesetJsonLoader.cpp index ee5d273b3..2a30acbcf 100644 --- a/Cesium3DTilesSelection/test/TestTilesetJsonLoader.cpp +++ b/Cesium3DTilesSelection/test/TestTilesetJsonLoader.cpp @@ -18,7 +18,6 @@ using namespace CesiumAsync; using namespace Cesium3DTilesSelection; using namespace CesiumNativeTests; -using namespace CesiumNativeTests; namespace { std::filesystem::path testDataPath = Cesium3DTilesSelection_TEST_DATA_DIR; From 684396aa1254e274d5cb12c462cb6b4e3aaef004 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Fri, 10 Nov 2023 10:59:21 -0500 Subject: [PATCH 343/421] Formatting --- Cesium3DTilesSelection/src/TilesetContentManager.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Cesium3DTilesSelection/src/TilesetContentManager.cpp b/Cesium3DTilesSelection/src/TilesetContentManager.cpp index 8ad812a41..0d3b9f489 100644 --- a/Cesium3DTilesSelection/src/TilesetContentManager.cpp +++ b/Cesium3DTilesSelection/src/TilesetContentManager.cpp @@ -1173,7 +1173,6 @@ bool TilesetContentManager::tileNeedsMainThreadLoading( tile.isRenderContent(); } - void TilesetContentManager::finishLoading( Tile& tile, const TilesetOptions& tilesetOptions) { @@ -1432,8 +1431,8 @@ void TilesetContentManager::unloadDoneState(Tile& tile) { pRenderContent->setRenderResources(nullptr); } -void TilesetContentManager::notifyTileStartLoading( - [[maybe_unused]] const Tile* pTile) noexcept { +void TilesetContentManager::notifyTileStartLoading([ + [maybe_unused]] const Tile* pTile) noexcept { ++this->_tileLoadsInProgress; } From 6d62a76691e3021cb953f8a6bd3423882950bb7c Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Fri, 10 Nov 2023 11:03:21 -0500 Subject: [PATCH 344/421] Proper formatting --- Cesium3DTilesSelection/src/TilesetContentManager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cesium3DTilesSelection/src/TilesetContentManager.cpp b/Cesium3DTilesSelection/src/TilesetContentManager.cpp index 0d3b9f489..669af3119 100644 --- a/Cesium3DTilesSelection/src/TilesetContentManager.cpp +++ b/Cesium3DTilesSelection/src/TilesetContentManager.cpp @@ -1431,8 +1431,8 @@ void TilesetContentManager::unloadDoneState(Tile& tile) { pRenderContent->setRenderResources(nullptr); } -void TilesetContentManager::notifyTileStartLoading([ - [maybe_unused]] const Tile* pTile) noexcept { +void TilesetContentManager::notifyTileStartLoading( + [[maybe_unused]] const Tile* pTile) noexcept { ++this->_tileLoadsInProgress; } From c702a92a01f2c252f4f430f1d0f4a8f3fdbc3c4d Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Fri, 10 Nov 2023 18:11:38 -0500 Subject: [PATCH 345/421] Begin implementing conversion logic --- .../include/CesiumGltf/MetadataConversions.h | 722 ++++++++++++++++++ CesiumGltf/test/TestMetadataConversions.cpp | 647 ++++++++++++++++ 2 files changed, 1369 insertions(+) create mode 100644 CesiumGltf/include/CesiumGltf/MetadataConversions.h create mode 100644 CesiumGltf/test/TestMetadataConversions.cpp diff --git a/CesiumGltf/include/CesiumGltf/MetadataConversions.h b/CesiumGltf/include/CesiumGltf/MetadataConversions.h new file mode 100644 index 000000000..0738c4b82 --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/MetadataConversions.h @@ -0,0 +1,722 @@ +#pragma once + +#include "PropertyTypeTraits.h" + +#include + +#include +#include + +#include +#include +#include + +namespace CesiumGltf { +/** + * @brief Default conversion between two types. No actual conversion is defined; + * this simply returns the default value. + */ +template +struct MetadataConversions { + static TTo convert(TFrom /*from*/, TTo defaultValue) { return defaultValue; } +}; + +/** + * @brief Trivially converts any type to itself. + */ +template struct MetadataConversions { + static T convert(T from, T /* defaultValue*/) { return from; } +}; + +#pragma region Conversions to boolean +/** + * @brief Converts from a scalar to a bool. + */ +template +struct MetadataConversions< + bool, + TFrom, + std::enable_if_t::value>> { + /** + * @brief Converts a scalar to a boolean. Zero is converted to false, while + * nonzero values are converted to true. + * + * @param from The scalar to convert from. + * @param defaultValue The default value to be returned if conversion fails. + */ + static bool convert(TFrom from, bool /*defaultValue*/) { + return from != static_cast(0); + } +}; + +/** + * @brief Converts from std::string_view to a bool. + */ +template <> struct MetadataConversions { +private: + static bool + isEqualCaseInsensitive(const std::string_view& a, const std::string_view& b) { + if (a.size() != b.size()) { + return false; + } + + for (size_t i = 0; i < a.size(); i++) { + if (std::tolower(a[i]) != std::tolower(b[i])) { + return false; + } + } + return true; + } + +public: + /** + * @brief Converts the contents of a std::string_view to a boolean. + * + * "0", "false", and "no" (case-insensitive) are converted to false, while + * "1", "true", and "yes" are converted to true. All other strings will return + * the default value. + * + * @param from The std::string_view to convert from. + * @param defaultValue The default value to be returned if conversion fails. + */ + static bool convert(const std::string_view& from, bool defaultValue) { + if (isEqualCaseInsensitive(from, "1") || + isEqualCaseInsensitive(from, "true") || + isEqualCaseInsensitive(from, "yes")) { + return true; + } + + if (isEqualCaseInsensitive(from, "0") || + isEqualCaseInsensitive(from, "false") || + isEqualCaseInsensitive(from, "no")) { + return false; + } + + return defaultValue; + } +}; + +#pragma endregion + +#pragma region Conversions to integer +/** + * @brief Converts from one integer type to another. + */ +template +struct MetadataConversions< + TTo, + TFrom, + std::enable_if_t< + CesiumGltf::IsMetadataInteger::value && + CesiumGltf::IsMetadataInteger::value && + !std::is_same_v>> { + /** + * @brief Converts a value of the given integer to another integer type. If + * the integer cannot be losslessly converted to the desired type, the default + * value is returned. + * + * @param from The integer value to convert from. + * @param defaultValue The default value to be returned if conversion fails. + */ + static TTo convert(TFrom from, TTo defaultValue) { + return CesiumUtility::losslessNarrowOrDefault(from, defaultValue); + } +}; + +/** + * @brief Converts from a floating-point type to an integer. + */ +template +struct MetadataConversions< + TTo, + TFrom, + std::enable_if_t< + CesiumGltf::IsMetadataInteger::value && + CesiumGltf::IsMetadataFloating::value>> { + /** + * @brief Converts a floating-point value to an integer type. This truncates + * the floating-point value, rounding it towards zero. + * + * If the value is outside the range of the integer type, the default value is + * returned. + * + * @param from The floating-point value to convert from. + * @param defaultValue The default value to be returned if conversion fails. + */ + static TTo convert(TFrom from, TTo defaultValue) { + if (double(std::numeric_limits::max()) < from || + double(std::numeric_limits::lowest()) > from) { + // Floating-point number is outside the range of this integer type. + return defaultValue; + } + + return static_cast(from); + } +}; + +/** + * @brief Converts from std::string_view to a signed integer. + */ +template +struct MetadataConversions< + TTo, + std::string_view, + std::enable_if_t< + CesiumGltf::IsMetadataInteger::value && std::is_signed_v>> { + /** + * @brief Converts the contents of a std::string_view to a signed integer. + * This assumes that the entire std::string_view represents the number, not + * just a part of it. + * + * This returns the default value if no number is parsed from the string. + * + * @param from The std::string_view to parse from. + * @param defaultValue The default value to be returned if conversion fails. + */ + static TTo convert(const std::string_view& from, TTo defaultValue) { + // Amazingly, C++ has no* string parsing functions that work with strings + // that might not be null-terminated. So we have to copy to a std::string + // (which _is_ guaranteed to be null terminated) before parsing. + // * except std::from_chars, but compiler/library support for the + // floating-point version of that method is spotty at best. + std::string temp(from); + + char* pLastUsed; + int64_t parsedValue = std::strtoll(temp.c_str(), &pLastUsed, 10); + if (pLastUsed == temp.c_str() + temp.size()) { + // Successfully parsed the entire string as an integer of this type. + return CesiumUtility::losslessNarrowOrDefault(parsedValue, defaultValue); + } + + // Failed to parse as an integer. Maybe we can parse as a double and + // truncate it? + double parsedDouble = std::strtod(temp.c_str(), &pLastUsed); + if (pLastUsed == temp.c_str() + temp.size()) { + // Successfully parsed the entire string as a double. + // Convert it to an integer if we can. + double truncated = glm::trunc(parsedDouble); + + int64_t asInteger = static_cast(truncated); + double roundTrip = static_cast(asInteger); + if (roundTrip == truncated) { + return CesiumUtility::losslessNarrowOrDefault(asInteger, defaultValue); + } + } + + return defaultValue; + } +}; + +/** + * @brief Converts from std::string_view to an unsigned integer. + */ +template +struct MetadataConversions< + TTo, + std::string_view, + std::enable_if_t< + CesiumGltf::IsMetadataInteger::value && !std::is_signed_v>> { + /** + * @brief Converts the contents of a std::string_view to an signed integer. + * This assumes that the entire std::string_view represents the number, not + * just a part of it. + * + * This returns the default value if no number is parsed from the string. + * + * @param from The std::string_view to parse from. + * @param defaultValue The default value to be returned if conversion fails. + */ + static TTo convert(const std::string_view& from, TTo defaultValue) { + // Amazingly, C++ has no* string parsing functions that work with strings + // that might not be null-terminated. So we have to copy to a std::string + // (which _is_ guaranteed to be null terminated) before parsing. + // * except std::from_chars, but compiler/library support for the + // floating-point version of that method is spotty at best. + std::string temp(from); + + char* pLastUsed; + uint64_t parsedValue = std::strtoull(temp.c_str(), &pLastUsed, 10); + if (pLastUsed == temp.c_str() + temp.size()) { + // Successfully parsed the entire string as an integer of this type. + return CesiumUtility::losslessNarrowOrDefault(parsedValue, defaultValue); + } + + // Failed to parse as an integer. Maybe we can parse as a double and + // truncate it? + double parsedDouble = std::strtod(temp.c_str(), &pLastUsed); + if (pLastUsed == temp.c_str() + temp.size()) { + // Successfully parsed the entire string as a double. + // Convert it to an integer if we can. + double truncated = glm::trunc(parsedDouble); + + uint64_t asInteger = static_cast(truncated); + double roundTrip = static_cast(asInteger); + if (roundTrip == truncated) { + return CesiumUtility::losslessNarrowOrDefault(asInteger, defaultValue); + } + } + + return defaultValue; + } +}; + +/** + * @brief Converts from a boolean to an integer type. + */ +template +struct MetadataConversions< + TTo, + bool, + std::enable_if_t::value>> { + /** + * @brief Converts a boolean to an integer. This returns 1 for true, 0 for + * false. + * + * @param from The boolean value to be converted. + * @param defaultValue The default value to be returned if conversion fails. + */ + static TTo convert(bool from, TTo /*defaultValue*/) { return from ? 1 : 0; } +}; +#pragma endregion + +#pragma region Conversions to float +/** + * @brief Converts from a boolean to a float. + */ +template <> struct MetadataConversions { + /** + * @brief Converts a boolean to a float. This returns 1.0f for true, 0.0f for + * false. + * + * @param from The boolean value to be converted. + * @param defaultValue The default value to be returned if conversion fails. + */ + static float convert(bool from, float /*defaultValue*/) { + return from ? 1.0f : 0.0f; + } +}; + +/** + * @brief Converts from an integer type to a float. + */ +template +struct MetadataConversions< + float, + TFrom, + std::enable_if_t::value>> { + /** + * @brief Converts an integer to a float. The value may lose precision during + * conversion. + * + * @param from The integer value to be converted. + * @param defaultValue The default value to be returned if conversion fails. + */ + static float convert(TFrom from, float /*defaultValue*/) { + return static_cast(from); + } +}; + +/** + * @brief Converts from a double to a float. + */ +template <> struct MetadataConversions { + /** + * @brief Converts a double to a float. The value may lose precision during + * conversion. + * + * If the value is outside the range of a float, the default value is + * returned. + * + * @param from The double value to be converted. + * @param defaultValue The default value to be returned if conversion fails. + */ + static float convert(double from, float defaultValue) { + if (from > std::numeric_limits::max() || + from < std::numeric_limits::lowest()) { + return defaultValue; + } + return static_cast(from); + } +}; + +/** + * @brief Converts from a std::string_view to a float. + */ +template <> struct MetadataConversions { + /** + * @brief Converts a std::string_view to a float. This assumes that the entire + * std::string_view represents the number, not just a part of it. + * + * This returns the default value if no number is parsed from the string. + * + * @param from The std::string_view to parse from. + * @param defaultValue The default value to be returned if conversion fails. + */ + static float convert(const std::string_view& from, float defaultValue) { + // Amazingly, C++ has no* string parsing functions that work with strings + // that might not be null-terminated. So we have to copy to a std::string + // (which _is_ guaranteed to be null terminated) before parsing. + // * except std::from_chars, but compiler/library support for the + // floating-point version of that method is spotty at best. + std::string temp(from); + + char* pLastUsed; + float parsedValue = std::strtof(temp.c_str(), &pLastUsed); + if (pLastUsed == temp.c_str() + temp.size() && !std::isinf(parsedValue)) { + // Successfully parsed the entire string as a float. + return parsedValue; + } + return defaultValue; + } +}; +#pragma endregion + +#pragma region Conversions to double +/** + * @brief Converts from a boolean to a double. + */ +template <> struct MetadataConversions { + /** + * @brief Converts a boolean to a double. This returns 1.0 for true, 0.0 for + * false. + * + * @param from The boolean value to be converted. + * @param defaultValue The default value to be returned if conversion fails. + */ + static double convert(bool from, double /*defaultValue*/) { + return from ? 1.0 : 0.0; + } +}; + +/** + * @brief Converts from any integer type to a double. + */ +template +struct MetadataConversions< + double, + TFrom, + std::enable_if_t::value>> { + /** + * @brief Converts any integer type to a double. The value may lose precision + * during conversion. + * + * @param from The boolean value to be converted. + * @param defaultValue The default value to be returned if conversion fails. + */ + static double convert(TFrom from, double /*defaultValue*/) { + return static_cast(from); + } +}; + +/** + * @brief Converts from a float to a double. + */ +template <> struct MetadataConversions { + /** + * @brief Converts from a float to a double. + * + * @param from The float value to be converted. + * @param defaultValue The default value to be returned if conversion fails. + */ + static double convert(float from, double /*defaultValue*/) { + return static_cast(from); + } +}; + +/** + * @brief Converts from std::string_view to a double. + */ +template <> struct MetadataConversions { + /** + * Converts a std::string_view to a double. This assumes that the entire + * std::string_view represents the number, not just a part of it. + * + * This returns the default value if no number is parsed from the string. + * + * @param from The std::string_view to parse from. + * @param defaultValue The default value to be returned if conversion fails. + */ + static double convert(const std::string_view& from, double defaultValue) { + // Amazingly, C++ has no* string parsing functions that work with strings + // that might not be null-terminated. So we have to copy to a std::string + // (which _is_ guaranteed to be null terminated) before parsing. + // * except std::from_chars, but compiler/library support for the + // floating-point version of that method is spotty at best. + std::string temp(from); + + char* pLastUsed; + double parsedValue = std::strtod(temp.c_str(), &pLastUsed); + if (pLastUsed == temp.c_str() + temp.size() && !std::isinf(parsedValue)) { + // Successfully parsed the entire string as a double. + return parsedValue; + } + return defaultValue; + } +}; +#pragma endregion + +#pragma region Conversions to string +/** + * @brief Converts from a boolean to a string. + */ +template <> struct MetadataConversions { + /** + * @brief Converts a boolean to a FString. Returns "true" for true and "false" + * for false. + * + * @param from The boolean to be converted. + * @param defaultValue The default value to be returned if conversion fails. + */ + static std::string convert(bool from, const std::string& /*defaultValue*/) { + return from ? "true" : "false"; + } +}; + +/** + * @brief Converts from a scalar to a string. + */ +template +struct MetadataConversions< + std::string, + TFrom, + std::enable_if_t::value>> { + /** + * @brief Converts a scalar to a FString. + * + * @param from The scalar to be converted. + * @param defaultValue The default value to be returned if conversion fails. + */ + static std::string convert(TFrom from, const std::string& /*defaultValue*/) { + return std::to_string(from); + } +}; + +/** + * @brief Converts from a glm::vecN to a string. + */ +template +struct MetadataConversions< + std::string, + TFrom, + std::enable_if_t::value>> { + /** + * @brief Converts a glm::vecN to a FString. This uses the format that + * glm::to_string() outputs for vecNs, + * + * @param from The glm::vecN to be converted. + * @param defaultValue The default value to be returned if conversion fails. + */ + static std::string + convert(const TFrom& from, const std::string& /*defaultValue*/) { + return glm::to_string(from); + } +}; + +/** + * @brief Converts from a glm::matN to a string. + */ +template +struct MetadataConversions< + std::string, + TFrom, + std::enable_if_t::value>> { + /** + * @brief Converts a glm::matN to a FString. This uses the format that + * glm::to_string() outputs for matNs. + * + * @param from The glm::matN to be converted. + * @param defaultValue The default value to be returned if conversion fails. + */ + static std::string + convert(const TFrom& from, const std::string& /*defaultValue*/) { + return glm::to_string(from); + } +}; + +/** + * @brief Converts from a std::string_view to a std::string. + */ +template <> struct MetadataConversions { + /** + * @brief Converts from a std::string_view to a std::string. + */ + static std::string + convert(const std::string_view& from, const std::string& /*defaultValue*/) { + return std::string(from.data(), from.size()); + } +}; + +#pragma endregion + +#pragma region Conversions to glm::vecN +/** + * Converts from a boolean to a vecN. + */ +template +struct MetadataConversions< + TTo, + bool, + std::enable_if_t::value>> { + /** + * Converts a boolean to a vecN. The boolean is converted to an integer + * value of 1 for true or 0 for false. The returned vector is initialized with + * this value in both of its components. + * + * @param from The boolean to be converted. + * @param defaultValue The default value to be returned if conversion fails. + */ + static TTo convert(bool from, const TTo& /*defaultValue*/) { + return from ? TTo(1) : TTo(0); + } +}; + +/** + * Converts from a signed integer type to a vecN. + */ +template +struct MetadataConversions< + TTo, + TFrom, + std::enable_if_t< + CesiumGltf::IsMetadataVecN::value && + CesiumGltf::IsMetadataInteger::value && + std::is_signed_v>> { + /** + * Converts a signed integer to a vecN. The returned vector is initialized + * with the value in all of its components. If the integer cannot be + * reasonably converted to the component type of the vecN, the default value + * is returned. + * + * @param from The signed integer value to be converted. + * @param defaultValue The default value to be returned if conversion fails. + */ + static TTo convert(TFrom from, const TTo& defaultValue) { + using ToValueType = typename TTo::value_type; + ToValueType max = std::numeric_limits::max(); + + if constexpr (std::is_signed_v) { + ToValueType min = std::numeric_limits::lowest(); + if (from > max || from < min) { + return defaultValue; + } + } else { + if (from > static_cast(max)) { + return defaultValue; + } + } + return TTo(MetadataConversions::convert(from, 0)); + } +}; + +/** + * Converts from an unsigned integer type to a vecN. + */ +template +struct MetadataConversions< + TTo, + TFrom, + std::enable_if_t< + CesiumGltf::IsMetadataVecN::value && + CesiumGltf::IsMetadataInteger::value && + std::is_unsigned_v>> { + /** + * Converts an unsigned integer to a vecN. The returned vector is initialized + * with the value in all of its components. If the integer cannot be + * reasonably converted to the component type of the vecN, the default value + * is returned. + * + * @param from The unsigned integer value to be converted. + * @param defaultValue The default value to be returned if conversion fails. + */ + static TTo convert(TFrom from, const TTo& defaultValue) { + using ToValueType = typename TTo::value_type; + ToValueType max = std::numeric_limits::max(); + + if (from > static_cast(max)) { + return defaultValue; + } + return TTo(MetadataConversions::convert(from, 0)); + } +}; + +/** + * Converts from a floating-point scalar type to a vecN. + */ +template +struct MetadataConversions< + TTo, + TFrom, + std::enable_if_t< + CesiumGltf::IsMetadataVecN::value && + CesiumGltf::IsMetadataFloating::value>> { + /** + * Converts a floating-point scalar to a vecN. The returned vector is + * initialized with the value in all of its components. If the scalar cannot + * be reasonably converted to the component type of the vecN, the default + * value is returned. The value may lose precision during conversion. + * + * @param from The floating-point scalar value to be converted. + * @param defaultValue The default value to be returned if conversion fails. + */ + static TTo convert(TFrom from, const TTo& defaultValue) { + using ToValueType = typename TTo::value_type; + ToValueType max = std::numeric_limits::max(); + ToValueType min = std::numeric_limits::lowest(); + if (from > static_cast(max) || from < static_cast(min)) { + return defaultValue; + } + return TTo(MetadataConversions::convert(from, 0)); + } +}; + +/** + * @brief Converts from a vecN type to another vecN type. + */ +template +struct MetadataConversions< + TTo, + TFrom, + std::enable_if_t< + CesiumGltf::IsMetadataVecN::value && + CesiumGltf::IsMetadataVecN::value && + !std::is_same_v>> { + /** + * @brief Converts a value of the given vecN to another vecN type. If the + * given vector has more components than the target vecN type, then only its + * first N components will be used. Otherwise, if the target vecN type has + * more components, the extra components will be initialized to zero. + * + * If any of the relevant components cannot be converted to the target vecN + * component type, the default value is returned. + * + * @param from The vecN value to convert from. + * @param defaultValue The default value to be returned if conversion fails. + */ + static TTo convert(TFrom from, TTo defaultValue) { + TTo result = TTo(0); + + constexpr glm::length_t toLength = TTo::length(); + constexpr glm::length_t fromLength = TFrom::length(); + + constexpr glm::length_t validLength = glm::min(toLength, fromLength); + + using ToValueType = typename TTo::value_type; + ToValueType max = std::numeric_limits::max(); + ToValueType min = std::numeric_limits::lowest(); + + using FromValueType = typename TFrom::value_type; + + for (glm::length_t i = 0; i < validLength; i++) { + if (from[i] > max || from[i] < min) { + return defaultValue; + } + result[i] = + MetadataConversions::convert(from[i], 0); + } + + return result; + } +}; +#pragma endregion + +} // namespace CesiumGltf diff --git a/CesiumGltf/test/TestMetadataConversions.cpp b/CesiumGltf/test/TestMetadataConversions.cpp new file mode 100644 index 000000000..e414b2c6d --- /dev/null +++ b/CesiumGltf/test/TestMetadataConversions.cpp @@ -0,0 +1,647 @@ +#include "CesiumGltf/MetadataConversions.h" + +#include + +using namespace CesiumGltf; + +TEST_CASE("Test PropertyConversions for boolean") { + SECTION("converts from boolean") { + REQUIRE(MetadataConversions::convert(true, false)); + REQUIRE(!MetadataConversions::convert(false, true)); + } + + SECTION("converts from scalar") { + // true for nonzero value + REQUIRE(MetadataConversions::convert(10, false)); + // false for zero value + REQUIRE(!MetadataConversions::convert(0, true)); + } + + SECTION("converts from string") { + std::string_view stringView("true"); + REQUIRE(MetadataConversions::convert( + stringView, + false)); + + stringView = std::string_view("yes"); + REQUIRE(MetadataConversions::convert( + stringView, + false)); + + stringView = std::string_view("1"); + REQUIRE(MetadataConversions::convert( + stringView, + false)); + + stringView = std::string_view("false"); + REQUIRE(!MetadataConversions::convert( + stringView, + true)); + + stringView = std::string_view("no"); + REQUIRE(!MetadataConversions::convert( + stringView, + true)); + + stringView = std::string_view("0"); + REQUIRE(!MetadataConversions::convert( + stringView, + true)); + } + + SECTION("uses default value for incompatible strings") { + std::string_view stringView("11"); + // invalid number + REQUIRE(!MetadataConversions::convert( + stringView, + false)); + + stringView = std::string_view("this is true"); + // invalid word + REQUIRE(!MetadataConversions::convert( + stringView, + false)); + } + + SECTION("uses default value for incompatible types") { + // vecN + REQUIRE(!MetadataConversions::convert( + glm::vec3(1, 2, 3), + false)); + // matN + REQUIRE(!MetadataConversions::convert(glm::mat2(), false)); + // array + REQUIRE(!MetadataConversions>::convert( + PropertyArrayView(), + false)); + } +} + +TEST_CASE("Test PropertyConversions for integer") { + SECTION("converts from in-range integer") { + // same type + REQUIRE(MetadataConversions::convert(50, 0) == 50); + // different size + REQUIRE(MetadataConversions::convert(50, 0) == 50); + // different sign + REQUIRE(MetadataConversions::convert(50, 0) == 50); + } + + SECTION("converts from in-range floating point number") { + REQUIRE(MetadataConversions::convert(50.125f, 0) == 50); + REQUIRE( + MetadataConversions::convert(1234.05678f, 0) == 1234); + } + + SECTION("converts from boolean") { + REQUIRE(MetadataConversions::convert(true, -1) == 1); + REQUIRE(MetadataConversions::convert(false, -1) == 0); + } + + SECTION("converts from string") { + // integer string + std::string_view value("-123"); + REQUIRE( + MetadataConversions::convert(value, 0) == + -123); + // double string + value = std::string_view("123.456"); + REQUIRE( + MetadataConversions::convert(value, 0) == + 123); + } + + SECTION("uses default value for out-of-range numbers") { + // out-of-range unsigned int + REQUIRE( + MetadataConversions::convert( + std::numeric_limits::max(), + 0) == 0); + // out-of-range signed int + REQUIRE( + MetadataConversions::convert( + std::numeric_limits::min(), + 0) == 0); + // out-of-range float + REQUIRE(MetadataConversions::convert(1234.56f, 0) == 0); + // out-of-range double + REQUIRE( + MetadataConversions::convert( + std::numeric_limits::max(), + 0) == 0); + } + + SECTION("uses default value for invalid strings") { + // out-of-range number + REQUIRE( + MetadataConversions::convert( + std::string_view("-255"), + 0) == 0); + // mixed number and non-number input + REQUIRE( + MetadataConversions::convert( + std::string_view("10 hello"), + 0) == 0); + // non-number input + REQUIRE( + MetadataConversions::convert( + std::string_view("not a number"), + 0) == 0); + // empty input + REQUIRE( + MetadataConversions::convert( + std::string_view(), + 0) == 0); + } + + SECTION("uses default value for incompatible types") { + // vecN + REQUIRE( + MetadataConversions::convert( + glm::ivec3(1, 2, 3), + 0) == 0); + // matN + REQUIRE( + MetadataConversions::convert( + glm::imat2x2(), + 0) == 0); + // array + PropertyArrayView arrayView; + REQUIRE( + MetadataConversions>::convert( + arrayView, + 0) == 0); + } +} + +TEST_CASE("Test PropertyConversions for float") { + SECTION("converts from in-range floating point number") { + REQUIRE( + MetadataConversions::convert(123.45f, 0.0f) == 123.45f); + REQUIRE( + MetadataConversions::convert(123.45, 0.0f) == + static_cast(123.45)); + } + + SECTION("converts from integer") { + int32_t int32Value = -1234; + REQUIRE( + MetadataConversions::convert(int32Value, 0.0f) == + static_cast(int32Value)); + constexpr uint64_t uint64Value = std::numeric_limits::max(); + REQUIRE( + MetadataConversions::convert(uint64Value, 0.0f) == + static_cast(uint64Value)); + } + + SECTION("converts from boolean") { + REQUIRE(MetadataConversions::convert(true, -1.0f) == 1.0f); + REQUIRE(MetadataConversions::convert(false, -1.0f) == 0.0f); + } + + SECTION("converts from string") { + REQUIRE( + MetadataConversions::convert( + std::string_view("123"), + 0) == static_cast(123)); + REQUIRE( + MetadataConversions::convert( + std::string_view("123.456"), + 0) == static_cast(123.456)); + } + + SECTION("uses default value for invalid strings") { + // out-of-range number + REQUIRE( + MetadataConversions::convert( + std::string_view( + std::to_string(std::numeric_limits::max())), + 0.0f) == 0.0f); + // mixed number and non-number input + REQUIRE( + MetadataConversions::convert( + std::string_view("10.00f hello"), + 0.0f) == 0.0f); + // non-number input + REQUIRE( + MetadataConversions::convert( + std::string_view("not a number"), + 0.0f) == 0.0f); + // empty input + REQUIRE( + MetadataConversions::convert( + std::string_view(), + 0.0f) == 0.0f); + } + + SECTION("uses default value for incompatible types") { + // vecN + REQUIRE( + MetadataConversions::convert( + glm::vec3(1, 2, 3), + 0.0f) == 0.0f); + // matN + REQUIRE( + MetadataConversions::convert(glm::mat2(), 0.0f) == + 0.0f); + // array + PropertyArrayView arrayView; + REQUIRE( + MetadataConversions>::convert( + arrayView, + 0.0f) == 0.0f); + } +} + +TEST_CASE("Test PropertyConversions for double") { + SECTION("converts from floating point number") { + REQUIRE( + MetadataConversions::convert(123.45f, 0.0) == + static_cast(123.45f)); + REQUIRE( + MetadataConversions::convert(123.45, 0.0) == 123.45); + } + + SECTION("converts from integer") { + constexpr uint64_t uint64Value = std::numeric_limits::max(); + REQUIRE( + MetadataConversions::convert(uint64Value, 0.0) == + static_cast(uint64Value)); + } + + SECTION("converts from boolean") { + REQUIRE(MetadataConversions::convert(true, -1.0) == 1.0); + REQUIRE(MetadataConversions::convert(false, -1.0) == 0.0); + } + + SECTION("converts from string") { + REQUIRE( + MetadataConversions::convert( + std::string_view("123"), + 0) == static_cast(123)); + REQUIRE( + MetadataConversions::convert( + std::string_view("123.456"), + 0) == 123.456); + } + + SECTION("uses default value for invalid strings") { + // mixed number and non-number input + REQUIRE( + MetadataConversions::convert( + std::string_view("10.00 hello"), + 0.0) == 0.0); + // non-number input + REQUIRE( + MetadataConversions::convert( + std::string_view("not a number"), + 0.0) == 0.0); + // empty input + REQUIRE( + MetadataConversions::convert( + std::string_view(), + 0.0) == 0.0); + } + + SECTION("uses default value for incompatible types") { + // vecN + REQUIRE( + MetadataConversions::convert( + glm::dvec3(1, 2, 3), + 0.0) == 0.0); + // matN + REQUIRE( + MetadataConversions::convert( + glm::dmat2(1.0, 2.0, 3.0, 4.0), + 0.0) == 0.0); + // array + PropertyArrayView arrayView; + REQUIRE( + MetadataConversions>::convert( + arrayView, + 0.0) == 0.0); + } +} + +TEST_CASE("Test PropertyConversions for vec2") { + SECTION("converts from same vec2 type") { + // int-to-int + REQUIRE( + MetadataConversions::convert( + glm::ivec2(12, 76), + glm::ivec2(0)) == glm::ivec2(12, 76)); + // float-to-float + REQUIRE( + MetadataConversions::convert( + glm::vec2(-3.5f, 1.234f), + glm::vec2(0.0)) == glm::vec2(-3.5f, 1.234f)); + } + + SECTION("converts from other vec2 types") { + // int-to-int + REQUIRE( + MetadataConversions::convert( + glm::u8vec2(12, 76), + glm::ivec2(0)) == glm::ivec2(12, 76)); + // int-to-float + REQUIRE( + MetadataConversions::convert( + glm::ivec2(12, 76), + glm::vec2(0)) == glm::vec2(12, 76)); + // float-to-int + REQUIRE( + MetadataConversions::convert( + glm::dvec2(-3.5, 1.23456), + glm::i8vec2(0)) == glm::i8vec2(-3, 1)); + // float-to-float + REQUIRE( + MetadataConversions::convert( + glm::vec2(-3.5f, 1.234f), + glm::dvec2(0.0)) == glm::dvec2(-3.5f, 1.234f)); + } + + SECTION("converts from vec3 types") { + // int-to-int + REQUIRE( + MetadataConversions::convert( + glm::ivec3(-84, 5, 129), + glm::i8vec2(0)) == glm::i8vec2(-84, 5)); + // int-to-float + REQUIRE( + MetadataConversions::convert( + glm::ivec3(-84, 5, 25), + glm::dvec2(0.0)) == glm::dvec2(-84, 5)); + // float-to-int + REQUIRE( + MetadataConversions::convert( + glm::vec3(4.5f, 2.345f, 81.0f), + glm::uvec2(0)) == glm::uvec2(4, 2)); + // float-to-float + REQUIRE( + MetadataConversions::convert( + glm::dvec3(4.5, -2.345, 81.0), + glm::vec2(0)) == + glm::vec2(static_cast(4.5), static_cast(-2.345))); + } + + SECTION("converts from vec4 types") { + // int-to-int + REQUIRE( + MetadataConversions::convert( + glm::i16vec4(-42, 278, 23, 1), + glm::ivec2(0)) == glm::ivec2(-42, 278)); + // float-to-int + REQUIRE( + MetadataConversions::convert( + glm::dvec4(-3.5, 1.23456, 26.0, 8.0), + glm::i8vec2(0)) == glm::i8vec2(-3, 1)); + } + + SECTION("converts from boolean") { + REQUIRE( + MetadataConversions::convert( + true, + glm::dvec2(-1.0)) == glm ::dvec2(1.0)); + } + + SECTION("converts from integer") { + // int to int + REQUIRE( + MetadataConversions::convert( + 45, + glm::u8vec2(0)) == glm::u8vec2(45)); + REQUIRE( + MetadataConversions::convert( + 45, + glm::i16vec2(0)) == glm::i16vec2(45)); + // int to float + REQUIRE( + MetadataConversions::convert( + -12345, + glm::dvec2(0)) == glm::dvec2(-12345)); + REQUIRE( + MetadataConversions::convert(12, glm::vec2(0)) == + glm::vec2(12)); + } + + SECTION("converts from float") { + // float to int + REQUIRE( + MetadataConversions::convert( + 45.4f, + glm::u8vec2(0)) == glm::u8vec2(45)); + REQUIRE( + MetadataConversions::convert( + -1.0111, + glm::i16vec2(0)) == glm::i16vec2(-1)); + // float to float + REQUIRE( + MetadataConversions::convert( + -1234.5f, + glm::dvec2(0)) == glm::dvec2(-1234.5f)); + REQUIRE( + MetadataConversions::convert(12.0, glm::vec2(0)) == + glm::vec2(12.0)); + } + + SECTION("uses default value if not all components can be converted") { + // scalar + REQUIRE( + MetadataConversions::convert( + -1, + glm::u8vec2(0)) == glm::u8vec2(0)); + // int + REQUIRE( + MetadataConversions::convert( + glm::ivec3(0, -1, 2), + glm::u8vec2(0)) == glm::u8vec2(0)); + REQUIRE( + MetadataConversions::convert( + glm::u8vec4(0, 255, 2, 1), + glm::i8vec2(0)) == glm::i8vec2(0)); + // float + REQUIRE( + MetadataConversions::convert( + glm::vec2(129.0f, -129.0f), + glm::i8vec2(0)) == glm::i8vec2(0)); + REQUIRE( + MetadataConversions::convert( + glm::dvec3(std::numeric_limits::max()), + glm::vec2(0)) == glm::vec2(0)); + }; + + SECTION("uses default value for incompatible types") { + // matN + REQUIRE( + MetadataConversions::convert( + glm::dmat2(1.0, 2.0, 3.0, 4.0), + glm::dvec2(0.0)) == glm::dvec2(0.0)); + // array + PropertyArrayView arrayView; + REQUIRE( + MetadataConversions>::convert( + arrayView, + glm::ivec2(0)) == glm::ivec2(0)); + }; +} + +TEST_CASE("Test PropertyConversions for vec3") { + SECTION("converts from same vec3 type") { + // int-to-int + REQUIRE( + MetadataConversions::convert( + glm::ivec3(12, 76, -1), + glm::ivec3(0)) == glm::ivec3(12, 76, -1)); + // float-to-float + REQUIRE( + MetadataConversions::convert( + glm::vec3(-3.5f, 1.234f, 1.0f), + glm::vec3(0.0)) == glm::vec3(-3.5f, 1.234f, 1.0f)); + } + + SECTION("converts from other vec3 types") { + // int-to-int + REQUIRE( + MetadataConversions::convert( + glm::u8vec3(12, 76, 1), + glm::ivec3(0)) == glm::ivec3(12, 76, 1)); + // int-to-float + REQUIRE( + MetadataConversions::convert( + glm::ivec3(12, 76, 1), + glm::vec3(0)) == glm::vec3(12, 76, 1)); + // float-to-int + REQUIRE( + MetadataConversions::convert( + glm::dvec3(-3.5, 1.23456, -1.40), + glm::i8vec3(0)) == glm::i8vec3(-3, 1, -1)); + // float-to-float + REQUIRE( + MetadataConversions::convert( + glm::vec3(-3.5f, 1.234f, 2.4f), + glm::dvec3(0.0)) == glm::dvec3(-3.5f, 1.234f, 2.4f)); + } + + SECTION("converts from vec2 types") { + // int-to-int + REQUIRE( + MetadataConversions::convert( + glm::ivec2(-84, 5), + glm::i8vec3(0)) == glm::i8vec3(-84, 5, 0)); + // int-to-float + REQUIRE( + MetadataConversions::convert( + glm::ivec2(-84, 5), + glm::dvec3(0.0)) == glm::dvec3(-84, 5, 0)); + // float-to-int + REQUIRE( + MetadataConversions::convert( + glm::vec2(4.5f, 2.345f), + glm::uvec3(0)) == glm::uvec3(4, 2, 0)); + // float-to-float + REQUIRE( + MetadataConversions::convert( + glm::dvec2(4.5, -2.345), + glm::vec3(0)) == glm::vec3(4.5, -2.345, 0)); + } + + SECTION("converts from vec4 types") { + // int-to-int + REQUIRE( + MetadataConversions::convert( + glm::i16vec4(-42, 278, 23, 1), + glm::ivec3(0)) == glm::ivec3(-42, 278, 23)); + // float-to-int + REQUIRE( + MetadataConversions::convert( + glm::dvec4(-3.5, 1.23456, 26.0, 8.0), + glm::i8vec3(0)) == glm::i8vec3(-3, 1, 26)); + } + + SECTION("converts from boolean") { + REQUIRE( + MetadataConversions::convert( + true, + glm::dvec3(-1.0)) == glm ::dvec3(1.0)); + } + + SECTION("converts from integer") { + // int to int + REQUIRE( + MetadataConversions::convert( + 45, + glm::u8vec3(0)) == glm::u8vec3(45)); + REQUIRE( + MetadataConversions::convert( + 45, + glm::i16vec3(0)) == glm::i16vec3(45)); + // int to float + REQUIRE( + MetadataConversions::convert( + -12345, + glm::dvec3(0)) == glm::dvec3(-12345)); + REQUIRE( + MetadataConversions::convert(12, glm::vec3(0)) == + glm::vec3(12)); + } + + SECTION("converts from float") { + // float to int + REQUIRE( + MetadataConversions::convert( + 45.4f, + glm::u8vec3(0)) == glm::u8vec3(45)); + REQUIRE( + MetadataConversions::convert( + -1.0111, + glm::i16vec3(0)) == glm::i16vec3(-1)); + // float to float + REQUIRE( + MetadataConversions::convert( + -1234.5f, + glm::dvec3(0)) == glm::dvec3(-1234.5f)); + REQUIRE( + MetadataConversions::convert(12.0, glm::vec3(0)) == + glm::vec3(12.0)); + } + + SECTION("uses default value if not all components can be converted") { + // scalar + REQUIRE( + MetadataConversions::convert( + -1, + glm::u8vec3(0)) == glm::u8vec3(0)); + // int + REQUIRE( + MetadataConversions::convert( + glm::ivec3(0, -1, 2), + glm::u8vec3(0)) == glm::u8vec3(0)); + REQUIRE( + MetadataConversions::convert( + glm::u8vec4(0, 255, 2, 1), + glm::i8vec3(0)) == glm::i8vec3(0)); + // float + REQUIRE( + MetadataConversions::convert( + glm::vec2(129.0f, -129.0f), + glm::i8vec3(0)) == glm::i8vec3(0)); + REQUIRE( + MetadataConversions::convert( + glm::dvec4(std::numeric_limits::max()), + glm::vec3(0)) == glm::vec3(0)); + }; + + SECTION("uses default value for incompatible types") { + // matN + REQUIRE( + MetadataConversions::convert( + glm::dmat2(1.0, 2.0, 3.0, 4.0), + glm::dvec3(0.0)) == glm::dvec3(0.0)); + // array + PropertyArrayView arrayView; + REQUIRE( + MetadataConversions>::convert( + arrayView, + glm::ivec3(0)) == glm::ivec3(0)); + }; +} From 4edde85c411adc3dc8dcf01a5d49ccb22215ef48 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Mon, 13 Nov 2023 18:11:49 +1100 Subject: [PATCH 346/421] Allow modification of SubtreeAvailability. --- .../SubtreeAvailability.h | 92 +++++- .../src/SubtreeAvailability.cpp | 298 +++++++++++++++++- .../test/TestSubtreeAvailability.cpp | 94 +++++- .../src/ImplicitOctreeLoader.cpp | 1 + .../src/ImplicitQuadtreeLoader.cpp | 1 + .../test/TestImplicitOctreeLoader.cpp | 5 + .../test/TestImplicitQuadtreeLoader.cpp | 6 + 7 files changed, 484 insertions(+), 13 deletions(-) diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/SubtreeAvailability.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/SubtreeAvailability.h index 8ff4a5158..abc4ccd53 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/SubtreeAvailability.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/SubtreeAvailability.h @@ -21,7 +21,7 @@ struct SubtreeConstantAvailability { }; struct SubtreeBufferViewAvailability { - gsl::span view; + gsl::span view; }; using AvailabilityView = @@ -29,11 +29,25 @@ using AvailabilityView = class SubtreeAvailability { public: + static std::optional fromSubtree( + uint32_t powerOfTwo, + uint32_t levelsInSubtree, + Cesium3DTiles::Subtree&& subtree) noexcept; + + /** + * @brief Creates an empty instance with all tiles initially available, while + * all content and subtrees are initially unavailable. + * + * @param powerOfTwo + * @param levelsInSubtree + * @return SubtreeAvailability + */ static std::optional - fromSubtree(uint32_t powerOfTwo, Cesium3DTiles::Subtree&& subtree) noexcept; + createEmpty(uint32_t powerOfTwo, uint32_t levelsInSubtree) noexcept; static CesiumAsync::Future> loadSubtree( uint32_t powerOf2, + uint32_t levelsInSubtree, const CesiumAsync::AsyncSystem& asyncSystem, const std::shared_ptr& pAssetAccessor, const std::shared_ptr& pLogger, @@ -42,6 +56,7 @@ class SubtreeAvailability { SubtreeAvailability( uint32_t powerOf2, + uint32_t levelsInSubtree, AvailabilityView tileAvailability, AvailabilityView subtreeAvailability, std::vector&& contentAvailability, @@ -59,6 +74,21 @@ class SubtreeAvailability { uint32_t relativeTileLevel, uint64_t relativeTileMortonId) const noexcept; + void setTileAvailable( + const CesiumGeometry::QuadtreeTileID& subtreeID, + const CesiumGeometry::QuadtreeTileID& tileID, + bool isAvailable) noexcept; + + void setTileAvailable( + const CesiumGeometry::OctreeTileID& subtreeID, + const CesiumGeometry::OctreeTileID& tileID, + bool isAvailable) noexcept; + + void setTileAvailable( + uint32_t relativeTileLevel, + uint64_t relativeTileMortonId, + bool isAvailable) noexcept; + bool isContentAvailable( const CesiumGeometry::QuadtreeTileID& subtreeID, const CesiumGeometry::QuadtreeTileID& tileID, @@ -74,22 +104,78 @@ class SubtreeAvailability { uint64_t relativeTileMortonId, uint64_t contentId) const noexcept; + void setContentAvailable( + const CesiumGeometry::QuadtreeTileID& subtreeID, + const CesiumGeometry::QuadtreeTileID& tileID, + uint64_t contentId, + bool isAvailable) noexcept; + + void setContentAvailable( + const CesiumGeometry::OctreeTileID& subtreeID, + const CesiumGeometry::OctreeTileID& tileID, + uint64_t contentId, + bool isAvailable) noexcept; + + void setContentAvailable( + uint32_t relativeTileLevel, + uint64_t relativeTileMortonId, + uint64_t contentId, + bool isAvailable) noexcept; + bool isSubtreeAvailable(uint64_t relativeSubtreeMortonId) const noexcept; + bool isSubtreeAvailable( + const CesiumGeometry::QuadtreeTileID& thisSubtreeID, + const CesiumGeometry::QuadtreeTileID& checkSubtreeID) const noexcept; + + bool isSubtreeAvailable( + const CesiumGeometry::OctreeTileID& thisSubtreeID, + const CesiumGeometry::OctreeTileID& checkSubtreeID) const noexcept; + + void setSubtreeAvailable( + uint64_t relativeSubtreeMortonId, + bool isAvailable) noexcept; + + void setSubtreeAvailable( + const CesiumGeometry::QuadtreeTileID& thisSubtreeID, + const CesiumGeometry::QuadtreeTileID& setSubtreeID, + bool isAvailable) noexcept; + + void setSubtreeAvailable( + const CesiumGeometry::OctreeTileID& thisSubtreeID, + const CesiumGeometry::OctreeTileID& setSubtreeID, + bool isAvailable) noexcept; + + Cesium3DTiles::Subtree& getSubtree() noexcept { return this->_subtree; } + const Cesium3DTiles::Subtree& getSubtree() const noexcept { + return this->_subtree; + } + private: bool isAvailable( uint32_t relativeTileLevel, uint64_t relativeTileMortonId, const AvailabilityView& availabilityView) const noexcept; + void setAvailable( + uint32_t relativeTileLevel, + uint64_t relativeTileMortonId, + AvailabilityView& availabilityView, + bool isAvailable) noexcept; bool isAvailableUsingBufferView( uint64_t numOfTilesFromRootToParentLevel, uint64_t relativeTileMortonId, const AvailabilityView& availabilityView) const noexcept; + void setAvailableUsingBufferView( + uint64_t numOfTilesFromRootToParentLevel, + uint64_t relativeTileMortonId, + AvailabilityView& availabilityView, + bool isAvailable) noexcept; + uint32_t _powerOf2; + uint32_t _levelsInSubtree; Cesium3DTiles::Subtree _subtree; uint32_t _childCount; - uint32_t _powerOf2; AvailabilityView _tileAvailability; AvailabilityView _subtreeAvailability; std::vector _contentAvailability; diff --git a/Cesium3DTilesContent/src/SubtreeAvailability.cpp b/Cesium3DTilesContent/src/SubtreeAvailability.cpp index be2e31f6c..325d67d4a 100644 --- a/Cesium3DTilesContent/src/SubtreeAvailability.cpp +++ b/Cesium3DTilesContent/src/SubtreeAvailability.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include @@ -25,8 +26,8 @@ namespace { std::optional parseAvailabilityView( const Cesium3DTiles::Availability& availability, - const std::vector& buffers, - const std::vector& bufferViews) { + std::vector& buffers, + std::vector& bufferViews) { if (availability.constant) { return SubtreeConstantAvailability{*availability.constant == 1}; } @@ -51,12 +52,12 @@ std::optional parseAvailabilityView( if (bufferView.buffer >= 0 && bufferView.buffer < static_cast(buffers.size())) { - const Cesium3DTiles::Buffer& buffer = buffers[bufferView.buffer]; - const std::vector& data = buffer.cesium.data; + Cesium3DTiles::Buffer& buffer = buffers[bufferView.buffer]; + std::vector& data = buffer.cesium.data; int64_t bufferSize = std::min(static_cast(data.size()), buffer.byteLength); if (bufferView.byteOffset + bufferView.byteLength <= bufferSize) { - return SubtreeBufferViewAvailability{gsl::span( + return SubtreeBufferViewAvailability{gsl::span( data.data() + bufferView.byteOffset, bufferView.byteLength)}; } @@ -70,6 +71,7 @@ std::optional parseAvailabilityView( /*static*/ std::optional SubtreeAvailability::fromSubtree( uint32_t powerOfTwo, + uint32_t levelsInSubtree, Cesium3DTiles::Subtree&& subtree) noexcept { std::optional maybeTileAvailability = parseAvailabilityView( subtree.tileAvailability, @@ -105,15 +107,31 @@ std::optional parseAvailabilityView( return SubtreeAvailability( powerOfTwo, + levelsInSubtree, *maybeTileAvailability, *maybeChildSubtreeAvailability, std::move(contentAvailability), std::move(subtree)); } +/*static*/ std::optional SubtreeAvailability::createEmpty( + uint32_t powerOfTwo, + uint32_t levelsInSubtree) noexcept { + Subtree subtree; + subtree.tileAvailability.constant = true; + subtree.contentAvailability.emplace_back().constant = false; + subtree.childSubtreeAvailability.constant = false; + + return SubtreeAvailability::fromSubtree( + powerOfTwo, + levelsInSubtree, + std::move(subtree)); +} + /*static*/ CesiumAsync::Future> SubtreeAvailability::loadSubtree( uint32_t powerOf2, + uint32_t levelsInSubtree, const CesiumAsync::AsyncSystem& asyncSystem, const std::shared_ptr& pAssetAccessor, const std::shared_ptr& pLogger, @@ -122,7 +140,8 @@ SubtreeAvailability::loadSubtree( SubtreeFileReader reader; return reader.load(asyncSystem, pAssetAccessor, subtreeUrl, requestHeaders) .thenInMainThread( - [pLogger, subtreeUrl, powerOf2](ReadJsonResult&& subtree) + [pLogger, subtreeUrl, powerOf2, levelsInSubtree]( + ReadJsonResult&& subtree) -> std::optional { if (!subtree.value) { if (!subtree.errors.empty()) { @@ -144,19 +163,22 @@ SubtreeAvailability::loadSubtree( return SubtreeAvailability::fromSubtree( powerOf2, + levelsInSubtree, std::move(*subtree.value)); }); } SubtreeAvailability::SubtreeAvailability( uint32_t powerOf2, + uint32_t levelsInSubtree, AvailabilityView tileAvailability, AvailabilityView subtreeAvailability, std::vector&& contentAvailability, Cesium3DTiles::Subtree&& subtree) - : _subtree(std::move(subtree)), + : _powerOf2{powerOf2}, _childCount{1U << powerOf2}, - _powerOf2{powerOf2}, + _levelsInSubtree{levelsInSubtree}, + _subtree{std::move(subtree)}, _tileAvailability{tileAvailability}, _subtreeAvailability{subtreeAvailability}, _contentAvailability{std::move(contentAvailability)} { @@ -194,6 +216,41 @@ bool SubtreeAvailability::isTileAvailable( this->_tileAvailability); } +void SubtreeAvailability::setTileAvailable( + const CesiumGeometry::QuadtreeTileID& subtreeID, + const CesiumGeometry::QuadtreeTileID& tileID, + bool isAvailable) noexcept { + uint64_t relativeTileMortonIdx = + ImplicitTilingUtilities::computeRelativeMortonIndex(subtreeID, tileID); + this->setTileAvailable( + tileID.level - subtreeID.level, + relativeTileMortonIdx, + isAvailable); +} + +void SubtreeAvailability::setTileAvailable( + const CesiumGeometry::OctreeTileID& subtreeID, + const CesiumGeometry::OctreeTileID& tileID, + bool isAvailable) noexcept { + uint64_t relativeTileMortonIdx = + ImplicitTilingUtilities::computeRelativeMortonIndex(subtreeID, tileID); + this->setTileAvailable( + tileID.level - subtreeID.level, + relativeTileMortonIdx, + isAvailable); +} + +void SubtreeAvailability::setTileAvailable( + uint32_t relativeTileLevel, + uint64_t relativeTileMortonId, + bool isAvailable) noexcept { + this->setAvailable( + relativeTileLevel, + relativeTileMortonId, + this->_tileAvailability, + isAvailable); +} + bool SubtreeAvailability::isContentAvailable( const CesiumGeometry::QuadtreeTileID& subtreeID, const CesiumGeometry::QuadtreeTileID& tileID, @@ -228,6 +285,46 @@ bool SubtreeAvailability::isContentAvailable( this->_contentAvailability[contentId]); } +void SubtreeAvailability::setContentAvailable( + const CesiumGeometry::QuadtreeTileID& subtreeID, + const CesiumGeometry::QuadtreeTileID& tileID, + uint64_t contentId, + bool isAvailable) noexcept { + uint64_t relativeTileMortonIdx = + ImplicitTilingUtilities::computeRelativeMortonIndex(subtreeID, tileID); + this->setContentAvailable( + tileID.level - subtreeID.level, + relativeTileMortonIdx, + contentId, + isAvailable); +} + +void SubtreeAvailability::setContentAvailable( + const CesiumGeometry::OctreeTileID& subtreeID, + const CesiumGeometry::OctreeTileID& tileID, + uint64_t contentId, + bool isAvailable) noexcept { + uint64_t relativeTileMortonIdx = + ImplicitTilingUtilities::computeRelativeMortonIndex(subtreeID, tileID); + this->setContentAvailable( + tileID.level - subtreeID.level, + relativeTileMortonIdx, + contentId, + isAvailable); +} + +void SubtreeAvailability::setContentAvailable( + uint32_t relativeTileLevel, + uint64_t relativeTileMortonId, + uint64_t contentId, + bool isAvailable) noexcept { + this->setAvailable( + relativeTileLevel, + relativeTileMortonId, + this->_contentAvailability[contentId], + isAvailable); +} + bool SubtreeAvailability::isSubtreeAvailable( uint64_t relativeSubtreeMortonId) const noexcept { const SubtreeConstantAvailability* constantAvailability = @@ -242,6 +339,120 @@ bool SubtreeAvailability::isSubtreeAvailable( this->_subtreeAvailability); } +bool SubtreeAvailability::isSubtreeAvailable( + const CesiumGeometry::QuadtreeTileID& thisSubtreeID, + const CesiumGeometry::QuadtreeTileID& checkSubtreeID) const noexcept { + return isSubtreeAvailable(ImplicitTilingUtilities::computeRelativeMortonIndex( + thisSubtreeID, + checkSubtreeID)); +} + +bool SubtreeAvailability::isSubtreeAvailable( + const CesiumGeometry::OctreeTileID& thisSubtreeID, + const CesiumGeometry::OctreeTileID& checkSubtreeID) const noexcept { + return isSubtreeAvailable(ImplicitTilingUtilities::computeRelativeMortonIndex( + thisSubtreeID, + checkSubtreeID)); +} + +namespace { + +void convertConstantAvailabilityToBitstream( + Subtree& subtree, + uint64_t numberOfTiles, + AvailabilityView& availabilityView) { + const SubtreeConstantAvailability* pConstantAvailability = + std::get_if(&availabilityView); + if (!pConstantAvailability) + return; + + bool oldValue = pConstantAvailability->constant; + + uint64_t numberOfBytes = numberOfTiles / 8; + if (numberOfBytes * 8 < numberOfTiles) + ++numberOfBytes; + + BufferView& bufferView = subtree.bufferViews.emplace_back(); + bufferView.buffer = 0; + bufferView.byteLength = numberOfBytes; + + Buffer& buffer = !subtree.buffers.empty() ? subtree.buffers[0] + : subtree.buffers.emplace_back(); + + int64_t start = buffer.byteLength; + + // Align the new bufferView to a multiple of 8 bytes, as required by the spec. + int64_t paddingRemainder = start % 8; + if (paddingRemainder > 0) { + start += 8 - paddingRemainder; + } + + int64_t end = start + numberOfBytes; + + bufferView.byteOffset = start; + buffer.byteLength = end; + + buffer.cesium.data.resize( + buffer.byteLength, + oldValue ? std::byte(0xFF) : std::byte(0x00)); + + gsl::span view( + buffer.cesium.data.data() + start, + buffer.cesium.data.data() + end); + availabilityView = SubtreeBufferViewAvailability{view}; +} + +} // namespace + +void SubtreeAvailability::setSubtreeAvailable( + uint64_t relativeSubtreeMortonId, + bool isAvailable) noexcept { + const SubtreeConstantAvailability* pConstantAvailability = + std::get_if(&this->_subtreeAvailability); + if (pConstantAvailability) { + if (pConstantAvailability->constant == isAvailable) { + // New state matches the constant, so there is nothing to do. + return; + } else { + uint64_t numberOfTilesInNextLevel = + uint64_t(1) << (this->_powerOf2 * this->_levelsInSubtree); + + convertConstantAvailabilityToBitstream( + this->_subtree, + numberOfTilesInNextLevel, + this->_subtreeAvailability); + } + } + + setAvailableUsingBufferView( + 0, + relativeSubtreeMortonId, + this->_subtreeAvailability, + isAvailable); +} + +void SubtreeAvailability::setSubtreeAvailable( + const CesiumGeometry::QuadtreeTileID& thisSubtreeID, + const CesiumGeometry::QuadtreeTileID& setSubtreeID, + bool isAvailable) noexcept { + this->setSubtreeAvailable( + ImplicitTilingUtilities::computeRelativeMortonIndex( + thisSubtreeID, + setSubtreeID), + isAvailable); +} + +void SubtreeAvailability::setSubtreeAvailable( + const CesiumGeometry::OctreeTileID& thisSubtreeID, + const CesiumGeometry::OctreeTileID& setSubtreeID, + bool isAvailable) noexcept { + this->setSubtreeAvailable( + ImplicitTilingUtilities::computeRelativeMortonIndex( + thisSubtreeID, + setSubtreeID), + isAvailable); +} + bool SubtreeAvailability::isAvailable( uint32_t relativeTileLevel, uint64_t relativeTileMortonId, @@ -267,6 +478,49 @@ bool SubtreeAvailability::isAvailable( availabilityView); } +void SubtreeAvailability::setAvailable( + uint32_t relativeTileLevel, + uint64_t relativeTileMortonId, + AvailabilityView& availabilityView, + bool isAvailable) noexcept { + const SubtreeConstantAvailability* pConstantAvailability = + std::get_if(&availabilityView); + if (pConstantAvailability) { + if (pConstantAvailability->constant == isAvailable) { + // New state matches the constant, so there is nothing to do. + return; + } else { + uint64_t numberOfTilesInNextLevel = + uint64_t(1) << (this->_powerOf2 * this->_levelsInSubtree); + uint64_t numberOfTilesInSubtree = + (numberOfTilesInNextLevel - 1U) / (this->_childCount - 1U); + + convertConstantAvailabilityToBitstream( + this->_subtree, + numberOfTilesInSubtree, + availabilityView); + } + } + + // At this point we're definitely working with a bitstream (not a constant). + uint64_t numOfTilesInLevel = uint64_t(1) + << (this->_powerOf2 * relativeTileLevel); + if (relativeTileMortonId >= numOfTilesInLevel) { + // Attempting to set an invalid level. Assert, but otherwise ignore it. + assert(false); + return; + } + + uint64_t numOfTilesFromRootToParentLevel = + (numOfTilesInLevel - 1U) / (this->_childCount - 1U); + + return setAvailableUsingBufferView( + numOfTilesFromRootToParentLevel, + relativeTileMortonId, + availabilityView, + isAvailable); +} + bool SubtreeAvailability::isAvailableUsingBufferView( uint64_t numOfTilesFromRootToParentLevel, uint64_t relativeTileMortonId, @@ -289,4 +543,32 @@ bool SubtreeAvailability::isAvailableUsingBufferView( return bitValue == 1; } + +void SubtreeAvailability::setAvailableUsingBufferView( + uint64_t numOfTilesFromRootToParentLevel, + uint64_t relativeTileMortonId, + AvailabilityView& availabilityView, + bool isAvailable) noexcept { + uint64_t availabilityBitIndex = + numOfTilesFromRootToParentLevel + relativeTileMortonId; + + const SubtreeBufferViewAvailability* pBufferViewAvailability = + std::get_if(&availabilityView); + + const uint64_t byteIndex = availabilityBitIndex / 8; + if (byteIndex >= pBufferViewAvailability->view.size()) { + // Attempting to set an invalid tile. Assert, but otherwise ignore it. + assert(false); + return; + } + + const uint64_t bitIndex = availabilityBitIndex % 8; + + if (isAvailable) { + pBufferViewAvailability->view[byteIndex] |= std::byte(1) << bitIndex; + } else { + pBufferViewAvailability->view[byteIndex] &= ~(std::byte(1) << bitIndex); + } +} + } // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/test/TestSubtreeAvailability.cpp b/Cesium3DTilesContent/test/TestSubtreeAvailability.cpp index 3546de655..ebbe645a3 100644 --- a/Cesium3DTilesContent/test/TestSubtreeAvailability.cpp +++ b/Cesium3DTilesContent/test/TestSubtreeAvailability.cpp @@ -16,6 +16,7 @@ using namespace Cesium3DTiles; using namespace Cesium3DTilesContent; +using namespace CesiumGeometry; using namespace CesiumNativeTests; namespace { @@ -238,6 +239,7 @@ rapidjson::Document createSubtreeJson( } std::optional mockLoadSubtreeJson( + uint32_t levelsInSubtree, SubtreeBuffers&& subtreeBuffers, rapidjson::Document&& subtreeJson) { rapidjson::StringBuffer subtreeJsonBuffer; @@ -284,6 +286,7 @@ std::optional mockLoadSubtreeJson( auto subtreeFuture = SubtreeAvailability::loadSubtree( 2, + levelsInSubtree, asyncSystem, pMockAssetAccessor, spdlog::default_logger(), @@ -299,6 +302,7 @@ TEST_CASE("Test SubtreeAvailability methods") { SECTION("Availability stored in constant") { SubtreeAvailability subtreeAvailability{ 2, + 5, SubtreeConstantAvailability{true}, SubtreeConstantAvailability{false}, {SubtreeConstantAvailability{false}}, @@ -416,6 +420,7 @@ TEST_CASE("Test SubtreeAvailability methods") { SubtreeAvailability quadtreeAvailability( 2, + static_cast(maxSubtreeLevels), tileAvailability, subtreeAvailability, std::move(contentAvailability), @@ -557,6 +562,7 @@ TEST_CASE("Test parsing subtree format") { auto subtreeFuture = SubtreeAvailability::loadSubtree( 2, + maxSubtreeLevels, asyncSystem, pMockAssetAccessor, spdlog::default_logger(), @@ -593,8 +599,10 @@ TEST_CASE("Test parsing subtree format") { SECTION("Parse json subtree") { auto subtreeJson = createSubtreeJson(subtreeBuffers, "buffer"); - auto parsedSubtree = - mockLoadSubtreeJson(std::move(subtreeBuffers), std::move(subtreeJson)); + auto parsedSubtree = mockLoadSubtreeJson( + maxSubtreeLevels, + std::move(subtreeBuffers), + std::move(subtreeJson)); REQUIRE(parsedSubtree != std::nullopt); @@ -628,6 +636,7 @@ TEST_CASE("Test parsing subtree format") { subtreeJson.RemoveMember("tileAvailability"); CHECK( mockLoadSubtreeJson( + maxSubtreeLevels, std::move(subtreeBuffers), std::move(subtreeJson)) == std::nullopt); } @@ -636,6 +645,7 @@ TEST_CASE("Test parsing subtree format") { subtreeJson.RemoveMember("contentAvailability"); CHECK( mockLoadSubtreeJson( + maxSubtreeLevels, std::move(subtreeBuffers), std::move(subtreeJson)) == std::nullopt); } @@ -644,6 +654,7 @@ TEST_CASE("Test parsing subtree format") { subtreeJson.RemoveMember("childSubtreeAvailability"); CHECK( mockLoadSubtreeJson( + maxSubtreeLevels, std::move(subtreeBuffers), std::move(subtreeJson)) == std::nullopt); } @@ -653,6 +664,7 @@ TEST_CASE("Test parsing subtree format") { subtreeJson.RemoveMember("buffers"); CHECK( mockLoadSubtreeJson( + maxSubtreeLevels, std::move(subtreeBuffers), std::move(subtreeJson)) == std::nullopt); } @@ -663,6 +675,7 @@ TEST_CASE("Test parsing subtree format") { bufferObj->RemoveMember("byteLength"); CHECK( mockLoadSubtreeJson( + maxSubtreeLevels, std::move(subtreeBuffers), std::move(subtreeJson)) == std::nullopt); } @@ -674,6 +687,7 @@ TEST_CASE("Test parsing subtree format") { bufferObj->AddMember("uri", 12, subtreeJson.GetAllocator()); CHECK( mockLoadSubtreeJson( + maxSubtreeLevels, std::move(subtreeBuffers), std::move(subtreeJson)) == std::nullopt); } @@ -683,8 +697,84 @@ TEST_CASE("Test parsing subtree format") { subtreeJson.RemoveMember("bufferViews"); CHECK( mockLoadSubtreeJson( + maxSubtreeLevels, std::move(subtreeBuffers), std::move(subtreeJson)) == std::nullopt); } } } + +TEST_CASE("SubtreeAvailability modifications") { + std::optional maybeAvailability = + SubtreeAvailability::createEmpty(2, 5); + REQUIRE(maybeAvailability); + + SubtreeAvailability& availability = *maybeAvailability; + + SECTION("initially has all tiles available, and no content or subtress " + "available") { + CHECK(availability.isTileAvailable( + QuadtreeTileID(0, 0, 0), + QuadtreeTileID(0, 0, 0))); + CHECK(availability.isTileAvailable( + QuadtreeTileID(0, 0, 0), + QuadtreeTileID(4, 15, 15))); + + CHECK(!availability.isContentAvailable( + QuadtreeTileID(0, 0, 0), + QuadtreeTileID(0, 0, 0), + 0)); + CHECK(!availability.isContentAvailable( + QuadtreeTileID(0, 0, 0), + QuadtreeTileID(4, 15, 15), + 0)); + + CHECK(!availability.isSubtreeAvailable( + QuadtreeTileID(0, 0, 0), + QuadtreeTileID(5, 0, 0))); + CHECK(!availability.isSubtreeAvailable( + QuadtreeTileID(0, 0, 0), + QuadtreeTileID(5, 31, 31))); + } + + SECTION("can set a single tile's state") { + availability.setTileAvailable( + QuadtreeTileID(0, 0, 0), + QuadtreeTileID(4, 15, 15), + false); + + CHECK(availability.isTileAvailable( + QuadtreeTileID(0, 0, 0), + QuadtreeTileID(0, 0, 0))); + CHECK(!availability.isTileAvailable( + QuadtreeTileID(0, 0, 0), + QuadtreeTileID(4, 15, 15))); + + availability.setContentAvailable( + QuadtreeTileID(0, 0, 0), + QuadtreeTileID(4, 15, 15), + 0, + true); + + CHECK(!availability.isContentAvailable( + QuadtreeTileID(0, 0, 0), + QuadtreeTileID(0, 0, 0), + 0)); + CHECK(availability.isContentAvailable( + QuadtreeTileID(0, 0, 0), + QuadtreeTileID(4, 15, 15), + 0)); + + availability.setSubtreeAvailable( + QuadtreeTileID(0, 0, 0), + QuadtreeTileID(5, 31, 31), + true); + + CHECK(!availability.isSubtreeAvailable( + QuadtreeTileID(0, 0, 0), + QuadtreeTileID(5, 0, 0))); + CHECK(availability.isSubtreeAvailable( + QuadtreeTileID(0, 0, 0), + QuadtreeTileID(5, 31, 31))); + } +} diff --git a/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp b/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp index 04d0e6589..a212c1b16 100644 --- a/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp +++ b/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp @@ -209,6 +209,7 @@ ImplicitOctreeLoader::loadTileContent(const TileLoadInput& loadInput) { subtreeID); return SubtreeAvailability::loadSubtree( 3, + this->_subtreeLevels, asyncSystem, pAssetAccessor, pLogger, diff --git a/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp b/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp index e8dadf7df..a9d668de3 100644 --- a/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp +++ b/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp @@ -248,6 +248,7 @@ ImplicitQuadtreeLoader::loadTileContent(const TileLoadInput& loadInput) { subtreeID); return SubtreeAvailability::loadSubtree( 2, + this->_subtreeLevels, asyncSystem, pAssetAccessor, pLogger, diff --git a/Cesium3DTilesSelection/test/TestImplicitOctreeLoader.cpp b/Cesium3DTilesSelection/test/TestImplicitOctreeLoader.cpp index f7f49045a..b8bab6ae2 100644 --- a/Cesium3DTilesSelection/test/TestImplicitOctreeLoader.cpp +++ b/Cesium3DTilesSelection/test/TestImplicitOctreeLoader.cpp @@ -69,6 +69,7 @@ TEST_CASE("Test implicit octree loader") { OctreeTileID{0, 0, 0, 0}, SubtreeAvailability{ 3, + 5, SubtreeConstantAvailability{true}, SubtreeConstantAvailability{false}, {SubtreeConstantAvailability{false}}, @@ -104,6 +105,7 @@ TEST_CASE("Test implicit octree loader") { OctreeTileID{0, 0, 0, 0}, SubtreeAvailability{ 2, + 5, SubtreeConstantAvailability{true}, SubtreeConstantAvailability{false}, {SubtreeConstantAvailability{true}}, @@ -156,6 +158,7 @@ TEST_CASE("Test implicit octree loader") { OctreeTileID{0, 0, 0, 0}, SubtreeAvailability{ 2, + 5, SubtreeConstantAvailability{true}, SubtreeConstantAvailability{false}, {SubtreeConstantAvailability{true}}, @@ -245,6 +248,7 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { OctreeTileID{0, 0, 0, 0}, SubtreeAvailability{ 3, + 5, SubtreeConstantAvailability{true}, SubtreeConstantAvailability{false}, {SubtreeConstantAvailability{true}}, @@ -445,6 +449,7 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { OctreeTileID{0, 0, 0, 0}, SubtreeAvailability{ 3, + 5, SubtreeConstantAvailability{true}, SubtreeConstantAvailability{false}, {SubtreeConstantAvailability{true}}, diff --git a/Cesium3DTilesSelection/test/TestImplicitQuadtreeLoader.cpp b/Cesium3DTilesSelection/test/TestImplicitQuadtreeLoader.cpp index 00209e323..e2777312c 100644 --- a/Cesium3DTilesSelection/test/TestImplicitQuadtreeLoader.cpp +++ b/Cesium3DTilesSelection/test/TestImplicitQuadtreeLoader.cpp @@ -69,6 +69,7 @@ TEST_CASE("Test implicit quadtree loader") { QuadtreeTileID{0, 0, 0}, SubtreeAvailability{ 2, + 5, SubtreeConstantAvailability{true}, SubtreeConstantAvailability{false}, {SubtreeConstantAvailability{false}}, @@ -104,6 +105,7 @@ TEST_CASE("Test implicit quadtree loader") { QuadtreeTileID{0, 0, 0}, SubtreeAvailability{ 2, + 5, SubtreeConstantAvailability{true}, SubtreeConstantAvailability{false}, {SubtreeConstantAvailability{true}}, @@ -156,6 +158,7 @@ TEST_CASE("Test implicit quadtree loader") { QuadtreeTileID{0, 0, 0}, SubtreeAvailability{ 2, + 5, SubtreeConstantAvailability{true}, SubtreeConstantAvailability{false}, {SubtreeConstantAvailability{true}}, @@ -221,6 +224,7 @@ TEST_CASE("Test tile subdivision for implicit quadtree loader") { QuadtreeTileID{0, 0, 0}, SubtreeAvailability{ 2, + 5, SubtreeConstantAvailability{true}, SubtreeConstantAvailability{false}, {SubtreeConstantAvailability{true}}, @@ -362,6 +366,7 @@ TEST_CASE("Test tile subdivision for implicit quadtree loader") { QuadtreeTileID{0, 0, 0}, SubtreeAvailability{ 2, + 5, SubtreeConstantAvailability{true}, SubtreeConstantAvailability{false}, {SubtreeConstantAvailability{true}}, @@ -496,6 +501,7 @@ TEST_CASE("Test tile subdivision for implicit quadtree loader") { QuadtreeTileID{0, 0, 0}, SubtreeAvailability{ 2, + 5, SubtreeConstantAvailability{true}, SubtreeConstantAvailability{false}, {SubtreeConstantAvailability{true}}, From dfe69c5baf92cb4d1d7dfe2df9390eb04fe9fd5e Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Mon, 13 Nov 2023 19:36:35 +1100 Subject: [PATCH 347/421] Doc comments, slight reorg. --- .../SubtreeAvailability.h | 296 ++++++++++++++++-- .../src/SubtreeAvailability.cpp | 59 ++-- .../test/TestSubtreeAvailability.cpp | 43 +-- .../src/ImplicitOctreeLoader.cpp | 2 +- .../src/ImplicitQuadtreeLoader.cpp | 2 +- .../test/TestImplicitOctreeLoader.cpp | 40 +-- .../test/TestImplicitQuadtreeLoader.cpp | 48 +-- 7 files changed, 378 insertions(+), 112 deletions(-) diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/SubtreeAvailability.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/SubtreeAvailability.h index abc4ccd53..2dcffe141 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/SubtreeAvailability.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/SubtreeAvailability.h @@ -16,21 +16,40 @@ struct ImplicitTiling; } // namespace Cesium3DTiles namespace Cesium3DTilesContent { -struct SubtreeConstantAvailability { - bool constant; -}; -struct SubtreeBufferViewAvailability { - gsl::span view; -}; +/** + * @brief Indicates how an implicit tile is subdivided. + */ +enum class ImplicitTileSubdivisionScheme { + /** + * @brief Implicit tiles are divided into four children, forming a quadree. + */ + Quadtree, -using AvailabilityView = - std::variant; + /** + * @brief Implicit tiles are divided into eight children, forming an octree. + */ + Octree +}; +/** + * @brief Supports querying and modifying the various types of availablity + * information included in a {@link Cesium3DTiles::Subtree}. + */ class SubtreeAvailability { public: + /** + * @brief Creates an instance from a `Subtree`. + * + * @param subdivisionScheme The subdivision scheme of the subtree (quadtree or + * octree). + * @param levelsInSubtree The number of levels in this subtree. + * @param subtree The subtree. + * @return The subtree availability, or std::nullopt if the subtree definition + * is invalid. + */ static std::optional fromSubtree( - uint32_t powerOfTwo, + ImplicitTileSubdivisionScheme subdivisionScheme, uint32_t levelsInSubtree, Cesium3DTiles::Subtree&& subtree) noexcept; @@ -38,15 +57,36 @@ class SubtreeAvailability { * @brief Creates an empty instance with all tiles initially available, while * all content and subtrees are initially unavailable. * - * @param powerOfTwo - * @param levelsInSubtree - * @return SubtreeAvailability + * @param subdivisionScheme The subdivision scheme of the subtree (quadtree or + * octree). + * @param levelsInSubtree The number of levels in this subtree. + * @return The subtree availability, or std::nullopt if the subtree definition + * is invalid. */ - static std::optional - createEmpty(uint32_t powerOfTwo, uint32_t levelsInSubtree) noexcept; + static std::optional createEmpty( + ImplicitTileSubdivisionScheme subdivisionScheme, + uint32_t levelsInSubtree) noexcept; + /** + * @brief Asynchronously loads a subtree from a URL. The resource downloaded + * from the URL may be either a JSON or a binary subtree file. + * + * @param subdivisionScheme The subdivision scheme of the subtree (quadtree or + * octree). + * @param levelsInSubtree The number of levels in this subtree. + * @param asyncSystem The async system with which to do background work. + * @param pAssetAccessor The asset accessor to use to retrieve the subtree + * resource from the URL. + * @param pLogger The logger to which to load errors and warnings and occur + * during subtree load. + * @param subtreeUrl The URL from which to retrieve the subtree file. + * @param requestHeaders HTTP headers to include in the request for the + * subtree file. + * @return A future that resolves to a `SubtreeAvailability` instance for the + * subtree file, or std::nullopt if something goes wrong. + */ static CesiumAsync::Future> loadSubtree( - uint32_t powerOf2, + ImplicitTileSubdivisionScheme subdivisionScheme, uint32_t levelsInSubtree, const CesiumAsync::AsyncSystem& asyncSystem, const std::shared_ptr& pAssetAccessor, @@ -54,99 +94,311 @@ class SubtreeAvailability { const std::string& subtreeUrl, const std::vector& requestHeaders); + /** + * @brief An AvailibilityView that indicates all tiles are either available or + * all tiles are unavailable. + */ + struct SubtreeConstantAvailability { + /** + * @brief True if all tiles are availability, false if all tiles are + * unavailable. + */ + bool constant; + }; + + /** + * @brief An AvailabilityView that access availability information from a + * bitstream. + */ + struct SubtreeBufferViewAvailability { + /** + * @brief The buffer from which to read and write availability information. + */ + gsl::span view; + }; + + /** + * @brief A mechanism for accessing availability information. It may be a + * constant value, or it may be read from a bitstream. + */ + using AvailabilityView = + std::variant; + + /** + * @brief Constructs a new instance. + * + * @param subdivisionScheme The subdivision scheme of the subtree (quadtree or + * octree). + * @param levelsInSubtree The number of levels in this subtree. + * @param tileAvailability A view on the tile availability. If backed by a + * buffer, the buffer is expected to be in `subtree`. + * @param subtreeAvailability A view on the subtree availability. If backed by + * a buffer, the buffer is expected to be in `subtree`. + * @param contentAvailability A view on the content availability. If backed by + * a buffer, the buffer is expected to be in `subtree`. + * @param subtree The subtree with which this instance queries and modifies + * availability information. + */ SubtreeAvailability( - uint32_t powerOf2, + ImplicitTileSubdivisionScheme subdivisionScheme, uint32_t levelsInSubtree, AvailabilityView tileAvailability, AvailabilityView subtreeAvailability, std::vector&& contentAvailability, Cesium3DTiles::Subtree&& subtree); + /** + * @brief Determines if a given tile in the subtree is available. + * + * @param subtreeID The ID of the root tile of the subtree. + * @param tileID The ID of the tile to query. + * @return True if the tile is available; otherwise, false. + */ bool isTileAvailable( const CesiumGeometry::QuadtreeTileID& subtreeID, const CesiumGeometry::QuadtreeTileID& tileID) const noexcept; + /** + * @brief Determines if a given tile in the subtree is available. + * + * @param subtreeID The ID of the root tile of the subtree. + * @param tileID The ID of the tile to query. + * @return True if the tile is available; otherwise, false. + */ bool isTileAvailable( const CesiumGeometry::OctreeTileID& subtreeID, const CesiumGeometry::OctreeTileID& tileID) const noexcept; + /** + * @brief Determines if a given tile in the subtree is available. + * + * @param relativeTileLevel The level of the tile to query, relative to the + * root of the subtree. + * @param relativeTileMortonId The Morton ID of the tile to query. See + * {@link ImplicitTilingUtilities::computeRelativeMortonIndex}. + * @return True if the tile is available; otherwise, false. + */ bool isTileAvailable( uint32_t relativeTileLevel, uint64_t relativeTileMortonId) const noexcept; + /** + * @brief Sets the availability state of a given tile in the subtree. + * + * @param subtreeID The ID of the root tile of the subtree. + * @param tileID The ID of the tile for which to set availability. + * @param isAvailable The new availability state for the tile. + */ void setTileAvailable( const CesiumGeometry::QuadtreeTileID& subtreeID, const CesiumGeometry::QuadtreeTileID& tileID, bool isAvailable) noexcept; + /** + * @brief Sets the availability state of a given tile in the subtree. + * + * @param subtreeID The ID of the root tile of the subtree. + * @param tileID The ID of the tile for which to set availability. + * @param isAvailable The new availability state for the tile. + */ void setTileAvailable( const CesiumGeometry::OctreeTileID& subtreeID, const CesiumGeometry::OctreeTileID& tileID, bool isAvailable) noexcept; + /** + * @brief Sets the availability state of a given tile in the subtree. + * + * @param relativeTileLevel The level of the tile for which to set + * availability, relative to the root of the subtree. + * @param relativeTileMortonId The Morton ID of the tile for which to set + * availability. See + * {@link ImplicitTilingUtilities::computeRelativeMortonIndex}. + * @param isAvailable The new availability state of the tile. + */ void setTileAvailable( uint32_t relativeTileLevel, uint64_t relativeTileMortonId, bool isAvailable) noexcept; + /** + * @brief Determines if content for a given tile in the subtree is available. + * + * @param subtreeID The ID of the root tile of the subtree. + * @param tileID The ID of the tile to query. + * @return True if the tile's content is available; otherwise, false. + */ bool isContentAvailable( const CesiumGeometry::QuadtreeTileID& subtreeID, const CesiumGeometry::QuadtreeTileID& tileID, uint64_t contentId) const noexcept; + /** + * @brief Determines if content for a given tile in the subtree is available. + * + * @param subtreeID The ID of the root tile of the subtree. + * @param tileID The ID of the tile to query. + * @return True if the tile's content is available; otherwise, false. + */ bool isContentAvailable( const CesiumGeometry::OctreeTileID& subtreeID, const CesiumGeometry::OctreeTileID& tileID, uint64_t contentId) const noexcept; + /** + * @brief Determines if content for a given tile in the subtree is available. + * + * @param relativeTileLevel The level of the tile to query, relative to the + * root of the subtree. + * @param relativeTileMortonId The Morton ID of the tile to query. See + * {@link ImplicitTilingUtilities::computeRelativeMortonIndex}. + * @return True if the tile's content is available; otherwise, false. + */ bool isContentAvailable( uint32_t relativeTileLevel, uint64_t relativeTileMortonId, uint64_t contentId) const noexcept; + /** + * @brief Sets the availability state of the content for a given tile in the + * subtree. + * + * @param subtreeID The ID of the root tile of the subtree. + * @param tileID The ID of the tile for which to set content availability. + * @param isAvailable The new availability state for the tile's content. + */ void setContentAvailable( const CesiumGeometry::QuadtreeTileID& subtreeID, const CesiumGeometry::QuadtreeTileID& tileID, uint64_t contentId, bool isAvailable) noexcept; + /** + * @brief Sets the availability state of the content for a given tile in the + * subtree. + * + * @param subtreeID The ID of the root tile of the subtree. + * @param tileID The ID of the tile for which to set content availability. + * @param isAvailable The new availability state for the tile's content. + */ void setContentAvailable( const CesiumGeometry::OctreeTileID& subtreeID, const CesiumGeometry::OctreeTileID& tileID, uint64_t contentId, bool isAvailable) noexcept; + /** + * @brief Sets the availability state of the content for a given tile in the + * subtree. + * + * @param relativeTileLevel The level of the tile for which to set + * content availability, relative to the root of the subtree. + * @param relativeTileMortonId The Morton ID of the tile for which to set + * content availability. See + * {@link ImplicitTilingUtilities::computeRelativeMortonIndex}. + * @param isAvailable The new availability state for the tile's content. + */ void setContentAvailable( uint32_t relativeTileLevel, uint64_t relativeTileMortonId, uint64_t contentId, bool isAvailable) noexcept; - bool isSubtreeAvailable(uint64_t relativeSubtreeMortonId) const noexcept; - + /** + * @brief Determines if the subtree rooted at the given tile is available. + * + * The provided `checkSubtreeID` must be a child of the leaves of this + * subtree. + * + * @param thisSubtreeID The ID of the root tile of this subtree. + * @param checkSubtreeID The ID of the tile to query to see if its subtree is + * available. + * @return True if the tile's content is available; otherwise, false. + */ bool isSubtreeAvailable( const CesiumGeometry::QuadtreeTileID& thisSubtreeID, const CesiumGeometry::QuadtreeTileID& checkSubtreeID) const noexcept; + /** + * @brief Determines if the subtree rooted at the given tile is available. + * + * The provided `checkSubtreeID` must be a child of the leaves of this + * subtree. + * + * @param thisSubtreeID The ID of the root tile of this subtree. + * @param checkSubtreeID The ID of the tile to query to see if its subtree is + * available. + * @return True if the tile's content is available; otherwise, false. + */ bool isSubtreeAvailable( const CesiumGeometry::OctreeTileID& thisSubtreeID, const CesiumGeometry::OctreeTileID& checkSubtreeID) const noexcept; - void setSubtreeAvailable( - uint64_t relativeSubtreeMortonId, - bool isAvailable) noexcept; + /** + * @brief Determines if the subtree rooted at the given tile is available. + * + * The provided `relativeSubtreeMortonId` must refer to a child of the leaves + * of this subtree. + * + * @param relativeTileMortonId The Morton ID of the tile for which to check + * subtree availability. See + * {@link ImplicitTilingUtilities::computeRelativeMortonIndex}. + * @return True if the tile's subtree is available; otherwise, false. + */ + bool isSubtreeAvailable(uint64_t relativeSubtreeMortonId) const noexcept; + /** + * @brief Sets the availability state of the child subtree rooted at the given + * tile. + * + * The provided `setSubtreeID` must be a child of the leaves of this + * subtree. + * + * @param thisSubtreeID The ID of the root tile of this subtree. + * @param setSubtreeID The ID of the tile to query to see if its subtree is + * available. + * @return True if the tile's subtree is available; otherwise, false. + */ void setSubtreeAvailable( const CesiumGeometry::QuadtreeTileID& thisSubtreeID, const CesiumGeometry::QuadtreeTileID& setSubtreeID, bool isAvailable) noexcept; + /** + * @brief Sets the availability state of the child subtree rooted at the given + * tile. + * + * The provided `setSubtreeID` must be a child of the leaves of this + * subtree. + * + * @param thisSubtreeID The ID of the root tile of this subtree. + * @param setSubtreeID The ID of the tile to query to see if its subtree is + * available. + * @return True if the tile's subtree is available; otherwise, false. + */ void setSubtreeAvailable( const CesiumGeometry::OctreeTileID& thisSubtreeID, const CesiumGeometry::OctreeTileID& setSubtreeID, bool isAvailable) noexcept; - Cesium3DTiles::Subtree& getSubtree() noexcept { return this->_subtree; } + /** + * @brief Sets the availability state of the child subtree rooted at the given + * tile. + * + * The provided `relativeSubtreeMortonId` must refer to a child of the leaves + * of this subtree. + * + * @param relativeTileMortonId The Morton ID of the tile for which to set + * subtree availability. See + * {@link ImplicitTilingUtilities::computeRelativeMortonIndex}. + */ + void setSubtreeAvailable( + uint64_t relativeSubtreeMortonId, + bool isAvailable) noexcept; + + /** + * @brief Gets the subtree that this instance queries and modifies. + */ const Cesium3DTiles::Subtree& getSubtree() const noexcept { return this->_subtree; } diff --git a/Cesium3DTilesContent/src/SubtreeAvailability.cpp b/Cesium3DTilesContent/src/SubtreeAvailability.cpp index 325d67d4a..3e4cb2840 100644 --- a/Cesium3DTilesContent/src/SubtreeAvailability.cpp +++ b/Cesium3DTilesContent/src/SubtreeAvailability.cpp @@ -24,12 +24,13 @@ namespace Cesium3DTilesContent { namespace { -std::optional parseAvailabilityView( +std::optional parseAvailabilityView( const Cesium3DTiles::Availability& availability, std::vector& buffers, std::vector& bufferViews) { if (availability.constant) { - return SubtreeConstantAvailability{*availability.constant == 1}; + return SubtreeAvailability::SubtreeConstantAvailability{ + *availability.constant == 1}; } int64_t bufferViewIndex = -1; @@ -57,9 +58,10 @@ std::optional parseAvailabilityView( int64_t bufferSize = std::min(static_cast(data.size()), buffer.byteLength); if (bufferView.byteOffset + bufferView.byteLength <= bufferSize) { - return SubtreeBufferViewAvailability{gsl::span( - data.data() + bufferView.byteOffset, - bufferView.byteLength)}; + return SubtreeAvailability::SubtreeBufferViewAvailability{ + gsl::span( + data.data() + bufferView.byteOffset, + bufferView.byteLength)}; } } } @@ -70,18 +72,19 @@ std::optional parseAvailabilityView( } // namespace /*static*/ std::optional SubtreeAvailability::fromSubtree( - uint32_t powerOfTwo, + ImplicitTileSubdivisionScheme subdivisionScheme, uint32_t levelsInSubtree, Cesium3DTiles::Subtree&& subtree) noexcept { - std::optional maybeTileAvailability = parseAvailabilityView( - subtree.tileAvailability, - subtree.buffers, - subtree.bufferViews); + std::optional maybeTileAvailability = + parseAvailabilityView( + subtree.tileAvailability, + subtree.buffers, + subtree.bufferViews); if (!maybeTileAvailability) return std::nullopt; - std::optional maybeChildSubtreeAvailability = - parseAvailabilityView( + std::optional + maybeChildSubtreeAvailability = parseAvailabilityView( subtree.childSubtreeAvailability, subtree.buffers, subtree.bufferViews); @@ -92,7 +95,7 @@ std::optional parseAvailabilityView( if (subtree.contentAvailability.empty()) return std::nullopt; - std::vector contentAvailability; + std::vector contentAvailability; contentAvailability.reserve(subtree.contentAvailability.size()); for (const auto& availabilityDesc : subtree.contentAvailability) { @@ -106,7 +109,7 @@ std::optional parseAvailabilityView( } return SubtreeAvailability( - powerOfTwo, + subdivisionScheme, levelsInSubtree, *maybeTileAvailability, *maybeChildSubtreeAvailability, @@ -115,7 +118,7 @@ std::optional parseAvailabilityView( } /*static*/ std::optional SubtreeAvailability::createEmpty( - uint32_t powerOfTwo, + ImplicitTileSubdivisionScheme subdivisionScheme, uint32_t levelsInSubtree) noexcept { Subtree subtree; subtree.tileAvailability.constant = true; @@ -123,14 +126,14 @@ std::optional parseAvailabilityView( subtree.childSubtreeAvailability.constant = false; return SubtreeAvailability::fromSubtree( - powerOfTwo, + subdivisionScheme, levelsInSubtree, std::move(subtree)); } /*static*/ CesiumAsync::Future> SubtreeAvailability::loadSubtree( - uint32_t powerOf2, + ImplicitTileSubdivisionScheme subdivisionScheme, uint32_t levelsInSubtree, const CesiumAsync::AsyncSystem& asyncSystem, const std::shared_ptr& pAssetAccessor, @@ -140,7 +143,7 @@ SubtreeAvailability::loadSubtree( SubtreeFileReader reader; return reader.load(asyncSystem, pAssetAccessor, subtreeUrl, requestHeaders) .thenInMainThread( - [pLogger, subtreeUrl, powerOf2, levelsInSubtree]( + [pLogger, subtreeUrl, subdivisionScheme, levelsInSubtree]( ReadJsonResult&& subtree) -> std::optional { if (!subtree.value) { @@ -162,21 +165,23 @@ SubtreeAvailability::loadSubtree( } return SubtreeAvailability::fromSubtree( - powerOf2, + subdivisionScheme, levelsInSubtree, std::move(*subtree.value)); }); } SubtreeAvailability::SubtreeAvailability( - uint32_t powerOf2, + ImplicitTileSubdivisionScheme subdivisionScheme, uint32_t levelsInSubtree, AvailabilityView tileAvailability, AvailabilityView subtreeAvailability, std::vector&& contentAvailability, Cesium3DTiles::Subtree&& subtree) - : _powerOf2{powerOf2}, - _childCount{1U << powerOf2}, + : _powerOf2{subdivisionScheme == ImplicitTileSubdivisionScheme::Quadtree ? 2U : 3U}, + _childCount{ + subdivisionScheme == ImplicitTileSubdivisionScheme::Quadtree ? 4U + : 8U}, _levelsInSubtree{levelsInSubtree}, _subtree{std::move(subtree)}, _tileAvailability{tileAvailability}, @@ -360,9 +365,11 @@ namespace { void convertConstantAvailabilityToBitstream( Subtree& subtree, uint64_t numberOfTiles, - AvailabilityView& availabilityView) { - const SubtreeConstantAvailability* pConstantAvailability = - std::get_if(&availabilityView); + SubtreeAvailability::AvailabilityView& availabilityView) { + const SubtreeAvailability::SubtreeConstantAvailability* + pConstantAvailability = + std::get_if( + &availabilityView); if (!pConstantAvailability) return; @@ -399,7 +406,7 @@ void convertConstantAvailabilityToBitstream( gsl::span view( buffer.cesium.data.data() + start, buffer.cesium.data.data() + end); - availabilityView = SubtreeBufferViewAvailability{view}; + availabilityView = SubtreeAvailability::SubtreeBufferViewAvailability{view}; } } // namespace diff --git a/Cesium3DTilesContent/test/TestSubtreeAvailability.cpp b/Cesium3DTilesContent/test/TestSubtreeAvailability.cpp index ebbe645a3..12de14743 100644 --- a/Cesium3DTilesContent/test/TestSubtreeAvailability.cpp +++ b/Cesium3DTilesContent/test/TestSubtreeAvailability.cpp @@ -29,9 +29,9 @@ struct SubtreeHeader { struct SubtreeBuffers { std::vector buffers; - SubtreeBufferViewAvailability tileAvailability; - SubtreeBufferViewAvailability subtreeAvailability; - SubtreeBufferViewAvailability contentAvailability; + SubtreeAvailability::SubtreeBufferViewAvailability tileAvailability; + SubtreeAvailability::SubtreeBufferViewAvailability subtreeAvailability; + SubtreeAvailability::SubtreeBufferViewAvailability contentAvailability; }; uint64_t calculateTotalNumberOfTilesForQuadtree(uint64_t subtreeLevels) { @@ -96,9 +96,12 @@ SubtreeBuffers createSubtreeBuffers( markSubtreeAvailableForQuadtree(subtreeID, subtreeAvailabilityBuffer); } - SubtreeBufferViewAvailability tileAvailability{tileAvailabilityBuffer}; - SubtreeBufferViewAvailability subtreeAvailability{subtreeAvailabilityBuffer}; - SubtreeBufferViewAvailability contentAvailability{contentAvailabilityBuffer}; + SubtreeAvailability::SubtreeBufferViewAvailability tileAvailability{ + tileAvailabilityBuffer}; + SubtreeAvailability::SubtreeBufferViewAvailability subtreeAvailability{ + subtreeAvailabilityBuffer}; + SubtreeAvailability::SubtreeBufferViewAvailability contentAvailability{ + contentAvailabilityBuffer}; return { std::move(availabilityBuffer), @@ -285,7 +288,7 @@ std::optional mockLoadSubtreeJson( CesiumAsync::AsyncSystem asyncSystem{pMockTaskProcessor}; auto subtreeFuture = SubtreeAvailability::loadSubtree( - 2, + ImplicitTileSubdivisionScheme::Quadtree, levelsInSubtree, asyncSystem, pMockAssetAccessor, @@ -301,11 +304,11 @@ std::optional mockLoadSubtreeJson( TEST_CASE("Test SubtreeAvailability methods") { SECTION("Availability stored in constant") { SubtreeAvailability subtreeAvailability{ - 2, + ImplicitTileSubdivisionScheme::Quadtree, 5, - SubtreeConstantAvailability{true}, - SubtreeConstantAvailability{false}, - {SubtreeConstantAvailability{false}}, + SubtreeAvailability::SubtreeConstantAvailability{true}, + SubtreeAvailability::SubtreeConstantAvailability{false}, + {SubtreeAvailability::SubtreeConstantAvailability{false}}, {}}; SECTION("isTileAvailable()") { @@ -412,14 +415,16 @@ TEST_CASE("Test SubtreeAvailability methods") { markSubtreeAvailableForQuadtree(subtreeID, subtreeAvailabilityBuffer); } - SubtreeBufferViewAvailability tileAvailability{tileAvailabilityBuffer}; - SubtreeBufferViewAvailability subtreeAvailability{ + SubtreeAvailability::SubtreeBufferViewAvailability tileAvailability{ + tileAvailabilityBuffer}; + SubtreeAvailability::SubtreeBufferViewAvailability subtreeAvailability{ subtreeAvailabilityBuffer}; - std::vector contentAvailability{ - SubtreeBufferViewAvailability{contentAvailabilityBuffer}}; + std::vector contentAvailability{ + SubtreeAvailability::SubtreeBufferViewAvailability{ + contentAvailabilityBuffer}}; SubtreeAvailability quadtreeAvailability( - 2, + ImplicitTileSubdivisionScheme::Quadtree, static_cast(maxSubtreeLevels), tileAvailability, subtreeAvailability, @@ -561,7 +566,7 @@ TEST_CASE("Test parsing subtree format") { CesiumAsync::AsyncSystem asyncSystem{pMockTaskProcessor}; auto subtreeFuture = SubtreeAvailability::loadSubtree( - 2, + ImplicitTileSubdivisionScheme::Quadtree, maxSubtreeLevels, asyncSystem, pMockAssetAccessor, @@ -706,7 +711,9 @@ TEST_CASE("Test parsing subtree format") { TEST_CASE("SubtreeAvailability modifications") { std::optional maybeAvailability = - SubtreeAvailability::createEmpty(2, 5); + SubtreeAvailability::createEmpty( + ImplicitTileSubdivisionScheme::Quadtree, + 5); REQUIRE(maybeAvailability); SubtreeAvailability& availability = *maybeAvailability; diff --git a/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp b/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp index a212c1b16..2809d6fe8 100644 --- a/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp +++ b/Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp @@ -208,7 +208,7 @@ ImplicitOctreeLoader::loadTileContent(const TileLoadInput& loadInput) { this->_subtreeUrlTemplate, subtreeID); return SubtreeAvailability::loadSubtree( - 3, + ImplicitTileSubdivisionScheme::Octree, this->_subtreeLevels, asyncSystem, pAssetAccessor, diff --git a/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp b/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp index a9d668de3..d38b3525b 100644 --- a/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp +++ b/Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp @@ -247,7 +247,7 @@ ImplicitQuadtreeLoader::loadTileContent(const TileLoadInput& loadInput) { this->_subtreeUrlTemplate, subtreeID); return SubtreeAvailability::loadSubtree( - 2, + ImplicitTileSubdivisionScheme::Quadtree, this->_subtreeLevels, asyncSystem, pAssetAccessor, diff --git a/Cesium3DTilesSelection/test/TestImplicitOctreeLoader.cpp b/Cesium3DTilesSelection/test/TestImplicitOctreeLoader.cpp index b8bab6ae2..bc01b0591 100644 --- a/Cesium3DTilesSelection/test/TestImplicitOctreeLoader.cpp +++ b/Cesium3DTilesSelection/test/TestImplicitOctreeLoader.cpp @@ -68,11 +68,11 @@ TEST_CASE("Test implicit octree loader") { loader.addSubtreeAvailability( OctreeTileID{0, 0, 0, 0}, SubtreeAvailability{ - 3, + ImplicitTileSubdivisionScheme::Octree, 5, - SubtreeConstantAvailability{true}, - SubtreeConstantAvailability{false}, - {SubtreeConstantAvailability{false}}, + SubtreeAvailability::SubtreeConstantAvailability{true}, + SubtreeAvailability::SubtreeConstantAvailability{false}, + {SubtreeAvailability::SubtreeConstantAvailability{false}}, {}}); // check that this tile will have empty content @@ -104,11 +104,11 @@ TEST_CASE("Test implicit octree loader") { loader.addSubtreeAvailability( OctreeTileID{0, 0, 0, 0}, SubtreeAvailability{ - 2, + ImplicitTileSubdivisionScheme::Quadtree, 5, - SubtreeConstantAvailability{true}, - SubtreeConstantAvailability{false}, - {SubtreeConstantAvailability{true}}, + SubtreeAvailability::SubtreeConstantAvailability{true}, + SubtreeAvailability::SubtreeConstantAvailability{false}, + {SubtreeAvailability::SubtreeConstantAvailability{true}}, {}}); // mock tile content b3dm @@ -157,11 +157,11 @@ TEST_CASE("Test implicit octree loader") { loader.addSubtreeAvailability( OctreeTileID{0, 0, 0, 0}, SubtreeAvailability{ - 2, + ImplicitTileSubdivisionScheme::Quadtree, 5, - SubtreeConstantAvailability{true}, - SubtreeConstantAvailability{false}, - {SubtreeConstantAvailability{true}}, + SubtreeAvailability::SubtreeConstantAvailability{true}, + SubtreeAvailability::SubtreeConstantAvailability{false}, + {SubtreeAvailability::SubtreeConstantAvailability{true}}, {}}); // mock random tile content @@ -247,11 +247,11 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { loader.addSubtreeAvailability( OctreeTileID{0, 0, 0, 0}, SubtreeAvailability{ - 3, + ImplicitTileSubdivisionScheme::Octree, 5, - SubtreeConstantAvailability{true}, - SubtreeConstantAvailability{false}, - {SubtreeConstantAvailability{true}}, + SubtreeAvailability::SubtreeConstantAvailability{true}, + SubtreeAvailability::SubtreeConstantAvailability{false}, + {SubtreeAvailability::SubtreeConstantAvailability{true}}, {}}); // check subdivide root tile first @@ -448,11 +448,11 @@ TEST_CASE("Test tile subdivision for implicit octree loader") { loader.addSubtreeAvailability( OctreeTileID{0, 0, 0, 0}, SubtreeAvailability{ - 3, + ImplicitTileSubdivisionScheme::Octree, 5, - SubtreeConstantAvailability{true}, - SubtreeConstantAvailability{false}, - {SubtreeConstantAvailability{true}}, + SubtreeAvailability::SubtreeConstantAvailability{true}, + SubtreeAvailability::SubtreeConstantAvailability{false}, + {SubtreeAvailability::SubtreeConstantAvailability{true}}, {}}); // check subdivide root tile first diff --git a/Cesium3DTilesSelection/test/TestImplicitQuadtreeLoader.cpp b/Cesium3DTilesSelection/test/TestImplicitQuadtreeLoader.cpp index e2777312c..f3880ebe6 100644 --- a/Cesium3DTilesSelection/test/TestImplicitQuadtreeLoader.cpp +++ b/Cesium3DTilesSelection/test/TestImplicitQuadtreeLoader.cpp @@ -68,11 +68,11 @@ TEST_CASE("Test implicit quadtree loader") { loader.addSubtreeAvailability( QuadtreeTileID{0, 0, 0}, SubtreeAvailability{ - 2, + ImplicitTileSubdivisionScheme::Quadtree, 5, - SubtreeConstantAvailability{true}, - SubtreeConstantAvailability{false}, - {SubtreeConstantAvailability{false}}, + SubtreeAvailability::SubtreeConstantAvailability{true}, + SubtreeAvailability::SubtreeConstantAvailability{false}, + {SubtreeAvailability::SubtreeConstantAvailability{false}}, {}}); // check that this tile will have empty content @@ -104,11 +104,11 @@ TEST_CASE("Test implicit quadtree loader") { loader.addSubtreeAvailability( QuadtreeTileID{0, 0, 0}, SubtreeAvailability{ - 2, + ImplicitTileSubdivisionScheme::Quadtree, 5, - SubtreeConstantAvailability{true}, - SubtreeConstantAvailability{false}, - {SubtreeConstantAvailability{true}}, + SubtreeAvailability::SubtreeConstantAvailability{true}, + SubtreeAvailability::SubtreeConstantAvailability{false}, + {SubtreeAvailability::SubtreeConstantAvailability{true}}, {}}); // mock tile content b3dm @@ -157,11 +157,11 @@ TEST_CASE("Test implicit quadtree loader") { loader.addSubtreeAvailability( QuadtreeTileID{0, 0, 0}, SubtreeAvailability{ - 2, + ImplicitTileSubdivisionScheme::Quadtree, 5, - SubtreeConstantAvailability{true}, - SubtreeConstantAvailability{false}, - {SubtreeConstantAvailability{true}}, + SubtreeAvailability::SubtreeConstantAvailability{true}, + SubtreeAvailability::SubtreeConstantAvailability{false}, + {SubtreeAvailability::SubtreeConstantAvailability{true}}, {}}); // mock tile content b3dm @@ -223,11 +223,11 @@ TEST_CASE("Test tile subdivision for implicit quadtree loader") { loader.addSubtreeAvailability( QuadtreeTileID{0, 0, 0}, SubtreeAvailability{ - 2, + ImplicitTileSubdivisionScheme::Quadtree, 5, - SubtreeConstantAvailability{true}, - SubtreeConstantAvailability{false}, - {SubtreeConstantAvailability{true}}, + SubtreeAvailability::SubtreeConstantAvailability{true}, + SubtreeAvailability::SubtreeConstantAvailability{false}, + {SubtreeAvailability::SubtreeConstantAvailability{true}}, {}}); // check subdivide root tile first @@ -365,11 +365,11 @@ TEST_CASE("Test tile subdivision for implicit quadtree loader") { loader.addSubtreeAvailability( QuadtreeTileID{0, 0, 0}, SubtreeAvailability{ - 2, + ImplicitTileSubdivisionScheme::Quadtree, 5, - SubtreeConstantAvailability{true}, - SubtreeConstantAvailability{false}, - {SubtreeConstantAvailability{true}}, + SubtreeAvailability::SubtreeConstantAvailability{true}, + SubtreeAvailability::SubtreeConstantAvailability{false}, + {SubtreeAvailability::SubtreeConstantAvailability{true}}, {}}); // check subdivide root tile first @@ -500,11 +500,11 @@ TEST_CASE("Test tile subdivision for implicit quadtree loader") { loader.addSubtreeAvailability( QuadtreeTileID{0, 0, 0}, SubtreeAvailability{ - 2, + ImplicitTileSubdivisionScheme::Quadtree, 5, - SubtreeConstantAvailability{true}, - SubtreeConstantAvailability{false}, - {SubtreeConstantAvailability{true}}, + SubtreeAvailability::SubtreeConstantAvailability{true}, + SubtreeAvailability::SubtreeConstantAvailability{false}, + {SubtreeAvailability::SubtreeConstantAvailability{true}}, {}}); Tile tile(&loader); From 97eea46ea5de29341be0a9522bcda0ecc30f160c Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Mon, 13 Nov 2023 21:33:17 +1100 Subject: [PATCH 348/421] Fix Clang/GCC warning. --- Cesium3DTilesReader/src/SubtreeFileReader.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Cesium3DTilesReader/src/SubtreeFileReader.cpp b/Cesium3DTilesReader/src/SubtreeFileReader.cpp index dbc0461b8..f46a66cf8 100644 --- a/Cesium3DTilesReader/src/SubtreeFileReader.cpp +++ b/Cesium3DTilesReader/src/SubtreeFileReader.cpp @@ -280,7 +280,8 @@ Future> SubtreeFileReader::postprocess( "updated to match.", buffer.byteLength, completedBuffer.data.size())); - buffer.byteLength = completedBuffer.data.size(); + buffer.byteLength = + static_cast(completedBuffer.data.size()); } buffer.cesium.data = std::move(completedBuffer.data); } From 18392e7c40cd91dbbbf685374567079f6ae9506d Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Mon, 13 Nov 2023 21:43:10 +1100 Subject: [PATCH 349/421] Update CHANGES.md. --- CHANGES.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 317b08d9c..32391e08a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -8,6 +8,8 @@ - Moved `GltfUtilities` from `Cesium3DTilesSelection` to `Cesium3DTilesContent`. - Moved `createRasterOverlayTextureCoordinates` method from `GltfUtilities` to a new `RasterOverlayUtilities` class in the `Cesium3DTilesSelection` library. - `GltfUtilities::parseGltfCopyright` now returns the credits as a vector of string_views. Previously it took a `CreditSystem` and created credits directly. +- The `SubtreeAvailability` constructor and `loadSubtree` static method now take an `ImplicitTileSubdivisionScheme` enumeration parameter instead of a `powerOf2` parameter. They also now require a `levelsInSubtree` parameter, which is needed when switching from constant to bitstream availability. Lastly, the constructor now takes a `Subtree` parameter instead of a `std::vector>` representing the buffers. +- `SubtreeConstantAvailability`, `SubtreeBufferViewAvailability`, and `AvailabilityView` are now members of `SubtreeAvailability`. ##### Additions :tada: @@ -17,7 +19,10 @@ - Added `toSphere`, `fromSphere`, and `fromAxisAligned` methods to `CesiumGeometry::OrientedBoundingBox`. - Added `TileTransform` class to `Cesium3DTilesContent`, making it easier to create a `glm::dmat4` from the `transform` property of a `Cesium3DTiles::Tile`. - Added `ImplicitTilingUtilities` class to `Cesium3DTilesContent`. -- Added overloads of `isTileAvailable` and `isContentAvailable` on the `SubtreeAvailability` class that take the subtree root tile ID and the tile ID of interest, instead of a relative level and Morton index. +- Added overloads of `isTileAvailable`, `isContentAvailable`, and `isSubtreeAvailable` on the `SubtreeAvailability` class that take the subtree root tile ID and the tile ID of interest, instead of a relative level and Morton index. +- Added `fromSubtree` and `createEmpty` static methods to `SubtreeAvailability`. +- Added new `set` methods to `SubtreeAvailability`, allowing the availability information to be modified. +- Added `SubtreeFileReader` class, used to read `Cesium3DTiles::Subtree` from a binary or JSON subtree file. ##### Fixes :wrench: From 3d990cc617cf7b5a0b506b112f0a9426d3c02671 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Mon, 13 Nov 2023 21:51:43 +1100 Subject: [PATCH 350/421] More GCC/Clang warnings. --- Cesium3DTilesContent/src/SubtreeAvailability.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/Cesium3DTilesContent/src/SubtreeAvailability.cpp b/Cesium3DTilesContent/src/SubtreeAvailability.cpp index 3e4cb2840..316c6da8f 100644 --- a/Cesium3DTilesContent/src/SubtreeAvailability.cpp +++ b/Cesium3DTilesContent/src/SubtreeAvailability.cpp @@ -49,19 +49,21 @@ std::optional parseAvailabilityView( if (bufferViewIndex >= 0 && bufferViewIndex < static_cast(bufferViews.size())) { - const Cesium3DTiles::BufferView& bufferView = bufferViews[bufferViewIndex]; + const Cesium3DTiles::BufferView& bufferView = + bufferViews[size_t(bufferViewIndex)]; if (bufferView.buffer >= 0 && bufferView.buffer < static_cast(buffers.size())) { - Cesium3DTiles::Buffer& buffer = buffers[bufferView.buffer]; + Cesium3DTiles::Buffer& buffer = buffers[size_t(bufferView.buffer)]; std::vector& data = buffer.cesium.data; int64_t bufferSize = std::min(static_cast(data.size()), buffer.byteLength); - if (bufferView.byteOffset + bufferView.byteLength <= bufferSize) { + if (bufferView.byteLength >= 0 && + bufferView.byteOffset + bufferView.byteLength <= bufferSize) { return SubtreeAvailability::SubtreeBufferViewAvailability{ gsl::span( data.data() + bufferView.byteOffset, - bufferView.byteLength)}; + size_t(bufferView.byteLength))}; } } } @@ -381,7 +383,7 @@ void convertConstantAvailabilityToBitstream( BufferView& bufferView = subtree.bufferViews.emplace_back(); bufferView.buffer = 0; - bufferView.byteLength = numberOfBytes; + bufferView.byteLength = int64_t(numberOfBytes); Buffer& buffer = !subtree.buffers.empty() ? subtree.buffers[0] : subtree.buffers.emplace_back(); @@ -394,13 +396,13 @@ void convertConstantAvailabilityToBitstream( start += 8 - paddingRemainder; } - int64_t end = start + numberOfBytes; + int64_t end = start + int64_t(numberOfBytes); bufferView.byteOffset = start; buffer.byteLength = end; buffer.cesium.data.resize( - buffer.byteLength, + size_t(buffer.byteLength), oldValue ? std::byte(0xFF) : std::byte(0x00)); gsl::span view( From 61c29cbf24ec3ea96e8b0d57f8691869d14f649c Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Mon, 13 Nov 2023 22:12:14 +1100 Subject: [PATCH 351/421] Fix SubtreeAvailability field init order. --- Cesium3DTilesContent/src/SubtreeAvailability.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cesium3DTilesContent/src/SubtreeAvailability.cpp b/Cesium3DTilesContent/src/SubtreeAvailability.cpp index 316c6da8f..8f2bc3d04 100644 --- a/Cesium3DTilesContent/src/SubtreeAvailability.cpp +++ b/Cesium3DTilesContent/src/SubtreeAvailability.cpp @@ -181,11 +181,11 @@ SubtreeAvailability::SubtreeAvailability( std::vector&& contentAvailability, Cesium3DTiles::Subtree&& subtree) : _powerOf2{subdivisionScheme == ImplicitTileSubdivisionScheme::Quadtree ? 2U : 3U}, + _levelsInSubtree{levelsInSubtree}, + _subtree{std::move(subtree)}, _childCount{ subdivisionScheme == ImplicitTileSubdivisionScheme::Quadtree ? 4U : 8U}, - _levelsInSubtree{levelsInSubtree}, - _subtree{std::move(subtree)}, _tileAvailability{tileAvailability}, _subtreeAvailability{subtreeAvailability}, _contentAvailability{std::move(contentAvailability)} { From 8a0293ec63a0bf264b4f615d9399dde8136b10d3 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Mon, 13 Nov 2023 22:18:10 +1100 Subject: [PATCH 352/421] Clang warnings in tests. --- Cesium3DTilesContent/test/TestSubtreeAvailability.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cesium3DTilesContent/test/TestSubtreeAvailability.cpp b/Cesium3DTilesContent/test/TestSubtreeAvailability.cpp index 12de14743..dc55d1747 100644 --- a/Cesium3DTilesContent/test/TestSubtreeAvailability.cpp +++ b/Cesium3DTilesContent/test/TestSubtreeAvailability.cpp @@ -400,11 +400,11 @@ TEST_CASE("Test SubtreeAvailability methods") { subtreeAvailabilityBuffer.resize(subtreeBufferSize); subtree.buffers[0].byteLength = subtree.bufferViews[0].byteLength = - bufferSize; + int64_t(bufferSize); subtree.buffers[1].byteLength = subtree.bufferViews[1].byteLength = - bufferSize; + int64_t(bufferSize); subtree.buffers[2].byteLength = subtree.bufferViews[2].byteLength = - subtreeBufferSize; + int64_t(subtreeBufferSize); for (const auto& tileID : availableTileIDs) { markTileAvailableForQuadtree(tileID, tileAvailabilityBuffer); From d18af8593c3ab953f9d453a9bdbcdcf068d86573 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Mon, 13 Nov 2023 11:16:18 -0500 Subject: [PATCH 353/421] Fix PropertyAttributeView forEachProperty --- CesiumGltf/include/CesiumGltf/PropertyAttributeView.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CesiumGltf/include/CesiumGltf/PropertyAttributeView.h b/CesiumGltf/include/CesiumGltf/PropertyAttributeView.h index f07682ecc..771ee8c9d 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyAttributeView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyAttributeView.h @@ -308,8 +308,8 @@ class PropertyAttributeView { forEachProperty(const MeshPrimitive& primitive, Callback&& callback) const { for (const auto& property : this->_pClass->properties) { getPropertyView( - property.first, primitive, + property.first, std::forward(callback)); } } From f6e7401bb12045c0a9df404f120cd3bb838f067b Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Mon, 13 Nov 2023 11:21:11 -0500 Subject: [PATCH 354/421] Upate CHANGES.md --- CHANGES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 273ad30da..6d4a17c37 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -13,6 +13,10 @@ - Added `Cesium3DTilesContent` library and namespace. It has classes for loading, converting, and manipulating 3D Tiles tile content. +##### Fixes :wrench: + +- Fixed compiler error when calling `PropertyAttributeView::forEachProperty`. + ### v0.29.0 - 2023-11-01 ##### Breaking Changes :mega: From c7122591d66b3c30168207244f74be0cacc9a2bf Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Mon, 13 Nov 2023 11:47:14 -0500 Subject: [PATCH 355/421] Switch to std::optional syntax instead of defaulting to some value --- .../include/CesiumGltf/MetadataConversions.h | 304 +++----- CesiumGltf/test/TestMetadataConversions.cpp | 662 +++++++++--------- .../include/CesiumUtility/JsonValue.h | 15 + 3 files changed, 464 insertions(+), 517 deletions(-) diff --git a/CesiumGltf/include/CesiumGltf/MetadataConversions.h b/CesiumGltf/include/CesiumGltf/MetadataConversions.h index 0738c4b82..53b277976 100644 --- a/CesiumGltf/include/CesiumGltf/MetadataConversions.h +++ b/CesiumGltf/include/CesiumGltf/MetadataConversions.h @@ -8,24 +8,25 @@ #include #include +#include #include #include namespace CesiumGltf { /** - * @brief Default conversion between two types. No actual conversion is defined; - * this simply returns the default value. + * @brief Default conversion between two types. No actual conversion is defined. + * This returns std::nullopt to indicate the conversion was not successful. */ template struct MetadataConversions { - static TTo convert(TFrom /*from*/, TTo defaultValue) { return defaultValue; } + static std::optional convert(TFrom /*from*/) { return std::nullopt; } }; /** * @brief Trivially converts any type to itself. */ template struct MetadataConversions { - static T convert(T from, T /* defaultValue*/) { return from; } + static std::optional convert(T from) { return from; } }; #pragma region Conversions to boolean @@ -42,9 +43,8 @@ struct MetadataConversions< * nonzero values are converted to true. * * @param from The scalar to convert from. - * @param defaultValue The default value to be returned if conversion fails. */ - static bool convert(TFrom from, bool /*defaultValue*/) { + static std::optional convert(TFrom from) { return from != static_cast(0); } }; @@ -74,12 +74,11 @@ template <> struct MetadataConversions { * * "0", "false", and "no" (case-insensitive) are converted to false, while * "1", "true", and "yes" are converted to true. All other strings will return - * the default value. + * std::nullopt. * * @param from The std::string_view to convert from. - * @param defaultValue The default value to be returned if conversion fails. */ - static bool convert(const std::string_view& from, bool defaultValue) { + static std::optional convert(const std::string_view& from) { if (isEqualCaseInsensitive(from, "1") || isEqualCaseInsensitive(from, "true") || isEqualCaseInsensitive(from, "yes")) { @@ -92,7 +91,7 @@ template <> struct MetadataConversions { return false; } - return defaultValue; + return std::nullopt; } }; @@ -112,14 +111,13 @@ struct MetadataConversions< !std::is_same_v>> { /** * @brief Converts a value of the given integer to another integer type. If - * the integer cannot be losslessly converted to the desired type, the default - * value is returned. + * the integer cannot be losslessly converted to the desired type, + * std::nullopt is returned. * * @param from The integer value to convert from. - * @param defaultValue The default value to be returned if conversion fails. */ - static TTo convert(TFrom from, TTo defaultValue) { - return CesiumUtility::losslessNarrowOrDefault(from, defaultValue); + static std::optional convert(TFrom from) { + return CesiumUtility::losslessNarrow(from); } }; @@ -137,17 +135,16 @@ struct MetadataConversions< * @brief Converts a floating-point value to an integer type. This truncates * the floating-point value, rounding it towards zero. * - * If the value is outside the range of the integer type, the default value is + * If the value is outside the range of the integer type, std::nullopt is * returned. * * @param from The floating-point value to convert from. - * @param defaultValue The default value to be returned if conversion fails. */ - static TTo convert(TFrom from, TTo defaultValue) { + static std::optional convert(TFrom from) { if (double(std::numeric_limits::max()) < from || double(std::numeric_limits::lowest()) > from) { // Floating-point number is outside the range of this integer type. - return defaultValue; + return std::nullopt; } return static_cast(from); @@ -168,12 +165,17 @@ struct MetadataConversions< * This assumes that the entire std::string_view represents the number, not * just a part of it. * - * This returns the default value if no number is parsed from the string. + * This returns std::nullopt if no number is parsed from the string. * * @param from The std::string_view to parse from. - * @param defaultValue The default value to be returned if conversion fails. */ - static TTo convert(const std::string_view& from, TTo defaultValue) { + static std::optional convert(const std::string_view& from) { + if (from.size() == 0) { + // Return early. Otherwise, empty strings will be parsed as 0, which is + // misleading. + return std::nullopt; + } + // Amazingly, C++ has no* string parsing functions that work with strings // that might not be null-terminated. So we have to copy to a std::string // (which _is_ guaranteed to be null terminated) before parsing. @@ -185,7 +187,7 @@ struct MetadataConversions< int64_t parsedValue = std::strtoll(temp.c_str(), &pLastUsed, 10); if (pLastUsed == temp.c_str() + temp.size()) { // Successfully parsed the entire string as an integer of this type. - return CesiumUtility::losslessNarrowOrDefault(parsedValue, defaultValue); + return CesiumUtility::losslessNarrow(parsedValue); } // Failed to parse as an integer. Maybe we can parse as a double and @@ -199,11 +201,11 @@ struct MetadataConversions< int64_t asInteger = static_cast(truncated); double roundTrip = static_cast(asInteger); if (roundTrip == truncated) { - return CesiumUtility::losslessNarrowOrDefault(asInteger, defaultValue); + return CesiumUtility::losslessNarrow(asInteger); } } - return defaultValue; + return std::nullopt; } }; @@ -221,12 +223,18 @@ struct MetadataConversions< * This assumes that the entire std::string_view represents the number, not * just a part of it. * - * This returns the default value if no number is parsed from the string. + * This returns std::nullopt if no number is parsed from the string. * * @param from The std::string_view to parse from. * @param defaultValue The default value to be returned if conversion fails. */ - static TTo convert(const std::string_view& from, TTo defaultValue) { + static std::optional convert(const std::string_view& from) { + if (from.size() == 0) { + // Return early. Otherwise, empty strings will be parsed as 0, which is + // misleading. + return std::nullopt; + } + // Amazingly, C++ has no* string parsing functions that work with strings // that might not be null-terminated. So we have to copy to a std::string // (which _is_ guaranteed to be null terminated) before parsing. @@ -238,7 +246,7 @@ struct MetadataConversions< uint64_t parsedValue = std::strtoull(temp.c_str(), &pLastUsed, 10); if (pLastUsed == temp.c_str() + temp.size()) { // Successfully parsed the entire string as an integer of this type. - return CesiumUtility::losslessNarrowOrDefault(parsedValue, defaultValue); + return CesiumUtility::losslessNarrow(parsedValue); } // Failed to parse as an integer. Maybe we can parse as a double and @@ -252,11 +260,11 @@ struct MetadataConversions< uint64_t asInteger = static_cast(truncated); double roundTrip = static_cast(asInteger); if (roundTrip == truncated) { - return CesiumUtility::losslessNarrowOrDefault(asInteger, defaultValue); + return CesiumUtility::losslessNarrow(asInteger); } } - return defaultValue; + return std::nullopt; } }; @@ -273,9 +281,8 @@ struct MetadataConversions< * false. * * @param from The boolean value to be converted. - * @param defaultValue The default value to be returned if conversion fails. */ - static TTo convert(bool from, TTo /*defaultValue*/) { return from ? 1 : 0; } + static std::optional convert(bool from) { return from ? 1 : 0; } }; #pragma endregion @@ -289,11 +296,8 @@ template <> struct MetadataConversions { * false. * * @param from The boolean value to be converted. - * @param defaultValue The default value to be returned if conversion fails. */ - static float convert(bool from, float /*defaultValue*/) { - return from ? 1.0f : 0.0f; - } + static std::optional convert(bool from) { return from ? 1.0f : 0.0f; } }; /** @@ -309,9 +313,8 @@ struct MetadataConversions< * conversion. * * @param from The integer value to be converted. - * @param defaultValue The default value to be returned if conversion fails. */ - static float convert(TFrom from, float /*defaultValue*/) { + static std::optional convert(TFrom from) { return static_cast(from); } }; @@ -324,16 +327,14 @@ template <> struct MetadataConversions { * @brief Converts a double to a float. The value may lose precision during * conversion. * - * If the value is outside the range of a float, the default value is - * returned. + * If the value is outside the range of a float, std::nullopt is returned. * * @param from The double value to be converted. - * @param defaultValue The default value to be returned if conversion fails. */ - static float convert(double from, float defaultValue) { + static std::optional convert(double from) { if (from > std::numeric_limits::max() || from < std::numeric_limits::lowest()) { - return defaultValue; + return std::nullopt; } return static_cast(from); } @@ -347,12 +348,16 @@ template <> struct MetadataConversions { * @brief Converts a std::string_view to a float. This assumes that the entire * std::string_view represents the number, not just a part of it. * - * This returns the default value if no number is parsed from the string. + * This returns std::nullopt if no number is parsed from the string. * * @param from The std::string_view to parse from. - * @param defaultValue The default value to be returned if conversion fails. */ - static float convert(const std::string_view& from, float defaultValue) { + static std::optional convert(const std::string_view& from) { + if (from.size() == 0) { + // Return early. Otherwise, empty strings will be parsed as 0, which is + // misleading. + return std::nullopt; + } // Amazingly, C++ has no* string parsing functions that work with strings // that might not be null-terminated. So we have to copy to a std::string // (which _is_ guaranteed to be null terminated) before parsing. @@ -366,7 +371,7 @@ template <> struct MetadataConversions { // Successfully parsed the entire string as a float. return parsedValue; } - return defaultValue; + return std::nullopt; } }; #pragma endregion @@ -381,11 +386,8 @@ template <> struct MetadataConversions { * false. * * @param from The boolean value to be converted. - * @param defaultValue The default value to be returned if conversion fails. */ - static double convert(bool from, double /*defaultValue*/) { - return from ? 1.0 : 0.0; - } + static std::optional convert(bool from) { return from ? 1.0 : 0.0; } }; /** @@ -401,9 +403,8 @@ struct MetadataConversions< * during conversion. * * @param from The boolean value to be converted. - * @param defaultValue The default value to be returned if conversion fails. */ - static double convert(TFrom from, double /*defaultValue*/) { + static std::optional convert(TFrom from) { return static_cast(from); } }; @@ -416,9 +417,8 @@ template <> struct MetadataConversions { * @brief Converts from a float to a double. * * @param from The float value to be converted. - * @param defaultValue The default value to be returned if conversion fails. */ - static double convert(float from, double /*defaultValue*/) { + static std::optional convert(float from) { return static_cast(from); } }; @@ -431,12 +431,16 @@ template <> struct MetadataConversions { * Converts a std::string_view to a double. This assumes that the entire * std::string_view represents the number, not just a part of it. * - * This returns the default value if no number is parsed from the string. + * This returns std::nullopt if no number is parsed from the string. * * @param from The std::string_view to parse from. - * @param defaultValue The default value to be returned if conversion fails. */ - static double convert(const std::string_view& from, double defaultValue) { + static std::optional convert(const std::string_view& from) { + if (from.size() == 0) { + // Return early. Otherwise, empty strings will be parsed as 0, which is + // misleading. + return std::nullopt; + } // Amazingly, C++ has no* string parsing functions that work with strings // that might not be null-terminated. So we have to copy to a std::string // (which _is_ guaranteed to be null terminated) before parsing. @@ -450,7 +454,7 @@ template <> struct MetadataConversions { // Successfully parsed the entire string as a double. return parsedValue; } - return defaultValue; + return std::nullopt; } }; #pragma endregion @@ -461,13 +465,12 @@ template <> struct MetadataConversions { */ template <> struct MetadataConversions { /** - * @brief Converts a boolean to a FString. Returns "true" for true and "false" - * for false. + * @brief Converts a boolean to a std::string. Returns "true" for true and + * "false" for false. * * @param from The boolean to be converted. - * @param defaultValue The default value to be returned if conversion fails. */ - static std::string convert(bool from, const std::string& /*defaultValue*/) { + static std::optional convert(bool from) { return from ? "true" : "false"; } }; @@ -481,54 +484,32 @@ struct MetadataConversions< TFrom, std::enable_if_t::value>> { /** - * @brief Converts a scalar to a FString. + * @brief Converts a scalar to a std::string. * * @param from The scalar to be converted. * @param defaultValue The default value to be returned if conversion fails. */ - static std::string convert(TFrom from, const std::string& /*defaultValue*/) { + static std::optional convert(TFrom from) { return std::to_string(from); } }; /** - * @brief Converts from a glm::vecN to a string. - */ -template -struct MetadataConversions< - std::string, - TFrom, - std::enable_if_t::value>> { - /** - * @brief Converts a glm::vecN to a FString. This uses the format that - * glm::to_string() outputs for vecNs, - * - * @param from The glm::vecN to be converted. - * @param defaultValue The default value to be returned if conversion fails. - */ - static std::string - convert(const TFrom& from, const std::string& /*defaultValue*/) { - return glm::to_string(from); - } -}; - -/** - * @brief Converts from a glm::matN to a string. + * @brief Converts from a glm::vecN or glm::matN to a string. */ template struct MetadataConversions< std::string, TFrom, - std::enable_if_t::value>> { + std::enable_if_t< + IsMetadataVecN::value || IsMetadataMatN::value>> { /** - * @brief Converts a glm::matN to a FString. This uses the format that - * glm::to_string() outputs for matNs. + * @brief Converts a glm::vecN or glm::matN to a std::string. This uses the + * format that glm::to_string() outputs for vecNs or matNs respectively. * - * @param from The glm::matN to be converted. - * @param defaultValue The default value to be returned if conversion fails. + * @param from The glm::vecN or glm::matN to be converted. */ - static std::string - convert(const TFrom& from, const std::string& /*defaultValue*/) { + static std::optional convert(const TFrom& from) { return glm::to_string(from); } }; @@ -540,8 +521,7 @@ template <> struct MetadataConversions { /** * @brief Converts from a std::string_view to a std::string. */ - static std::string - convert(const std::string_view& from, const std::string& /*defaultValue*/) { + static std::optional convert(const std::string_view& from) { return std::string(from.data(), from.size()); } }; @@ -563,15 +543,14 @@ struct MetadataConversions< * this value in both of its components. * * @param from The boolean to be converted. - * @param defaultValue The default value to be returned if conversion fails. */ - static TTo convert(bool from, const TTo& /*defaultValue*/) { + static std::optional convert(bool from) { return from ? TTo(1) : TTo(0); } }; /** - * Converts from a signed integer type to a vecN. + * Converts from a scalar type to a vecN. */ template struct MetadataConversions< @@ -579,93 +558,26 @@ struct MetadataConversions< TFrom, std::enable_if_t< CesiumGltf::IsMetadataVecN::value && - CesiumGltf::IsMetadataInteger::value && - std::is_signed_v>> { + CesiumGltf::IsMetadataScalar::value>> { /** - * Converts a signed integer to a vecN. The returned vector is initialized - * with the value in all of its components. If the integer cannot be - * reasonably converted to the component type of the vecN, the default value - * is returned. - * - * @param from The signed integer value to be converted. - * @param defaultValue The default value to be returned if conversion fails. - */ - static TTo convert(TFrom from, const TTo& defaultValue) { - using ToValueType = typename TTo::value_type; - ToValueType max = std::numeric_limits::max(); - - if constexpr (std::is_signed_v) { - ToValueType min = std::numeric_limits::lowest(); - if (from > max || from < min) { - return defaultValue; - } - } else { - if (from > static_cast(max)) { - return defaultValue; - } - } - return TTo(MetadataConversions::convert(from, 0)); - } -}; - -/** - * Converts from an unsigned integer type to a vecN. - */ -template -struct MetadataConversions< - TTo, - TFrom, - std::enable_if_t< - CesiumGltf::IsMetadataVecN::value && - CesiumGltf::IsMetadataInteger::value && - std::is_unsigned_v>> { - /** - * Converts an unsigned integer to a vecN. The returned vector is initialized - * with the value in all of its components. If the integer cannot be - * reasonably converted to the component type of the vecN, the default value - * is returned. + * Converts a scalar to a vecN. The returned vector is initialized + * with the value in all of its components. If the scalar cannot be + * reasonably converted to the component type of the vecN, std::nullopt is + * returned. * - * @param from The unsigned integer value to be converted. - * @param defaultValue The default value to be returned if conversion fails. + * @param from The scalar value to be converted. */ - static TTo convert(TFrom from, const TTo& defaultValue) { + static std::optional convert(TFrom from) { using ToValueType = typename TTo::value_type; - ToValueType max = std::numeric_limits::max(); - if (from > static_cast(max)) { - return defaultValue; + std::optional maybeValue = + MetadataConversions::convert(from); + if (maybeValue) { + ToValueType value = *maybeValue; + return TTo(value); } - return TTo(MetadataConversions::convert(from, 0)); - } -}; -/** - * Converts from a floating-point scalar type to a vecN. - */ -template -struct MetadataConversions< - TTo, - TFrom, - std::enable_if_t< - CesiumGltf::IsMetadataVecN::value && - CesiumGltf::IsMetadataFloating::value>> { - /** - * Converts a floating-point scalar to a vecN. The returned vector is - * initialized with the value in all of its components. If the scalar cannot - * be reasonably converted to the component type of the vecN, the default - * value is returned. The value may lose precision during conversion. - * - * @param from The floating-point scalar value to be converted. - * @param defaultValue The default value to be returned if conversion fails. - */ - static TTo convert(TFrom from, const TTo& defaultValue) { - using ToValueType = typename TTo::value_type; - ToValueType max = std::numeric_limits::max(); - ToValueType min = std::numeric_limits::lowest(); - if (from > static_cast(max) || from < static_cast(min)) { - return defaultValue; - } - return TTo(MetadataConversions::convert(from, 0)); + return std::nullopt; } }; @@ -681,37 +593,35 @@ struct MetadataConversions< CesiumGltf::IsMetadataVecN::value && !std::is_same_v>> { /** - * @brief Converts a value of the given vecN to another vecN type. If the - * given vector has more components than the target vecN type, then only its - * first N components will be used. Otherwise, if the target vecN type has - * more components, the extra components will be initialized to zero. + * @brief Converts a value of the given vecN to another vecN type. + * + * If the given vector has more components than the target vecN type, then + * only its first N components will be used, where N is the dimension of the + * target vecN type. Otherwise, if the target vecN type has more components, + * its extra components will be initialized to zero. * * If any of the relevant components cannot be converted to the target vecN - * component type, the default value is returned. + * component type, std::nullopt is returned. * * @param from The vecN value to convert from. - * @param defaultValue The default value to be returned if conversion fails. */ - static TTo convert(TFrom from, TTo defaultValue) { + static std::optional convert(TFrom from) { TTo result = TTo(0); - constexpr glm::length_t toLength = TTo::length(); - constexpr glm::length_t fromLength = TFrom::length(); - - constexpr glm::length_t validLength = glm::min(toLength, fromLength); + constexpr glm::length_t validLength = + glm::min(TTo::length(), TFrom::length()); using ToValueType = typename TTo::value_type; - ToValueType max = std::numeric_limits::max(); - ToValueType min = std::numeric_limits::lowest(); - using FromValueType = typename TFrom::value_type; for (glm::length_t i = 0; i < validLength; i++) { - if (from[i] > max || from[i] < min) { - return defaultValue; + auto maybeValue = + MetadataConversions::convert(from[i]); + if (!maybeValue) { + return std::nullopt; } - result[i] = - MetadataConversions::convert(from[i], 0); + + result[i] = *maybeValue; } return result; diff --git a/CesiumGltf/test/TestMetadataConversions.cpp b/CesiumGltf/test/TestMetadataConversions.cpp index e414b2c6d..dbc45fcdf 100644 --- a/CesiumGltf/test/TestMetadataConversions.cpp +++ b/CesiumGltf/test/TestMetadataConversions.cpp @@ -6,320 +6,257 @@ using namespace CesiumGltf; TEST_CASE("Test PropertyConversions for boolean") { SECTION("converts from boolean") { - REQUIRE(MetadataConversions::convert(true, false)); - REQUIRE(!MetadataConversions::convert(false, true)); + REQUIRE(MetadataConversions::convert(true) == true); + REQUIRE(MetadataConversions::convert(false) == false); } SECTION("converts from scalar") { // true for nonzero value - REQUIRE(MetadataConversions::convert(10, false)); + REQUIRE(MetadataConversions::convert(10) == true); // false for zero value - REQUIRE(!MetadataConversions::convert(0, true)); + REQUIRE(MetadataConversions::convert(0) == false); } SECTION("converts from string") { std::string_view stringView("true"); - REQUIRE(MetadataConversions::convert( - stringView, - false)); + REQUIRE( + MetadataConversions::convert(stringView) == + true); stringView = std::string_view("yes"); - REQUIRE(MetadataConversions::convert( - stringView, - false)); + REQUIRE( + MetadataConversions::convert(stringView) == + true); stringView = std::string_view("1"); - REQUIRE(MetadataConversions::convert( - stringView, - false)); + REQUIRE( + MetadataConversions::convert(stringView) == + true); stringView = std::string_view("false"); - REQUIRE(!MetadataConversions::convert( - stringView, - true)); + REQUIRE( + MetadataConversions::convert(stringView) == + false); stringView = std::string_view("no"); - REQUIRE(!MetadataConversions::convert( - stringView, - true)); + REQUIRE( + MetadataConversions::convert(stringView) == + false); stringView = std::string_view("0"); - REQUIRE(!MetadataConversions::convert( - stringView, - true)); + REQUIRE( + MetadataConversions::convert(stringView) == + false); } - SECTION("uses default value for incompatible strings") { + SECTION("returns std::nullopt for incompatible strings") { std::string_view stringView("11"); // invalid number - REQUIRE(!MetadataConversions::convert( - stringView, - false)); + REQUIRE(!MetadataConversions::convert(stringView)); stringView = std::string_view("this is true"); // invalid word - REQUIRE(!MetadataConversions::convert( - stringView, - false)); + REQUIRE(!MetadataConversions::convert(stringView)); } - SECTION("uses default value for incompatible types") { + SECTION("returns std::nullopt for incompatible types") { // vecN - REQUIRE(!MetadataConversions::convert( - glm::vec3(1, 2, 3), - false)); + REQUIRE(!MetadataConversions::convert(glm::vec3(1, 2, 3))); // matN - REQUIRE(!MetadataConversions::convert(glm::mat2(), false)); + REQUIRE(!MetadataConversions::convert(glm::mat2())); // array REQUIRE(!MetadataConversions>::convert( - PropertyArrayView(), - false)); + PropertyArrayView())); } } TEST_CASE("Test PropertyConversions for integer") { SECTION("converts from in-range integer") { // same type - REQUIRE(MetadataConversions::convert(50, 0) == 50); + REQUIRE(MetadataConversions::convert(50) == 50); // different size - REQUIRE(MetadataConversions::convert(50, 0) == 50); + REQUIRE(MetadataConversions::convert(50) == 50); // different sign - REQUIRE(MetadataConversions::convert(50, 0) == 50); + REQUIRE(MetadataConversions::convert(50) == 50); } SECTION("converts from in-range floating point number") { - REQUIRE(MetadataConversions::convert(50.125f, 0) == 50); - REQUIRE( - MetadataConversions::convert(1234.05678f, 0) == 1234); + REQUIRE(MetadataConversions::convert(50.125f) == 50); + REQUIRE(MetadataConversions::convert(1234.05678f) == 1234); } SECTION("converts from boolean") { - REQUIRE(MetadataConversions::convert(true, -1) == 1); - REQUIRE(MetadataConversions::convert(false, -1) == 0); + REQUIRE(MetadataConversions::convert(true) == 1); + REQUIRE(MetadataConversions::convert(false) == 0); } SECTION("converts from string") { // integer string std::string_view value("-123"); REQUIRE( - MetadataConversions::convert(value, 0) == - -123); + MetadataConversions::convert(value) == -123); // double string value = std::string_view("123.456"); REQUIRE( - MetadataConversions::convert(value, 0) == - 123); + MetadataConversions::convert(value) == 123); } - SECTION("uses default value for out-of-range numbers") { + SECTION("returns std::nullopt for out-of-range numbers") { // out-of-range unsigned int - REQUIRE( - MetadataConversions::convert( - std::numeric_limits::max(), - 0) == 0); + REQUIRE(!MetadataConversions::convert( + std::numeric_limits::max())); // out-of-range signed int - REQUIRE( - MetadataConversions::convert( - std::numeric_limits::min(), - 0) == 0); + REQUIRE(!MetadataConversions::convert( + std::numeric_limits::min())); // out-of-range float - REQUIRE(MetadataConversions::convert(1234.56f, 0) == 0); + REQUIRE(!MetadataConversions::convert(1234.56f)); // out-of-range double - REQUIRE( - MetadataConversions::convert( - std::numeric_limits::max(), - 0) == 0); + REQUIRE(!MetadataConversions::convert( + std::numeric_limits::max())); } - SECTION("uses default value for invalid strings") { + SECTION("returns std::nullopt for invalid strings") { // out-of-range number - REQUIRE( - MetadataConversions::convert( - std::string_view("-255"), - 0) == 0); + REQUIRE(!MetadataConversions::convert( + std::string_view("-255"))); // mixed number and non-number input - REQUIRE( - MetadataConversions::convert( - std::string_view("10 hello"), - 0) == 0); + REQUIRE(!MetadataConversions::convert( + std::string_view("10 hello"))); // non-number input - REQUIRE( - MetadataConversions::convert( - std::string_view("not a number"), - 0) == 0); + REQUIRE(!MetadataConversions::convert( + std::string_view("not a number"))); // empty input - REQUIRE( - MetadataConversions::convert( - std::string_view(), - 0) == 0); + REQUIRE(!MetadataConversions::convert( + std::string_view())); } - SECTION("uses default value for incompatible types") { + SECTION("returns std::nullopt for incompatible types") { // vecN - REQUIRE( - MetadataConversions::convert( - glm::ivec3(1, 2, 3), - 0) == 0); + REQUIRE(!MetadataConversions::convert( + glm::ivec3(1, 2, 3))); // matN REQUIRE( - MetadataConversions::convert( - glm::imat2x2(), - 0) == 0); + !MetadataConversions::convert(glm::imat2x2())); // array PropertyArrayView arrayView; - REQUIRE( - MetadataConversions>::convert( - arrayView, - 0) == 0); + REQUIRE(!MetadataConversions>::convert( + arrayView)); } } TEST_CASE("Test PropertyConversions for float") { SECTION("converts from in-range floating point number") { + REQUIRE(MetadataConversions::convert(123.45f) == 123.45f); REQUIRE( - MetadataConversions::convert(123.45f, 0.0f) == 123.45f); - REQUIRE( - MetadataConversions::convert(123.45, 0.0f) == + MetadataConversions::convert(123.45) == static_cast(123.45)); } SECTION("converts from integer") { int32_t int32Value = -1234; REQUIRE( - MetadataConversions::convert(int32Value, 0.0f) == + MetadataConversions::convert(int32Value) == static_cast(int32Value)); constexpr uint64_t uint64Value = std::numeric_limits::max(); REQUIRE( - MetadataConversions::convert(uint64Value, 0.0f) == + MetadataConversions::convert(uint64Value) == static_cast(uint64Value)); } SECTION("converts from boolean") { - REQUIRE(MetadataConversions::convert(true, -1.0f) == 1.0f); - REQUIRE(MetadataConversions::convert(false, -1.0f) == 0.0f); + REQUIRE(MetadataConversions::convert(true) == 1.0f); + REQUIRE(MetadataConversions::convert(false) == 0.0f); } SECTION("converts from string") { REQUIRE( MetadataConversions::convert( - std::string_view("123"), - 0) == static_cast(123)); + std::string_view("123")) == static_cast(123)); REQUIRE( MetadataConversions::convert( - std::string_view("123.456"), - 0) == static_cast(123.456)); + std::string_view("123.456")) == static_cast(123.456)); } - SECTION("uses default value for invalid strings") { + SECTION("returns std::nullopt for invalid strings") { // out-of-range number - REQUIRE( - MetadataConversions::convert( - std::string_view( - std::to_string(std::numeric_limits::max())), - 0.0f) == 0.0f); + REQUIRE(!MetadataConversions::convert( + std::string_view(std::to_string(std::numeric_limits::max())))); // mixed number and non-number input - REQUIRE( - MetadataConversions::convert( - std::string_view("10.00f hello"), - 0.0f) == 0.0f); + REQUIRE(!MetadataConversions::convert( + std::string_view("10.00f hello"))); // non-number input - REQUIRE( - MetadataConversions::convert( - std::string_view("not a number"), - 0.0f) == 0.0f); + REQUIRE(!MetadataConversions::convert( + std::string_view("not a number"))); // empty input - REQUIRE( - MetadataConversions::convert( - std::string_view(), - 0.0f) == 0.0f); + REQUIRE(!MetadataConversions::convert( + std::string_view())); } - SECTION("uses default value for incompatible types") { + SECTION("returns std::nullopt for incompatible types") { // vecN REQUIRE( - MetadataConversions::convert( - glm::vec3(1, 2, 3), - 0.0f) == 0.0f); + !MetadataConversions::convert(glm::vec3(1, 2, 3))); // matN - REQUIRE( - MetadataConversions::convert(glm::mat2(), 0.0f) == - 0.0f); + REQUIRE(!MetadataConversions::convert(glm::mat2())); // array PropertyArrayView arrayView; - REQUIRE( - MetadataConversions>::convert( - arrayView, - 0.0f) == 0.0f); + REQUIRE(!MetadataConversions>::convert( + arrayView)); } } TEST_CASE("Test PropertyConversions for double") { SECTION("converts from floating point number") { REQUIRE( - MetadataConversions::convert(123.45f, 0.0) == + MetadataConversions::convert(123.45f) == static_cast(123.45f)); - REQUIRE( - MetadataConversions::convert(123.45, 0.0) == 123.45); + REQUIRE(MetadataConversions::convert(123.45) == 123.45); } SECTION("converts from integer") { constexpr uint64_t uint64Value = std::numeric_limits::max(); REQUIRE( - MetadataConversions::convert(uint64Value, 0.0) == + MetadataConversions::convert(uint64Value) == static_cast(uint64Value)); } SECTION("converts from boolean") { - REQUIRE(MetadataConversions::convert(true, -1.0) == 1.0); - REQUIRE(MetadataConversions::convert(false, -1.0) == 0.0); + REQUIRE(MetadataConversions::convert(true) == 1.0); + REQUIRE(MetadataConversions::convert(false) == 0.0); } SECTION("converts from string") { REQUIRE( MetadataConversions::convert( - std::string_view("123"), - 0) == static_cast(123)); + std::string_view("123")) == static_cast(123)); REQUIRE( MetadataConversions::convert( - std::string_view("123.456"), - 0) == 123.456); + std::string_view("123.456")) == 123.456); } - SECTION("uses default value for invalid strings") { + SECTION("returns std::nullopt for invalid strings") { // mixed number and non-number input - REQUIRE( - MetadataConversions::convert( - std::string_view("10.00 hello"), - 0.0) == 0.0); + REQUIRE(!MetadataConversions::convert( + std::string_view("10.00 hello"))); // non-number input - REQUIRE( - MetadataConversions::convert( - std::string_view("not a number"), - 0.0) == 0.0); + REQUIRE(!MetadataConversions::convert( + std::string_view("not a number"))); // empty input - REQUIRE( - MetadataConversions::convert( - std::string_view(), - 0.0) == 0.0); + REQUIRE(!MetadataConversions::convert( + std::string_view())); } - SECTION("uses default value for incompatible types") { + SECTION("returns std::nullopt for incompatible types") { // vecN REQUIRE( - MetadataConversions::convert( - glm::dvec3(1, 2, 3), - 0.0) == 0.0); + !MetadataConversions::convert(glm::dvec3(1, 2, 3))); // matN - REQUIRE( - MetadataConversions::convert( - glm::dmat2(1.0, 2.0, 3.0, 4.0), - 0.0) == 0.0); + REQUIRE(!MetadataConversions::convert( + glm::dmat2(1.0, 2.0, 3.0, 4.0))); // array PropertyArrayView arrayView; - REQUIRE( - MetadataConversions>::convert( - arrayView, - 0.0) == 0.0); + REQUIRE(!MetadataConversions>::convert( + arrayView)); } } @@ -328,59 +265,49 @@ TEST_CASE("Test PropertyConversions for vec2") { // int-to-int REQUIRE( MetadataConversions::convert( - glm::ivec2(12, 76), - glm::ivec2(0)) == glm::ivec2(12, 76)); + glm::ivec2(12, 76)) == glm::ivec2(12, 76)); // float-to-float REQUIRE( MetadataConversions::convert( - glm::vec2(-3.5f, 1.234f), - glm::vec2(0.0)) == glm::vec2(-3.5f, 1.234f)); + glm::vec2(-3.5f, 1.234f)) == glm::vec2(-3.5f, 1.234f)); } SECTION("converts from other vec2 types") { // int-to-int REQUIRE( MetadataConversions::convert( - glm::u8vec2(12, 76), - glm::ivec2(0)) == glm::ivec2(12, 76)); + glm::u8vec2(12, 76)) == glm::ivec2(12, 76)); // int-to-float REQUIRE( MetadataConversions::convert( - glm::ivec2(12, 76), - glm::vec2(0)) == glm::vec2(12, 76)); + glm::ivec2(12, 76)) == glm::vec2(12, 76)); // float-to-int REQUIRE( MetadataConversions::convert( - glm::dvec2(-3.5, 1.23456), - glm::i8vec2(0)) == glm::i8vec2(-3, 1)); + glm::dvec2(-3.5, 1.23456)) == glm::i8vec2(-3, 1)); // float-to-float REQUIRE( MetadataConversions::convert( - glm::vec2(-3.5f, 1.234f), - glm::dvec2(0.0)) == glm::dvec2(-3.5f, 1.234f)); + glm::vec2(-3.5f, 1.234f)) == glm::dvec2(-3.5f, 1.234f)); } SECTION("converts from vec3 types") { // int-to-int REQUIRE( MetadataConversions::convert( - glm::ivec3(-84, 5, 129), - glm::i8vec2(0)) == glm::i8vec2(-84, 5)); + glm::ivec3(-84, 5, 129)) == glm::i8vec2(-84, 5)); // int-to-float REQUIRE( MetadataConversions::convert( - glm::ivec3(-84, 5, 25), - glm::dvec2(0.0)) == glm::dvec2(-84, 5)); + glm::ivec3(-84, 5, 25)) == glm::dvec2(-84, 5)); // float-to-int REQUIRE( MetadataConversions::convert( - glm::vec3(4.5f, 2.345f, 81.0f), - glm::uvec2(0)) == glm::uvec2(4, 2)); + glm::vec3(4.5f, 2.345f, 81.0f)) == glm::uvec2(4, 2)); // float-to-float REQUIRE( MetadataConversions::convert( - glm::dvec3(4.5, -2.345, 81.0), - glm::vec2(0)) == + glm::dvec3(4.5, -2.345, 81.0)) == glm::vec2(static_cast(4.5), static_cast(-2.345))); } @@ -388,100 +315,84 @@ TEST_CASE("Test PropertyConversions for vec2") { // int-to-int REQUIRE( MetadataConversions::convert( - glm::i16vec4(-42, 278, 23, 1), - glm::ivec2(0)) == glm::ivec2(-42, 278)); + glm::i16vec4(-42, 278, 23, 1)) == glm::ivec2(-42, 278)); + // int-to-float + REQUIRE( + MetadataConversions::convert( + glm::ivec4(-84, 5, 25, 1)) == glm::dvec2(-84, 5)); // float-to-int REQUIRE( MetadataConversions::convert( - glm::dvec4(-3.5, 1.23456, 26.0, 8.0), - glm::i8vec2(0)) == glm::i8vec2(-3, 1)); + glm::dvec4(-3.5, 1.23456, 26.0, 8.0)) == glm::i8vec2(-3, 1)); + // float-to-float + REQUIRE( + MetadataConversions::convert( + glm::dvec4(4.5, -2.345, 81.0, 1.0)) == + glm::vec2(static_cast(4.5), static_cast(-2.345))); } SECTION("converts from boolean") { REQUIRE( - MetadataConversions::convert( - true, - glm::dvec2(-1.0)) == glm ::dvec2(1.0)); + MetadataConversions::convert(true) == + glm ::dvec2(1.0)); } SECTION("converts from integer") { // int to int REQUIRE( - MetadataConversions::convert( - 45, - glm::u8vec2(0)) == glm::u8vec2(45)); + MetadataConversions::convert(45) == + glm::u8vec2(45)); REQUIRE( - MetadataConversions::convert( - 45, - glm::i16vec2(0)) == glm::i16vec2(45)); + MetadataConversions::convert(45) == + glm::i16vec2(45)); // int to float REQUIRE( - MetadataConversions::convert( - -12345, - glm::dvec2(0)) == glm::dvec2(-12345)); + MetadataConversions::convert(-12345) == + glm::dvec2(-12345)); REQUIRE( - MetadataConversions::convert(12, glm::vec2(0)) == - glm::vec2(12)); + MetadataConversions::convert(12) == glm::vec2(12)); } SECTION("converts from float") { // float to int REQUIRE( - MetadataConversions::convert( - 45.4f, - glm::u8vec2(0)) == glm::u8vec2(45)); + MetadataConversions::convert(45.4f) == + glm::u8vec2(45)); REQUIRE( - MetadataConversions::convert( - -1.0111, - glm::i16vec2(0)) == glm::i16vec2(-1)); + MetadataConversions::convert(-1.0111) == + glm::i16vec2(-1)); // float to float REQUIRE( - MetadataConversions::convert( - -1234.5f, - glm::dvec2(0)) == glm::dvec2(-1234.5f)); + MetadataConversions::convert(-1234.5f) == + glm::dvec2(-1234.5f)); REQUIRE( - MetadataConversions::convert(12.0, glm::vec2(0)) == + MetadataConversions::convert(12.0) == glm::vec2(12.0)); } - SECTION("uses default value if not all components can be converted") { + SECTION("returns std::nullopt if not all components can be converted") { // scalar - REQUIRE( - MetadataConversions::convert( - -1, - glm::u8vec2(0)) == glm::u8vec2(0)); + REQUIRE(!MetadataConversions::convert(-1)); // int - REQUIRE( - MetadataConversions::convert( - glm::ivec3(0, -1, 2), - glm::u8vec2(0)) == glm::u8vec2(0)); - REQUIRE( - MetadataConversions::convert( - glm::u8vec4(0, 255, 2, 1), - glm::i8vec2(0)) == glm::i8vec2(0)); + REQUIRE(!MetadataConversions::convert( + glm::ivec3(0, -1, 2))); + REQUIRE(!MetadataConversions::convert( + glm::u8vec4(0, 255, 2, 1))); // float - REQUIRE( - MetadataConversions::convert( - glm::vec2(129.0f, -129.0f), - glm::i8vec2(0)) == glm::i8vec2(0)); - REQUIRE( - MetadataConversions::convert( - glm::dvec3(std::numeric_limits::max()), - glm::vec2(0)) == glm::vec2(0)); + REQUIRE(!MetadataConversions::convert( + glm::vec2(129.0f, -129.0f))); + REQUIRE(!MetadataConversions::convert( + glm::dvec3(std::numeric_limits::max()))); }; - SECTION("uses default value for incompatible types") { + SECTION("returns std::nullopt for incompatible types") { // matN - REQUIRE( - MetadataConversions::convert( - glm::dmat2(1.0, 2.0, 3.0, 4.0), - glm::dvec2(0.0)) == glm::dvec2(0.0)); + REQUIRE(!MetadataConversions::convert( + glm::dmat2(1.0, 2.0, 3.0, 4.0))); // array PropertyArrayView arrayView; - REQUIRE( - MetadataConversions>::convert( - arrayView, - glm::ivec2(0)) == glm::ivec2(0)); + REQUIRE(!MetadataConversions>:: + convert(arrayView)); }; } @@ -490,158 +401,269 @@ TEST_CASE("Test PropertyConversions for vec3") { // int-to-int REQUIRE( MetadataConversions::convert( - glm::ivec3(12, 76, -1), - glm::ivec3(0)) == glm::ivec3(12, 76, -1)); + glm::ivec3(12, 76, -1)) == glm::ivec3(12, 76, -1)); // float-to-float REQUIRE( MetadataConversions::convert( - glm::vec3(-3.5f, 1.234f, 1.0f), - glm::vec3(0.0)) == glm::vec3(-3.5f, 1.234f, 1.0f)); + glm::vec3(-3.5f, 1.234f, 1.0f)) == glm::vec3(-3.5f, 1.234f, 1.0f)); } SECTION("converts from other vec3 types") { // int-to-int REQUIRE( MetadataConversions::convert( - glm::u8vec3(12, 76, 1), - glm::ivec3(0)) == glm::ivec3(12, 76, 1)); + glm::u8vec3(12, 76, 1)) == glm::ivec3(12, 76, 1)); // int-to-float REQUIRE( MetadataConversions::convert( - glm::ivec3(12, 76, 1), - glm::vec3(0)) == glm::vec3(12, 76, 1)); + glm::ivec3(12, 76, 1)) == glm::vec3(12, 76, 1)); // float-to-int REQUIRE( MetadataConversions::convert( - glm::dvec3(-3.5, 1.23456, -1.40), - glm::i8vec3(0)) == glm::i8vec3(-3, 1, -1)); + glm::dvec3(-3.5, 1.23456, -1.40)) == glm::i8vec3(-3, 1, -1)); // float-to-float REQUIRE( MetadataConversions::convert( - glm::vec3(-3.5f, 1.234f, 2.4f), - glm::dvec3(0.0)) == glm::dvec3(-3.5f, 1.234f, 2.4f)); + glm::vec3(-3.5f, 1.234f, 2.4f)) == glm::dvec3(-3.5f, 1.234f, 2.4f)); } SECTION("converts from vec2 types") { // int-to-int REQUIRE( MetadataConversions::convert( - glm::ivec2(-84, 5), - glm::i8vec3(0)) == glm::i8vec3(-84, 5, 0)); + glm::ivec2(-84, 5)) == glm::i8vec3(-84, 5, 0)); // int-to-float REQUIRE( MetadataConversions::convert( - glm::ivec2(-84, 5), - glm::dvec3(0.0)) == glm::dvec3(-84, 5, 0)); + glm::ivec2(-84, 5)) == glm::dvec3(-84, 5, 0)); // float-to-int REQUIRE( MetadataConversions::convert( - glm::vec2(4.5f, 2.345f), - glm::uvec3(0)) == glm::uvec3(4, 2, 0)); + glm::vec2(4.5f, 2.345f)) == glm::uvec3(4, 2, 0)); // float-to-float REQUIRE( MetadataConversions::convert( - glm::dvec2(4.5, -2.345), - glm::vec3(0)) == glm::vec3(4.5, -2.345, 0)); + glm::dvec2(4.5, -2.345)) == glm::vec3(4.5, -2.345, 0)); } SECTION("converts from vec4 types") { // int-to-int REQUIRE( MetadataConversions::convert( - glm::i16vec4(-42, 278, 23, 1), - glm::ivec3(0)) == glm::ivec3(-42, 278, 23)); + glm::i16vec4(-42, 278, 23, 1)) == glm::ivec3(-42, 278, 23)); + // int-to-float + REQUIRE( + MetadataConversions::convert( + glm::ivec4(-84, 5, 10, 23)) == glm::dvec3(-84, 5, 10)); // float-to-int REQUIRE( MetadataConversions::convert( - glm::dvec4(-3.5, 1.23456, 26.0, 8.0), - glm::i8vec3(0)) == glm::i8vec3(-3, 1, 26)); + glm::dvec4(-3.5, 1.23456, 26.0, 8.0)) == glm::i8vec3(-3, 1, 26)); + // float-to-float + REQUIRE( + MetadataConversions::convert( + glm::dvec4(4.5, -2.345, 102.3, 1)) == + glm::vec3(4.5, -2.345, 102.3)); } SECTION("converts from boolean") { REQUIRE( - MetadataConversions::convert( - true, - glm::dvec3(-1.0)) == glm ::dvec3(1.0)); + MetadataConversions::convert(true) == + glm ::dvec3(1.0)); } SECTION("converts from integer") { // int to int REQUIRE( - MetadataConversions::convert( - 45, - glm::u8vec3(0)) == glm::u8vec3(45)); + MetadataConversions::convert(45) == + glm::u8vec3(45)); REQUIRE( - MetadataConversions::convert( - 45, - glm::i16vec3(0)) == glm::i16vec3(45)); + MetadataConversions::convert(45) == + glm::i16vec3(45)); // int to float REQUIRE( - MetadataConversions::convert( - -12345, - glm::dvec3(0)) == glm::dvec3(-12345)); + MetadataConversions::convert(-12345) == + glm::dvec3(-12345)); REQUIRE( - MetadataConversions::convert(12, glm::vec3(0)) == - glm::vec3(12)); + MetadataConversions::convert(12) == glm::vec3(12)); } SECTION("converts from float") { // float to int REQUIRE( - MetadataConversions::convert( - 45.4f, - glm::u8vec3(0)) == glm::u8vec3(45)); + MetadataConversions::convert(45.4f) == + glm::u8vec3(45)); REQUIRE( - MetadataConversions::convert( - -1.0111, - glm::i16vec3(0)) == glm::i16vec3(-1)); + MetadataConversions::convert(-1.0111) == + glm::i16vec3(-1)); // float to float REQUIRE( - MetadataConversions::convert( - -1234.5f, - glm::dvec3(0)) == glm::dvec3(-1234.5f)); + MetadataConversions::convert(-1234.5f) == + glm::dvec3(-1234.5f)); REQUIRE( - MetadataConversions::convert(12.0, glm::vec3(0)) == + MetadataConversions::convert(12.0) == glm::vec3(12.0)); } - SECTION("uses default value if not all components can be converted") { + SECTION("returns std::nullopt if not all components can be converted") { // scalar - REQUIRE( - MetadataConversions::convert( - -1, - glm::u8vec3(0)) == glm::u8vec3(0)); + REQUIRE(!MetadataConversions::convert(-1)); // int + REQUIRE(!MetadataConversions::convert( + glm::ivec3(0, -1, 2))); + REQUIRE(!MetadataConversions::convert( + glm::u8vec4(0, 255, 2, 1))); + // float + REQUIRE(!MetadataConversions::convert( + glm::vec2(129.0f, -129.0f))); + REQUIRE(!MetadataConversions::convert( + glm::dvec4(std::numeric_limits::max()))); + }; + + SECTION("returns std::nullopt for incompatible types") { + // matN + REQUIRE(!MetadataConversions::convert( + glm::dmat2(1.0, 2.0, 3.0, 4.0))); + // array + PropertyArrayView arrayView; + REQUIRE(!MetadataConversions>:: + convert(arrayView)); + }; +} + +TEST_CASE("Test PropertyConversions for vec4") { + SECTION("converts from same vec4 type") { + // int-to-int REQUIRE( - MetadataConversions::convert( - glm::ivec3(0, -1, 2), - glm::u8vec3(0)) == glm::u8vec3(0)); + MetadataConversions::convert( + glm::ivec4(12, 76, -1, 1)) == glm::ivec4(12, 76, -1, 1)); + // float-to-float REQUIRE( - MetadataConversions::convert( - glm::u8vec4(0, 255, 2, 1), - glm::i8vec3(0)) == glm::i8vec3(0)); - // float + MetadataConversions::convert( + glm::vec4(-3.5f, 1.234f, 1.0f, 1.0f)) == + glm::vec4(-3.5f, 1.234f, 1.0f, 1.0f)); + } + + SECTION("converts from other vec4 types") { + // int-to-int REQUIRE( - MetadataConversions::convert( - glm::vec2(129.0f, -129.0f), - glm::i8vec3(0)) == glm::i8vec3(0)); + MetadataConversions::convert( + glm::u8vec4(12, 76, 1, 1)) == glm::ivec4(12, 76, 1, 1)); + // int-to-float REQUIRE( - MetadataConversions::convert( - glm::dvec4(std::numeric_limits::max()), - glm::vec3(0)) == glm::vec3(0)); + MetadataConversions::convert( + glm::ivec4(12, 76, 1, 1)) == glm::vec4(12, 76, 1, 1)); + // float-to-int + REQUIRE( + MetadataConversions::convert( + glm::dvec4(-3.5, 1.23456, -1.40, 1.0)) == + glm::i8vec4(-3, 1, -1, 1)); + // float-to-float + REQUIRE( + MetadataConversions::convert( + glm::vec4(-3.5f, 1.234f, 2.4f, 1.0f)) == + glm::dvec4(-3.5f, 1.234f, 2.4f, 1.0f)); + } + + SECTION("converts from vec2 types") { + // int-to-int + REQUIRE( + MetadataConversions::convert( + glm::ivec2(-84, 5)) == glm::i8vec4(-84, 5, 0, 0)); + // int-to-float + REQUIRE( + MetadataConversions::convert( + glm::ivec2(-84, 5)) == glm::dvec4(-84, 5, 0, 0)); + // float-to-int + REQUIRE( + MetadataConversions::convert( + glm::vec2(4.5f, 2.345f)) == glm::uvec4(4, 2, 0, 0)); + // float-to-float + REQUIRE( + MetadataConversions::convert( + glm::dvec2(4.5, -2.345)) == glm::vec4(4.5, -2.345, 0, 0)); + } + + SECTION("converts from vec3 types") { + // int-to-int + REQUIRE( + MetadataConversions::convert( + glm::i16vec3(-42, 278, 23)) == glm::ivec4(-42, 278, 23, 0)); + // int-to-float + REQUIRE( + MetadataConversions::convert( + glm::ivec3(-84, 5, 1)) == glm::dvec4(-84, 5, 1, 0)); + // float-to-int + REQUIRE( + MetadataConversions::convert( + glm::dvec3(-3.5, 1.23456, 26.0)) == glm::i8vec4(-3, 1, 26, 0)); + // float-to-float + REQUIRE( + MetadataConversions::convert( + glm::dvec3(4.5, -2.345, 12.0)) == glm::vec4(4.5, -2.345, 12.0, 0)); + } + + SECTION("converts from boolean") { + REQUIRE( + MetadataConversions::convert(true) == + glm ::dvec4(1.0)); + } + + SECTION("converts from integer") { + // int to int + REQUIRE( + MetadataConversions::convert(45) == + glm::u8vec4(45)); + REQUIRE( + MetadataConversions::convert(45) == + glm::i16vec4(45)); + // int to float + REQUIRE( + MetadataConversions::convert(-12345) == + glm::dvec4(-12345)); + REQUIRE( + MetadataConversions::convert(12) == glm::vec4(12)); + } + + SECTION("converts from float") { + // float to int + REQUIRE( + MetadataConversions::convert(45.4f) == + glm::u8vec4(45)); + REQUIRE( + MetadataConversions::convert(-1.0111) == + glm::i16vec4(-1)); + // float to float + REQUIRE( + MetadataConversions::convert(-1234.5f) == + glm::dvec4(-1234.5f)); + REQUIRE( + MetadataConversions::convert(12.0) == + glm::vec4(12.0)); + } + + SECTION("returns std::nullopt if not all components can be converted") { + // scalar + REQUIRE(!MetadataConversions::convert(-1)); + // int + REQUIRE(!MetadataConversions::convert( + glm::ivec3(0, -1, 2))); + REQUIRE(!MetadataConversions::convert( + glm::u8vec4(0, 255, 2, 1))); + // float + REQUIRE(!MetadataConversions::convert( + glm::vec2(129.0f, -129.0f))); + REQUIRE(!MetadataConversions::convert( + glm::dvec4(std::numeric_limits::max()))); }; - SECTION("uses default value for incompatible types") { + SECTION("returns std::nullopt for incompatible types") { // matN - REQUIRE( - MetadataConversions::convert( - glm::dmat2(1.0, 2.0, 3.0, 4.0), - glm::dvec3(0.0)) == glm::dvec3(0.0)); + REQUIRE(!MetadataConversions::convert( + glm::dmat2(1.0, 2.0, 3.0, 4.0))); // array - PropertyArrayView arrayView; - REQUIRE( - MetadataConversions>::convert( - arrayView, - glm::ivec3(0)) == glm::ivec3(0)); + PropertyArrayView arrayView; + REQUIRE(!MetadataConversions>:: + convert(arrayView)); }; } diff --git a/CesiumUtility/include/CesiumUtility/JsonValue.h b/CesiumUtility/include/CesiumUtility/JsonValue.h index 64d4c0bc8..833820141 100644 --- a/CesiumUtility/include/CesiumUtility/JsonValue.h +++ b/CesiumUtility/include/CesiumUtility/JsonValue.h @@ -28,6 +28,21 @@ struct JsonValueNotRealValue : public std::runtime_error { : std::runtime_error("this->value was not double, uint64_t or int64_t") {} }; +template +constexpr std::optional losslessNarrow(U u) noexcept { + constexpr const bool is_different_signedness = + (std::is_signed::value != std::is_signed::value); + + const T t = gsl::narrow_cast(u); + + if (static_cast(t) != u || + (is_different_signedness && ((t < T{}) != (u < U{})))) { + return std::nullopt; + } + + return t; +} + template constexpr T losslessNarrowOrDefault(U u, T defaultValue) noexcept { constexpr const bool is_different_signedness = From 4b0c18d6abd9449ed5996f6f641fd0262d8bf208 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Mon, 13 Nov 2023 14:27:12 -0500 Subject: [PATCH 356/421] Add support for matN conversions --- .../include/CesiumGltf/MetadataConversions.h | 128 +++- CesiumGltf/test/TestMetadataConversions.cpp | 623 +++++++++++++++++- 2 files changed, 735 insertions(+), 16 deletions(-) diff --git a/CesiumGltf/include/CesiumGltf/MetadataConversions.h b/CesiumGltf/include/CesiumGltf/MetadataConversions.h index 53b277976..e3008e637 100644 --- a/CesiumGltf/include/CesiumGltf/MetadataConversions.h +++ b/CesiumGltf/include/CesiumGltf/MetadataConversions.h @@ -246,7 +246,7 @@ struct MetadataConversions< uint64_t parsedValue = std::strtoull(temp.c_str(), &pLastUsed, 10); if (pLastUsed == temp.c_str() + temp.size()) { // Successfully parsed the entire string as an integer of this type. - return CesiumUtility::losslessNarrow(parsedValue); + return CesiumUtility::losslessNarrow(parsedValue); } // Failed to parse as an integer. Maybe we can parse as a double and @@ -260,7 +260,7 @@ struct MetadataConversions< uint64_t asInteger = static_cast(truncated); double roundTrip = static_cast(asInteger); if (roundTrip == truncated) { - return CesiumUtility::losslessNarrow(asInteger); + return CesiumUtility::losslessNarrow(asInteger); } } @@ -530,7 +530,7 @@ template <> struct MetadataConversions { #pragma region Conversions to glm::vecN /** - * Converts from a boolean to a vecN. + * @brief Converts from a boolean to a vecN. */ template struct MetadataConversions< @@ -538,7 +538,7 @@ struct MetadataConversions< bool, std::enable_if_t::value>> { /** - * Converts a boolean to a vecN. The boolean is converted to an integer + * @brief Converts a boolean to a vecN. The boolean is converted to an integer * value of 1 for true or 0 for false. The returned vector is initialized with * this value in both of its components. * @@ -550,7 +550,7 @@ struct MetadataConversions< }; /** - * Converts from a scalar type to a vecN. + * @brief Converts from a scalar type to a vecN. */ template struct MetadataConversions< @@ -560,10 +560,13 @@ struct MetadataConversions< CesiumGltf::IsMetadataVecN::value && CesiumGltf::IsMetadataScalar::value>> { /** - * Converts a scalar to a vecN. The returned vector is initialized - * with the value in all of its components. If the scalar cannot be - * reasonably converted to the component type of the vecN, std::nullopt is - * returned. + * @brief Converts a scalar to a vecN. The returned vector is initialized + * with the value in all of its components. The value may lose precision + * during conversion depending on the type of the scalar and the component + * type of the matrix. + * + * If the scalar cannot be reasonably converted to the component type of the + * vecN, std::nullopt is returned. * * @param from The scalar value to be converted. */ @@ -629,4 +632,111 @@ struct MetadataConversions< }; #pragma endregion +#pragma region Conversions to glm::matN +/** + * @brief Converts from a boolean to a matN. + */ +template +struct MetadataConversions< + TTo, + bool, + std::enable_if_t::value>> { + /** + * @brief Converts a boolean to a matN. The boolean is converted to an integer + * value of 1 for true or 0 for false. The returned matrix is initialized with + * this value in all of its components. + * + * @param from The boolean to be converted. + */ + static std::optional convert(bool from) { + return from ? TTo(1) : TTo(0); + } +}; + +/** + * Converts from a scalar type to a matN. + */ +template +struct MetadataConversions< + TTo, + TFrom, + std::enable_if_t< + CesiumGltf::IsMetadataMatN::value && + CesiumGltf::IsMetadataScalar::value>> { + /** + * Converts a scalar to a matN. The returned vector is initialized + * with the value in all components. The value may lose precision during + * conversion depending on the type of the scalar and the component type of + * the matrix. + * + * If the scalar cannot be reasonably converted to the component type of the + * matN, std::nullopt is returned. + * + * @param from The scalar value to be converted. + */ + static std::optional convert(TFrom from) { + using ToValueType = typename TTo::value_type; + + std::optional maybeValue = + MetadataConversions::convert(from); + if (!maybeValue) { + return std::nullopt; + } + + ToValueType value = *maybeValue; + return TTo(value); + } +}; + +/** + * @brief Converts from a matN type to another matN type. + */ +template +struct MetadataConversions< + TTo, + TFrom, + std::enable_if_t< + CesiumGltf::IsMetadataMatN::value && + CesiumGltf::IsMetadataMatN::value && + !std::is_same_v>> { + /** + * @brief Converts a value of the given matN to another matN type. + * + * Let M be the length of the given matN, and N be the length of the target + * matN. If M > N, then only the first N components of the first N columns + * will be used. Otherwise, if M < N, all other elements in the N x N matrix + * will be initialized to zero. + * + * If any of the relevant components cannot be converted to the target matN + * component type, std::nullopt is returned. + * + * @param from The matN value to convert from. + */ + static std::optional convert(TFrom from) { + TTo result = TTo(0); + + constexpr glm::length_t validLength = + glm::min(TTo::length(), TFrom::length()); + + using ToValueType = typename TTo::value_type; + using FromValueType = typename TFrom::value_type; + + for (glm::length_t c = 0; c < validLength; c++) { + for (glm::length_t r = 0; r < validLength; r++) { + auto maybeValue = + MetadataConversions::convert( + from[c][r]); + if (!maybeValue) { + return std::nullopt; + } + + result[c][r] = *maybeValue; + } + } + + return result; + } +}; +#pragma endregion + } // namespace CesiumGltf diff --git a/CesiumGltf/test/TestMetadataConversions.cpp b/CesiumGltf/test/TestMetadataConversions.cpp index dbc45fcdf..868654204 100644 --- a/CesiumGltf/test/TestMetadataConversions.cpp +++ b/CesiumGltf/test/TestMetadataConversions.cpp @@ -4,7 +4,7 @@ using namespace CesiumGltf; -TEST_CASE("Test PropertyConversions for boolean") { +TEST_CASE("Test MetadataConversions for boolean") { SECTION("converts from boolean") { REQUIRE(MetadataConversions::convert(true) == true); REQUIRE(MetadataConversions::convert(false) == false); @@ -70,7 +70,7 @@ TEST_CASE("Test PropertyConversions for boolean") { } } -TEST_CASE("Test PropertyConversions for integer") { +TEST_CASE("Test MetadataConversions for integer") { SECTION("converts from in-range integer") { // same type REQUIRE(MetadataConversions::convert(50) == 50); @@ -144,7 +144,7 @@ TEST_CASE("Test PropertyConversions for integer") { } } -TEST_CASE("Test PropertyConversions for float") { +TEST_CASE("Test MetadataConversions for float") { SECTION("converts from in-range floating point number") { REQUIRE(MetadataConversions::convert(123.45f) == 123.45f); REQUIRE( @@ -205,7 +205,7 @@ TEST_CASE("Test PropertyConversions for float") { } } -TEST_CASE("Test PropertyConversions for double") { +TEST_CASE("Test MetadataConversions for double") { SECTION("converts from floating point number") { REQUIRE( MetadataConversions::convert(123.45f) == @@ -260,7 +260,7 @@ TEST_CASE("Test PropertyConversions for double") { } } -TEST_CASE("Test PropertyConversions for vec2") { +TEST_CASE("Test MetadataConversions for vec2") { SECTION("converts from same vec2 type") { // int-to-int REQUIRE( @@ -396,7 +396,7 @@ TEST_CASE("Test PropertyConversions for vec2") { }; } -TEST_CASE("Test PropertyConversions for vec3") { +TEST_CASE("Test MetadataConversions for vec3") { SECTION("converts from same vec3 type") { // int-to-int REQUIRE( @@ -531,7 +531,7 @@ TEST_CASE("Test PropertyConversions for vec3") { }; } -TEST_CASE("Test PropertyConversions for vec4") { +TEST_CASE("Test MetadataConversions for vec4") { SECTION("converts from same vec4 type") { // int-to-int REQUIRE( @@ -667,3 +667,612 @@ TEST_CASE("Test PropertyConversions for vec4") { convert(arrayView)); }; } + +TEST_CASE("Test MetadataConversions for mat2") { + SECTION("converts from same mat2 type") { + // int-to-int + REQUIRE( + MetadataConversions::convert( + glm::imat2x2(12, 76, -1, 1)) == glm::imat2x2(12, 76, -1, 1)); + // float-to-float + REQUIRE( + MetadataConversions::convert( + glm::mat2(-3.5f, 1.234f, 1.0f, 1.0f)) == + glm::mat2(-3.5f, 1.234f, 1.0f, 1.0f)); + } + + SECTION("converts from other mat2 types") { + // int-to-int + REQUIRE( + MetadataConversions::convert( + glm::u8mat2x2(12, 76, 1, 1)) == glm::imat2x2(12, 76, 1, 1)); + // int-to-float + REQUIRE( + MetadataConversions::convert( + glm::imat2x2(12, 76, 1, 1)) == glm::mat2(12, 76, 1, 1)); + // float-to-int + REQUIRE( + MetadataConversions::convert( + glm::dmat2(-3.5, 1.23456, -1.40, 1.0)) == + glm::i8mat2x2(-3, 1, -1, 1)); + // float-to-float + REQUIRE( + MetadataConversions::convert( + glm::mat2(-3.5f, 1.234f, 2.4f, 1.0f)) == + glm::dmat2(-3.5f, 1.234f, 2.4f, 1.0f)); + } + + SECTION("converts from mat3 types") { + // clang-format off + glm::imat3x3 imat3x3( + 1, 2, 3, + 4, 5, 6, + 7, 8, 9); + // clang-format on + // int-to-int + REQUIRE( + MetadataConversions::convert(imat3x3) == + glm::i8mat2x2(1, 2, 4, 5)); + + // int-to-float + REQUIRE( + MetadataConversions::convert(imat3x3) == + glm::dmat2(1, 2, 4, 5)); + + // clang-format off + glm::mat3 mat3( + 1.0f, 2.5f, 3.0f, + 4.5f, 5.0f, 6.0f, + -7.8f, 8.9f, -9.99f + ); + // clang-format on + // float-to-int + REQUIRE( + MetadataConversions::convert(mat3) == + glm::u8mat2x2(1, 2, 4, 5)); + // float-to-float + REQUIRE( + MetadataConversions::convert(mat3) == + glm::dmat2(mat3[0][0], mat3[0][1], mat3[1][0], mat3[1][1])); + } + + SECTION("converts from mat4 types") { + // clang-format off + glm::imat4x4 imat4x4( + 1, 2, 3, 0, + 4, 5, 6, 0, + 7, 8, 9, 0, + 0, 0, 0, 1); + // clang-format on + // int-to-int + REQUIRE( + MetadataConversions::convert(imat4x4) == + glm::u8mat2x2(1, 2, 4, 5)); + // int-to-float + REQUIRE( + MetadataConversions::convert(imat4x4) == + glm::dmat2(1, 2, 4, 5)); + + // clang-format off + glm::dmat4 dmat4( + 1.0, 2.5, 3.0, 0.0, + 4.5, 5.0, 6.0, 0.0, + -7.8, 8.9, -9.99, 0.0, + 0.0, 0.0, 0.0, 1.0 + ); + // clang-format on + // float-to-int + REQUIRE( + MetadataConversions::convert(dmat4) == + glm::i8mat2x2(1, 2, 4, 5)); + // float-to-float + REQUIRE( + MetadataConversions::convert(dmat4) == + glm::mat2(1.0, 2.5, 4.5, 5.0)); + } + + SECTION("converts from boolean") { + REQUIRE( + MetadataConversions::convert(true) == + glm ::dmat2(1.0)); + } + + SECTION("converts from integer") { + // int to int + REQUIRE( + MetadataConversions::convert(45) == + glm::u16mat2x2(45)); + REQUIRE( + MetadataConversions::convert(45) == + glm::i64mat2x2(45)); + // int to float + REQUIRE( + MetadataConversions::convert(-12345) == + glm::dmat2(-12345)); + REQUIRE( + MetadataConversions::convert(12) == glm::mat2(12)); + } + + SECTION("converts from float") { + // float to int + REQUIRE( + MetadataConversions::convert(45.4f) == + glm::u8mat2x2(45)); + REQUIRE( + MetadataConversions::convert(-1.0111) == + glm::i16mat2x2(-1)); + // float to float + REQUIRE( + MetadataConversions::convert(-1234.5f) == + glm::dmat2(-1234.5f)); + REQUIRE( + MetadataConversions::convert(12.0) == + glm::mat2(12.0)); + } + + SECTION("returns std::nullopt if not all components can be converted") { + // scalar + REQUIRE(!MetadataConversions::convert(-1)); + // int + REQUIRE(!MetadataConversions::convert( + glm::imat2x2(0, -1, 2, 1))); + REQUIRE(!MetadataConversions::convert( + glm::u8mat2x2(0, 255, 2, 1))); + // float + REQUIRE(!MetadataConversions::convert( + glm::mat2(129.0f))); + REQUIRE(!MetadataConversions::convert( + glm::dmat2(std::numeric_limits::max()))); + }; + + SECTION("returns std::nullopt for incompatible types") { + // vecN + REQUIRE(!MetadataConversions::convert( + glm::dvec4(1.0, 2.0, 3.0, 4.0))); + // array + PropertyArrayView arrayView; + REQUIRE( + !MetadataConversions>::convert( + arrayView)); + }; +} + +TEST_CASE("Test MetadataConversions for mat3") { + SECTION("converts from same mat3 type") { + // clang-format off + glm::imat3x3 imat3x3( + 0, 1, 2, + 3, 4, 5, + 6, 7, 8 + ); + // clang-format on + // int-to-int + REQUIRE( + MetadataConversions::convert(imat3x3) == + imat3x3); + + // clang-format off + glm::mat3 mat3( + 1.0f, 2.4f, 3.0f, + 4.0f, 5.55f, 6.0f, + -7.0f, 8.0f, -9.01f + ); + // clang-format on + // float-to-float + REQUIRE(MetadataConversions::convert(mat3) == mat3); + } + + SECTION("converts from other mat3 types") { + // clang-format off + glm::u8mat3x3 u8mat3x3( + 0, 1, 2, + 3, 4, 5, + 6, 7, 8 + ); + // clang-format on + // int-to-int + REQUIRE( + MetadataConversions::convert(u8mat3x3) == + glm::i8mat3x3(0, 1, 2, 3, 4, 5, 6, 7, 8)); + // int-to-float + REQUIRE( + MetadataConversions::convert(u8mat3x3) == + glm::mat3(0, 1, 2, 3, 4, 5, 6, 7, 8)); + + // clang-format off + glm::mat3 mat3( + 1.0f, 2.4f, 3.0f, + 4.0f, -5.0f, 6.0f, + 7.7f, 8.01f, -9.3f + ); + // clang-format on + // float-to-int + REQUIRE( + MetadataConversions::convert(mat3) == + glm::i8mat3x3(1, 2, 3, 4, -5, 6, 7, 8, -9)); + // float-to-float + REQUIRE( + MetadataConversions::convert(mat3) == + glm::dmat3(mat3[0], mat3[1], mat3[2])); + } + + SECTION("converts from mat2 types") { + // clang-format off + glm::imat2x2 imat2x2( + 1, 2, + 3, 4); + glm::u8mat3x3 expectedIntMat( + 1, 2, 0, + 3, 4, 0, + 0, 0, 0); + // clang-format on + // int-to-int + REQUIRE( + MetadataConversions::convert(imat2x2) == + expectedIntMat); + + // int-to-float + REQUIRE( + MetadataConversions::convert(imat2x2) == + glm::dmat3(expectedIntMat[0], expectedIntMat[1], expectedIntMat[2])); + + // clang-format off + glm::mat2 mat2( + 1.0f, 2.5f, + 3.0f, 4.5f + ); + glm::dmat3 expectedDoubleMat( + 1.0f, 2.5f, 0, + 3.0f, 4.5f, 0, + 0, 0, 0 + ); + // clang-format on + // float-to-int + REQUIRE( + MetadataConversions::convert(mat2) == + expectedIntMat); + // float-to-float + REQUIRE( + MetadataConversions::convert(mat2) == + expectedDoubleMat); + } + + SECTION("converts from mat4 types") { + // clang-format off + glm::imat4x4 imat4x4( + 1, 2, 3, 4, + 4, 5, 6, 7, + 7, 8, 9, 10, + 0, 0, 0, 1); + glm::u8mat3x3 expectedIntMat( + 1, 2, 3, + 4, 5, 6, + 7, 8, 9); + // clang-format on + // int-to-int + REQUIRE( + MetadataConversions::convert(imat4x4) == + expectedIntMat); + // int-to-float + REQUIRE( + MetadataConversions::convert(imat4x4) == + glm::dmat3(expectedIntMat[0], expectedIntMat[1], expectedIntMat[2])); + + // clang-format off + glm::mat4 mat4( + 1.0f, 2.5f, 3.0f, -4.0f, + 4.5f, 5.0f, 6.0f, 7.0f, + 7.8f, 8.9f, 9.99f, 10.1f, + 0.0f, 0.0f, 0.0f, 1.0f + ); + glm::dmat3 expectedDoubleMat( + 1.0f, 2.5f, 3.0f, + 4.5f, 5.0f, 6.0f, + 7.8f, 8.9f, 9.99f + ); + // clang-format on + // float-to-int + REQUIRE( + MetadataConversions::convert(mat4) == + expectedIntMat); + // float-to-float + REQUIRE( + MetadataConversions::convert(mat4) == + expectedDoubleMat); + } + + SECTION("converts from boolean") { + REQUIRE( + MetadataConversions::convert(true) == + glm ::dmat3(1.0)); + } + + SECTION("converts from integer") { + // int to int + REQUIRE( + MetadataConversions::convert(45) == + glm::u16mat3x3(45)); + REQUIRE( + MetadataConversions::convert(45) == + glm::i64mat3x3(45)); + // int to float + REQUIRE( + MetadataConversions::convert(-12345) == + glm::dmat3(-12345)); + REQUIRE( + MetadataConversions::convert(12) == glm::mat3(12)); + } + + SECTION("converts from float") { + // float to int + REQUIRE( + MetadataConversions::convert(45.4f) == + glm::u8mat3x3(45)); + REQUIRE( + MetadataConversions::convert(-1.0111) == + glm::i16mat3x3(-1)); + // float to float + REQUIRE( + MetadataConversions::convert(-1234.5f) == + glm::dmat3(-1234.5f)); + REQUIRE( + MetadataConversions::convert(12.0) == + glm::mat3(12.0)); + } + + SECTION("returns std::nullopt if not all components can be converted") { + // scalar + REQUIRE(!MetadataConversions::convert(-1)); + // int + REQUIRE(!MetadataConversions::convert( + glm::imat2x2(0, -1, 2, 1))); + REQUIRE(!MetadataConversions::convert( + glm::u8mat2x2(0, 255, 2, 1))); + // float + REQUIRE(!MetadataConversions::convert( + glm::mat2(129.0f))); + REQUIRE(!MetadataConversions::convert( + glm::dmat2(std::numeric_limits::max()))); + }; + + SECTION("returns std::nullopt for incompatible types") { + // vecN + REQUIRE(!MetadataConversions::convert( + glm::dvec3(1.0, 2.0, 3.0))); + // array + PropertyArrayView arrayView; + REQUIRE( + !MetadataConversions>::convert( + arrayView)); + }; +} + +TEST_CASE("Test MetadataConversions for mat4") { + SECTION("converts from same mat4 type") { + // clang-format off + glm::imat4x4 imat4x4( + 0, 1, 2, 3, + 4, 5, 6, 7, + 8, 9, -1, 1, + 0, 0, 0, 1 + ); + // clang-format on + // int-to-int + REQUIRE( + MetadataConversions::convert(imat4x4) == + imat4x4); + + // clang-format off + glm::mat4 mat4( + 1.0f, 2.4f, 3.0f, 0.0f, + 4.0f, 5.55f, 6.0f, 0.0f, + -7.0f, 8.0f, -9.01f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + ); + // clang-format on + // float-to-float + REQUIRE(MetadataConversions::convert(mat4) == mat4); + } + + SECTION("converts from other mat4 types") { + // clang-format off + glm::u8mat4x4 u8mat4x4( + 0, 1, 2, 0, + 3, 4, 5, 0, + 6, 7, 8, 0, + 0, 0, 0, 1 + ); + // clang-format on + // int-to-int + REQUIRE( + MetadataConversions::convert(u8mat4x4) == + glm::i8mat4x4(u8mat4x4[0], u8mat4x4[1], u8mat4x4[2], u8mat4x4[3])); + // int-to-float + REQUIRE( + MetadataConversions::convert(u8mat4x4) == + glm::mat4(u8mat4x4[0], u8mat4x4[1], u8mat4x4[2], u8mat4x4[3])); + + // clang-format off + glm::mat4 mat4( + 1.0f, 2.4f, 3.0f, 0.0f, + 4.0f, -5.0f, 6.0f, 0.0f, + 7.7f, 8.01f, -9.3f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + ); + glm::i8mat4x4 expected( + 1, 2, 3, 0, + 4, -5, 6, 0, + 7, 8, -9, 0, + 0, 0, 0, 1 + ); + // clang-format on + // float-to-int + REQUIRE( + MetadataConversions::convert(mat4) == + expected); + // float-to-float + REQUIRE( + MetadataConversions::convert(mat4) == + glm::dmat4(mat4[0], mat4[1], mat4[2], mat4[3])); + } + + SECTION("converts from mat2 types") { + // clang-format off + glm::imat2x2 imat2x2( + 1, 2, + 3, 4); + glm::u8mat4x4 expectedIntMat( + 1, 2, 0, 0, + 3, 4, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0); + // clang-format on + // int-to-int + REQUIRE( + MetadataConversions::convert(imat2x2) == + expectedIntMat); + + // int-to-float + REQUIRE( + MetadataConversions::convert(imat2x2) == + glm::dmat4( + expectedIntMat[0], + expectedIntMat[1], + expectedIntMat[2], + expectedIntMat[3])); + + // clang-format off + glm::mat2 mat2( + 1.0f, 2.5f, + 3.0f, 4.5f + ); + glm::dmat4 expectedDoubleMat( + 1.0f, 2.5f, 0, 0, + 3.0f, 4.5f, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 + ); + // clang-format on + // float-to-int + REQUIRE( + MetadataConversions::convert(mat2) == + expectedIntMat); + // float-to-float + REQUIRE( + MetadataConversions::convert(mat2) == + expectedDoubleMat); + } + + SECTION("converts from mat3 types") { + // clang-format off + glm::imat3x3 imat3x3( + 1, 2, 3, + 4, 5, 6, + 7, 8, 9); + glm::u8mat4x4 expectedIntMat( + 1, 2, 3, 0, + 4, 5, 6, 0, + 7, 8, 9, 0, + 0, 0, 0, 0); + // clang-format on + // int-to-int + REQUIRE( + MetadataConversions::convert(imat3x3) == + expectedIntMat); + // int-to-float + REQUIRE( + MetadataConversions::convert(imat3x3) == + glm::dmat4( + expectedIntMat[0], + expectedIntMat[1], + expectedIntMat[2], + expectedIntMat[3])); + + // clang-format off + glm::mat3 mat3( + 1.0f, 2.5f, 3.0f, + 4.5f, 5.0f, 6.0f, + 7.8f, 8.9f, 9.99f + ); + glm::dmat4 expectedDoubleMat( + 1.0f, 2.5f, 3.0f, 0.0f, + 4.5f, 5.0f, 6.0f, 0.0f, + 7.8f, 8.9f, 9.99f, 0.0f, + 0.0f, 0.0f, 0.0f, 0.0f + ); + // clang-format on + // float-to-int + REQUIRE( + MetadataConversions::convert(mat3) == + expectedIntMat); + // float-to-float + REQUIRE( + MetadataConversions::convert(mat3) == + expectedDoubleMat); + } + + SECTION("converts from boolean") { + REQUIRE( + MetadataConversions::convert(true) == + glm ::dmat4(1.0)); + } + + SECTION("converts from integer") { + // int to int + REQUIRE( + MetadataConversions::convert(45) == + glm::u16mat4x4(45)); + REQUIRE( + MetadataConversions::convert(45) == + glm::i64mat4x4(45)); + // int to float + REQUIRE( + MetadataConversions::convert(-12345) == + glm::dmat4(-12345)); + REQUIRE( + MetadataConversions::convert(12) == glm::mat4(12)); + } + + SECTION("converts from float") { + // float to int + REQUIRE( + MetadataConversions::convert(45.4f) == + glm::u8mat4x4(45)); + REQUIRE( + MetadataConversions::convert(-1.0111) == + glm::i16mat4x4(-1)); + // float to float + REQUIRE( + MetadataConversions::convert(-1234.5f) == + glm::dmat4(-1234.5f)); + REQUIRE( + MetadataConversions::convert(12.0) == + glm::mat4(12.0)); + } + + SECTION("returns std::nullopt if not all components can be converted") { + // scalar + REQUIRE(!MetadataConversions::convert(-1)); + // int + REQUIRE(!MetadataConversions::convert( + glm::imat2x2(0, -1, 2, 1))); + REQUIRE(!MetadataConversions::convert( + glm::u8mat2x2(0, 255, 2, 1))); + // float + REQUIRE(!MetadataConversions::convert( + glm::mat2(129.0f))); + REQUIRE(!MetadataConversions::convert( + glm::dmat2(std::numeric_limits::max()))); + }; + + SECTION("returns std::nullopt for incompatible types") { + // vecN + REQUIRE(!MetadataConversions::convert( + glm::dvec4(1.0, 2.0, 3.0, 4.0))); + // array + PropertyArrayView arrayView; + REQUIRE( + !MetadataConversions>::convert( + arrayView)); + }; +} From 3868dcf0335a1da1e3081236a6d9ed7d127e77c3 Mon Sep 17 00:00:00 2001 From: Janine Liu <32226860+j9liu@users.noreply.github.com> Date: Mon, 13 Nov 2023 15:28:26 -0500 Subject: [PATCH 357/421] Update CHANGES.md --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index 6d4a17c37..b4f5dc49a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -12,6 +12,7 @@ ##### Additions :tada: - Added `Cesium3DTilesContent` library and namespace. It has classes for loading, converting, and manipulating 3D Tiles tile content. +- Added `MetadataConversions`, which enables metadata values to be converted to different types for better usability in runtime engines. ##### Fixes :wrench: From dff7e61171919bcef7506fb0aaa5dea875469a37 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Tue, 14 Nov 2023 18:29:47 +1100 Subject: [PATCH 358/421] Create CesiumRasterOverlays library. --- CMakeLists.txt | 1 + Cesium3DTilesSelection/CMakeLists.txt | 1 + .../IPrepareRendererResources.h | 67 +----- .../RasterMappedTo3DTile.h | 28 ++- .../RasterOverlayCollection.h | 54 +++-- .../Cesium3DTilesSelection/TileContent.h | 12 +- .../include/Cesium3DTilesSelection/Tileset.h | 2 +- .../Cesium3DTilesSelection/TilesetExternals.h | 7 +- .../src/LayerJsonTerrainLoader.h | 1 - .../src/RasterMappedTo3DTile.cpp | 5 +- .../src/RasterOverlayCollection.cpp | 4 +- .../src/RasterOverlayUpsampler.cpp | 5 +- Cesium3DTilesSelection/src/TileContent.cpp | 2 + Cesium3DTilesSelection/src/TileUtilities.cpp | 212 +----------------- Cesium3DTilesSelection/src/TileUtilities.h | 39 ---- Cesium3DTilesSelection/src/Tileset.cpp | 5 +- .../src/TilesetContentManager.cpp | 8 +- .../src/TilesetContentManager.h | 10 +- .../test/SimplePrepareRendererResource.h | 10 +- .../test/TestCreditSystem.cpp | 4 +- .../TestQuadtreeRasterOverlayTileProvider.cpp | 11 +- .../test/TestTilesetContentManager.cpp | 4 +- .../test/TestTilesetJsonLoader.cpp | 1 + .../CesiumGeometry/IntersectionTests.h | 16 ++ CesiumGeometry/src/IntersectionTests.cpp | 28 +++ .../CesiumGeospatial/CartographicPolygon.h | 28 +++ CesiumGeospatial/src/CartographicPolygon.cpp | 183 +++++++++++++++ CesiumRasterOverlays/CMakeLists.txt | 56 +++++ .../BingMapsRasterOverlay.h | 12 +- .../DebugColorizeTilesRasterOverlay.h | 10 +- .../IPrepareRasterOverlayRendererResources.h | 74 ++++++ .../CesiumRasterOverlays}/IonRasterOverlay.h | 16 +- .../include/CesiumRasterOverlays/Library.h | 17 ++ .../QuadtreeRasterOverlayTileProvider.h | 15 +- .../CesiumRasterOverlays}/RasterOverlay.h | 26 ++- .../RasterOverlayLoadFailureDetails.h | 4 +- .../CesiumRasterOverlays}/RasterOverlayTile.h | 13 +- .../RasterOverlayTileProvider.h | 32 +-- .../RasterizedPolygonsOverlay.h | 10 +- .../TileMapServiceRasterOverlay.h | 12 +- .../WebMapServiceRasterOverlay.h | 13 +- .../src/BingMapsRasterOverlay.cpp | 39 ++-- .../src/DebugColorizeTilesRasterOverlay.cpp | 14 +- .../src/IonRasterOverlay.cpp | 27 ++- .../src/QuadtreeRasterOverlayTileProvider.cpp | 14 +- .../src/RasterOverlay.cpp | 11 +- .../src/RasterOverlayTile.cpp | 19 +- .../src/RasterOverlayTileProvider.cpp | 33 ++- .../src/RasterizedPolygonsOverlay.cpp | 25 +-- .../src/TileMapServiceRasterOverlay.cpp | 27 ++- .../src/WebMapServiceRasterOverlay.cpp | 26 +-- .../include/CesiumUtility}/CreditSystem.h | 8 +- .../src/CreditSystem.cpp | 6 +- 53 files changed, 719 insertions(+), 588 deletions(-) create mode 100644 CesiumRasterOverlays/CMakeLists.txt rename {Cesium3DTilesSelection/include/Cesium3DTilesSelection => CesiumRasterOverlays/include/CesiumRasterOverlays}/BingMapsRasterOverlay.h (93%) rename {Cesium3DTilesSelection/include/Cesium3DTilesSelection => CesiumRasterOverlays/include/CesiumRasterOverlays}/DebugColorizeTilesRasterOverlay.h (77%) create mode 100644 CesiumRasterOverlays/include/CesiumRasterOverlays/IPrepareRasterOverlayRendererResources.h rename {Cesium3DTilesSelection/include/Cesium3DTilesSelection => CesiumRasterOverlays/include/CesiumRasterOverlays}/IonRasterOverlay.h (84%) create mode 100644 CesiumRasterOverlays/include/CesiumRasterOverlays/Library.h rename {Cesium3DTilesSelection/include/Cesium3DTilesSelection => CesiumRasterOverlays/include/CesiumRasterOverlays}/QuadtreeRasterOverlayTileProvider.h (94%) rename {Cesium3DTilesSelection/include/Cesium3DTilesSelection => CesiumRasterOverlays/include/CesiumRasterOverlays}/RasterOverlay.h (93%) rename {Cesium3DTilesSelection/include/Cesium3DTilesSelection => CesiumRasterOverlays/include/CesiumRasterOverlays}/RasterOverlayLoadFailureDetails.h (93%) rename {Cesium3DTilesSelection/include/Cesium3DTilesSelection => CesiumRasterOverlays/include/CesiumRasterOverlays}/RasterOverlayTile.h (97%) rename {Cesium3DTilesSelection/include/Cesium3DTilesSelection => CesiumRasterOverlays/include/CesiumRasterOverlays}/RasterOverlayTileProvider.h (94%) rename {Cesium3DTilesSelection/include/Cesium3DTilesSelection => CesiumRasterOverlays/include/CesiumRasterOverlays}/RasterizedPolygonsOverlay.h (84%) rename {Cesium3DTilesSelection/include/Cesium3DTilesSelection => CesiumRasterOverlays/include/CesiumRasterOverlays}/TileMapServiceRasterOverlay.h (93%) rename {Cesium3DTilesSelection/include/Cesium3DTilesSelection => CesiumRasterOverlays/include/CesiumRasterOverlays}/WebMapServiceRasterOverlay.h (90%) rename {Cesium3DTilesSelection => CesiumRasterOverlays}/src/BingMapsRasterOverlay.cpp (95%) rename {Cesium3DTilesSelection => CesiumRasterOverlays}/src/DebugColorizeTilesRasterOverlay.cpp (87%) rename {Cesium3DTilesSelection => CesiumRasterOverlays}/src/IonRasterOverlay.cpp (92%) rename {Cesium3DTilesSelection => CesiumRasterOverlays}/src/QuadtreeRasterOverlayTileProvider.cpp (98%) rename {Cesium3DTilesSelection => CesiumRasterOverlays}/src/RasterOverlay.cpp (86%) rename {Cesium3DTilesSelection => CesiumRasterOverlays}/src/RasterOverlayTile.cpp (83%) rename {Cesium3DTilesSelection => CesiumRasterOverlays}/src/RasterOverlayTileProvider.cpp (93%) rename {Cesium3DTilesSelection => CesiumRasterOverlays}/src/RasterizedPolygonsOverlay.cpp (93%) rename {Cesium3DTilesSelection => CesiumRasterOverlays}/src/TileMapServiceRasterOverlay.cpp (96%) rename {Cesium3DTilesSelection => CesiumRasterOverlays}/src/WebMapServiceRasterOverlay.cpp (94%) rename {Cesium3DTilesSelection/include/Cesium3DTilesSelection => CesiumUtility/include/CesiumUtility}/CreditSystem.h (94%) rename {Cesium3DTilesSelection => CesiumUtility}/src/CreditSystem.cpp (96%) diff --git a/CMakeLists.txt b/CMakeLists.txt index efd419bef..3192ced42 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -149,6 +149,7 @@ add_subdirectory(Cesium3DTiles) add_subdirectory(Cesium3DTilesReader) add_subdirectory(Cesium3DTilesWriter) add_subdirectory(Cesium3DTilesContent) +add_subdirectory(CesiumRasterOverlays) add_subdirectory(Cesium3DTilesSelection) add_subdirectory(CesiumIonClient) diff --git a/Cesium3DTilesSelection/CMakeLists.txt b/Cesium3DTilesSelection/CMakeLists.txt index c07cd2c1d..b51400cc6 100644 --- a/Cesium3DTilesSelection/CMakeLists.txt +++ b/Cesium3DTilesSelection/CMakeLists.txt @@ -51,6 +51,7 @@ target_link_libraries(Cesium3DTilesSelection CesiumGeometry CesiumGltf CesiumGltfReader + CesiumRasterOverlays CesiumUtility spdlog # PRIVATE diff --git a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/IPrepareRendererResources.h b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/IPrepareRendererResources.h index 1209a6062..225166a39 100644 --- a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/IPrepareRendererResources.h +++ b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/IPrepareRendererResources.h @@ -4,6 +4,7 @@ #include "TileLoadResult.h" #include +#include #include #include @@ -24,10 +25,13 @@ struct ImageCesium; struct Model; } // namespace CesiumGltf +namespace CesiumRasterOverlays { +class RasterOverlayTile; +} + namespace Cesium3DTilesSelection { class Tile; -class RasterOverlayTile; struct TileLoadResultAndRenderResources { TileLoadResult result; @@ -46,7 +50,8 @@ struct TileLoadResultAndRenderResources { * {@link TilesetExternals} structure that can be obtained * via {@link Tileset::getExternals}. */ -class CESIUM3DTILESSELECTION_API IPrepareRendererResources { +class CESIUM3DTILESSELECTION_API IPrepareRendererResources + : public CesiumRasterOverlays::IPrepareRasterOverlayRendererResources { public: virtual ~IPrepareRendererResources() = default; @@ -109,60 +114,6 @@ class CESIUM3DTILESSELECTION_API IPrepareRendererResources { void* pLoadThreadResult, void* pMainThreadResult) noexcept = 0; - /** - * @brief Prepares a raster overlay tile. - * - * This method is invoked in the load thread and may modify the image. - * - * @param image The raster tile image to prepare. - * @param rendererOptions Renderer options associated with the raster overlay tile from {@link RasterOverlayOptions::rendererOptions}. - * @returns Arbitrary data representing the result of the load process. This - * data is passed to {@link prepareRasterInMainThread} as the - * `pLoadThreadResult` parameter. - */ - virtual void* prepareRasterInLoadThread( - CesiumGltf::ImageCesium& image, - const std::any& rendererOptions) = 0; - - /** - * @brief Further preprares a raster overlay tile. - * - * This is called after {@link prepareRasterInLoadThread}, and unlike that - * method, this one is called from the same thread that called - * {@link Tileset::updateView}. - * - * @param rasterTile The raster tile to prepare. - * @param pLoadThreadResult The value returned from - * {@link prepareRasterInLoadThread}. - * @returns Arbitrary data representing the result of the load process. Note - * that the value returned by {@link prepareRasterInLoadThread} will _not_ be - * automatically preserved and passed to {@link free}. If you need to free - * that value, do it in this method before returning. If you need that value - * later, add it to the object returned from this method. - */ - virtual void* prepareRasterInMainThread( - RasterOverlayTile& rasterTile, - void* pLoadThreadResult) = 0; - - /** - * @brief Frees previously-prepared renderer resources for a raster tile. - * - * This method is always called from the thread that called - * {@link Tileset::updateView} or deleted the tileset. - * - * @param rasterTile The tile for which to free renderer resources. - * @param pLoadThreadResult The result returned by - * {@link prepareRasterInLoadThread}. If {@link prepareRasterInMainThread} - * has already been called, this parameter will be `nullptr`. - * @param pMainThreadResult The result returned by - * {@link prepareRasterInMainThread}. If {@link prepareRasterInMainThread} - * has not yet been called, this parameter will be `nullptr`. - */ - virtual void freeRaster( - const RasterOverlayTile& rasterTile, - void* pLoadThreadResult, - void* pMainThreadResult) noexcept = 0; - /** * @brief Attaches a raster overlay tile to a geometry tile. * @@ -186,7 +137,7 @@ class CESIUM3DTILESSELECTION_API IPrepareRendererResources { virtual void attachRasterInMainThread( const Tile& tile, int32_t overlayTextureCoordinateID, - const RasterOverlayTile& rasterTile, + const CesiumRasterOverlays::RasterOverlayTile& rasterTile, void* pMainThreadRendererResources, const glm::dvec2& translation, const glm::dvec2& scale) = 0; @@ -204,7 +155,7 @@ class CESIUM3DTILESSELECTION_API IPrepareRendererResources { virtual void detachRasterInMainThread( const Tile& tile, int32_t overlayTextureCoordinateID, - const RasterOverlayTile& rasterTile, + const CesiumRasterOverlays::RasterOverlayTile& rasterTile, void* pMainThreadRendererResources) noexcept = 0; }; diff --git a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/RasterMappedTo3DTile.h b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/RasterMappedTo3DTile.h index 0b986d3bb..7845c983e 100644 --- a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/RasterMappedTo3DTile.h +++ b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/RasterMappedTo3DTile.h @@ -1,10 +1,10 @@ #pragma once #include "IPrepareRendererResources.h" -#include "RasterOverlayTile.h" #include #include +#include #include #include @@ -53,7 +53,8 @@ class RasterMappedTo3DTile final { * with this mapped raster overlay. */ RasterMappedTo3DTile( - const CesiumUtility::IntrusivePointer& pRasterTile, + const CesiumUtility::IntrusivePointer< + CesiumRasterOverlays::RasterOverlayTile>& pRasterTile, int32_t textureCoordinateIndex); /** @@ -64,12 +65,13 @@ class RasterMappedTo3DTile final { * * @return The tile that is loading, or `nullptr`. */ - RasterOverlayTile* getLoadingTile() noexcept { + CesiumRasterOverlays::RasterOverlayTile* getLoadingTile() noexcept { return this->_pLoadingTile.get(); } /** @copydoc getLoadingTile */ - const RasterOverlayTile* getLoadingTile() const noexcept { + const CesiumRasterOverlays::RasterOverlayTile* + getLoadingTile() const noexcept { return this->_pLoadingTile.get(); } @@ -81,10 +83,12 @@ class RasterMappedTo3DTile final { * * @return The tile, or `nullptr`. */ - RasterOverlayTile* getReadyTile() noexcept { return this->_pReadyTile.get(); } + CesiumRasterOverlays::RasterOverlayTile* getReadyTile() noexcept { + return this->_pReadyTile.get(); + } /** @copydoc getReadyTile */ - const RasterOverlayTile* getReadyTile() const noexcept { + const CesiumRasterOverlays::RasterOverlayTile* getReadyTile() const noexcept { return this->_pReadyTile.get(); } @@ -158,7 +162,7 @@ class RasterMappedTo3DTile final { * @param tile The owner tile. * @return The {@link MoreDetailAvailable} state. */ - RasterOverlayTile::MoreDetailAvailable + CesiumRasterOverlays::RasterOverlayTile::MoreDetailAvailable update(IPrepareRendererResources& prepareRendererResources, Tile& tile); bool isMoreDetailAvailable() const noexcept; @@ -216,16 +220,18 @@ class RasterMappedTo3DTile final { */ static RasterMappedTo3DTile* mapOverlayToTile( double maximumScreenSpaceError, - RasterOverlayTileProvider& tileProvider, - RasterOverlayTileProvider& placeholder, + CesiumRasterOverlays::RasterOverlayTileProvider& tileProvider, + CesiumRasterOverlays::RasterOverlayTileProvider& placeholder, Tile& tile, std::vector& missingProjections); private: void computeTranslationAndScale(const Tile& tile); - CesiumUtility::IntrusivePointer _pLoadingTile; - CesiumUtility::IntrusivePointer _pReadyTile; + CesiumUtility::IntrusivePointer + _pLoadingTile; + CesiumUtility::IntrusivePointer + _pReadyTile; int32_t _textureCoordinateID; glm::dvec2 _translation; glm::dvec2 _scale; diff --git a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/RasterOverlayCollection.h b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/RasterOverlayCollection.h index 971073771..8f49ad6de 100644 --- a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/RasterOverlayCollection.h +++ b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/RasterOverlayCollection.h @@ -1,11 +1,10 @@ #pragma once #include "Library.h" -#include "RasterOverlay.h" -#include "RasterOverlayTileProvider.h" -#include "Tile.h" #include "TilesetExternals.h" +#include +#include #include #include #include @@ -79,18 +78,20 @@ class CESIUM3DTILESSELECTION_API RasterOverlayCollection final { * * @param pOverlay The pointer to the overlay. This may not be `nullptr`. */ - void add(const CesiumUtility::IntrusivePointer& pOverlay); + void add(const CesiumUtility::IntrusivePointer< + CesiumRasterOverlays::RasterOverlay>& pOverlay); /** * @brief Remove the given {@link RasterOverlay} from this collection. */ - void remove( - const CesiumUtility::IntrusivePointer& pOverlay) noexcept; + void remove(const CesiumUtility::IntrusivePointer< + CesiumRasterOverlays::RasterOverlay>& pOverlay) noexcept; /** * @brief Gets the overlays in this collection. */ - const std::vector>& + const std::vector< + CesiumUtility::IntrusivePointer>& getOverlays() const; /** @@ -98,7 +99,8 @@ class CESIUM3DTILESSELECTION_API RasterOverlayCollection final { * corresponds with the overlay at the same position in the collection * returned by {@link getOverlays}. */ - const std::vector>& + const std::vector>& getTileProviders() const; /** @@ -106,7 +108,8 @@ class CESIUM3DTILESSELECTION_API RasterOverlayCollection final { * placeholder tile provider corresponds with the overlay at the same position * in the collection returned by {@link getOverlays}. */ - const std::vector>& + const std::vector>& getPlaceholderTileProviders() const; /** @@ -122,14 +125,15 @@ class CESIUM3DTILESSELECTION_API RasterOverlayCollection final { * @param overlay The overlay for which to obtain the tile provider. * @return The tile provider, if any, corresponding to the raster overlay. */ - RasterOverlayTileProvider* - findTileProviderForOverlay(RasterOverlay& overlay) noexcept; + CesiumRasterOverlays::RasterOverlayTileProvider* findTileProviderForOverlay( + CesiumRasterOverlays::RasterOverlay& overlay) noexcept; /** * @copydoc findTileProviderForOverlay */ - const RasterOverlayTileProvider* - findTileProviderForOverlay(const RasterOverlay& overlay) const noexcept; + const CesiumRasterOverlays::RasterOverlayTileProvider* + findTileProviderForOverlay( + const CesiumRasterOverlays::RasterOverlay& overlay) const noexcept; /** * @brief Finds the placeholder tile provider for a given overlay. @@ -145,20 +149,22 @@ class CESIUM3DTILESSELECTION_API RasterOverlayCollection final { * @return The placeholder tile provider, if any, corresponding to the raster * overlay. */ - RasterOverlayTileProvider* - findPlaceholderTileProviderForOverlay(RasterOverlay& overlay) noexcept; + CesiumRasterOverlays::RasterOverlayTileProvider* + findPlaceholderTileProviderForOverlay( + CesiumRasterOverlays::RasterOverlay& overlay) noexcept; /** * @copydoc findPlaceholderTileProviderForOverlay */ - const RasterOverlayTileProvider* findPlaceholderTileProviderForOverlay( - const RasterOverlay& overlay) const noexcept; + const CesiumRasterOverlays::RasterOverlayTileProvider* + findPlaceholderTileProviderForOverlay( + const CesiumRasterOverlays::RasterOverlay& overlay) const noexcept; /** * @brief A constant iterator for {@link RasterOverlay} instances. */ - typedef std::vector>:: - const_iterator const_iterator; + typedef std::vector>::const_iterator const_iterator; /** * @brief Returns an iterator at the beginning of this collection. @@ -187,10 +193,14 @@ class CESIUM3DTILESSELECTION_API RasterOverlayCollection final { // complete. struct OverlayList : public CesiumUtility::ReferenceCountedNonThreadSafe { - std::vector> overlays{}; - std::vector> + std::vector< + CesiumUtility::IntrusivePointer> + overlays{}; + std::vector> tileProviders{}; - std::vector> + std::vector> placeholders{}; }; diff --git a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/TileContent.h b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/TileContent.h index c7d1c487c..848d2f836 100644 --- a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/TileContent.h +++ b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/TileContent.h @@ -1,12 +1,12 @@ #pragma once -#include "CreditSystem.h" #include "Library.h" #include "RasterOverlayDetails.h" #include "TilesetMetadata.h" #include #include +#include #include #include @@ -131,28 +131,28 @@ class CESIUM3DTILESSELECTION_API TileRenderContent { * * @return The list of {@link Credit} of the content */ - const std::vector& getCredits() const noexcept; + const std::vector& getCredits() const noexcept; /** * @brief Get the list of {@link Credit} of the content * * @return The list of {@link Credit} of the content */ - std::vector& getCredits() noexcept; + std::vector& getCredits() noexcept; /** * @brief Set the list of {@link Credit} for the content * * @param credits The list of {@link Credit} to be owned by the content */ - void setCredits(std::vector&& credits); + void setCredits(std::vector&& credits); /** * @brief Set the list of {@link Credit} for the content * * @param credits The list of {@link Credit} to be owned by the content */ - void setCredits(const std::vector& credits); + void setCredits(const std::vector& credits); /** * @brief Get the render resources created for the glTF model of the content @@ -193,7 +193,7 @@ class CESIUM3DTILESSELECTION_API TileRenderContent { CesiumGltf::Model _model; void* _pRenderResources; RasterOverlayDetails _rasterOverlayDetails; - std::vector _credits; + std::vector _credits; float _lodTransitionFadePercentage; }; diff --git a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/Tileset.h b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/Tileset.h index 5d959320f..f8bb44fb7 100644 --- a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/Tileset.h +++ b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/Tileset.h @@ -104,7 +104,7 @@ class CESIUM3DTILESSELECTION_API Tileset final { /** * @brief Get tileset credits. */ - const std::vector& getTilesetCredits() const noexcept; + const std::vector& getTilesetCredits() const noexcept; /** * @brief Sets whether or not the tileset's credits should be shown on screen. diff --git a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/TilesetExternals.h b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/TilesetExternals.h index 95cb9cad4..53c290373 100644 --- a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/TilesetExternals.h +++ b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/TilesetExternals.h @@ -13,8 +13,11 @@ class IAssetAccessor; class ITaskProcessor; } // namespace CesiumAsync -namespace Cesium3DTilesSelection { +namespace CesiumUtility { class CreditSystem; +} + +namespace Cesium3DTilesSelection { class IPrepareRendererResources; /** @@ -48,7 +51,7 @@ class CESIUM3DTILESSELECTION_API TilesetExternals final { * strings and track which which credits to show and remove from the screen * each frame. */ - std::shared_ptr pCreditSystem; + std::shared_ptr pCreditSystem; /** * @brief A spdlog logger that will receive log messages. diff --git a/Cesium3DTilesSelection/src/LayerJsonTerrainLoader.h b/Cesium3DTilesSelection/src/LayerJsonTerrainLoader.h index 4698be683..a539711ce 100644 --- a/Cesium3DTilesSelection/src/LayerJsonTerrainLoader.h +++ b/Cesium3DTilesSelection/src/LayerJsonTerrainLoader.h @@ -2,7 +2,6 @@ #include "TilesetContentLoaderResult.h" -#include #include #include #include diff --git a/Cesium3DTilesSelection/src/RasterMappedTo3DTile.cpp b/Cesium3DTilesSelection/src/RasterMappedTo3DTile.cpp index a8f3c9933..f307e8e87 100644 --- a/Cesium3DTilesSelection/src/RasterMappedTo3DTile.cpp +++ b/Cesium3DTilesSelection/src/RasterMappedTo3DTile.cpp @@ -3,15 +3,16 @@ #include #include #include -#include #include #include #include #include +#include +using namespace Cesium3DTilesSelection; using namespace CesiumGeometry; using namespace CesiumGeospatial; -using namespace Cesium3DTilesSelection; +using namespace CesiumRasterOverlays; using namespace CesiumUtility; namespace { diff --git a/Cesium3DTilesSelection/src/RasterOverlayCollection.cpp b/Cesium3DTilesSelection/src/RasterOverlayCollection.cpp index 261ff35b5..b06495176 100644 --- a/Cesium3DTilesSelection/src/RasterOverlayCollection.cpp +++ b/Cesium3DTilesSelection/src/RasterOverlayCollection.cpp @@ -1,9 +1,9 @@ -#include "Cesium3DTilesSelection/RasterOverlayCollection.h" - +#include #include using namespace CesiumGeometry; using namespace CesiumGeospatial; +using namespace CesiumRasterOverlays; using namespace CesiumUtility; namespace Cesium3DTilesSelection { diff --git a/Cesium3DTilesSelection/src/RasterOverlayUpsampler.cpp b/Cesium3DTilesSelection/src/RasterOverlayUpsampler.cpp index 5d58929a3..57aaf78b7 100644 --- a/Cesium3DTilesSelection/src/RasterOverlayUpsampler.cpp +++ b/Cesium3DTilesSelection/src/RasterOverlayUpsampler.cpp @@ -2,16 +2,17 @@ #include #include -#include -#include #include #include #include +#include +#include #include #include using namespace Cesium3DTilesContent; +using namespace CesiumRasterOverlays; namespace Cesium3DTilesSelection { CesiumAsync::Future diff --git a/Cesium3DTilesSelection/src/TileContent.cpp b/Cesium3DTilesSelection/src/TileContent.cpp index 2d357563c..30ed49bbe 100644 --- a/Cesium3DTilesSelection/src/TileContent.cpp +++ b/Cesium3DTilesSelection/src/TileContent.cpp @@ -1,5 +1,7 @@ #include +using namespace CesiumUtility; + namespace Cesium3DTilesSelection { TileRenderContent::TileRenderContent(CesiumGltf::Model&& model) : _model{std::move(model)}, diff --git a/Cesium3DTilesSelection/src/TileUtilities.cpp b/Cesium3DTilesSelection/src/TileUtilities.cpp index e9b708925..4ede5c5c6 100644 --- a/Cesium3DTilesSelection/src/TileUtilities.cpp +++ b/Cesium3DTilesSelection/src/TileUtilities.cpp @@ -11,33 +11,6 @@ using namespace CesiumGeospatial; namespace Cesium3DTilesSelection { namespace CesiumImpl { -bool withinTriangle( - const glm::dvec2& point, - const glm::dvec2& triangleVertA, - const glm::dvec2& triangleVertB, - const glm::dvec2& triangleVertC) noexcept { - - const glm::dvec2 ab = triangleVertB - triangleVertA; - const glm::dvec2 ab_perp(-ab.y, ab.x); - const glm::dvec2 bc = triangleVertC - triangleVertB; - const glm::dvec2 bc_perp(-bc.y, bc.x); - const glm::dvec2 ca = triangleVertA - triangleVertC; - const glm::dvec2 ca_perp(-ca.y, ca.x); - - const glm::dvec2 av = point - triangleVertA; - const glm::dvec2 cv = point - triangleVertC; - - const double v_proj_ab_perp = glm::dot(av, ab_perp); - const double v_proj_bc_perp = glm::dot(cv, bc_perp); - const double v_proj_ca_perp = glm::dot(cv, ca_perp); - - // This will determine in or out, irrespective of winding. - return (v_proj_ab_perp >= 0.0 && v_proj_ca_perp >= 0.0 && - v_proj_bc_perp >= 0.0) || - (v_proj_ab_perp <= 0.0 && v_proj_ca_perp <= 0.0 && - v_proj_bc_perp <= 0.0); -} - bool withinPolygons( const BoundingVolume& boundingVolume, const std::vector& cartographicPolygons) noexcept { @@ -48,98 +21,9 @@ bool withinPolygons( return false; } - return withinPolygons(*maybeRectangle, cartographicPolygons); -} - -bool withinPolygons( - const CesiumGeospatial::GlobeRectangle& rectangle, - const std::vector& cartographicPolygons) noexcept { - - glm::dvec2 rectangleCorners[] = { - glm::dvec2(rectangle.getWest(), rectangle.getSouth()), - glm::dvec2(rectangle.getWest(), rectangle.getNorth()), - glm::dvec2(rectangle.getEast(), rectangle.getNorth()), - glm::dvec2(rectangle.getEast(), rectangle.getSouth())}; - - glm::dvec2 rectangleEdges[] = { - rectangleCorners[1] - rectangleCorners[0], - rectangleCorners[2] - rectangleCorners[1], - rectangleCorners[3] - rectangleCorners[2], - rectangleCorners[0] - rectangleCorners[3]}; - - // Iterate through all polygons. - for (size_t i = 0; i < cartographicPolygons.size(); ++i) { - const CartographicPolygon& selection = cartographicPolygons[i]; - - const std::optional& - polygonBoundingRectangle = selection.getBoundingRectangle(); - if (!polygonBoundingRectangle || - !rectangle.computeIntersection(*polygonBoundingRectangle)) { - continue; - } - - const std::vector& vertices = selection.getVertices(); - const std::vector& indices = selection.getIndices(); - - // First check if an arbitrary point on the bounding globe rectangle is - // inside the polygon. - bool inside = false; - for (size_t j = 2; j < indices.size(); j += 3) { - if (withinTriangle( - rectangleCorners[0], - vertices[indices[j - 2]], - vertices[indices[j - 1]], - vertices[indices[j]])) { - inside = true; - break; - } - } - - // If the arbitrary point was outside, then this polygon does not entirely - // cull the tile. - if (!inside) { - continue; - } - - // Check if the polygon perimeter intersects the bounding globe rectangle - // edges. - bool intersectionFound = false; - for (size_t j = 0; j < vertices.size(); ++j) { - const glm::dvec2& a = vertices[j]; - const glm::dvec2& b = vertices[(j + 1) % vertices.size()]; - - const glm::dvec2 ba = a - b; - - // Check each rectangle edge. - for (size_t k = 0; k < 4; ++k) { - const glm::dvec2& cd = rectangleEdges[k]; - const glm::dmat2 lineSegmentMatrix(cd, ba); - const glm::dvec2 ca = a - rectangleCorners[k]; - - // s and t are calculated such that: - // line_intersection = a + t * ab = c + s * cd - const glm::dvec2 st = glm::inverse(lineSegmentMatrix) * ca; - - // check that the intersection is within the line segments - if (st.x <= 1.0 && st.x >= 0.0 && st.y <= 1.0 && st.y >= 0.0) { - intersectionFound = true; - break; - } - } - - if (intersectionFound) { - break; - } - } - - // There is no intersection with the perimeter and at least one point is - // inside the polygon so the tile is completely inside this polygon. - if (!intersectionFound) { - return true; - } - } - - return false; + return CartographicPolygon::rectangleIsWithinPolygons( + *maybeRectangle, + cartographicPolygons); } bool outsidePolygons( @@ -153,95 +37,11 @@ bool outsidePolygons( return false; } - return outsidePolygons(*maybeRectangle, cartographicPolygons); + return CartographicPolygon::rectangleIsOutsidePolygons( + *maybeRectangle, + cartographicPolygons); } -bool outsidePolygons( - const CesiumGeospatial::GlobeRectangle& rectangle, - const std::vector& - cartographicPolygons) noexcept { - - glm::dvec2 rectangleCorners[] = { - glm::dvec2(rectangle.getWest(), rectangle.getSouth()), - glm::dvec2(rectangle.getWest(), rectangle.getNorth()), - glm::dvec2(rectangle.getEast(), rectangle.getNorth()), - glm::dvec2(rectangle.getEast(), rectangle.getSouth())}; - - glm::dvec2 rectangleEdges[] = { - rectangleCorners[1] - rectangleCorners[0], - rectangleCorners[2] - rectangleCorners[1], - rectangleCorners[3] - rectangleCorners[2], - rectangleCorners[0] - rectangleCorners[3]}; - - // Iterate through all polygons. - for (size_t i = 0; i < cartographicPolygons.size(); ++i) { - const CartographicPolygon& selection = cartographicPolygons[i]; - - const std::optional& - polygonBoundingRectangle = selection.getBoundingRectangle(); - if (!polygonBoundingRectangle || - !rectangle.computeIntersection(*polygonBoundingRectangle)) { - continue; - } - - const std::vector& vertices = selection.getVertices(); - const std::vector& indices = selection.getIndices(); - - // Check if an arbitrary point on the polygon is in the globe rectangle. - if (withinTriangle( - vertices[0], - rectangleCorners[0], - rectangleCorners[1], - rectangleCorners[2]) || - withinTriangle( - vertices[0], - rectangleCorners[0], - rectangleCorners[2], - rectangleCorners[3])) { - return false; - } - - // Check if an arbitrary point on the bounding globe rectangle is - // inside the polygon. - for (size_t j = 2; j < indices.size(); j += 3) { - if (withinTriangle( - rectangleCorners[0], - vertices[indices[j - 2]], - vertices[indices[j - 1]], - vertices[indices[j]])) { - return false; - } - } - - // Now we know the rectangle does not fully contain the polygon and the - // polygon does not fully contain the rectangle. Now check if the polygon - // perimeter intersects the bounding globe rectangle edges. - for (size_t j = 0; j < vertices.size(); ++j) { - const glm::dvec2& a = vertices[j]; - const glm::dvec2& b = vertices[(j + 1) % vertices.size()]; - - const glm::dvec2 ba = a - b; - - // Check each rectangle edge. - for (size_t k = 0; k < 4; ++k) { - const glm::dvec2& cd = rectangleEdges[k]; - const glm::dmat2 lineSegmentMatrix(cd, ba); - const glm::dvec2 ca = a - rectangleCorners[k]; - - // s and t are calculated such that: - // line_intersection = a + t * ab = c + s * cd - const glm::dvec2 st = glm::inverse(lineSegmentMatrix) * ca; - - // check that the intersection is within the line segments - if (st.x <= 1.0 && st.x >= 0.0 && st.y <= 1.0 && st.y >= 0.0) { - return false; - } - } - } - } - - return true; -} } // namespace CesiumImpl } // namespace Cesium3DTilesSelection diff --git a/Cesium3DTilesSelection/src/TileUtilities.h b/Cesium3DTilesSelection/src/TileUtilities.h index 90f04cd91..1258caf1a 100644 --- a/Cesium3DTilesSelection/src/TileUtilities.h +++ b/Cesium3DTilesSelection/src/TileUtilities.h @@ -9,22 +9,6 @@ namespace Cesium3DTilesSelection { namespace CesiumImpl { - -/** - * @brief Returns whether the point is completely inside the triangle. - * - * @param point The point to check. - * @param triangleVertA The first vertex of the triangle. - * @param triangleVertB The second vertex of the triangle. - * @param triangleVertC The third vertex of the triangle. - * @return Whether the point is within the triangle. - */ -bool withinTriangle( - const glm::dvec2& point, - const glm::dvec2& triangleVertA, - const glm::dvec2& triangleVertB, - const glm::dvec2& triangleVertC) noexcept; - /** * @brief Returns whether the tile is completely inside a polygon. * @@ -37,18 +21,6 @@ bool withinPolygons( const std::vector& cartographicPolygons) noexcept; -/** - * @brief Returns whether the tile is completely inside a polygon. - * - * @param rectangle The {@link CesiumGeospatial::GlobeRectangle} of the tile. - * @param cartographicPolygons The list of polygons to check. - * @return Whether the tile is completely inside a polygon. - */ -bool withinPolygons( - const CesiumGeospatial::GlobeRectangle& rectangle, - const std::vector& - cartographicPolygons) noexcept; - /** * @brief Returns whether the tile is completely outside all the polygons. * @@ -60,16 +32,5 @@ bool outsidePolygons( const BoundingVolume& boundingVolume, const std::vector& cartographicPolygons) noexcept; - -/** - * @brief Returns whether the tile is completely outside all the polygons. - * - * @param rectangle The {@link CesiumGeospatial::GlobeRectangle} of the tile. - * @param cartographicPolygons The list of polygons to check. - * @return Whether the tile is completely outside all the polygons. - */ -bool outsidePolygons( - const CesiumGeospatial::GlobeRectangle& rectangle, - const std::vector&) noexcept; } // namespace CesiumImpl } // namespace Cesium3DTilesSelection diff --git a/Cesium3DTilesSelection/src/Tileset.cpp b/Cesium3DTilesSelection/src/Tileset.cpp index 787545a88..1ae7b6015 100644 --- a/Cesium3DTilesSelection/src/Tileset.cpp +++ b/Cesium3DTilesSelection/src/Tileset.cpp @@ -1,9 +1,7 @@ #include "TileUtilities.h" #include "TilesetContentManager.h" -#include #include -#include #include #include #include @@ -12,6 +10,8 @@ #include #include #include +#include +#include #include #include #include @@ -28,6 +28,7 @@ using namespace CesiumAsync; using namespace CesiumGeometry; using namespace CesiumGeospatial; +using namespace CesiumRasterOverlays; using namespace CesiumUtility; namespace Cesium3DTilesSelection { diff --git a/Cesium3DTilesSelection/src/TilesetContentManager.cpp b/Cesium3DTilesSelection/src/TilesetContentManager.cpp index 8d4e46d7c..5f6274977 100644 --- a/Cesium3DTilesSelection/src/TilesetContentManager.cpp +++ b/Cesium3DTilesSelection/src/TilesetContentManager.cpp @@ -7,13 +7,13 @@ #include #include -#include -#include -#include #include #include #include #include +#include +#include +#include #include #include @@ -23,6 +23,8 @@ #include using namespace Cesium3DTilesContent; +using namespace CesiumRasterOverlays; +using namespace CesiumUtility; namespace Cesium3DTilesSelection { namespace { diff --git a/Cesium3DTilesSelection/src/TilesetContentManager.h b/Cesium3DTilesSelection/src/TilesetContentManager.h index deb32257f..611e218f2 100644 --- a/Cesium3DTilesSelection/src/TilesetContentManager.h +++ b/Cesium3DTilesSelection/src/TilesetContentManager.h @@ -3,7 +3,6 @@ #include "RasterOverlayUpsampler.h" #include "TilesetContentLoaderResult.h" -#include #include #include #include @@ -12,6 +11,7 @@ #include #include #include +#include #include #include @@ -90,9 +90,9 @@ class TilesetContentManager RasterOverlayCollection& getRasterOverlayCollection() noexcept; - const Credit* getUserCredit() const noexcept; + const CesiumUtility::Credit* getUserCredit() const noexcept; - const std::vector& getTilesetCredits() const noexcept; + const std::vector& getTilesetCredits() const noexcept; int32_t getNumberOfTilesLoading() const noexcept; @@ -138,8 +138,8 @@ class TilesetContentManager std::vector _requestHeaders; std::unique_ptr _pLoader; std::unique_ptr _pRootTile; - std::optional _userCredit; - std::vector _tilesetCredits; + std::optional _userCredit; + std::vector _tilesetCredits; RasterOverlayUpsampler _upsampler; RasterOverlayCollection _overlayCollection; int32_t _tileLoadsInProgress; diff --git a/Cesium3DTilesSelection/test/SimplePrepareRendererResource.h b/Cesium3DTilesSelection/test/SimplePrepareRendererResource.h index 59417ce53..19126357c 100644 --- a/Cesium3DTilesSelection/test/SimplePrepareRendererResource.h +++ b/Cesium3DTilesSelection/test/SimplePrepareRendererResource.h @@ -1,8 +1,8 @@ #pragma once #include "Cesium3DTilesSelection/IPrepareRendererResources.h" -#include "Cesium3DTilesSelection/RasterOverlayTile.h" #include "Cesium3DTilesSelection/Tile.h" +#include "CesiumRasterOverlays/RasterOverlayTile.h" #include @@ -74,7 +74,7 @@ class SimplePrepareRendererResource } virtual void* prepareRasterInMainThread( - Cesium3DTilesSelection::RasterOverlayTile& /*rasterTile*/, + CesiumRasterOverlays::RasterOverlayTile& /*rasterTile*/, void* pLoadThreadResult) override { if (pLoadThreadResult) { AllocationResult* loadThreadResult = @@ -86,7 +86,7 @@ class SimplePrepareRendererResource } virtual void freeRaster( - const Cesium3DTilesSelection::RasterOverlayTile& /*rasterTile*/, + const CesiumRasterOverlays::RasterOverlayTile& /*rasterTile*/, void* pLoadThreadResult, void* pMainThreadResult) noexcept override { if (pMainThreadResult) { @@ -105,7 +105,7 @@ class SimplePrepareRendererResource virtual void attachRasterInMainThread( const Cesium3DTilesSelection::Tile& /*tile*/, int32_t /*overlayTextureCoordinateID*/, - const Cesium3DTilesSelection::RasterOverlayTile& /*rasterTile*/, + const CesiumRasterOverlays::RasterOverlayTile& /*rasterTile*/, void* /*pMainThreadRendererResources*/, const glm::dvec2& /*translation*/, const glm::dvec2& /*scale*/) override {} @@ -113,7 +113,7 @@ class SimplePrepareRendererResource virtual void detachRasterInMainThread( const Cesium3DTilesSelection::Tile& /*tile*/, int32_t /*overlayTextureCoordinateID*/, - const Cesium3DTilesSelection::RasterOverlayTile& /*rasterTile*/, + const CesiumRasterOverlays::RasterOverlayTile& /*rasterTile*/, void* /*pMainThreadRendererResources*/) noexcept override {} }; } // namespace Cesium3DTilesSelection diff --git a/Cesium3DTilesSelection/test/TestCreditSystem.cpp b/Cesium3DTilesSelection/test/TestCreditSystem.cpp index df3df6b82..19b9a4578 100644 --- a/Cesium3DTilesSelection/test/TestCreditSystem.cpp +++ b/Cesium3DTilesSelection/test/TestCreditSystem.cpp @@ -1,8 +1,8 @@ -#include "Cesium3DTilesSelection/CreditSystem.h" +#include "CesiumUtility/CreditSystem.h" #include -using namespace Cesium3DTilesSelection; +using namespace CesiumUtility; TEST_CASE("Test basic credit handling") { diff --git a/Cesium3DTilesSelection/test/TestQuadtreeRasterOverlayTileProvider.cpp b/Cesium3DTilesSelection/test/TestQuadtreeRasterOverlayTileProvider.cpp index a04fe2fe2..7db8c37bb 100644 --- a/Cesium3DTilesSelection/test/TestQuadtreeRasterOverlayTileProvider.cpp +++ b/Cesium3DTilesSelection/test/TestQuadtreeRasterOverlayTileProvider.cpp @@ -1,18 +1,19 @@ -#include "Cesium3DTilesSelection/QuadtreeRasterOverlayTileProvider.h" -#include "Cesium3DTilesSelection/RasterOverlay.h" +#include "CesiumRasterOverlays/QuadtreeRasterOverlayTileProvider.h" +#include "CesiumRasterOverlays/RasterOverlay.h" +#include "CesiumRasterOverlays/RasterOverlayTile.h" #include #include #include -using namespace Cesium3DTilesSelection; using namespace CesiumAsync; using namespace CesiumGeometry; using namespace CesiumGeospatial; using namespace CesiumGltf; using namespace CesiumUtility; using namespace CesiumNativeTests; +using namespace CesiumRasterOverlays; namespace { @@ -23,7 +24,7 @@ class TestTileProvider : public QuadtreeRasterOverlayTileProvider { const CesiumAsync::AsyncSystem& asyncSystem, const std::shared_ptr& pAssetAccessor, std::optional credit, - const std::shared_ptr& + const std::shared_ptr& pPrepareRendererResources, const std::shared_ptr& pLogger, const CesiumGeospatial::Projection& projection, @@ -87,7 +88,7 @@ class TestRasterOverlay : public RasterOverlay { const CesiumAsync::AsyncSystem& asyncSystem, const std::shared_ptr& pAssetAccessor, const std::shared_ptr& /* pCreditSystem */, - const std::shared_ptr& + const std::shared_ptr& pPrepareRendererResources, const std::shared_ptr& pLogger, CesiumUtility::IntrusivePointer pOwner) diff --git a/Cesium3DTilesSelection/test/TestTilesetContentManager.cpp b/Cesium3DTilesSelection/test/TestTilesetContentManager.cpp index af75480df..8bc04fbac 100644 --- a/Cesium3DTilesSelection/test/TestTilesetContentManager.cpp +++ b/Cesium3DTilesSelection/test/TestTilesetContentManager.cpp @@ -3,7 +3,7 @@ #include #include -#include +#include #include #include #include @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -29,6 +30,7 @@ using namespace CesiumGeospatial; using namespace CesiumGeometry; using namespace CesiumUtility; using namespace CesiumNativeTests; +using namespace CesiumRasterOverlays; namespace { std::filesystem::path testDataPath = Cesium3DTilesSelection_TEST_DATA_DIR; diff --git a/Cesium3DTilesSelection/test/TestTilesetJsonLoader.cpp b/Cesium3DTilesSelection/test/TestTilesetJsonLoader.cpp index ee5d273b3..dd1c71bae 100644 --- a/Cesium3DTilesSelection/test/TestTilesetJsonLoader.cpp +++ b/Cesium3DTilesSelection/test/TestTilesetJsonLoader.cpp @@ -19,6 +19,7 @@ using namespace CesiumAsync; using namespace Cesium3DTilesSelection; using namespace CesiumNativeTests; using namespace CesiumNativeTests; +using namespace CesiumUtility; namespace { std::filesystem::path testDataPath = Cesium3DTilesSelection_TEST_DATA_DIR; diff --git a/CesiumGeometry/include/CesiumGeometry/IntersectionTests.h b/CesiumGeometry/include/CesiumGeometry/IntersectionTests.h index 204c61ecf..c56c27376 100644 --- a/CesiumGeometry/include/CesiumGeometry/IntersectionTests.h +++ b/CesiumGeometry/include/CesiumGeometry/IntersectionTests.h @@ -2,6 +2,7 @@ #include "Library.h" +#include #include #include @@ -26,6 +27,21 @@ class CESIUMGEOMETRY_API IntersectionTests final { */ static std::optional rayPlane(const Ray& ray, const Plane& plane) noexcept; + + /** + * @brief Determines whether the point is completely inside the triangle. + * + * @param point The point to check. + * @param triangleVertA The first vertex of the triangle. + * @param triangleVertB The second vertex of the triangle. + * @param triangleVertC The third vertex of the triangle. + * @return Whether the point is within the triangle. + */ + static bool pointInTriangle2D( + const glm::dvec2& point, + const glm::dvec2& triangleVertA, + const glm::dvec2& triangleVertB, + const glm::dvec2& triangleVertC) noexcept; }; } // namespace CesiumGeometry diff --git a/CesiumGeometry/src/IntersectionTests.cpp b/CesiumGeometry/src/IntersectionTests.cpp index 2fc68e2f4..479ea7de2 100644 --- a/CesiumGeometry/src/IntersectionTests.cpp +++ b/CesiumGeometry/src/IntersectionTests.cpp @@ -30,4 +30,32 @@ IntersectionTests::rayPlane(const Ray& ray, const Plane& plane) noexcept { return ray.getOrigin() + ray.getDirection() * t; } + +bool IntersectionTests::pointInTriangle2D( + const glm::dvec2& point, + const glm::dvec2& triangleVertA, + const glm::dvec2& triangleVertB, + const glm::dvec2& triangleVertC) noexcept { + + const glm::dvec2 ab = triangleVertB - triangleVertA; + const glm::dvec2 ab_perp(-ab.y, ab.x); + const glm::dvec2 bc = triangleVertC - triangleVertB; + const glm::dvec2 bc_perp(-bc.y, bc.x); + const glm::dvec2 ca = triangleVertA - triangleVertC; + const glm::dvec2 ca_perp(-ca.y, ca.x); + + const glm::dvec2 av = point - triangleVertA; + const glm::dvec2 cv = point - triangleVertC; + + const double v_proj_ab_perp = glm::dot(av, ab_perp); + const double v_proj_bc_perp = glm::dot(cv, bc_perp); + const double v_proj_ca_perp = glm::dot(cv, ca_perp); + + // This will determine in or out, irrespective of winding. + return (v_proj_ab_perp >= 0.0 && v_proj_ca_perp >= 0.0 && + v_proj_bc_perp >= 0.0) || + (v_proj_ab_perp <= 0.0 && v_proj_ca_perp <= 0.0 && + v_proj_bc_perp <= 0.0); +} + } // namespace CesiumGeometry diff --git a/CesiumGeospatial/include/CesiumGeospatial/CartographicPolygon.h b/CesiumGeospatial/include/CesiumGeospatial/CartographicPolygon.h index 4a624c725..25740d184 100644 --- a/CesiumGeospatial/include/CesiumGeospatial/CartographicPolygon.h +++ b/CesiumGeospatial/include/CesiumGeospatial/CartographicPolygon.h @@ -64,6 +64,34 @@ class CESIUMGEOSPATIAL_API CartographicPolygon final { return this->_boundingRectangle; } + /** + * @brief Determines whether a globe rectangle is completely inside any of the + * polygons in a list. + * + * @param rectangle The {@link CesiumGeospatial::GlobeRectangle} of the tile. + * @param cartographicPolygons The list of polygons to check. + * @return True if the rectangle is completely inside a polygon; otherwise, + * false. + */ + static bool rectangleIsWithinPolygons( + const CesiumGeospatial::GlobeRectangle& rectangle, + const std::vector& + cartographicPolygons) noexcept; + + /** + * @brief Determines whether a globe rectangle is completely outside all the + * polygons in a list. + * + * @param rectangle The {@link CesiumGeospatial::GlobeRectangle} of the tile. + * @param cartographicPolygons The list of polygons to check. + * @return True if the rectangle is completely outside all the polygons; + * otherwise, false. + */ + static bool rectangleIsOutsidePolygons( + const CesiumGeospatial::GlobeRectangle& rectangle, + const std::vector& + cartographicPolygons) noexcept; + private: std::vector _vertices; std::vector _indices; diff --git a/CesiumGeospatial/src/CartographicPolygon.cpp b/CesiumGeospatial/src/CartographicPolygon.cpp index 0401c4374..6aa466b4a 100644 --- a/CesiumGeospatial/src/CartographicPolygon.cpp +++ b/CesiumGeospatial/src/CartographicPolygon.cpp @@ -1,9 +1,14 @@ #include "CesiumGeospatial/CartographicPolygon.h" +#include + +#include #include #include +using namespace CesiumGeometry; + namespace CesiumGeospatial { static std::vector @@ -106,4 +111,182 @@ CartographicPolygon::CartographicPolygon(const std::vector& polygon) _indices(triangulatePolygon(polygon)), _boundingRectangle(computeBoundingRectangle(polygon)) {} +/*static*/ bool CartographicPolygon::rectangleIsWithinPolygons( + const CesiumGeospatial::GlobeRectangle& rectangle, + const std::vector& cartographicPolygons) noexcept { + + glm::dvec2 rectangleCorners[] = { + glm::dvec2(rectangle.getWest(), rectangle.getSouth()), + glm::dvec2(rectangle.getWest(), rectangle.getNorth()), + glm::dvec2(rectangle.getEast(), rectangle.getNorth()), + glm::dvec2(rectangle.getEast(), rectangle.getSouth())}; + + glm::dvec2 rectangleEdges[] = { + rectangleCorners[1] - rectangleCorners[0], + rectangleCorners[2] - rectangleCorners[1], + rectangleCorners[3] - rectangleCorners[2], + rectangleCorners[0] - rectangleCorners[3]}; + + // Iterate through all polygons. + for (size_t i = 0; i < cartographicPolygons.size(); ++i) { + const CartographicPolygon& selection = cartographicPolygons[i]; + + const std::optional& + polygonBoundingRectangle = selection.getBoundingRectangle(); + if (!polygonBoundingRectangle || + !rectangle.computeIntersection(*polygonBoundingRectangle)) { + continue; + } + + const std::vector& vertices = selection.getVertices(); + const std::vector& indices = selection.getIndices(); + + // First check if an arbitrary point on the bounding globe rectangle is + // inside the polygon. + bool inside = false; + for (size_t j = 2; j < indices.size(); j += 3) { + if (IntersectionTests::pointInTriangle2D( + rectangleCorners[0], + vertices[indices[j - 2]], + vertices[indices[j - 1]], + vertices[indices[j]])) { + inside = true; + break; + } + } + + // If the arbitrary point was outside, then this polygon does not entirely + // cull the tile. + if (!inside) { + continue; + } + + // Check if the polygon perimeter intersects the bounding globe rectangle + // edges. + bool intersectionFound = false; + for (size_t j = 0; j < vertices.size(); ++j) { + const glm::dvec2& a = vertices[j]; + const glm::dvec2& b = vertices[(j + 1) % vertices.size()]; + + const glm::dvec2 ba = a - b; + + // Check each rectangle edge. + for (size_t k = 0; k < 4; ++k) { + const glm::dvec2& cd = rectangleEdges[k]; + const glm::dmat2 lineSegmentMatrix(cd, ba); + const glm::dvec2 ca = a - rectangleCorners[k]; + + // s and t are calculated such that: + // line_intersection = a + t * ab = c + s * cd + const glm::dvec2 st = glm::inverse(lineSegmentMatrix) * ca; + + // check that the intersection is within the line segments + if (st.x <= 1.0 && st.x >= 0.0 && st.y <= 1.0 && st.y >= 0.0) { + intersectionFound = true; + break; + } + } + + if (intersectionFound) { + break; + } + } + + // There is no intersection with the perimeter and at least one point is + // inside the polygon so the tile is completely inside this polygon. + if (!intersectionFound) { + return true; + } + } + + return false; +} + +/*static*/ bool CartographicPolygon::rectangleIsOutsidePolygons( + const CesiumGeospatial::GlobeRectangle& rectangle, + const std::vector& + cartographicPolygons) noexcept { + + glm::dvec2 rectangleCorners[] = { + glm::dvec2(rectangle.getWest(), rectangle.getSouth()), + glm::dvec2(rectangle.getWest(), rectangle.getNorth()), + glm::dvec2(rectangle.getEast(), rectangle.getNorth()), + glm::dvec2(rectangle.getEast(), rectangle.getSouth())}; + + glm::dvec2 rectangleEdges[] = { + rectangleCorners[1] - rectangleCorners[0], + rectangleCorners[2] - rectangleCorners[1], + rectangleCorners[3] - rectangleCorners[2], + rectangleCorners[0] - rectangleCorners[3]}; + + // Iterate through all polygons. + for (size_t i = 0; i < cartographicPolygons.size(); ++i) { + const CartographicPolygon& selection = cartographicPolygons[i]; + + const std::optional& + polygonBoundingRectangle = selection.getBoundingRectangle(); + if (!polygonBoundingRectangle || + !rectangle.computeIntersection(*polygonBoundingRectangle)) { + continue; + } + + const std::vector& vertices = selection.getVertices(); + const std::vector& indices = selection.getIndices(); + + // Check if an arbitrary point on the polygon is in the globe rectangle. + if (IntersectionTests::pointInTriangle2D( + vertices[0], + rectangleCorners[0], + rectangleCorners[1], + rectangleCorners[2]) || + IntersectionTests::pointInTriangle2D( + vertices[0], + rectangleCorners[0], + rectangleCorners[2], + rectangleCorners[3])) { + return false; + } + + // Check if an arbitrary point on the bounding globe rectangle is + // inside the polygon. + for (size_t j = 2; j < indices.size(); j += 3) { + if (IntersectionTests::pointInTriangle2D( + rectangleCorners[0], + vertices[indices[j - 2]], + vertices[indices[j - 1]], + vertices[indices[j]])) { + return false; + } + } + + // Now we know the rectangle does not fully contain the polygon and the + // polygon does not fully contain the rectangle. Now check if the polygon + // perimeter intersects the bounding globe rectangle edges. + for (size_t j = 0; j < vertices.size(); ++j) { + const glm::dvec2& a = vertices[j]; + const glm::dvec2& b = vertices[(j + 1) % vertices.size()]; + + const glm::dvec2 ba = a - b; + + // Check each rectangle edge. + for (size_t k = 0; k < 4; ++k) { + const glm::dvec2& cd = rectangleEdges[k]; + const glm::dmat2 lineSegmentMatrix(cd, ba); + const glm::dvec2 ca = a - rectangleCorners[k]; + + // s and t are calculated such that: + // line_intersection = a + t * ab = c + s * cd + const glm::dvec2 st = glm::inverse(lineSegmentMatrix) * ca; + + // check that the intersection is within the line segments + if (st.x <= 1.0 && st.x >= 0.0 && st.y <= 1.0 && st.y >= 0.0) { + return false; + } + } + } + } + + return true; +} + } // namespace CesiumGeospatial diff --git a/CesiumRasterOverlays/CMakeLists.txt b/CesiumRasterOverlays/CMakeLists.txt new file mode 100644 index 000000000..58a6d3e02 --- /dev/null +++ b/CesiumRasterOverlays/CMakeLists.txt @@ -0,0 +1,56 @@ +add_library(CesiumRasterOverlays "") + +configure_cesium_library(CesiumRasterOverlays) + +cesium_glob_files(CESIUM_RASTER_OVERLAYS_SOURCES src/*.cpp) +cesium_glob_files(CESIUM_RASTER_OVERLAYS_HEADERS src/*.h) +cesium_glob_files(CESIUM_RASTER_OVERLAYS_PUBLIC_HEADERS include/CesiumRasterOverlays/*.h) +cesium_glob_files(CESIUM_RASTER_OVERLAYS_TEST_SOURCES test/*.cpp) +cesium_glob_files(CESIUM_RASTER_OVERLAYS_TEST_HEADERS test/*.h) + +set_target_properties(CesiumRasterOverlays + PROPERTIES + TEST_SOURCES "${CESIUM_RASTER_OVERLAYS_TEST_SOURCES}" + TEST_HEADERS "${CESIUM_RASTER_OVERLAYS_TEST_HEADERS}" + TEST_DATA_DIR ${CMAKE_CURRENT_LIST_DIR}/test/data +) + +set_target_properties(CesiumRasterOverlays + PROPERTIES + PUBLIC_HEADER "${CESIUM_RASTER_OVERLAYS_PUBLIC_HEADERS}" +) + +target_sources( + CesiumRasterOverlays + PRIVATE + ${CESIUM_RASTER_OVERLAYS_SOURCES} + ${CESIUM_RASTER_OVERLAYS_HEADERS} + PUBLIC + ${CESIUM_RASTER_OVERLAYS_PUBLIC_HEADERS} +) + +target_include_directories( + CesiumRasterOverlays + SYSTEM PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/include + PRIVATE + ${CMAKE_CURRENT_LIST_DIR}/src +) + +target_link_libraries(CesiumRasterOverlays + PUBLIC + CesiumAsync + CesiumGeospatial + CesiumGeometry + CesiumGltf + CesiumGltfReader + CesiumUtility + PRIVATE + expected-lite + tinyxml2 +) + +install(TARGETS CesiumRasterOverlays + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/CesiumRasterOverlays +) diff --git a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/BingMapsRasterOverlay.h b/CesiumRasterOverlays/include/CesiumRasterOverlays/BingMapsRasterOverlay.h similarity index 93% rename from Cesium3DTilesSelection/include/Cesium3DTilesSelection/BingMapsRasterOverlay.h rename to CesiumRasterOverlays/include/CesiumRasterOverlays/BingMapsRasterOverlay.h index 3d42a5f88..6d962d954 100644 --- a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/BingMapsRasterOverlay.h +++ b/CesiumRasterOverlays/include/CesiumRasterOverlays/BingMapsRasterOverlay.h @@ -9,9 +9,7 @@ #include #include -namespace Cesium3DTilesSelection { - -class CreditSystem; +namespace CesiumRasterOverlays { /** * @brief Styles of Bing Maps overlays. @@ -81,7 +79,7 @@ struct BingMapsStyle final { * @brief A {@link RasterOverlay} that uses Bing Maps as the source for the * imagery data. */ -class CESIUM3DTILESSELECTION_API BingMapsRasterOverlay final +class CESIUMRASTEROVERLAYS_API BingMapsRasterOverlay final : public RasterOverlay { public: /** @@ -116,8 +114,8 @@ class CESIUM3DTILESSELECTION_API BingMapsRasterOverlay final virtual CesiumAsync::Future createTileProvider( const CesiumAsync::AsyncSystem& asyncSystem, const std::shared_ptr& pAssetAccessor, - const std::shared_ptr& pCreditSystem, - const std::shared_ptr& + const std::shared_ptr& pCreditSystem, + const std::shared_ptr& pPrepareRendererResources, const std::shared_ptr& pLogger, CesiumUtility::IntrusivePointer pOwner) @@ -133,4 +131,4 @@ class CESIUM3DTILESSELECTION_API BingMapsRasterOverlay final CesiumGeospatial::Ellipsoid _ellipsoid; }; -} // namespace Cesium3DTilesSelection +} // namespace CesiumRasterOverlays diff --git a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/DebugColorizeTilesRasterOverlay.h b/CesiumRasterOverlays/include/CesiumRasterOverlays/DebugColorizeTilesRasterOverlay.h similarity index 77% rename from Cesium3DTilesSelection/include/Cesium3DTilesSelection/DebugColorizeTilesRasterOverlay.h rename to CesiumRasterOverlays/include/CesiumRasterOverlays/DebugColorizeTilesRasterOverlay.h index 56e5443cb..fccb8a127 100644 --- a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/DebugColorizeTilesRasterOverlay.h +++ b/CesiumRasterOverlays/include/CesiumRasterOverlays/DebugColorizeTilesRasterOverlay.h @@ -2,14 +2,14 @@ #include "RasterOverlay.h" -namespace Cesium3DTilesSelection { +namespace CesiumRasterOverlays { /** * @brief A raster overlay that gives each tile to which it is attached a random * color with 50% opacity. This is useful for debugging a tileset, to visualize * how it is divided into tiles. */ -class CESIUM3DTILESSELECTION_API DebugColorizeTilesRasterOverlay +class CESIUMRASTEROVERLAYS_API DebugColorizeTilesRasterOverlay : public RasterOverlay { public: /** @@ -25,12 +25,12 @@ class CESIUM3DTILESSELECTION_API DebugColorizeTilesRasterOverlay virtual CesiumAsync::Future createTileProvider( const CesiumAsync::AsyncSystem& asyncSystem, const std::shared_ptr& pAssetAccessor, - const std::shared_ptr& pCreditSystem, - const std::shared_ptr& + const std::shared_ptr& pCreditSystem, + const std::shared_ptr& pPrepareRendererResources, const std::shared_ptr& pLogger, CesiumUtility::IntrusivePointer pOwner) const override; }; -} // namespace Cesium3DTilesSelection +} // namespace CesiumRasterOverlays diff --git a/CesiumRasterOverlays/include/CesiumRasterOverlays/IPrepareRasterOverlayRendererResources.h b/CesiumRasterOverlays/include/CesiumRasterOverlays/IPrepareRasterOverlayRendererResources.h new file mode 100644 index 000000000..7f5c40c52 --- /dev/null +++ b/CesiumRasterOverlays/include/CesiumRasterOverlays/IPrepareRasterOverlayRendererResources.h @@ -0,0 +1,74 @@ +#pragma once + +#include "Library.h" + +#include + +namespace CesiumGltf { +struct ImageCesium; +} + +namespace CesiumRasterOverlays { +class RasterOverlayTile; +} + +namespace CesiumRasterOverlays { + +class CESIUMRASTEROVERLAYS_API IPrepareRasterOverlayRendererResources { +public: + /** + * @brief Prepares a raster overlay tile. + * + * This method is invoked in the load thread and may modify the image. + * + * @param image The raster tile image to prepare. + * @param rendererOptions Renderer options associated with the raster overlay tile from {@link RasterOverlayOptions::rendererOptions}. + * @returns Arbitrary data representing the result of the load process. This + * data is passed to {@link prepareRasterInMainThread} as the + * `pLoadThreadResult` parameter. + */ + virtual void* prepareRasterInLoadThread( + CesiumGltf::ImageCesium& image, + const std::any& rendererOptions) = 0; + + /** + * @brief Further preprares a raster overlay tile. + * + * This is called after {@link prepareRasterInLoadThread}, and unlike that + * method, this one is called from the same thread that called + * {@link Tileset::updateView}. + * + * @param rasterTile The raster tile to prepare. + * @param pLoadThreadResult The value returned from + * {@link prepareRasterInLoadThread}. + * @returns Arbitrary data representing the result of the load process. Note + * that the value returned by {@link prepareRasterInLoadThread} will _not_ be + * automatically preserved and passed to {@link free}. If you need to free + * that value, do it in this method before returning. If you need that value + * later, add it to the object returned from this method. + */ + virtual void* prepareRasterInMainThread( + RasterOverlayTile& rasterTile, + void* pLoadThreadResult) = 0; + + /** + * @brief Frees previously-prepared renderer resources for a raster tile. + * + * This method is always called from the thread that called + * {@link Tileset::updateView} or deleted the tileset. + * + * @param rasterTile The tile for which to free renderer resources. + * @param pLoadThreadResult The result returned by + * {@link prepareRasterInLoadThread}. If {@link prepareRasterInMainThread} + * has already been called, this parameter will be `nullptr`. + * @param pMainThreadResult The result returned by + * {@link prepareRasterInMainThread}. If {@link prepareRasterInMainThread} + * has not yet been called, this parameter will be `nullptr`. + */ + virtual void freeRaster( + const RasterOverlayTile& rasterTile, + void* pLoadThreadResult, + void* pMainThreadResult) noexcept = 0; +}; + +} // namespace CesiumRasterOverlays diff --git a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/IonRasterOverlay.h b/CesiumRasterOverlays/include/CesiumRasterOverlays/IonRasterOverlay.h similarity index 84% rename from Cesium3DTilesSelection/include/Cesium3DTilesSelection/IonRasterOverlay.h rename to CesiumRasterOverlays/include/CesiumRasterOverlays/IonRasterOverlay.h index 4921eb459..b30fda1b3 100644 --- a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/IonRasterOverlay.h +++ b/CesiumRasterOverlays/include/CesiumRasterOverlays/IonRasterOverlay.h @@ -9,14 +9,12 @@ #include #include -namespace Cesium3DTilesSelection { - -class CreditSystem; +namespace CesiumRasterOverlays { /** * @brief A {@link RasterOverlay} that obtains imagery data from Cesium ion. */ -class CESIUM3DTILESSELECTION_API IonRasterOverlay final : public RasterOverlay { +class CESIUMRASTEROVERLAYS_API IonRasterOverlay final : public RasterOverlay { public: /** * @brief Creates a new instance. @@ -41,8 +39,8 @@ class CESIUM3DTILESSELECTION_API IonRasterOverlay final : public RasterOverlay { virtual CesiumAsync::Future createTileProvider( const CesiumAsync::AsyncSystem& asyncSystem, const std::shared_ptr& pAssetAccessor, - const std::shared_ptr& pCreditSystem, - const std::shared_ptr& + const std::shared_ptr& pCreditSystem, + const std::shared_ptr& pPrepareRendererResources, const std::shared_ptr& pLogger, CesiumUtility::IntrusivePointer pOwner) @@ -74,11 +72,11 @@ class CESIUM3DTILESSELECTION_API IonRasterOverlay final : public RasterOverlay { const ExternalAssetEndpoint& endpoint, const CesiumAsync::AsyncSystem& asyncSystem, const std::shared_ptr& pAssetAccessor, - const std::shared_ptr& pCreditSystem, - const std::shared_ptr& + const std::shared_ptr& pCreditSystem, + const std::shared_ptr& pPrepareRendererResources, const std::shared_ptr& pLogger, CesiumUtility::IntrusivePointer pOwner) const; }; -} // namespace Cesium3DTilesSelection +} // namespace CesiumRasterOverlays diff --git a/CesiumRasterOverlays/include/CesiumRasterOverlays/Library.h b/CesiumRasterOverlays/include/CesiumRasterOverlays/Library.h new file mode 100644 index 000000000..873c2df2a --- /dev/null +++ b/CesiumRasterOverlays/include/CesiumRasterOverlays/Library.h @@ -0,0 +1,17 @@ +#pragma once + +/** + * @brief Classes for raster overlays, which allow draping massive 2D textures + * over a model. + */ +namespace CesiumRasterOverlays {} + +#if defined(_WIN32) && defined(CESIUM_SHARED) +#ifdef CESIUMRASTEROVERLAYS_BUILDING +#define CESIUMRASTEROVERLAYS_API __declspec(dllexport) +#else +#define CESIUMRASTEROVERLAYS_API __declspec(dllimport) +#endif +#else +#define CESIUMRASTEROVERLAYS_API +#endif diff --git a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/QuadtreeRasterOverlayTileProvider.h b/CesiumRasterOverlays/include/CesiumRasterOverlays/QuadtreeRasterOverlayTileProvider.h similarity index 94% rename from Cesium3DTilesSelection/include/Cesium3DTilesSelection/QuadtreeRasterOverlayTileProvider.h rename to CesiumRasterOverlays/include/CesiumRasterOverlays/QuadtreeRasterOverlayTileProvider.h index 29001a349..ae2e79e2e 100644 --- a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/QuadtreeRasterOverlayTileProvider.h +++ b/CesiumRasterOverlays/include/CesiumRasterOverlays/QuadtreeRasterOverlayTileProvider.h @@ -1,23 +1,22 @@ #pragma once -#include "CreditSystem.h" -#include "IPrepareRendererResources.h" +#include "IPrepareRasterOverlayRendererResources.h" #include "Library.h" #include "RasterOverlayTileProvider.h" -#include "TileID.h" #include #include #include #include +#include #include #include #include -namespace Cesium3DTilesSelection { +namespace CesiumRasterOverlays { -class CESIUM3DTILESSELECTION_API QuadtreeRasterOverlayTileProvider +class CESIUMRASTEROVERLAYS_API QuadtreeRasterOverlayTileProvider : public RasterOverlayTileProvider { public: @@ -44,8 +43,8 @@ class CESIUM3DTILESSELECTION_API QuadtreeRasterOverlayTileProvider const CesiumUtility::IntrusivePointer& pOwner, const CesiumAsync::AsyncSystem& asyncSystem, const std::shared_ptr& pAssetAccessor, - std::optional credit, - const std::shared_ptr& + std::optional credit, + const std::shared_ptr& pPrepareRendererResources, const std::shared_ptr& pLogger, const CesiumGeospatial::Projection& projection, @@ -178,4 +177,4 @@ class CESIUM3DTILESSELECTION_API QuadtreeRasterOverlayTileProvider std::atomic _cachedBytes; }; -} // namespace Cesium3DTilesSelection +} // namespace CesiumRasterOverlays diff --git a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/RasterOverlay.h b/CesiumRasterOverlays/include/CesiumRasterOverlays/RasterOverlay.h similarity index 93% rename from Cesium3DTilesSelection/include/Cesium3DTilesSelection/RasterOverlay.h rename to CesiumRasterOverlays/include/CesiumRasterOverlays/RasterOverlay.h index 3ea6f1a7c..2176a804a 100644 --- a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/RasterOverlay.h +++ b/CesiumRasterOverlays/include/CesiumRasterOverlays/RasterOverlay.h @@ -17,18 +17,20 @@ #include #include -namespace Cesium3DTilesSelection { - +namespace CesiumUtility { struct Credit; class CreditSystem; -class IPrepareRendererResources; +} // namespace CesiumUtility + +namespace CesiumRasterOverlays { + +class IPrepareRasterOverlayRendererResources; class RasterOverlayTileProvider; -class RasterOverlayCollection; /** * @brief Options for loading raster overlays. */ -struct CESIUM3DTILESSELECTION_API RasterOverlayOptions { +struct CESIUMRASTEROVERLAYS_API RasterOverlayOptions { /** * @brief The maximum number of overlay tiles that may simultaneously be in * the process of loading. @@ -166,14 +168,16 @@ class RasterOverlay /** * @brief Gets the credits for this overlay. */ - const std::vector& getCredits() const noexcept { + const std::vector& getCredits() const noexcept { return this->_credits; } /** * @brief Gets the credits for this overlay. */ - std::vector& getCredits() noexcept { return this->_credits; } + std::vector& getCredits() noexcept { + return this->_credits; + } /** * @brief Create a placeholder tile provider can be used in place of the real @@ -213,8 +217,8 @@ class RasterOverlay virtual CesiumAsync::Future createTileProvider( const CesiumAsync::AsyncSystem& asyncSystem, const std::shared_ptr& pAssetAccessor, - const std::shared_ptr& pCreditSystem, - const std::shared_ptr& + const std::shared_ptr& pCreditSystem, + const std::shared_ptr& pPrepareRendererResources, const std::shared_ptr& pLogger, CesiumUtility::IntrusivePointer pOwner) const = 0; @@ -228,8 +232,8 @@ class RasterOverlay std::string _name; RasterOverlayOptions _options; - std::vector _credits; + std::vector _credits; std::optional _destructionCompleteDetails; }; -} // namespace Cesium3DTilesSelection +} // namespace CesiumRasterOverlays diff --git a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/RasterOverlayLoadFailureDetails.h b/CesiumRasterOverlays/include/CesiumRasterOverlays/RasterOverlayLoadFailureDetails.h similarity index 93% rename from Cesium3DTilesSelection/include/Cesium3DTilesSelection/RasterOverlayLoadFailureDetails.h rename to CesiumRasterOverlays/include/CesiumRasterOverlays/RasterOverlayLoadFailureDetails.h index 128e4e66e..2b3c735d2 100644 --- a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/RasterOverlayLoadFailureDetails.h +++ b/CesiumRasterOverlays/include/CesiumRasterOverlays/RasterOverlayLoadFailureDetails.h @@ -9,7 +9,7 @@ namespace CesiumAsync { class IAssetRequest; } -namespace Cesium3DTilesSelection { +namespace CesiumRasterOverlays { class RasterOverlay; @@ -52,4 +52,4 @@ class RasterOverlayLoadFailureDetails { std::string message = ""; }; -} // namespace Cesium3DTilesSelection +} // namespace CesiumRasterOverlays diff --git a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/RasterOverlayTile.h b/CesiumRasterOverlays/include/CesiumRasterOverlays/RasterOverlayTile.h similarity index 97% rename from Cesium3DTilesSelection/include/Cesium3DTilesSelection/RasterOverlayTile.h rename to CesiumRasterOverlays/include/CesiumRasterOverlays/RasterOverlayTile.h index d5d7f57d4..917ab902d 100644 --- a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/RasterOverlayTile.h +++ b/CesiumRasterOverlays/include/CesiumRasterOverlays/RasterOverlayTile.h @@ -9,9 +9,12 @@ #include -namespace Cesium3DTilesSelection { - +namespace CesiumUtility { struct Credit; +} + +namespace CesiumRasterOverlays { + class RasterOverlay; class RasterOverlayTileProvider; @@ -177,7 +180,7 @@ class RasterOverlayTile final /** * @brief Returns the list of {@link Credit}s needed for this tile. */ - const std::vector& getCredits() const noexcept { + const std::vector& getCredits() const noexcept { return this->_tileCredits; } @@ -251,10 +254,10 @@ class RasterOverlayTile final RasterOverlayTileProvider* _pTileProvider; glm::dvec2 _targetScreenPixels; CesiumGeometry::Rectangle _rectangle; - std::vector _tileCredits; + std::vector _tileCredits; LoadState _state; CesiumGltf::ImageCesium _image; void* _pRendererResources; MoreDetailAvailable _moreDetailAvailable; }; -} // namespace Cesium3DTilesSelection +} // namespace CesiumRasterOverlays diff --git a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/RasterOverlayTileProvider.h b/CesiumRasterOverlays/include/CesiumRasterOverlays/RasterOverlayTileProvider.h similarity index 94% rename from Cesium3DTilesSelection/include/Cesium3DTilesSelection/RasterOverlayTileProvider.h rename to CesiumRasterOverlays/include/CesiumRasterOverlays/RasterOverlayTileProvider.h index a84538286..d23bae609 100644 --- a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/RasterOverlayTileProvider.h +++ b/CesiumRasterOverlays/include/CesiumRasterOverlays/RasterOverlayTileProvider.h @@ -1,12 +1,11 @@ #pragma once -#include "CreditSystem.h" #include "Library.h" -#include "RasterMappedTo3DTile.h" #include #include #include +#include #include #include #include @@ -16,16 +15,16 @@ #include #include -namespace Cesium3DTilesSelection { +namespace CesiumRasterOverlays { class RasterOverlay; class RasterOverlayTile; -class IPrepareRendererResources; +class IPrepareRasterOverlayRendererResources; /** * @brief Summarizes the result of loading an image of a {@link RasterOverlay}. */ -struct CESIUM3DTILESSELECTION_API LoadedRasterOverlayImage { +struct CESIUMRASTEROVERLAYS_API LoadedRasterOverlayImage { /** * @brief The loaded image. * @@ -46,7 +45,7 @@ struct CESIUM3DTILESSELECTION_API LoadedRasterOverlayImage { * @brief The {@link Credit} objects that decribe the attributions that * are required when using the image. */ - std::vector credits{}; + std::vector credits{}; /** * @brief Error messages from loading the image. @@ -86,7 +85,7 @@ struct LoadTileImageFromUrlOptions { * This property is copied verbatim to the * {@link LoadedRasterOverlayImage::credits} property. */ - std::vector credits{}; + std::vector credits{}; /** * @brief Whether more detailed data, beyond this image, is available within @@ -120,7 +119,7 @@ struct LoadTileImageFromUrlOptions { * Instances of this class must be allocated on the heap, and their lifetimes * must be managed with {@link CesiumUtility::IntrusivePointer}. */ -class CESIUM3DTILESSELECTION_API RasterOverlayTileProvider +class CESIUMRASTEROVERLAYS_API RasterOverlayTileProvider : public CesiumUtility::ReferenceCountedNonThreadSafe< RasterOverlayTileProvider> { public: @@ -160,8 +159,8 @@ class CESIUM3DTILESSELECTION_API RasterOverlayTileProvider const CesiumUtility::IntrusivePointer& pOwner, const CesiumAsync::AsyncSystem& asyncSystem, const std::shared_ptr& pAssetAccessor, - std::optional credit, - const std::shared_ptr& + std::optional credit, + const std::shared_ptr& pPrepareRendererResources, const std::shared_ptr& pLogger, const CesiumGeospatial::Projection& projection, @@ -218,7 +217,7 @@ class CESIUM3DTILESSELECTION_API RasterOverlayTileProvider * @brief Gets the interface used to prepare raster overlay images for * rendering. */ - const std::shared_ptr& + const std::shared_ptr& getPrepareRendererResources() const noexcept { return this->_pPrepareRendererResources; } @@ -297,7 +296,9 @@ class CESIUM3DTILESSELECTION_API RasterOverlayTileProvider /** * @brief Get the per-TileProvider {@link Credit} if one exists. */ - const std::optional& getCredit() const noexcept { return _credit; } + const std::optional& getCredit() const noexcept { + return _credit; + } /** * @brief Loads a tile immediately, without throttling requests. @@ -389,8 +390,9 @@ class CESIUM3DTILESSELECTION_API RasterOverlayTileProvider CesiumUtility::IntrusivePointer _pOwner; CesiumAsync::AsyncSystem _asyncSystem; std::shared_ptr _pAssetAccessor; - std::optional _credit; - std::shared_ptr _pPrepareRendererResources; + std::optional _credit; + std::shared_ptr + _pPrepareRendererResources; std::shared_ptr _pLogger; CesiumGeospatial::Projection _projection; CesiumGeometry::Rectangle _coverageRectangle; @@ -404,4 +406,4 @@ class CESIUM3DTILESSELECTION_API RasterOverlayTileProvider static CesiumGltfReader::GltfReader _gltfReader; }; -} // namespace Cesium3DTilesSelection +} // namespace CesiumRasterOverlays diff --git a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/RasterizedPolygonsOverlay.h b/CesiumRasterOverlays/include/CesiumRasterOverlays/RasterizedPolygonsOverlay.h similarity index 84% rename from Cesium3DTilesSelection/include/Cesium3DTilesSelection/RasterizedPolygonsOverlay.h rename to CesiumRasterOverlays/include/CesiumRasterOverlays/RasterizedPolygonsOverlay.h index 589d4dd0f..1ff6d6308 100644 --- a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/RasterizedPolygonsOverlay.h +++ b/CesiumRasterOverlays/include/CesiumRasterOverlays/RasterizedPolygonsOverlay.h @@ -15,9 +15,9 @@ #include #include -namespace Cesium3DTilesSelection { +namespace CesiumRasterOverlays { -class CESIUM3DTILESSELECTION_API RasterizedPolygonsOverlay final +class CESIUMRASTEROVERLAYS_API RasterizedPolygonsOverlay final : public RasterOverlay { public: @@ -33,8 +33,8 @@ class CESIUM3DTILESSELECTION_API RasterizedPolygonsOverlay final virtual CesiumAsync::Future createTileProvider( const CesiumAsync::AsyncSystem& asyncSystem, const std::shared_ptr& pAssetAccessor, - const std::shared_ptr& pCreditSystem, - const std::shared_ptr& + const std::shared_ptr& pCreditSystem, + const std::shared_ptr& pPrepareRendererResources, const std::shared_ptr& pLogger, CesiumUtility::IntrusivePointer pOwner) @@ -53,4 +53,4 @@ class CESIUM3DTILESSELECTION_API RasterizedPolygonsOverlay final CesiumGeospatial::Ellipsoid _ellipsoid; CesiumGeospatial::Projection _projection; }; -} // namespace Cesium3DTilesSelection +} // namespace CesiumRasterOverlays diff --git a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/TileMapServiceRasterOverlay.h b/CesiumRasterOverlays/include/CesiumRasterOverlays/TileMapServiceRasterOverlay.h similarity index 93% rename from Cesium3DTilesSelection/include/Cesium3DTilesSelection/TileMapServiceRasterOverlay.h rename to CesiumRasterOverlays/include/CesiumRasterOverlays/TileMapServiceRasterOverlay.h index 0268dc1a5..fda3b78f1 100644 --- a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/TileMapServiceRasterOverlay.h +++ b/CesiumRasterOverlays/include/CesiumRasterOverlays/TileMapServiceRasterOverlay.h @@ -12,9 +12,7 @@ #include #include -namespace Cesium3DTilesSelection { - -class CreditSystem; +namespace CesiumRasterOverlays { /** * @brief Options for tile map service accesses. @@ -96,7 +94,7 @@ struct TileMapServiceRasterOverlayOptions { /** * @brief A {@link RasterOverlay} based on tile map service imagery. */ -class CESIUM3DTILESSELECTION_API TileMapServiceRasterOverlay final +class CESIUMRASTEROVERLAYS_API TileMapServiceRasterOverlay final : public RasterOverlay { public: /** @@ -120,8 +118,8 @@ class CESIUM3DTILESSELECTION_API TileMapServiceRasterOverlay final virtual CesiumAsync::Future createTileProvider( const CesiumAsync::AsyncSystem& asyncSystem, const std::shared_ptr& pAssetAccessor, - const std::shared_ptr& pCreditSystem, - const std::shared_ptr& + const std::shared_ptr& pCreditSystem, + const std::shared_ptr& pPrepareRendererResources, const std::shared_ptr& pLogger, CesiumUtility::IntrusivePointer pOwner) @@ -133,4 +131,4 @@ class CESIUM3DTILESSELECTION_API TileMapServiceRasterOverlay final TileMapServiceRasterOverlayOptions _options; }; -} // namespace Cesium3DTilesSelection +} // namespace CesiumRasterOverlays diff --git a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/WebMapServiceRasterOverlay.h b/CesiumRasterOverlays/include/CesiumRasterOverlays/WebMapServiceRasterOverlay.h similarity index 90% rename from Cesium3DTilesSelection/include/Cesium3DTilesSelection/WebMapServiceRasterOverlay.h rename to CesiumRasterOverlays/include/CesiumRasterOverlays/WebMapServiceRasterOverlay.h index 2d5402da9..8a01d1860 100644 --- a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/WebMapServiceRasterOverlay.h +++ b/CesiumRasterOverlays/include/CesiumRasterOverlays/WebMapServiceRasterOverlay.h @@ -9,12 +9,9 @@ #include #include -#include #include -namespace Cesium3DTilesSelection { - -class CreditSystem; +namespace CesiumRasterOverlays { /** * @brief Options for Web Map Service (WMS) overlays. @@ -70,7 +67,7 @@ struct WebMapServiceRasterOverlayOptions { /** * @brief A {@link RasterOverlay} accessing images from a Web Map Service (WMS) server. */ -class CESIUM3DTILESSELECTION_API WebMapServiceRasterOverlay final +class CESIUMRASTEROVERLAYS_API WebMapServiceRasterOverlay final : public RasterOverlay { public: /** @@ -94,8 +91,8 @@ class CESIUM3DTILESSELECTION_API WebMapServiceRasterOverlay final virtual CesiumAsync::Future createTileProvider( const CesiumAsync::AsyncSystem& asyncSystem, const std::shared_ptr& pAssetAccessor, - const std::shared_ptr& pCreditSystem, - const std::shared_ptr& + const std::shared_ptr& pCreditSystem, + const std::shared_ptr& pPrepareRendererResources, const std::shared_ptr& pLogger, CesiumUtility::IntrusivePointer pOwner) @@ -107,4 +104,4 @@ class CESIUM3DTILESSELECTION_API WebMapServiceRasterOverlay final WebMapServiceRasterOverlayOptions _options; }; -} // namespace Cesium3DTilesSelection +} // namespace CesiumRasterOverlays diff --git a/Cesium3DTilesSelection/src/BingMapsRasterOverlay.cpp b/CesiumRasterOverlays/src/BingMapsRasterOverlay.cpp similarity index 95% rename from Cesium3DTilesSelection/src/BingMapsRasterOverlay.cpp rename to CesiumRasterOverlays/src/BingMapsRasterOverlay.cpp index 8073e7836..fca88d623 100644 --- a/Cesium3DTilesSelection/src/BingMapsRasterOverlay.cpp +++ b/CesiumRasterOverlays/src/BingMapsRasterOverlay.cpp @@ -1,18 +1,14 @@ -#include "Cesium3DTilesSelection/BingMapsRasterOverlay.h" - -#include "Cesium3DTilesSelection/CreditSystem.h" -#include "Cesium3DTilesSelection/QuadtreeRasterOverlayTileProvider.h" -#include "Cesium3DTilesSelection/RasterOverlayLoadFailureDetails.h" -#include "Cesium3DTilesSelection/RasterOverlayTile.h" -#include "Cesium3DTilesSelection/RasterOverlayTileProvider.h" -#include "Cesium3DTilesSelection/TilesetExternals.h" -#include "Cesium3DTilesSelection/spdlog-cesium.h" - #include #include #include #include #include +#include +#include +#include +#include +#include +#include #include #include #include @@ -25,27 +21,27 @@ #include #include +using namespace CesiumAsync; +using namespace CesiumGeometry; +using namespace CesiumGeospatial; +using namespace CesiumUtility; + namespace { struct CoverageArea { - CesiumGeospatial::GlobeRectangle rectangle; + GlobeRectangle rectangle; uint32_t zoomMin; uint32_t zoomMax; }; struct CreditAndCoverageAreas { - Cesium3DTilesSelection::Credit credit; + Credit credit; std::vector coverageAreas; }; std::unordered_map> sessionCache; } // namespace -using namespace CesiumAsync; -using namespace CesiumGeometry; -using namespace CesiumGeospatial; -using namespace CesiumUtility; - -namespace Cesium3DTilesSelection { +namespace CesiumRasterOverlays { const std::string BingMapsStyle::AERIAL = "Aerial"; const std::string BingMapsStyle::AERIAL_WITH_LABELS = "AerialWithLabels"; @@ -93,7 +89,7 @@ class BingMapsTileProvider final : public QuadtreeRasterOverlayTileProvider { const std::shared_ptr& pAssetAccessor, Credit bingCredit, const std::vector& perTileCredits, - const std::shared_ptr& + const std::shared_ptr& pPrepareRendererResources, const std::shared_ptr& pLogger, const std::string& baseUrl, @@ -339,7 +335,8 @@ BingMapsRasterOverlay::createTileProvider( const AsyncSystem& asyncSystem, const std::shared_ptr& pAssetAccessor, const std::shared_ptr& pCreditSystem, - const std::shared_ptr& pPrepareRendererResources, + const std::shared_ptr& + pPrepareRendererResources, const std::shared_ptr& pLogger, IntrusivePointer pOwner) const { std::string metadataUrl = CesiumUtility::Uri::resolve( @@ -477,4 +474,4 @@ BingMapsRasterOverlay::createTileProvider( }); } -} // namespace Cesium3DTilesSelection +} // namespace CesiumRasterOverlays diff --git a/Cesium3DTilesSelection/src/DebugColorizeTilesRasterOverlay.cpp b/CesiumRasterOverlays/src/DebugColorizeTilesRasterOverlay.cpp similarity index 87% rename from Cesium3DTilesSelection/src/DebugColorizeTilesRasterOverlay.cpp rename to CesiumRasterOverlays/src/DebugColorizeTilesRasterOverlay.cpp index 69053c39e..77d37b688 100644 --- a/Cesium3DTilesSelection/src/DebugColorizeTilesRasterOverlay.cpp +++ b/CesiumRasterOverlays/src/DebugColorizeTilesRasterOverlay.cpp @@ -1,11 +1,10 @@ -#include "Cesium3DTilesSelection/DebugColorizeTilesRasterOverlay.h" - -#include "Cesium3DTilesSelection/RasterOverlayTileProvider.h" - #include +#include +#include +#include #include -using namespace Cesium3DTilesSelection; +using namespace CesiumRasterOverlays; using namespace CesiumGeospatial; using namespace CesiumGltf; using namespace CesiumUtility; @@ -18,7 +17,7 @@ class DebugTileProvider : public RasterOverlayTileProvider { const IntrusivePointer& pOwner, const CesiumAsync::AsyncSystem& asyncSystem, const std::shared_ptr& pAssetAccessor, - const std::shared_ptr& + const std::shared_ptr& pPrepareRendererResources, const std::shared_ptr& pLogger) : RasterOverlayTileProvider( @@ -74,7 +73,8 @@ DebugColorizeTilesRasterOverlay::createTileProvider( const CesiumAsync::AsyncSystem& asyncSystem, const std::shared_ptr& pAssetAccessor, const std::shared_ptr& /* pCreditSystem */, - const std::shared_ptr& pPrepareRendererResources, + const std::shared_ptr& + pPrepareRendererResources, const std::shared_ptr& pLogger, CesiumUtility::IntrusivePointer pOwner) const { pOwner = pOwner ? pOwner : this; diff --git a/Cesium3DTilesSelection/src/IonRasterOverlay.cpp b/CesiumRasterOverlays/src/IonRasterOverlay.cpp similarity index 92% rename from Cesium3DTilesSelection/src/IonRasterOverlay.cpp rename to CesiumRasterOverlays/src/IonRasterOverlay.cpp index 87d06bff1..196ab96d9 100644 --- a/Cesium3DTilesSelection/src/IonRasterOverlay.cpp +++ b/CesiumRasterOverlays/src/IonRasterOverlay.cpp @@ -1,24 +1,21 @@ -#include "Cesium3DTilesSelection/IonRasterOverlay.h" - -#include "Cesium3DTilesSelection/BingMapsRasterOverlay.h" -#include "Cesium3DTilesSelection/RasterOverlayLoadFailureDetails.h" -#include "Cesium3DTilesSelection/RasterOverlayTile.h" -#include "Cesium3DTilesSelection/RasterOverlayTileProvider.h" -#include "Cesium3DTilesSelection/TileMapServiceRasterOverlay.h" -#include "Cesium3DTilesSelection/TilesetExternals.h" -#include "Cesium3DTilesSelection/spdlog-cesium.h" - #include #include +#include +#include +#include +#include +#include +#include #include #include #include +#include using namespace CesiumAsync; using namespace CesiumUtility; -namespace Cesium3DTilesSelection { +namespace CesiumRasterOverlays { IonRasterOverlay::IonRasterOverlay( const std::string& name, @@ -42,7 +39,8 @@ IonRasterOverlay::createTileProvider( const CesiumAsync::AsyncSystem& asyncSystem, const std::shared_ptr& pAssetAccessor, const std::shared_ptr& pCreditSystem, - const std::shared_ptr& pPrepareRendererResources, + const std::shared_ptr& + pPrepareRendererResources, const std::shared_ptr& pLogger, CesiumUtility::IntrusivePointer pOwner) const { IntrusivePointer pOverlay = nullptr; @@ -84,7 +82,8 @@ IonRasterOverlay::createTileProvider( const CesiumAsync::AsyncSystem& asyncSystem, const std::shared_ptr& pAssetAccessor, const std::shared_ptr& pCreditSystem, - const std::shared_ptr& pPrepareRendererResources, + const std::shared_ptr& + pPrepareRendererResources, const std::shared_ptr& pLogger, CesiumUtility::IntrusivePointer pOwner) const { std::string ionUrl = this->_ionAssetEndpointUrl + "v1/assets/" + @@ -231,4 +230,4 @@ IonRasterOverlay::createTileProvider( } }); } -} // namespace Cesium3DTilesSelection +} // namespace CesiumRasterOverlays diff --git a/Cesium3DTilesSelection/src/QuadtreeRasterOverlayTileProvider.cpp b/CesiumRasterOverlays/src/QuadtreeRasterOverlayTileProvider.cpp similarity index 98% rename from Cesium3DTilesSelection/src/QuadtreeRasterOverlayTileProvider.cpp rename to CesiumRasterOverlays/src/QuadtreeRasterOverlayTileProvider.cpp index ed2ebb197..538618e0f 100644 --- a/Cesium3DTilesSelection/src/QuadtreeRasterOverlayTileProvider.cpp +++ b/CesiumRasterOverlays/src/QuadtreeRasterOverlayTileProvider.cpp @@ -1,9 +1,8 @@ -#include "Cesium3DTilesSelection/QuadtreeRasterOverlayTileProvider.h" - -#include "Cesium3DTilesSelection/RasterOverlay.h" - #include #include +#include +#include +#include #include #include @@ -22,14 +21,15 @@ constexpr double pixelTolerance = 0.01; } // namespace -namespace Cesium3DTilesSelection { +namespace CesiumRasterOverlays { QuadtreeRasterOverlayTileProvider::QuadtreeRasterOverlayTileProvider( const IntrusivePointer& pOwner, const CesiumAsync::AsyncSystem& asyncSystem, const std::shared_ptr& pAssetAccessor, std::optional credit, - const std::shared_ptr& pPrepareRendererResources, + const std::shared_ptr& + pPrepareRendererResources, const std::shared_ptr& pLogger, const CesiumGeospatial::Projection& projection, const CesiumGeometry::QuadtreeTilingScheme& tilingScheme, @@ -732,4 +732,4 @@ QuadtreeRasterOverlayTileProvider::combineImages( return result; } -} // namespace Cesium3DTilesSelection +} // namespace CesiumRasterOverlays diff --git a/Cesium3DTilesSelection/src/RasterOverlay.cpp b/CesiumRasterOverlays/src/RasterOverlay.cpp similarity index 86% rename from Cesium3DTilesSelection/src/RasterOverlay.cpp rename to CesiumRasterOverlays/src/RasterOverlay.cpp index 8f91760fc..7f43aa666 100644 --- a/Cesium3DTilesSelection/src/RasterOverlay.cpp +++ b/CesiumRasterOverlays/src/RasterOverlay.cpp @@ -1,12 +1,11 @@ -#include "Cesium3DTilesSelection/RasterOverlay.h" +#include +#include +#include -#include "Cesium3DTilesSelection/RasterOverlayCollection.h" -#include "Cesium3DTilesSelection/RasterOverlayLoadFailureDetails.h" -#include "Cesium3DTilesSelection/RasterOverlayTileProvider.h" -#include "Cesium3DTilesSelection/spdlog-cesium.h" +#include using namespace CesiumAsync; -using namespace Cesium3DTilesSelection; +using namespace CesiumRasterOverlays; using namespace CesiumUtility; namespace { diff --git a/Cesium3DTilesSelection/src/RasterOverlayTile.cpp b/CesiumRasterOverlays/src/RasterOverlayTile.cpp similarity index 83% rename from Cesium3DTilesSelection/src/RasterOverlayTile.cpp rename to CesiumRasterOverlays/src/RasterOverlayTile.cpp index 9ada54d4a..722ef553b 100644 --- a/Cesium3DTilesSelection/src/RasterOverlayTile.cpp +++ b/CesiumRasterOverlays/src/RasterOverlayTile.cpp @@ -1,17 +1,14 @@ -#include "Cesium3DTilesSelection/RasterOverlayTile.h" - -#include "Cesium3DTilesSelection/IPrepareRendererResources.h" -#include "Cesium3DTilesSelection/RasterOverlay.h" -#include "Cesium3DTilesSelection/RasterOverlayTileProvider.h" -#include "Cesium3DTilesSelection/TilesetExternals.h" - #include #include +#include +#include +#include +#include #include using namespace CesiumAsync; -namespace Cesium3DTilesSelection { +namespace CesiumRasterOverlays { RasterOverlayTile::RasterOverlayTile( RasterOverlayTileProvider& tileProvider) noexcept @@ -42,8 +39,8 @@ RasterOverlayTile::~RasterOverlayTile() { tileProvider.removeTile(this); - const std::shared_ptr& pPrepareRendererResources = - tileProvider.getPrepareRendererResources(); + const std::shared_ptr& + pPrepareRendererResources = tileProvider.getPrepareRendererResources(); if (pPrepareRendererResources) { void* pLoadThreadResult = @@ -92,4 +89,4 @@ void RasterOverlayTile::setState(LoadState newState) noexcept { this->_state = newState; } -} // namespace Cesium3DTilesSelection +} // namespace CesiumRasterOverlays diff --git a/Cesium3DTilesSelection/src/RasterOverlayTileProvider.cpp b/CesiumRasterOverlays/src/RasterOverlayTileProvider.cpp similarity index 93% rename from Cesium3DTilesSelection/src/RasterOverlayTileProvider.cpp rename to CesiumRasterOverlays/src/RasterOverlayTileProvider.cpp index 221551da0..d2b8d71b0 100644 --- a/Cesium3DTilesSelection/src/RasterOverlayTileProvider.cpp +++ b/CesiumRasterOverlays/src/RasterOverlayTileProvider.cpp @@ -1,17 +1,14 @@ -#include "Cesium3DTilesSelection/RasterOverlayTileProvider.h" - -#include "Cesium3DTilesSelection/IPrepareRendererResources.h" -#include "Cesium3DTilesSelection/RasterOverlay.h" -#include "Cesium3DTilesSelection/RasterOverlayTile.h" -#include "Cesium3DTilesSelection/TileID.h" -#include "Cesium3DTilesSelection/TilesetExternals.h" -#include "Cesium3DTilesSelection/spdlog-cesium.h" - #include #include +#include +#include +#include +#include #include #include +#include + using namespace CesiumAsync; using namespace CesiumGeometry; using namespace CesiumGeospatial; @@ -19,7 +16,7 @@ using namespace CesiumGltf; using namespace CesiumGltfReader; using namespace CesiumUtility; -namespace Cesium3DTilesSelection { +namespace CesiumRasterOverlays { /*static*/ CesiumGltfReader::GltfReader RasterOverlayTileProvider::_gltfReader{}; @@ -49,7 +46,8 @@ RasterOverlayTileProvider::RasterOverlayTileProvider( const CesiumAsync::AsyncSystem& asyncSystem, const std::shared_ptr& pAssetAccessor, std::optional credit, - const std::shared_ptr& pPrepareRendererResources, + const std::shared_ptr& + pPrepareRendererResources, const std::shared_ptr& pLogger, const CesiumGeospatial::Projection& projection, const Rectangle& coverageRectangle) noexcept @@ -223,19 +221,20 @@ struct LoadResult { * returned. * * Otherwise, the image data will be passed to - * `IPrepareRendererResources::prepareRasterInLoadThread`, and the function - * will return a `LoadResult` with the image, the prepared renderer resources, - * and the state `RasterOverlayTile::LoadState::Loaded`. + * `IPrepareRasterOverlayRendererResources::prepareRasterInLoadThread`, and the + * function will return a `LoadResult` with the image, the prepared renderer + * resources, and the state `RasterOverlayTile::LoadState::Loaded`. * * @param tileId The {@link TileID} - only used for logging - * @param pPrepareRendererResources The `IPrepareRendererResources` + * @param pPrepareRendererResources The `IPrepareRasterOverlayRendererResources` * @param pLogger The logger * @param loadedImage The `LoadedRasterOverlayImage` * @param rendererOptions Renderer options * @return The `LoadResult` */ static LoadResult createLoadResultFromLoadedImage( - const std::shared_ptr& pPrepareRendererResources, + const std::shared_ptr& + pPrepareRendererResources, const std::shared_ptr& pLogger, LoadedRasterOverlayImage&& loadedImage, const std::any& rendererOptions) { @@ -372,4 +371,4 @@ void RasterOverlayTileProvider::finalizeTileLoad( --this->_throttledTilesCurrentlyLoading; } } -} // namespace Cesium3DTilesSelection +} // namespace CesiumRasterOverlays diff --git a/Cesium3DTilesSelection/src/RasterizedPolygonsOverlay.cpp b/CesiumRasterOverlays/src/RasterizedPolygonsOverlay.cpp similarity index 93% rename from Cesium3DTilesSelection/src/RasterizedPolygonsOverlay.cpp rename to CesiumRasterOverlays/src/RasterizedPolygonsOverlay.cpp index 0a96f8680..2e80cef3b 100644 --- a/Cesium3DTilesSelection/src/RasterizedPolygonsOverlay.cpp +++ b/CesiumRasterOverlays/src/RasterizedPolygonsOverlay.cpp @@ -1,15 +1,13 @@ -#include "Cesium3DTilesSelection/RasterizedPolygonsOverlay.h" - -#include "Cesium3DTilesSelection/BoundingVolume.h" -#include "Cesium3DTilesSelection/RasterOverlayTileProvider.h" -#include "Cesium3DTilesSelection/spdlog-cesium.h" -#include "TileUtilities.h" - #include #include #include +#include +#include +#include #include +#include + #include #include @@ -17,7 +15,7 @@ using namespace CesiumGeometry; using namespace CesiumGeospatial; using namespace CesiumUtility; -namespace Cesium3DTilesSelection { +namespace CesiumRasterOverlays { namespace { void rasterizePolygons( LoadedRasterOverlayImage& loaded, @@ -40,7 +38,7 @@ void rasterizePolygons( } // create a 1x1 mask if the rectangle is completely inside a polygon - if (Cesium3DTilesSelection::CesiumImpl::withinPolygons( + if (CartographicPolygon::rectangleIsWithinPolygons( rectangle, cartographicPolygons)) { loaded.moreDetailAvailable = false; @@ -156,7 +154,7 @@ void rasterizePolygons( } } // namespace -class CESIUM3DTILESSELECTION_API RasterizedPolygonsTileProvider final +class CESIUMRASTEROVERLAYS_API RasterizedPolygonsTileProvider final : public RasterOverlayTileProvider { private: @@ -168,7 +166,7 @@ class CESIUM3DTILESSELECTION_API RasterizedPolygonsTileProvider final const IntrusivePointer& pOwner, const CesiumAsync::AsyncSystem& asyncSystem, const std::shared_ptr& pAssetAccessor, - const std::shared_ptr& + const std::shared_ptr& pPrepareRendererResources, const std::shared_ptr& pLogger, const CesiumGeospatial::Projection& projection, @@ -246,7 +244,8 @@ RasterizedPolygonsOverlay::createTileProvider( const CesiumAsync::AsyncSystem& asyncSystem, const std::shared_ptr& pAssetAccessor, const std::shared_ptr& /*pCreditSystem*/, - const std::shared_ptr& pPrepareRendererResources, + const std::shared_ptr& + pPrepareRendererResources, const std::shared_ptr& pLogger, CesiumUtility::IntrusivePointer pOwner) const { @@ -265,4 +264,4 @@ RasterizedPolygonsOverlay::createTileProvider( this->_invertSelection))); } -} // namespace Cesium3DTilesSelection +} // namespace CesiumRasterOverlays diff --git a/Cesium3DTilesSelection/src/TileMapServiceRasterOverlay.cpp b/CesiumRasterOverlays/src/TileMapServiceRasterOverlay.cpp similarity index 96% rename from Cesium3DTilesSelection/src/TileMapServiceRasterOverlay.cpp rename to CesiumRasterOverlays/src/TileMapServiceRasterOverlay.cpp index 79a6e2868..7a9f3dbb4 100644 --- a/Cesium3DTilesSelection/src/TileMapServiceRasterOverlay.cpp +++ b/CesiumRasterOverlays/src/TileMapServiceRasterOverlay.cpp @@ -1,25 +1,23 @@ -#include "Cesium3DTilesSelection/TileMapServiceRasterOverlay.h" - -#include "Cesium3DTilesSelection/CreditSystem.h" -#include "Cesium3DTilesSelection/QuadtreeRasterOverlayTileProvider.h" -#include "Cesium3DTilesSelection/RasterOverlayLoadFailureDetails.h" -#include "Cesium3DTilesSelection/RasterOverlayTile.h" -#include "Cesium3DTilesSelection/TilesetExternals.h" -#include "Cesium3DTilesSelection/spdlog-cesium.h" -#include "Cesium3DTilesSelection/tinyxml-cesium.h" - #include #include #include #include +#include +#include +#include +#include +#include #include +#include +#include + #include using namespace CesiumAsync; using namespace CesiumUtility; -namespace Cesium3DTilesSelection { +namespace CesiumRasterOverlays { namespace { struct TileMapServiceTileset { @@ -36,7 +34,7 @@ class TileMapServiceTileProvider final const CesiumAsync::AsyncSystem& asyncSystem, const std::shared_ptr& pAssetAccessor, std::optional credit, - const std::shared_ptr& + const std::shared_ptr& pPrepareRendererResources, const std::shared_ptr& pLogger, const CesiumGeospatial::Projection& projection, @@ -263,7 +261,8 @@ TileMapServiceRasterOverlay::createTileProvider( const CesiumAsync::AsyncSystem& asyncSystem, const std::shared_ptr& pAssetAccessor, const std::shared_ptr& pCreditSystem, - const std::shared_ptr& pPrepareRendererResources, + const std::shared_ptr& + pPrepareRendererResources, const std::shared_ptr& pLogger, CesiumUtility::IntrusivePointer pOwner) const { std::string xmlUrl = this->_url; @@ -467,4 +466,4 @@ TileMapServiceRasterOverlay::createTileProvider( }); } -} // namespace Cesium3DTilesSelection +} // namespace CesiumRasterOverlays diff --git a/Cesium3DTilesSelection/src/WebMapServiceRasterOverlay.cpp b/CesiumRasterOverlays/src/WebMapServiceRasterOverlay.cpp similarity index 94% rename from Cesium3DTilesSelection/src/WebMapServiceRasterOverlay.cpp rename to CesiumRasterOverlays/src/WebMapServiceRasterOverlay.cpp index ac39d5acc..e972e80b9 100644 --- a/Cesium3DTilesSelection/src/WebMapServiceRasterOverlay.cpp +++ b/CesiumRasterOverlays/src/WebMapServiceRasterOverlay.cpp @@ -1,20 +1,17 @@ -#include "Cesium3DTilesSelection/WebMapServiceRasterOverlay.h" - -#include "Cesium3DTilesSelection/CreditSystem.h" -#include "Cesium3DTilesSelection/QuadtreeRasterOverlayTileProvider.h" -#include "Cesium3DTilesSelection/RasterOverlayLoadFailureDetails.h" -#include "Cesium3DTilesSelection/RasterOverlayTile.h" -#include "Cesium3DTilesSelection/TilesetExternals.h" -#include "Cesium3DTilesSelection/spdlog-cesium.h" -#include "Cesium3DTilesSelection/tinyxml-cesium.h" - #include #include #include #include #include +#include +#include +#include +#include +#include #include +#include + #include #include @@ -23,7 +20,7 @@ using namespace CesiumGeometry; using namespace CesiumGeospatial; using namespace CesiumUtility; -namespace Cesium3DTilesSelection { +namespace CesiumRasterOverlays { class WebMapServiceTileProvider final : public QuadtreeRasterOverlayTileProvider { @@ -33,7 +30,7 @@ class WebMapServiceTileProvider final const CesiumAsync::AsyncSystem& asyncSystem, const std::shared_ptr& pAssetAccessor, std::optional credit, - const std::shared_ptr& + const std::shared_ptr& pPrepareRendererResources, const std::shared_ptr& pLogger, const CesiumGeospatial::Projection& projection, @@ -247,7 +244,8 @@ WebMapServiceRasterOverlay::createTileProvider( const CesiumAsync::AsyncSystem& asyncSystem, const std::shared_ptr& pAssetAccessor, const std::shared_ptr& pCreditSystem, - const std::shared_ptr& pPrepareRendererResources, + const std::shared_ptr& + pPrepareRendererResources, const std::shared_ptr& pLogger, CesiumUtility::IntrusivePointer pOwner) const { @@ -365,4 +363,4 @@ WebMapServiceRasterOverlay::createTileProvider( }); } -} // namespace Cesium3DTilesSelection +} // namespace CesiumRasterOverlays diff --git a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/CreditSystem.h b/CesiumUtility/include/CesiumUtility/CreditSystem.h similarity index 94% rename from Cesium3DTilesSelection/include/Cesium3DTilesSelection/CreditSystem.h rename to CesiumUtility/include/CesiumUtility/CreditSystem.h index 41612ca5c..138a33c0d 100644 --- a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/CreditSystem.h +++ b/CesiumUtility/include/CesiumUtility/CreditSystem.h @@ -7,14 +7,14 @@ #include #include -namespace Cesium3DTilesSelection { +namespace CesiumUtility { /** * @brief Represents an HTML string that should be shown on screen to attribute * third parties for used data, imagery, etc. Acts as a handle into a * {@link CreditSystem} object that actually holds the credit string. */ -struct CESIUM3DTILESSELECTION_API Credit { +struct CESIUMUTILITY_API Credit { public: /** * @brief Returns `true` if two credit objects have the same ID. @@ -36,7 +36,7 @@ struct CESIUM3DTILESSELECTION_API Credit { * tracks which credits should be shown and which credits should be removed this * frame. */ -class CESIUM3DTILESSELECTION_API CreditSystem final { +class CESIUMUTILITY_API CreditSystem final { public: /** * @brief Inserts a credit string @@ -111,4 +111,4 @@ class CESIUM3DTILESSELECTION_API CreditSystem final { std::vector _creditsToShowThisFrame; std::vector _creditsToNoLongerShowThisFrame; }; -} // namespace Cesium3DTilesSelection +} // namespace CesiumUtility diff --git a/Cesium3DTilesSelection/src/CreditSystem.cpp b/CesiumUtility/src/CreditSystem.cpp similarity index 96% rename from Cesium3DTilesSelection/src/CreditSystem.cpp rename to CesiumUtility/src/CreditSystem.cpp index 2f8537121..b33fe85dc 100644 --- a/Cesium3DTilesSelection/src/CreditSystem.cpp +++ b/CesiumUtility/src/CreditSystem.cpp @@ -1,8 +1,8 @@ -#include "Cesium3DTilesSelection/CreditSystem.h" +#include #include -namespace Cesium3DTilesSelection { +namespace CesiumUtility { Credit CreditSystem::createCredit(const std::string& html, bool showOnScreen) { return this->createCredit(std::string(html), showOnScreen); @@ -96,4 +96,4 @@ const std::vector& CreditSystem::getCreditsToShowThisFrame() noexcept { }); return _creditsToShowThisFrame; } -} // namespace Cesium3DTilesSelection +} // namespace CesiumUtility From 491835825baf2260312b29c59595497f549406fb Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Tue, 14 Nov 2023 18:42:54 +1100 Subject: [PATCH 359/421] Update CHANGES.md. --- CHANGES.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 32391e08a..dea5eb28e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -4,12 +4,13 @@ ##### Breaking Changes :mega: -- Moved `ErrorList` from `Cesium3DTilesSelection` to `CesiumUtility`. +- Moved `ErrorList`, `CreditSystem`, and `Credit` from `Cesium3DTilesSelection` to `CesiumUtility`. - Moved `GltfUtilities` from `Cesium3DTilesSelection` to `Cesium3DTilesContent`. - Moved `createRasterOverlayTextureCoordinates` method from `GltfUtilities` to a new `RasterOverlayUtilities` class in the `Cesium3DTilesSelection` library. - `GltfUtilities::parseGltfCopyright` now returns the credits as a vector of string_views. Previously it took a `CreditSystem` and created credits directly. - The `SubtreeAvailability` constructor and `loadSubtree` static method now take an `ImplicitTileSubdivisionScheme` enumeration parameter instead of a `powerOf2` parameter. They also now require a `levelsInSubtree` parameter, which is needed when switching from constant to bitstream availability. Lastly, the constructor now takes a `Subtree` parameter instead of a `std::vector>` representing the buffers. - `SubtreeConstantAvailability`, `SubtreeBufferViewAvailability`, and `AvailabilityView` are now members of `SubtreeAvailability`. +- Moved `RasterOverlay`, `RasterOverlayTileProvider`, `RasterOverlayTile`, `QuadtreeRasterOverlayTileProvider`, and all of the `RasterOverlay`-derived types to a new `CesiumRasterOverlays` library and namespace. ##### Additions :tada: @@ -23,6 +24,9 @@ - Added `fromSubtree` and `createEmpty` static methods to `SubtreeAvailability`. - Added new `set` methods to `SubtreeAvailability`, allowing the availability information to be modified. - Added `SubtreeFileReader` class, used to read `Cesium3DTiles::Subtree` from a binary or JSON subtree file. +- Added `pointInTriangle2D` static method to `CesiumGeometry::IntersectionTests`. +- Added `rectangleIsWithinPolygons` and `rectangleIsOutsidePolygons` static methods to `CartographicPolygon`. +- Raster overlays now use `IPrepareRasterOverlayRendererResources`, which contains only overlay-related methods, instead of `IPrepareRendererResources`, which contains tileset-related methods as well. `IPrepareRendererResources` derives from `IPrepareRasterOverlayRendererResources` so existing code should continue to work without modification. ##### Fixes :wrench: From 84c48747aeff85c288a5b45002ed2f2507e18eda Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Tue, 14 Nov 2023 18:57:47 +1100 Subject: [PATCH 360/421] Move overlay tests. --- CesiumNativeTests/CMakeLists.txt | 1 + .../test/TestQuadtreeRasterOverlayTileProvider.cpp | 0 2 files changed, 1 insertion(+) rename {Cesium3DTilesSelection => CesiumRasterOverlays}/test/TestQuadtreeRasterOverlayTileProvider.cpp (100%) diff --git a/CesiumNativeTests/CMakeLists.txt b/CesiumNativeTests/CMakeLists.txt index f2c5bba27..87930a178 100644 --- a/CesiumNativeTests/CMakeLists.txt +++ b/CesiumNativeTests/CMakeLists.txt @@ -16,6 +16,7 @@ set(cesium_native_targets CesiumGltfReader CesiumGltfWriter CesiumIonClient + CesiumRasterOverlays CesiumUtility ) diff --git a/Cesium3DTilesSelection/test/TestQuadtreeRasterOverlayTileProvider.cpp b/CesiumRasterOverlays/test/TestQuadtreeRasterOverlayTileProvider.cpp similarity index 100% rename from Cesium3DTilesSelection/test/TestQuadtreeRasterOverlayTileProvider.cpp rename to CesiumRasterOverlays/test/TestQuadtreeRasterOverlayTileProvider.cpp From e7293c0fa21c31fb4c7083a055320abddfcd0441 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Tue, 14 Nov 2023 19:15:19 +1100 Subject: [PATCH 361/421] Fix RasterizedPolygonsTileExcluder. --- .../RasterizedPolygonsTileExcluder.h | 15 ++++++++++----- .../src/RasterizedPolygonsTileExcluder.cpp | 5 +++-- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/RasterizedPolygonsTileExcluder.h b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/RasterizedPolygonsTileExcluder.h index 7ee8c22de..4d15c62c8 100644 --- a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/RasterizedPolygonsTileExcluder.h +++ b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/RasterizedPolygonsTileExcluder.h @@ -5,9 +5,11 @@ #include -namespace Cesium3DTilesSelection { - +namespace CesiumRasterOverlays { class RasterizedPolygonsOverlay; +} + +namespace Cesium3DTilesSelection { /** * @brief When provided to {@link TilesetOptions::excluders}, uses the polygons @@ -24,7 +26,8 @@ class CESIUM3DTILESSELECTION_API RasterizedPolygonsTileExcluder * @param overlay The overlay definining the polygons. */ RasterizedPolygonsTileExcluder( - const CesiumUtility::IntrusivePointer& + const CesiumUtility::IntrusivePointer< + const CesiumRasterOverlays::RasterizedPolygonsOverlay>& pOverlay) noexcept; /** @@ -40,10 +43,12 @@ class CESIUM3DTILESSELECTION_API RasterizedPolygonsTileExcluder /** * @brief Gets the overlay defining the polygons. */ - const RasterizedPolygonsOverlay& getOverlay() const; + const CesiumRasterOverlays::RasterizedPolygonsOverlay& getOverlay() const; private: - CesiumUtility::IntrusivePointer _pOverlay; + CesiumUtility::IntrusivePointer< + const CesiumRasterOverlays::RasterizedPolygonsOverlay> + _pOverlay; }; } // namespace Cesium3DTilesSelection diff --git a/Cesium3DTilesSelection/src/RasterizedPolygonsTileExcluder.cpp b/Cesium3DTilesSelection/src/RasterizedPolygonsTileExcluder.cpp index 0d8d5f8f7..ea5c56762 100644 --- a/Cesium3DTilesSelection/src/RasterizedPolygonsTileExcluder.cpp +++ b/Cesium3DTilesSelection/src/RasterizedPolygonsTileExcluder.cpp @@ -1,13 +1,14 @@ #include "Cesium3DTilesSelection/RasterizedPolygonsTileExcluder.h" -#include "Cesium3DTilesSelection/RasterizedPolygonsOverlay.h" #include "Cesium3DTilesSelection/Tile.h" +#include "CesiumRasterOverlays/RasterizedPolygonsOverlay.h" #include "TileUtilities.h" using namespace Cesium3DTilesSelection; RasterizedPolygonsTileExcluder::RasterizedPolygonsTileExcluder( - const CesiumUtility::IntrusivePointer& + const CesiumUtility::IntrusivePointer< + const CesiumRasterOverlays::RasterizedPolygonsOverlay>& pOverlay) noexcept : _pOverlay(pOverlay) {} From 840588a21425dd1e890f8bc3c42c72e48a6de1a9 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Tue, 14 Nov 2023 19:27:05 +1100 Subject: [PATCH 362/421] Mark system libraries. --- CesiumRasterOverlays/CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CesiumRasterOverlays/CMakeLists.txt b/CesiumRasterOverlays/CMakeLists.txt index 58a6d3e02..8bb9d78c7 100644 --- a/CesiumRasterOverlays/CMakeLists.txt +++ b/CesiumRasterOverlays/CMakeLists.txt @@ -45,6 +45,10 @@ target_link_libraries(CesiumRasterOverlays CesiumGltf CesiumGltfReader CesiumUtility +) + +target_link_libraries_system( + Cesium3DTilesContent PRIVATE expected-lite tinyxml2 From 2871d8e25dc410c0a90e0a39b657eff23aeed0b5 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Tue, 14 Nov 2023 19:31:14 +1100 Subject: [PATCH 363/421] Fix typo. --- CesiumRasterOverlays/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CesiumRasterOverlays/CMakeLists.txt b/CesiumRasterOverlays/CMakeLists.txt index 8bb9d78c7..7d30c4d86 100644 --- a/CesiumRasterOverlays/CMakeLists.txt +++ b/CesiumRasterOverlays/CMakeLists.txt @@ -48,7 +48,7 @@ target_link_libraries(CesiumRasterOverlays ) target_link_libraries_system( - Cesium3DTilesContent + CesiumRasterOverlays PRIVATE expected-lite tinyxml2 From 43979f64172775238136cdd5d75c5a86761dc6a0 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Tue, 14 Nov 2023 22:16:01 +1100 Subject: [PATCH 364/421] Return a future from RasterOverlayTileProvider::loadTile. --- .../RasterOverlayTileProvider.h | 6 ++++-- .../src/RasterOverlayTileProvider.cpp | 20 +++++++++++++------ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/CesiumRasterOverlays/include/CesiumRasterOverlays/RasterOverlayTileProvider.h b/CesiumRasterOverlays/include/CesiumRasterOverlays/RasterOverlayTileProvider.h index d23bae609..d07b07670 100644 --- a/CesiumRasterOverlays/include/CesiumRasterOverlays/RasterOverlayTileProvider.h +++ b/CesiumRasterOverlays/include/CesiumRasterOverlays/RasterOverlayTileProvider.h @@ -314,7 +314,8 @@ class CESIUMRASTEROVERLAYS_API RasterOverlayTileProvider * * @param tile The tile to load. */ - void loadTile(RasterOverlayTile& tile); + CesiumAsync::Future> + loadTile(RasterOverlayTile& tile); /** * @brief Loads a tile, unless there are too many tile loads already in @@ -365,7 +366,8 @@ class CESIUMRASTEROVERLAYS_API RasterOverlayTileProvider LoadTileImageFromUrlOptions&& options = {}) const; private: - void doLoad(RasterOverlayTile& tile, bool isThrottledLoad); + CesiumAsync::Future> + doLoad(RasterOverlayTile& tile, bool isThrottledLoad); /** * @brief Begins the process of loading of a tile. diff --git a/CesiumRasterOverlays/src/RasterOverlayTileProvider.cpp b/CesiumRasterOverlays/src/RasterOverlayTileProvider.cpp index d2b8d71b0..8dc4c416d 100644 --- a/CesiumRasterOverlays/src/RasterOverlayTileProvider.cpp +++ b/CesiumRasterOverlays/src/RasterOverlayTileProvider.cpp @@ -94,13 +94,15 @@ void RasterOverlayTileProvider::removeTile(RasterOverlayTile* pTile) noexcept { this->_tileDataBytes -= int64_t(pTile->getImage().pixelData.size()); } -void RasterOverlayTileProvider::loadTile(RasterOverlayTile& tile) { +CesiumAsync::Future> +RasterOverlayTileProvider::loadTile(RasterOverlayTile& tile) { if (this->_pPlaceholder) { // Refuse to load placeholders. - return; + return this->getAsyncSystem() + .createResolvedFuture>(nullptr); } - this->doLoad(tile, false); + return this->doLoad(tile, false); } bool RasterOverlayTileProvider::loadTileThrottled(RasterOverlayTile& tile) { @@ -296,12 +298,14 @@ static LoadResult createLoadResultFromLoadedImage( } // namespace -void RasterOverlayTileProvider::doLoad( +CesiumAsync::Future> +RasterOverlayTileProvider::doLoad( RasterOverlayTile& tile, bool isThrottledLoad) { if (tile.getState() != RasterOverlayTile::LoadState::Unloaded) { // Already loading or loaded, do nothing. - return; + return this->getAsyncSystem() + .createResolvedFuture>(nullptr); } // CESIUM_TRACE_USE_TRACK_SET(this->_loadingSlots); @@ -316,7 +320,7 @@ void RasterOverlayTileProvider::doLoad( IntrusivePointer pTile = &tile; IntrusivePointer thiz = this; - this->loadTileImage(tile) + return this->loadTileImage(tile) .thenInWorkerThread( [pPrepareRendererResources = this->getPrepareRendererResources(), pLogger = this->getLogger(), @@ -343,6 +347,8 @@ void RasterOverlayTileProvider::doLoad( thiz->_tileDataBytes += int64_t(pTile->getImage().pixelData.size()); thiz->finalizeTileLoad(isThrottledLoad); + + return pTile; }) .catchInMainThread( [thiz, pTile, isThrottledLoad](const std::exception& /*e*/) { @@ -354,6 +360,8 @@ void RasterOverlayTileProvider::doLoad( pTile->setState(RasterOverlayTile::LoadState::Failed); thiz->finalizeTileLoad(isThrottledLoad); + + return pTile; }); } From e19fa4dba2bf03bd7584c0a854d3d0032363621a Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Tue, 14 Nov 2023 11:48:09 -0500 Subject: [PATCH 365/421] Move accessor utility to cesium-native --- .../include/CesiumGltf/AccessorUtility.h | 182 ++++++++ CesiumGltf/src/AccessorUtility.cpp | 44 ++ CesiumGltf/test/TestAccessorUtility.cpp | 400 ++++++++++++++++++ 3 files changed, 626 insertions(+) create mode 100644 CesiumGltf/include/CesiumGltf/AccessorUtility.h create mode 100644 CesiumGltf/src/AccessorUtility.cpp create mode 100644 CesiumGltf/test/TestAccessorUtility.cpp diff --git a/CesiumGltf/include/CesiumGltf/AccessorUtility.h b/CesiumGltf/include/CesiumGltf/AccessorUtility.h new file mode 100644 index 000000000..3c60be2c7 --- /dev/null +++ b/CesiumGltf/include/CesiumGltf/AccessorUtility.h @@ -0,0 +1,182 @@ +#pragma once + +#include "AccessorView.h" + +#include + +#include + +namespace CesiumGltf { +/** + * @brief Type definition for a position accessor. + */ +typedef AccessorView> PositionAccessorType; + +/** + * @brief Visitor that retrieves the count of elements in the given accessor + * type as an int64_t. + */ +struct CountFromAccessor { + int64_t operator()(std::monostate) { return 0; } + + template int64_t operator()(const AccessorView& value) { + return value.size(); + } +}; + +/** + * Type definition for all kinds of feature ID attribute accessors. + */ +typedef std::variant< + AccessorView, + AccessorView, + AccessorView, + AccessorView, + AccessorView, + AccessorView> + FeatureIdAccessorType; + +/** + * Visitor that retrieves the feature ID from the given accessor type as an + * int64_t. This should be initialized with the index of the vertex whose + * feature ID is being queried. + * + * -1 is used to indicate errors retrieving the feature ID, e.g., if the given + * index was out-of-bounds. + */ +struct FeatureIdFromAccessor { + int64_t operator()(const AccessorView& value) { + if (index < 0 || index >= value.size()) { + return -1; + } + return static_cast(glm::round(value[index])); + } + + template int64_t operator()(const AccessorView& value) { + if (index < 0 || index >= value.size()) { + return -1; + } + return static_cast(value[index]); + } + + int64_t index; +}; + +/** + * Type definition for all kinds of index accessors. std::monostate + * indicates a nonexistent accessor, which can happen (and is valid) if the + * primitive vertices are defined without an index buffer. + */ +typedef std::variant< + std::monostate, + AccessorView, + AccessorView, + AccessorView> + IndexAccessorType; + +/** + * Visitor that retrieves the vertex indices from the given accessor type + * corresponding to a given face index. These indices are returned as an array + * of int64_ts. This should be initialized with the index of the face and the + * total number of vertices in the primitive. + * + * -1 is used to indicate errors retrieving the index, e.g., if the given + * index was out-of-bounds. + */ +struct IndicesForFaceFromAccessor { + std::array operator()(std::monostate) { + if (faceIndex < 0 || faceIndex >= vertexCount / 3) { + return {-1, -1, -1}; + } + + const int64_t firstVertex = faceIndex * 3; + std::array result; + + for (int64_t i = 0; i < 3; i++) { + int64_t vertexIndex = firstVertex + i; + result[i] = vertexIndex < vertexCount ? vertexIndex : -1; + } + + return result; + } + + template + std::array operator()(const AccessorView& value) { + if (faceIndex < 0 || faceIndex >= value.size() / 3) { + return {-1, -1, -1}; + } + + const int64_t firstVertex = faceIndex * 3; + std::array result; + + for (int64_t i = 0; i < 3; i++) { + int64_t vertexIndex = firstVertex + i; + result[i] = vertexIndex < value.size() + ? static_cast(value[vertexIndex]) + : -1; + } + + return result; + } + + int64_t faceIndex; + int64_t vertexCount; +}; + +/** + * Type definition for all kinds of texture coordinate (TEXCOORD_n) accessors. + */ +typedef std::variant< + AccessorView>, + AccessorView>, + AccessorView>> + TexCoordAccessorType; + +/** + * Retrieves an accessor view for the specified texture coordinate set from the + * given glTF primitive and model. This verifies that the accessor is of a valid + * type. If not, the returned accessor view will be invalid., + */ +TexCoordAccessorType GetTexCoordAccessorView( + const Model& model, + const MeshPrimitive& primitive, + int32_t textureCoordinateSetIndex); + +/** + * Visitor that retrieves the texture coordinates from the given accessor type + * as a glm::dvec2. This should be initialized with the target index. + * + * There are technically no invalid UV values because of clamp / wrap + * behavior, so we use std::nullopt to denote an erroneous value. + */ +struct TexCoordFromAccessor { + std::optional + operator()(const AccessorView>& value) { + if (index < 0 || index >= value.size()) { + return std::nullopt; + } + + return glm::dvec2(value[index].value[0], value[index].value[1]); + } + + template + std::optional + operator()(const AccessorView>& value) { + if (index < 0 || index >= value.size()) { + return std::nullopt; + } + + double u = static_cast(value[index].value[0]); + double v = static_cast(value[index].value[1]); + + // TODO: do normalization logic in accessor view? + u /= std::numeric_limits::max(); + v /= std::numeric_limits::max(); + + return glm::dvec2(u, v); + } + + int64_t index; +}; + +} // namespace CesiumGltf diff --git a/CesiumGltf/src/AccessorUtility.cpp b/CesiumGltf/src/AccessorUtility.cpp new file mode 100644 index 000000000..c7c639987 --- /dev/null +++ b/CesiumGltf/src/AccessorUtility.cpp @@ -0,0 +1,44 @@ +#include "CesiumGltf/AccessorUtility.h" + +#include "CesiumGltf/Model.h" + +namespace CesiumGltf { +TexCoordAccessorType GetTexCoordAccessorView( + const Model& model, + const MeshPrimitive& primitive, + int32_t textureCoordinateSetIndex) { + const std::string texCoordName = + "TEXCOORD_" + std::to_string(textureCoordinateSetIndex); + auto texCoord = primitive.attributes.find(texCoordName); + if (texCoord == primitive.attributes.end()) { + return TexCoordAccessorType(); + } + + const Accessor* pAccessor = model.getSafe( + &model.accessors, + primitive.attributes.at(texCoordName)); + if (!pAccessor || pAccessor->type != Accessor::Type::VEC2) { + return TexCoordAccessorType(); + } + + switch (pAccessor->componentType) { + case Accessor::ComponentType::UNSIGNED_BYTE: + if (pAccessor->normalized) { + // Unsigned byte texcoords must be normalized. + return AccessorView>(model, *pAccessor); + } + [[fallthrough]]; + case Accessor::ComponentType::UNSIGNED_SHORT: + if (pAccessor->normalized) { + // Unsigned short texcoords must be normalized. + return AccessorView>(model, *pAccessor); + } + [[fallthrough]]; + case Accessor::ComponentType::FLOAT: + return AccessorView>(model, *pAccessor); + default: + return TexCoordAccessorType(); + } +} + +} // namespace CesiumGltf diff --git a/CesiumGltf/test/TestAccessorUtility.cpp b/CesiumGltf/test/TestAccessorUtility.cpp new file mode 100644 index 000000000..d382331c7 --- /dev/null +++ b/CesiumGltf/test/TestAccessorUtility.cpp @@ -0,0 +1,400 @@ +#include "CesiumGltf/AccessorUtility.h" + +#include + +using namespace CesiumGltf; + +TEST_CASE("Test CountFromAccessor") { + Model model; + std::vector featureIds{1, 2, 3, 4}; + + Buffer& buffer = model.buffers.emplace_back(); + buffer.cesium.data.resize(featureIds.size() * sizeof(uint8_t)); + std::memcpy( + buffer.cesium.data.data(), + featureIds.data(), + buffer.cesium.data.size()); + buffer.byteLength = buffer.cesium.data.size(); + + BufferView& bufferView = model.bufferViews.emplace_back(); + bufferView.buffer = 0; + bufferView.byteLength = buffer.byteLength; + + Accessor& accessor = model.accessors.emplace_back(); + accessor.bufferView = 0; + accessor.componentType = Accessor::ComponentType::UNSIGNED_BYTE; + accessor.type = Accessor::Type::SCALAR; + accessor.count = bufferView.byteLength / sizeof(uint8_t); + + SECTION("Handles invalid accessor") { + // Wrong type + TexCoordAccessorType texcoordAccessor = + AccessorView>(model, accessor); + REQUIRE(std::visit(CountFromAccessor{}, texcoordAccessor) == 0); + + // Wrong component type + FeatureIdAccessorType featureIdAccessor = + AccessorView(model, accessor); + REQUIRE(std::visit(CountFromAccessor{}, featureIdAccessor) == 0); + } + + SECTION("Retrieves from valid accessor") { + FeatureIdAccessorType featureIdAccessor = + AccessorView(model, accessor); + int64_t count = std::visit(CountFromAccessor{}, featureIdAccessor); + REQUIRE(count == static_cast(featureIds.size())); + } +} + +TEST_CASE("FeatureIdFromAccessor") { + Model model; + std::vector featureIds{1, 2, 3, 4}; + + Buffer& buffer = model.buffers.emplace_back(); + buffer.cesium.data.resize(featureIds.size() * sizeof(int8_t)); + std::memcpy( + buffer.cesium.data.data(), + featureIds.data(), + buffer.cesium.data.size()); + buffer.byteLength = buffer.cesium.data.size(); + + BufferView& bufferView = model.bufferViews.emplace_back(); + bufferView.buffer = 0; + bufferView.byteLength = buffer.byteLength; + + Accessor& accessor = model.accessors.emplace_back(); + accessor.bufferView = 0; + accessor.componentType = Accessor::ComponentType::BYTE; + accessor.type = Accessor::Type::SCALAR; + accessor.count = bufferView.byteLength / sizeof(int8_t); + + SECTION("Handles invalid accessor") { + // Wrong component type + FeatureIdAccessorType featureIdAccessor = + AccessorView(model, accessor); + REQUIRE(std::visit(FeatureIdFromAccessor{0}, featureIdAccessor) == -1); + } + + SECTION("Retrieves from valid accessor") { + FeatureIdAccessorType featureIdAccessor = + AccessorView(model, accessor); + for (size_t i = 0; i < featureIds.size(); i++) { + int64_t featureID = std::visit( + FeatureIdFromAccessor{static_cast(i)}, + featureIdAccessor); + REQUIRE(featureID == featureIds[i]); + } + } +} + +TEST_CASE("Test FaceVertexIndicesFromAccessor") { + Model model; + std::vector indices{0, 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7, 6, 7, 8}; + int64_t vertexCount = 9; + + Buffer& buffer = model.buffers.emplace_back(); + buffer.cesium.data.resize(indices.size() * sizeof(uint32_t)); + std::memcpy( + buffer.cesium.data.data(), + indices.data(), + buffer.cesium.data.size()); + buffer.byteLength = buffer.cesium.data.size(); + + BufferView& bufferView = model.bufferViews.emplace_back(); + bufferView.buffer = 0; + bufferView.byteLength = buffer.byteLength; + + Accessor& accessor = model.accessors.emplace_back(); + accessor.bufferView = 0; + accessor.componentType = Accessor::ComponentType::UNSIGNED_INT; + accessor.type = Accessor::Type::SCALAR; + accessor.count = bufferView.byteLength / sizeof(uint32_t); + + SECTION("Handles invalid accessor") { + // Wrong component type + IndexAccessorType indexAccessor = AccessorView(model, accessor); + auto indicesForFace = + std::visit(IndicesForFaceFromAccessor{0, vertexCount}, indexAccessor); + for (int64_t index : indicesForFace) { + REQUIRE(index == -1); + } + } + + SECTION("Handles invalid face index") { + IndexAccessorType indexAccessor = AccessorView(model, accessor); + auto indicesForFace = + std::visit(IndicesForFaceFromAccessor{-1, vertexCount}, indexAccessor); + for (int64_t index : indicesForFace) { + REQUIRE(index == -1); + } + + indicesForFace = + std::visit(IndicesForFaceFromAccessor{10, vertexCount}, indexAccessor); + for (int64_t index : indicesForFace) { + REQUIRE(index == -1); + } + } + + SECTION("Retrieves from valid accessor and face index") { + IndexAccessorType indexAccessor = AccessorView(model, accessor); + const size_t numFaces = indices.size() / 3; + for (size_t i = 0; i < numFaces; i++) { + auto indicesForFace = std::visit( + IndicesForFaceFromAccessor{static_cast(i), vertexCount}, + indexAccessor); + + for (size_t j = 0; j < indicesForFace.size(); j++) { + int64_t expected = static_cast(indices[i * 3 + j]); + REQUIRE(indicesForFace[j] == expected); + } + } + } + + SECTION("Handles invalid face index for nonexistent accessor") { + IndexAccessorType indexAccessor; + auto indicesForFace = + std::visit(IndicesForFaceFromAccessor{-1, vertexCount}, indexAccessor); + for (int64_t index : indicesForFace) { + REQUIRE(index == -1); + } + + indicesForFace = + std::visit(IndicesForFaceFromAccessor{10, vertexCount}, indexAccessor); + for (int64_t index : indicesForFace) { + REQUIRE(index == -1); + } + } + + SECTION("Retrieves from valid face index for nonexistent accessor") { + IndexAccessorType indexAccessor; + const int64_t numFaces = vertexCount / 3; + + for (int64_t i = 0; i < numFaces; i++) { + auto indicesForFace = + std::visit(IndicesForFaceFromAccessor{i, vertexCount}, indexAccessor); + + for (size_t j = 0; j < indicesForFace.size(); j++) { + int64_t expected = i * 3 + static_cast(j); + REQUIRE(indicesForFace[j] == expected); + } + } + } +} + +TEST_CASE("Test GetTexCoordAccessorView") { + Model model; + std::vector texCoords0{ + glm::vec2(0, 0), + glm::vec2(1, 0), + glm::vec2(0, 1), + glm::vec2(1, 1)}; + + // First TEXCOORD set + { + Buffer& buffer = model.buffers.emplace_back(); + buffer.cesium.data.resize(texCoords0.size() * sizeof(glm::vec2)); + std::memcpy( + buffer.cesium.data.data(), + texCoords0.data(), + buffer.cesium.data.size()); + buffer.byteLength = buffer.cesium.data.size(); + + BufferView& bufferView = model.bufferViews.emplace_back(); + bufferView.buffer = 0; + bufferView.byteLength = buffer.byteLength; + + Accessor& accessor = model.accessors.emplace_back(); + accessor.bufferView = 0; + accessor.componentType = Accessor::ComponentType::FLOAT; + accessor.type = Accessor::Type::VEC2; + accessor.count = bufferView.byteLength / sizeof(glm::vec2); + } + + std::vector texCoords1{ + glm::u8vec2(0, 0), + glm::u8vec2(0, 255), + glm::u8vec2(255, 255), + glm::u8vec2(255, 0)}; + + // Second TEXCOORD set + { + Buffer& buffer = model.buffers.emplace_back(); + buffer.cesium.data.resize(texCoords1.size() * sizeof(glm::u8vec2)); + std::memcpy( + buffer.cesium.data.data(), + texCoords1.data(), + buffer.cesium.data.size()); + buffer.byteLength = buffer.cesium.data.size(); + + BufferView& bufferView = model.bufferViews.emplace_back(); + bufferView.buffer = 1; + bufferView.byteLength = buffer.byteLength; + + Accessor& accessor = model.accessors.emplace_back(); + accessor.bufferView = 1; + accessor.componentType = Accessor::ComponentType::UNSIGNED_BYTE; + accessor.type = Accessor::Type::VEC2; + accessor.normalized = true; + accessor.count = bufferView.byteLength / sizeof(glm::u8vec2); + } + + Mesh& mesh = model.meshes.emplace_back(); + MeshPrimitive primitive = mesh.primitives.emplace_back(); + + primitive.attributes.insert({"TEXCOORD_0", 0}); + primitive.attributes.insert({"TEXCOORD_1", 1}); + + SECTION("Handles invalid texture coordinate set index") { + TexCoordAccessorType texCoordAccessor = + GetTexCoordAccessorView(model, primitive, 2); + REQUIRE(std::visit(CountFromAccessor{}, texCoordAccessor) == 0); + } + + SECTION("Handles invalid accessor type") { + model.accessors[0].type = Accessor::Type::SCALAR; + + TexCoordAccessorType texCoordAccessor = + GetTexCoordAccessorView(model, primitive, 0); + REQUIRE(std::visit(CountFromAccessor{}, texCoordAccessor) == 0); + + model.accessors[0].type = Accessor::Type::VEC2; + } + + SECTION("Handles unsupported accessor component type") { + model.accessors[0].componentType = Accessor::ComponentType::BYTE; + + TexCoordAccessorType texCoordAccessor = + GetTexCoordAccessorView(model, primitive, 0); + REQUIRE(std::visit(CountFromAccessor{}, texCoordAccessor) == 0); + + model.accessors[0].componentType = Accessor::ComponentType::FLOAT; + } + + SECTION("Handles invalid un-normalized texcoord") { + model.accessors[1].normalized = false; + + TexCoordAccessorType texCoordAccessor = + GetTexCoordAccessorView(model, primitive, 2); + REQUIRE(std::visit(CountFromAccessor{}, texCoordAccessor) == 0); + + model.accessors[1].normalized = true; + } + + SECTION("Creates from valid texture coordinate sets") { + TexCoordAccessorType texCoordAccessor = + GetTexCoordAccessorView(model, primitive, 0); + REQUIRE( + std::visit(CountFromAccessor{}, texCoordAccessor) == + static_cast(texCoords0.size())); + + texCoordAccessor = GetTexCoordAccessorView(model, primitive, 1); + REQUIRE( + std::visit(CountFromAccessor{}, texCoordAccessor) == + static_cast(texCoords1.size())); + } +} + +TEST_CASE("Test TexCoordFromAccessor") { + Model model; + std::vector texCoords0{ + glm::vec2(0, 0), + glm::vec2(1, 0), + glm::vec2(0, 1), + glm::vec2(1, 1)}; + + // First TEXCOORD set + { + Buffer& buffer = model.buffers.emplace_back(); + buffer.cesium.data.resize(texCoords0.size() * sizeof(glm::vec2)); + std::memcpy( + buffer.cesium.data.data(), + texCoords0.data(), + buffer.cesium.data.size()); + buffer.byteLength = buffer.cesium.data.size(); + + BufferView& bufferView = model.bufferViews.emplace_back(); + bufferView.buffer = 0; + bufferView.byteLength = buffer.byteLength; + + Accessor& accessor = model.accessors.emplace_back(); + accessor.bufferView = 0; + accessor.componentType = Accessor::ComponentType::FLOAT; + accessor.type = Accessor::Type::VEC2; + accessor.count = bufferView.byteLength / sizeof(glm::vec2); + } + + std::vector texCoords1{ + glm::u8vec2(0, 0), + glm::u8vec2(0, 255), + glm::u8vec2(255, 255), + glm::u8vec2(255, 0)}; + + // Second TEXCOORD set + { + Buffer& buffer = model.buffers.emplace_back(); + buffer.cesium.data.resize(texCoords1.size() * sizeof(glm::u8vec2)); + std::memcpy( + buffer.cesium.data.data(), + texCoords1.data(), + buffer.cesium.data.size()); + buffer.byteLength = buffer.cesium.data.size(); + + BufferView& bufferView = model.bufferViews.emplace_back(); + bufferView.buffer = 1; + bufferView.byteLength = buffer.byteLength; + + Accessor& accessor = model.accessors.emplace_back(); + accessor.bufferView = 1; + accessor.componentType = Accessor::ComponentType::UNSIGNED_BYTE; + accessor.type = Accessor::Type::VEC2; + accessor.normalized = true; + accessor.count = bufferView.byteLength / sizeof(glm::u8vec2); + } + + Mesh& mesh = model.meshes.emplace_back(); + MeshPrimitive primitive = mesh.primitives.emplace_back(); + + primitive.attributes.insert({"TEXCOORD_0", 0}); + primitive.attributes.insert({"TEXCOORD_1", 1}); + + SECTION("Handles invalid accessor") { + TexCoordAccessorType texCoordAccessor = + GetTexCoordAccessorView(model, primitive, 2); + REQUIRE(!std::visit(TexCoordFromAccessor{0}, texCoordAccessor)); + } + + SECTION("Handles invalid index") { + TexCoordAccessorType texCoordAccessor = + GetTexCoordAccessorView(model, primitive, 0); + REQUIRE(!std::visit(TexCoordFromAccessor{-1}, texCoordAccessor)); + REQUIRE(!std::visit(TexCoordFromAccessor{10}, texCoordAccessor)); + } + + SECTION("Retrieves from valid accessor and index") { + TexCoordAccessorType texCoordAccessor = + GetTexCoordAccessorView(model, primitive, 0); + for (size_t i = 0; i < texCoords0.size(); i++) { + auto maybeTexCoord = std::visit( + TexCoordFromAccessor{static_cast(i)}, + texCoordAccessor); + REQUIRE(maybeTexCoord); + + auto expected = glm::dvec2(texCoords0[i][0], texCoords0[i][1]); + REQUIRE(*maybeTexCoord == expected); + } + } + SECTION("Retrieves from valid normalized accessor and index") { + TexCoordAccessorType texCoordAccessor = + GetTexCoordAccessorView(model, primitive, 1); + for (size_t i = 0; i < texCoords1.size(); i++) { + auto maybeTexCoord = std::visit( + TexCoordFromAccessor{static_cast(i)}, + texCoordAccessor); + REQUIRE(maybeTexCoord); + auto expected = glm::dvec2(texCoords1[i][0], texCoords1[i][1]); + expected /= 255; + + REQUIRE(*maybeTexCoord == expected); + } + } +} From df13673387b6ee25440b9ac4febeb1547834b845 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Tue, 14 Nov 2023 13:32:19 -0500 Subject: [PATCH 366/421] Add additional 'get' functions, update changelog --- CHANGES.md | 6 + .../include/CesiumGltf/AccessorUtility.h | 23 ++- CesiumGltf/src/AccessorUtility.cpp | 64 ++++++- CesiumGltf/test/TestAccessorUtility.cpp | 164 ++++++++++++++++++ 4 files changed, 249 insertions(+), 8 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index b4f5dc49a..49fb29825 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -13,6 +13,12 @@ - Added `Cesium3DTilesContent` library and namespace. It has classes for loading, converting, and manipulating 3D Tiles tile content. - Added `MetadataConversions`, which enables metadata values to be converted to different types for better usability in runtime engines. +- Added various `typedef`s to catch all possible types of `AccessorView`s for an attribute, including `FeatureIdAccessorType` for feature ID attribute accessors, `IndexAccessorType` for index accessors, and `TexCoordAccessorType` for texture coordinate attribute accessors. +- Added `GetFeatureIdAccessorView`, `GetIndexAccessorView`, and `GetTexCoordAccessorView` to retrieve the `AccessorView` as a `FeatureIdAccessorType`, `IndexAccessorType`, or `TexCoordAccessorType` respectively. +- Added `CountFromAccessor` to retrieve the accessor size from a `FeatureIdAccessorType`, `IndexAccessorType`, or `TexCoordAccessorType`. +- Added `FeatureIdFromAccessor` to retrieve feature IDs from a `FeatureIdAccessorType`. +- Added `IndicesForFaceFromAccessor` to retrieve the indices of the vertices that make up a face, as supplied by `IndexAccessorType`. +- Added `TexCoordFromAccessor` to retrieve the texture coordinates from a `TexCoordAccessorType`. ##### Fixes :wrench: diff --git a/CesiumGltf/include/CesiumGltf/AccessorUtility.h b/CesiumGltf/include/CesiumGltf/AccessorUtility.h index 3c60be2c7..63524de34 100644 --- a/CesiumGltf/include/CesiumGltf/AccessorUtility.h +++ b/CesiumGltf/include/CesiumGltf/AccessorUtility.h @@ -7,11 +7,6 @@ #include namespace CesiumGltf { -/** - * @brief Type definition for a position accessor. - */ -typedef AccessorView> PositionAccessorType; - /** * @brief Visitor that retrieves the count of elements in the given accessor * type as an int64_t. @@ -36,6 +31,16 @@ typedef std::variant< AccessorView> FeatureIdAccessorType; +/** + * Retrieves an accessor view for the specified feature ID attribute from the + * given glTF primitive and model. This verifies that the accessor is of a valid + * type. If not, the returned accessor view will be invalid. + */ +FeatureIdAccessorType GetFeatureIdAccessorView( + const Model& model, + const MeshPrimitive& primitive, + int32_t featureIdAttributeIndex); + /** * Visitor that retrieves the feature ID from the given accessor type as an * int64_t. This should be initialized with the index of the vertex whose @@ -74,6 +79,14 @@ typedef std::variant< AccessorView> IndexAccessorType; +/** + * Retrieves an accessor view for the indices of the given glTF primitive from + * the model. The primitive may not specify any indices; if so, std::monostate + * is returned. + */ +IndexAccessorType +GetIndexAccessorView(const Model& model, const MeshPrimitive& primitive); + /** * Visitor that retrieves the vertex indices from the given accessor type * corresponding to a given face index. These indices are returned as an array diff --git a/CesiumGltf/src/AccessorUtility.cpp b/CesiumGltf/src/AccessorUtility.cpp index c7c639987..8489fb65d 100644 --- a/CesiumGltf/src/AccessorUtility.cpp +++ b/CesiumGltf/src/AccessorUtility.cpp @@ -3,6 +3,65 @@ #include "CesiumGltf/Model.h" namespace CesiumGltf { +FeatureIdAccessorType GetFeatureIdAccessorView( + const Model& model, + const MeshPrimitive& primitive, + int32_t featureIdAttributeIndex) { + const std::string attributeName = + "_FEATURE_ID_" + std::to_string(featureIdAttributeIndex); + auto featureId = primitive.attributes.find(attributeName); + if (featureId == primitive.attributes.end()) { + return FeatureIdAccessorType(); + } + + const Accessor* pAccessor = + model.getSafe(&model.accessors, featureId->second); + if (!pAccessor || pAccessor->type != Accessor::Type::SCALAR || + pAccessor->normalized) { + return FeatureIdAccessorType(); + } + + switch (pAccessor->componentType) { + case Accessor::ComponentType::BYTE: + return AccessorView(model, *pAccessor); + case Accessor::ComponentType::UNSIGNED_BYTE: + return AccessorView(model, *pAccessor); + case Accessor::ComponentType::SHORT: + return AccessorView(model, *pAccessor); + case Accessor::ComponentType::UNSIGNED_SHORT: + return AccessorView(model, *pAccessor); + case Accessor::ComponentType::FLOAT: + return AccessorView(model, *pAccessor); + default: + return FeatureIdAccessorType(); + } +} + +IndexAccessorType +GetIndexAccessorView(const Model& model, const MeshPrimitive& primitive) { + if (primitive.indices < 0) { + return IndexAccessorType(); + } + + const Accessor* pAccessor = + model.getSafe(&model.accessors, primitive.indices); + if (!pAccessor || pAccessor->type != Accessor::Type::SCALAR || + pAccessor->normalized) { + return AccessorView(); + } + + switch (pAccessor->componentType) { + case Accessor::ComponentType::UNSIGNED_BYTE: + return AccessorView(model, *pAccessor); + case Accessor::ComponentType::UNSIGNED_SHORT: + return AccessorView(model, *pAccessor); + case Accessor::ComponentType::UNSIGNED_INT: + return AccessorView(model, *pAccessor); + default: + return AccessorView(); + } +} + TexCoordAccessorType GetTexCoordAccessorView( const Model& model, const MeshPrimitive& primitive, @@ -14,9 +73,8 @@ TexCoordAccessorType GetTexCoordAccessorView( return TexCoordAccessorType(); } - const Accessor* pAccessor = model.getSafe( - &model.accessors, - primitive.attributes.at(texCoordName)); + const Accessor* pAccessor = + model.getSafe(&model.accessors, texCoord->second); if (!pAccessor || pAccessor->type != Accessor::Type::VEC2) { return TexCoordAccessorType(); } diff --git a/CesiumGltf/test/TestAccessorUtility.cpp b/CesiumGltf/test/TestAccessorUtility.cpp index d382331c7..de760a5ea 100644 --- a/CesiumGltf/test/TestAccessorUtility.cpp +++ b/CesiumGltf/test/TestAccessorUtility.cpp @@ -46,6 +46,100 @@ TEST_CASE("Test CountFromAccessor") { } } +TEST_CASE("Test GetFeatureIdAccessorView") { + Model model; + std::vector featureIds0{1, 2, 3, 4}; + + // First _FEATURE_ID set + { + Buffer& buffer = model.buffers.emplace_back(); + buffer.cesium.data.resize(featureIds0.size() * sizeof(uint8_t)); + std::memcpy( + buffer.cesium.data.data(), + featureIds0.data(), + buffer.cesium.data.size()); + buffer.byteLength = buffer.cesium.data.size(); + + BufferView& bufferView = model.bufferViews.emplace_back(); + bufferView.buffer = 0; + bufferView.byteLength = buffer.byteLength; + + Accessor& accessor = model.accessors.emplace_back(); + accessor.bufferView = 0; + accessor.componentType = Accessor::ComponentType::UNSIGNED_BYTE; + accessor.type = Accessor::Type::SCALAR; + accessor.count = bufferView.byteLength / sizeof(uint8_t); + } + + std::vector featureIds1{5, 6, 7, 8}; + + // Second _FEATURE_ID set + { + Buffer& buffer = model.buffers.emplace_back(); + buffer.cesium.data.resize(featureIds1.size() * sizeof(uint16_t)); + std::memcpy( + buffer.cesium.data.data(), + featureIds1.data(), + buffer.cesium.data.size()); + buffer.byteLength = buffer.cesium.data.size(); + + BufferView& bufferView = model.bufferViews.emplace_back(); + bufferView.buffer = 1; + bufferView.byteLength = buffer.byteLength; + + Accessor& accessor = model.accessors.emplace_back(); + accessor.bufferView = 1; + accessor.componentType = Accessor::ComponentType::UNSIGNED_SHORT; + accessor.type = Accessor::Type::SCALAR; + accessor.count = bufferView.byteLength / sizeof(uint16_t); + } + + Mesh& mesh = model.meshes.emplace_back(); + MeshPrimitive primitive = mesh.primitives.emplace_back(); + + primitive.attributes.insert({"_FEATURE_ID_0", 0}); + primitive.attributes.insert({"_FEATURE_ID_1", 1}); + + SECTION("Handles invalid feature ID set index") { + FeatureIdAccessorType featureIDAccessor = + GetFeatureIdAccessorView(model, primitive, 2); + REQUIRE(std::visit(CountFromAccessor{}, featureIDAccessor) == 0); + } + + SECTION("Handles invalid accessor type") { + model.accessors[0].type = Accessor::Type::VEC2; + + FeatureIdAccessorType featureIDAccessor = + GetFeatureIdAccessorView(model, primitive, 0); + REQUIRE(std::visit(CountFromAccessor{}, featureIDAccessor) == 0); + + model.accessors[0].type = Accessor::Type::SCALAR; + } + + SECTION("Handles invalid normalized accessor") { + model.accessors[1].normalized = true; + + FeatureIdAccessorType featureIDAccessor = + GetFeatureIdAccessorView(model, primitive, 1); + REQUIRE(std::visit(CountFromAccessor{}, featureIDAccessor) == 0); + + model.accessors[1].normalized = false; + } + + SECTION("Creates from valid feature ID sets") { + FeatureIdAccessorType featureIDAccessor = + GetFeatureIdAccessorView(model, primitive, 0); + REQUIRE( + std::visit(CountFromAccessor{}, featureIDAccessor) == + static_cast(featureIds0.size())); + + featureIDAccessor = GetFeatureIdAccessorView(model, primitive, 1); + REQUIRE( + std::visit(CountFromAccessor{}, featureIDAccessor) == + static_cast(featureIds1.size())); + } +} + TEST_CASE("FeatureIdFromAccessor") { Model model; std::vector featureIds{1, 2, 3, 4}; @@ -87,6 +181,76 @@ TEST_CASE("FeatureIdFromAccessor") { } } +TEST_CASE("Test GetIndexAccessorView") { + Model model; + std::vector indices{0, 1, 2, 0, 2, 3}; + + { + Buffer& buffer = model.buffers.emplace_back(); + buffer.cesium.data.resize(indices.size() * sizeof(uint8_t)); + std::memcpy( + buffer.cesium.data.data(), + indices.data(), + buffer.cesium.data.size()); + buffer.byteLength = buffer.cesium.data.size(); + + BufferView& bufferView = model.bufferViews.emplace_back(); + bufferView.buffer = 0; + bufferView.byteLength = buffer.byteLength; + + Accessor& accessor = model.accessors.emplace_back(); + accessor.bufferView = 0; + accessor.componentType = Accessor::ComponentType::UNSIGNED_BYTE; + accessor.type = Accessor::Type::SCALAR; + accessor.count = bufferView.byteLength / sizeof(uint8_t); + } + + Mesh& mesh = model.meshes.emplace_back(); + MeshPrimitive primitive = mesh.primitives.emplace_back(); + primitive.indices = 0; + + SECTION("Handles invalid accessor type") { + model.accessors[0].type = Accessor::Type::VEC2; + + IndexAccessorType indexAccessor = GetIndexAccessorView(model, primitive); + REQUIRE(std::visit(CountFromAccessor{}, indexAccessor) == 0); + + model.accessors[0].type = Accessor::Type::SCALAR; + } + + SECTION("Handles unsupported accessor component type") { + model.accessors[0].componentType = Accessor::ComponentType::BYTE; + + IndexAccessorType indexAccessor = GetIndexAccessorView(model, primitive); + REQUIRE(std::visit(CountFromAccessor{}, indexAccessor) == 0); + + model.accessors[0].componentType = Accessor::ComponentType::UNSIGNED_BYTE; + } + + SECTION("Handles invalid normalized accessor") { + model.accessors[0].normalized = true; + + IndexAccessorType indexAccessor = GetIndexAccessorView(model, primitive); + REQUIRE(std::visit(CountFromAccessor{}, indexAccessor) == 0); + + model.accessors[0].normalized = false; + } + + SECTION("Creates from valid accessor") { + IndexAccessorType indexAccessor = GetIndexAccessorView(model, primitive); + REQUIRE( + std::visit(CountFromAccessor{}, indexAccessor) == + static_cast(indices.size())); + } + + SECTION("Creates from nonexistent accessor") { + primitive.indices = -1; + + IndexAccessorType indexAccessor = GetIndexAccessorView(model, primitive); + REQUIRE(std::get_if(&indexAccessor)); + } +} + TEST_CASE("Test FaceVertexIndicesFromAccessor") { Model model; std::vector indices{0, 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7, 6, 7, 8}; From d94200fe0e0fcc96519313066089188c5e34b938 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Tue, 14 Nov 2023 14:37:25 -0500 Subject: [PATCH 367/421] Fix doc --- .../PropertyAttributePropertyView.h | 6 +- .../CesiumGltf/PropertyAttributeView.h | 168 ++++---- .../include/CesiumGltf/PropertyTableView.h | 406 +++++++++--------- .../include/CesiumGltf/PropertyTextureView.h | 160 ++++--- CesiumGltf/include/CesiumGltf/PropertyView.h | 6 +- CesiumGltf/src/PropertyAttributeView.cpp | 4 +- CesiumGltf/src/PropertyTableView.cpp | 4 +- CesiumGltf/src/PropertyTextureView.cpp | 4 +- CesiumGltf/test/TestPropertyAttributeView.cpp | 24 +- CesiumGltf/test/TestPropertyTableView.cpp | 38 +- CesiumGltf/test/TestPropertyTextureView.cpp | 24 +- 11 files changed, 410 insertions(+), 434 deletions(-) diff --git a/CesiumGltf/include/CesiumGltf/PropertyAttributePropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyAttributePropertyView.h index 69a7689a3..af1668377 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyAttributePropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyAttributePropertyView.h @@ -16,7 +16,7 @@ namespace CesiumGltf { * * The {@link PropertyAttributePropertyView} constructor always completes * successfully. However it may not always reflect the actual content of the - * corresponding property texture property. This enumeration provides the + * corresponding property attribute property. This enumeration provides the * reason. */ class PropertyAttributePropertyViewStatus : public PropertyViewStatus { @@ -190,7 +190,7 @@ class PropertyAttributePropertyView : 0} {} /** - * @brief Gets the value of the property for the given texture coordinates + * @brief Gets the value of the property for the given vertex index * with all value transforms applied. That is, if the property specifies an * offset and scale, they will be applied to the value before the value is * returned. @@ -346,7 +346,7 @@ class PropertyAttributePropertyView : 0} {} /** - * @brief Gets the value of the property for the given texture coordinates + * @brief Gets the value of the property for the given vertex index * with all value transforms applied. That is, if the property specifies an * offset and scale, they will be applied to the value before the value is * returned. diff --git a/CesiumGltf/include/CesiumGltf/PropertyAttributeView.h b/CesiumGltf/include/CesiumGltf/PropertyAttributeView.h index 771ee8c9d..089f0c0ea 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyAttributeView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyAttributeView.h @@ -90,13 +90,13 @@ class PropertyAttributeView { /** * @brief Finds the {@link ClassProperty} that - * describes the type information of the property with the specified name. - * @param propertyName The name of the property to retrieve the class for. + * describes the type information of the property with the specified id. + * @param propertyId The id of the property to retrieve the class for. * @return A pointer to the {@link ClassProperty}. * Return nullptr if the PropertyAttributeView is invalid or if no class * property was found. */ - const ClassProperty* getClassProperty(const std::string& propertyName) const; + const ClassProperty* getClassProperty(const std::string& propertyId) const; /** * @brief Gets a {@link PropertyAttributePropertyView} that views the data of a @@ -118,19 +118,19 @@ class PropertyAttributeView { * @tparam Normalized Whether the property is normalized. Only applicable to * types with integer components. * @param primitive The target primitive - * @param propertyName The name of the property to retrieve data from + * @param propertyId The id of the property to retrieve data from * @return A {@link PropertyAttributePropertyView} of the property. If no valid * property is found, the property view will be invalid. */ template PropertyAttributePropertyView getPropertyView( const MeshPrimitive& primitive, - const std::string& propertyName) const { + const std::string& propertyId) const { if (this->_status != PropertyAttributeViewStatus::Valid) { return PropertyAttributePropertyView( PropertyAttributePropertyViewStatus::ErrorInvalidPropertyAttribute); } - const ClassProperty* pClassProperty = getClassProperty(propertyName); + const ClassProperty* pClassProperty = getClassProperty(propertyId); if (!pClassProperty) { return PropertyAttributePropertyView( PropertyAttributePropertyViewStatus::ErrorNonexistentProperty); @@ -145,14 +145,14 @@ class PropertyAttributeView { return getPropertyViewImpl( primitive, - propertyName, + propertyId, *pClassProperty); } /** * @brief Gets a {@link PropertyAttributePropertyView} through a callback that accepts a - * property name and a {@link PropertyAttributePropertyView} that views the data - * of the property with the specified name. + * property id and a {@link PropertyAttributePropertyView} that views the data + * of the property with the specified id. * * This method will validate the EXT_structural_metadata format to ensure * {@link PropertyAttributePropertyView} retrieves the correct data. T must @@ -165,28 +165,28 @@ class PropertyAttributeView { * property view will be passed to the callback. * * @param primitive The target primitive - * @param propertyName The name of the property to retrieve data from - * @tparam callback A callback function that accepts a property name and a + * @param propertyId The id of the property to retrieve data from + * @tparam callback A callback function that accepts a property id and a * {@link PropertyAttributePropertyView} */ template void getPropertyView( const MeshPrimitive& primitive, - const std::string& propertyName, + const std::string& propertyId, Callback&& callback) const { if (this->_status != PropertyAttributeViewStatus::Valid) { callback( - propertyName, + propertyId, PropertyAttributePropertyView( PropertyAttributePropertyViewStatus:: ErrorInvalidPropertyAttribute)); return; } - const ClassProperty* pClassProperty = getClassProperty(propertyName); + const ClassProperty* pClassProperty = getClassProperty(propertyId); if (!pClassProperty) { callback( - propertyName, + propertyId, PropertyAttributePropertyView( PropertyAttributePropertyViewStatus::ErrorNonexistentProperty)); return; @@ -194,7 +194,7 @@ class PropertyAttributeView { if (pClassProperty->array) { callback( - propertyName, + propertyId, PropertyAttributePropertyView( PropertyAttributePropertyViewStatus::ErrorUnsupportedProperty)); return; @@ -210,7 +210,7 @@ class PropertyAttributeView { bool normalized = pClassProperty->normalized; if (normalized && !isPropertyComponentTypeInteger(componentType)) { callback( - propertyName, + propertyId, PropertyAttributePropertyView( PropertyAttributePropertyViewStatus::ErrorInvalidNormalization)); return; @@ -220,14 +220,14 @@ class PropertyAttributeView { if (normalized) { getScalarPropertyViewImpl( primitive, - propertyName, + propertyId, *pClassProperty, componentType, std::forward(callback)); } else { getScalarPropertyViewImpl( primitive, - propertyName, + propertyId, *pClassProperty, componentType, std::forward(callback)); @@ -239,7 +239,7 @@ class PropertyAttributeView { if (normalized) { getVecNPropertyViewImpl( primitive, - propertyName, + propertyId, *pClassProperty, type, componentType, @@ -247,7 +247,7 @@ class PropertyAttributeView { } else { getVecNPropertyViewImpl( primitive, - propertyName, + propertyId, *pClassProperty, type, componentType, @@ -260,7 +260,7 @@ class PropertyAttributeView { if (normalized) { getMatNPropertyViewImpl( primitive, - propertyName, + propertyId, *pClassProperty, type, componentType, @@ -268,7 +268,7 @@ class PropertyAttributeView { } else { getMatNPropertyViewImpl( primitive, - propertyName, + propertyId, *pClassProperty, type, componentType, @@ -278,7 +278,7 @@ class PropertyAttributeView { } callback( - propertyName, + propertyId, PropertyAttributePropertyView( PropertyAttributePropertyViewStatus::ErrorUnsupportedProperty)); return; @@ -286,21 +286,21 @@ class PropertyAttributeView { /** * @brief Iterates over each property in the {@link PropertyAttribute} with a callback - * that accepts a property name and a {@link PropertyAttributePropertyView} to view + * that accepts a property id and a {@link PropertyAttributePropertyView} to view * the data stored in the {@link PropertyAttributeProperty}. * * This method will validate the EXT_structural_metadata format to ensure * {@link PropertyAttributePropertyView} retrieves the correct data. T must be - * a scalar with a supported component type (uint8_t, uint16_t, uint32_t, - * float), a glm vecN composed of one of the scalar types, or a - * PropertyArrayView containing one of the scalar types. + * a scalar with a supported component type (int8_t, uint8_t, int16_t, + * uint16_t, uint32_t, float), a glm vecN composed of one of the scalar types, + * or a PropertyArrayView containing one of the scalar types. * * If the property is invalid, an empty {@link PropertyAttributePropertyView} with an * error status will be passed to the callback. Otherwise, a valid property * view will be passed to the callback. * - * @param propertyName The name of the property to retrieve data from - * @tparam callback A callback function that accepts property name and + * @param propertyId The id of the property to retrieve data from + * @tparam callback A callback function that accepts property id and * {@link PropertyAttributePropertyView} */ template @@ -343,10 +343,10 @@ class PropertyAttributeView { template PropertyAttributePropertyView getPropertyViewImpl( const MeshPrimitive& primitive, - const std::string& propertyName, + const std::string& propertyId, const ClassProperty& classProperty) const { auto propertyAttributePropertyIter = - _pPropertyAttribute->properties.find(propertyName); + _pPropertyAttribute->properties.find(propertyId); if (propertyAttributePropertyIter == _pPropertyAttribute->properties.end()) { if (!classProperty.required && classProperty.defaultProperty) { @@ -375,62 +375,62 @@ class PropertyAttributeView { template void getScalarPropertyViewImpl( const MeshPrimitive& primitive, - const std::string& propertyName, + const std::string& propertyId, const ClassProperty& classProperty, PropertyComponentType componentType, Callback&& callback) const { switch (componentType) { case PropertyComponentType::Int8: callback( - propertyName, + propertyId, getPropertyViewImpl( primitive, - propertyName, + propertyId, classProperty)); return; case PropertyComponentType::Uint8: callback( - propertyName, + propertyId, getPropertyViewImpl( primitive, - propertyName, + propertyId, classProperty)); return; case PropertyComponentType::Int16: callback( - propertyName, + propertyId, getPropertyViewImpl( primitive, - propertyName, + propertyId, classProperty)); return; case PropertyComponentType::Uint16: callback( - propertyName, + propertyId, getPropertyViewImpl( primitive, - propertyName, + propertyId, classProperty)); break; case PropertyComponentType::Uint32: callback( - propertyName, + propertyId, getPropertyViewImpl( primitive, - propertyName, + propertyId, classProperty)); break; case PropertyComponentType::Float32: callback( - propertyName, + propertyId, getPropertyViewImpl( primitive, - propertyName, + propertyId, classProperty)); break; default: callback( - propertyName, + propertyId, PropertyAttributePropertyView( PropertyAttributePropertyViewStatus::ErrorUnsupportedProperty)); break; @@ -440,62 +440,62 @@ class PropertyAttributeView { template void getVecNPropertyViewImpl( const MeshPrimitive& primitive, - const std::string& propertyName, + const std::string& propertyId, const ClassProperty& classProperty, PropertyComponentType componentType, Callback&& callback) const { switch (componentType) { case PropertyComponentType::Int8: callback( - propertyName, + propertyId, getPropertyViewImpl, Normalized>( primitive, - propertyName, + propertyId, classProperty)); break; case PropertyComponentType::Uint8: callback( - propertyName, + propertyId, getPropertyViewImpl, Normalized>( primitive, - propertyName, + propertyId, classProperty)); break; case PropertyComponentType::Int16: callback( - propertyName, + propertyId, getPropertyViewImpl, Normalized>( primitive, - propertyName, + propertyId, classProperty)); break; case PropertyComponentType::Uint16: callback( - propertyName, + propertyId, getPropertyViewImpl, Normalized>( primitive, - propertyName, + propertyId, classProperty)); break; case PropertyComponentType::Uint32: callback( - propertyName, + propertyId, getPropertyViewImpl, Normalized>( primitive, - propertyName, + propertyId, classProperty)); break; case PropertyComponentType::Float32: callback( - propertyName, + propertyId, getPropertyViewImpl, false>( primitive, - propertyName, + propertyId, classProperty)); break; default: callback( - propertyName, + propertyId, PropertyAttributePropertyView( PropertyAttributePropertyViewStatus::ErrorUnsupportedProperty)); break; @@ -505,7 +505,7 @@ class PropertyAttributeView { template void getVecNPropertyViewImpl( const MeshPrimitive& primitive, - const std::string& propertyName, + const std::string& propertyId, const ClassProperty& classProperty, PropertyType type, PropertyComponentType componentType, @@ -515,7 +515,7 @@ class PropertyAttributeView { case 2: getVecNPropertyViewImpl( primitive, - propertyName, + propertyId, classProperty, componentType, std::forward(callback)); @@ -523,7 +523,7 @@ class PropertyAttributeView { case 3: getVecNPropertyViewImpl( primitive, - propertyName, + propertyId, classProperty, componentType, std::forward(callback)); @@ -531,14 +531,14 @@ class PropertyAttributeView { case 4: getVecNPropertyViewImpl( primitive, - propertyName, + propertyId, classProperty, componentType, std::forward(callback)); break; default: callback( - propertyName, + propertyId, PropertyAttributePropertyView( PropertyAttributePropertyViewStatus::ErrorTypeMismatch)); break; @@ -548,62 +548,62 @@ class PropertyAttributeView { template void getMatNPropertyViewImpl( const MeshPrimitive& primitive, - const std::string& propertyName, + const std::string& propertyId, const ClassProperty& classProperty, PropertyComponentType componentType, Callback&& callback) const { switch (componentType) { case PropertyComponentType::Int8: callback( - propertyName, + propertyId, getPropertyViewImpl, Normalized>( primitive, - propertyName, + propertyId, classProperty)); break; case PropertyComponentType::Uint8: callback( - propertyName, + propertyId, getPropertyViewImpl, Normalized>( primitive, - propertyName, + propertyId, classProperty)); break; case PropertyComponentType::Int16: callback( - propertyName, + propertyId, getPropertyViewImpl, Normalized>( primitive, - propertyName, + propertyId, classProperty)); break; case PropertyComponentType::Uint16: callback( - propertyName, + propertyId, getPropertyViewImpl, Normalized>( primitive, - propertyName, + propertyId, classProperty)); break; case PropertyComponentType::Uint32: callback( - propertyName, + propertyId, getPropertyViewImpl, Normalized>( primitive, - propertyName, + propertyId, classProperty)); break; case PropertyComponentType::Float32: callback( - propertyName, + propertyId, getPropertyViewImpl, false>( primitive, - propertyName, + propertyId, classProperty)); break; default: callback( - propertyName, + propertyId, PropertyAttributePropertyView( PropertyAttributePropertyViewStatus::ErrorUnsupportedProperty)); break; @@ -613,7 +613,7 @@ class PropertyAttributeView { template void getMatNPropertyViewImpl( const MeshPrimitive& primitive, - const std::string& propertyName, + const std::string& propertyId, const ClassProperty& classProperty, PropertyType type, PropertyComponentType componentType, @@ -623,7 +623,7 @@ class PropertyAttributeView { case 2: getMatNPropertyViewImpl( primitive, - propertyName, + propertyId, classProperty, componentType, std::forward(callback)); @@ -631,7 +631,7 @@ class PropertyAttributeView { case 3: getMatNPropertyViewImpl( primitive, - propertyName, + propertyId, classProperty, componentType, std::forward(callback)); @@ -639,14 +639,14 @@ class PropertyAttributeView { case 4: getMatNPropertyViewImpl( primitive, - propertyName, + propertyId, classProperty, componentType, std::forward(callback)); break; default: callback( - propertyName, + propertyId, PropertyAttributePropertyView( PropertyAttributePropertyViewStatus::ErrorTypeMismatch)); break; diff --git a/CesiumGltf/include/CesiumGltf/PropertyTableView.h b/CesiumGltf/include/CesiumGltf/PropertyTableView.h index bdd678d93..d3eb59cd0 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTableView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTableView.h @@ -100,12 +100,12 @@ class PropertyTableView { /** * @brief Finds the {@link ClassProperty} that - * describes the type information of the property with the specified name. - * @param propertyName The name of the property to retrieve the class for. + * describes the type information of the property with the specified id. + * @param propertyId The id of the property to retrieve the class for. * @return A pointer to the {@link ClassProperty}. Returns nullptr if the * PropertyTableView is invalid or if no class property was found. */ - const ClassProperty* getClassProperty(const std::string& propertyName) const; + const ClassProperty* getClassProperty(const std::string& propertyId) const; /** * @brief Gets a {@link PropertyTablePropertyView} that views the data of a property stored @@ -120,7 +120,7 @@ class PropertyTableView { * aforementioned types. * * If T does not match the type specified by the class property, this returns - * an invalid PropertyTexturePropertyView. Likewise, if the value of + * an invalid PropertyTablePropertyView. Likewise, if the value of * Normalized * does not match the value of {@ClassProperty::normalized} for that class property, * this returns an invalid property view. Only types with integer components @@ -129,31 +129,31 @@ class PropertyTableView { * @tparam T The C++ type corresponding to the type of the data retrieved. * @tparam Normalized Whether the property is normalized. Only applicable to * types with integer components. - * @param propertyName The name of the property to retrieve data from + * @param propertyId The id of the property to retrieve data from * @return A {@link PropertyTablePropertyView} of the property. If no valid property is * found, the property view will be invalid. */ template PropertyTablePropertyView - getPropertyView(const std::string& propertyName) const { + getPropertyView(const std::string& propertyId) const { if (this->size() <= 0) { return PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorInvalidPropertyTable); } - const ClassProperty* pClassProperty = getClassProperty(propertyName); + const ClassProperty* pClassProperty = getClassProperty(propertyId); if (!pClassProperty) { return PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorNonexistentProperty); } - return getPropertyViewImpl(propertyName, *pClassProperty); + return getPropertyViewImpl(propertyId, *pClassProperty); } /** * @brief Gets a {@link PropertyTablePropertyView} through a callback that accepts a - * property name and a {@link PropertyTablePropertyView} that views the data - * of the property with the specified name. + * property id and a {@link PropertyTablePropertyView} that views the data + * of the property with the specified id. * * This method will validate the EXT_structural_metadata format to ensure * {@link PropertyTablePropertyView} retrieves the correct data. T must be one of the @@ -167,25 +167,25 @@ class PropertyTableView { * error status will be passed to the callback. Otherwise, a valid property * view will be passed to the callback. * - * @param propertyName The name of the property to retrieve data from - * @tparam callback A callback function that accepts a property name and a + * @param propertyId The id of the property to retrieve data from + * @tparam callback A callback function that accepts a property id and a * {@link PropertyTablePropertyView} */ template void - getPropertyView(const std::string& propertyName, Callback&& callback) const { + getPropertyView(const std::string& propertyId, Callback&& callback) const { if (this->size() <= 0) { callback( - propertyName, + propertyId, PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorInvalidPropertyTable)); return; } - const ClassProperty* pClassProperty = getClassProperty(propertyName); + const ClassProperty* pClassProperty = getClassProperty(propertyId); if (!pClassProperty) { callback( - propertyName, + propertyId, PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorNonexistentProperty)); return; @@ -202,7 +202,7 @@ class PropertyTableView { if (normalized && !isPropertyComponentTypeInteger(componentType)) { // Only integer components may be normalized. callback( - propertyName, + propertyId, PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorInvalidNormalization)); return; @@ -211,14 +211,14 @@ class PropertyTableView { if (pClassProperty->array) { if (normalized) { getArrayPropertyViewImpl( - propertyName, + propertyId, *pClassProperty, type, componentType, std::forward(callback)); } else { getArrayPropertyViewImpl( - propertyName, + propertyId, *pClassProperty, type, componentType, @@ -230,13 +230,13 @@ class PropertyTableView { if (type == PropertyType::Scalar) { if (normalized) { getScalarPropertyViewImpl( - propertyName, + propertyId, *pClassProperty, componentType, std::forward(callback)); } else { getScalarPropertyViewImpl( - propertyName, + propertyId, *pClassProperty, componentType, std::forward(callback)); @@ -247,14 +247,14 @@ class PropertyTableView { if (isPropertyTypeVecN(type)) { if (normalized) { getVecNPropertyViewImpl( - propertyName, + propertyId, *pClassProperty, type, componentType, std::forward(callback)); } else { getVecNPropertyViewImpl( - propertyName, + propertyId, *pClassProperty, type, componentType, @@ -266,14 +266,14 @@ class PropertyTableView { if (isPropertyTypeMatN(type)) { if (normalized) { getMatNPropertyViewImpl( - propertyName, + propertyId, *pClassProperty, type, componentType, std::forward(callback)); } else { getMatNPropertyViewImpl( - propertyName, + propertyId, *pClassProperty, type, componentType, @@ -284,29 +284,29 @@ class PropertyTableView { if (type == PropertyType::String) { callback( - propertyName, + propertyId, getPropertyViewImpl( - propertyName, + propertyId, *pClassProperty)); return; } if (type == PropertyType::Boolean) { callback( - propertyName, - getPropertyViewImpl(propertyName, *pClassProperty)); + propertyId, + getPropertyViewImpl(propertyId, *pClassProperty)); return; } callback( - propertyName, + propertyId, PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorTypeMismatch)); } /** * @brief Iterates over each property in the {@link PropertyTable} with a callback - * that accepts a property name and a {@link PropertyTablePropertyView} to view + * that accepts a property id and a {@link PropertyTablePropertyView} to view * the data stored in the {@link PropertyTableProperty}. * * This method will validate the EXT_structural_metadata format to ensure @@ -321,8 +321,8 @@ class PropertyTableView { * an error status code will be passed to the callback. Otherwise, a valid * property view will be passed to the callback. * - * @param propertyName The name of the property to retrieve data from - * @tparam callback A callback function that accepts property name and + * @param propertyId The id of the property to retrieve data from + * @tparam callback A callback function that accepts property id and * {@link PropertyTablePropertyView} */ template void forEachProperty(Callback&& callback) const { @@ -334,84 +334,84 @@ class PropertyTableView { private: template void getScalarArrayPropertyViewImpl( - const std::string& propertyName, + const std::string& propertyId, const ClassProperty& classProperty, PropertyComponentType componentType, Callback&& callback) const { switch (componentType) { case PropertyComponentType::Int8: callback( - propertyName, + propertyId, getPropertyViewImpl, Normalized>( - propertyName, + propertyId, classProperty)); break; case PropertyComponentType::Uint8: callback( - propertyName, + propertyId, getPropertyViewImpl, Normalized>( - propertyName, + propertyId, classProperty)); break; case PropertyComponentType::Int16: callback( - propertyName, + propertyId, getPropertyViewImpl, Normalized>( - propertyName, + propertyId, classProperty)); break; case PropertyComponentType::Uint16: callback( - propertyName, + propertyId, getPropertyViewImpl, Normalized>( - propertyName, + propertyId, classProperty)); break; case PropertyComponentType::Int32: callback( - propertyName, + propertyId, getPropertyViewImpl, Normalized>( - propertyName, + propertyId, classProperty)); break; case PropertyComponentType::Uint32: callback( - propertyName, + propertyId, getPropertyViewImpl, Normalized>( - propertyName, + propertyId, classProperty)); break; case PropertyComponentType::Int64: callback( - propertyName, + propertyId, getPropertyViewImpl, Normalized>( - propertyName, + propertyId, classProperty)); break; case PropertyComponentType::Uint64: callback( - propertyName, + propertyId, getPropertyViewImpl, Normalized>( - propertyName, + propertyId, classProperty)); break; case PropertyComponentType::Float32: callback( - propertyName, + propertyId, getPropertyViewImpl, false>( - propertyName, + propertyId, classProperty)); break; case PropertyComponentType::Float64: callback( - propertyName, + propertyId, getPropertyViewImpl, false>( - propertyName, + propertyId, classProperty)); break; default: callback( - propertyName, + propertyId, PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch)); break; @@ -420,84 +420,84 @@ class PropertyTableView { template void getVecNArrayPropertyViewImpl( - const std::string& propertyName, + const std::string& propertyId, const ClassProperty& classProperty, PropertyComponentType componentType, Callback&& callback) const { switch (componentType) { case PropertyComponentType::Int8: callback( - propertyName, + propertyId, getPropertyViewImpl< PropertyArrayView>, - Normalized>(propertyName, classProperty)); + Normalized>(propertyId, classProperty)); break; case PropertyComponentType::Uint8: callback( - propertyName, + propertyId, getPropertyViewImpl< PropertyArrayView>, - Normalized>(propertyName, classProperty)); + Normalized>(propertyId, classProperty)); break; case PropertyComponentType::Int16: callback( - propertyName, + propertyId, getPropertyViewImpl< PropertyArrayView>, - Normalized>(propertyName, classProperty)); + Normalized>(propertyId, classProperty)); break; case PropertyComponentType::Uint16: callback( - propertyName, + propertyId, getPropertyViewImpl< PropertyArrayView>, - Normalized>(propertyName, classProperty)); + Normalized>(propertyId, classProperty)); break; case PropertyComponentType::Int32: callback( - propertyName, + propertyId, getPropertyViewImpl< PropertyArrayView>, - Normalized>(propertyName, classProperty)); + Normalized>(propertyId, classProperty)); break; case PropertyComponentType::Uint32: callback( - propertyName, + propertyId, getPropertyViewImpl< PropertyArrayView>, - Normalized>(propertyName, classProperty)); + Normalized>(propertyId, classProperty)); break; case PropertyComponentType::Int64: callback( - propertyName, + propertyId, getPropertyViewImpl< PropertyArrayView>, - Normalized>(propertyName, classProperty)); + Normalized>(propertyId, classProperty)); break; case PropertyComponentType::Uint64: callback( - propertyName, + propertyId, getPropertyViewImpl< PropertyArrayView>, - Normalized>(propertyName, classProperty)); + Normalized>(propertyId, classProperty)); break; case PropertyComponentType::Float32: callback( - propertyName, + propertyId, getPropertyViewImpl>, false>( - propertyName, + propertyId, classProperty)); break; case PropertyComponentType::Float64: callback( - propertyName, + propertyId, getPropertyViewImpl>, false>( - propertyName, + propertyId, classProperty)); break; default: callback( - propertyName, + propertyId, PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch)); break; @@ -506,7 +506,7 @@ class PropertyTableView { template void getVecNArrayPropertyViewImpl( - const std::string& propertyName, + const std::string& propertyId, const ClassProperty& classProperty, PropertyType type, PropertyComponentType componentType, @@ -515,28 +515,28 @@ class PropertyTableView { switch (N) { case 2: getVecNArrayPropertyViewImpl( - propertyName, + propertyId, classProperty, componentType, std::forward(callback)); break; case 3: getVecNArrayPropertyViewImpl( - propertyName, + propertyId, classProperty, componentType, std::forward(callback)); break; case 4: getVecNArrayPropertyViewImpl( - propertyName, + propertyId, classProperty, componentType, std::forward(callback)); break; default: callback( - propertyName, + propertyId, PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorTypeMismatch)); break; @@ -545,84 +545,84 @@ class PropertyTableView { template void getMatNArrayPropertyViewImpl( - const std::string& propertyName, + const std::string& propertyId, const ClassProperty& classProperty, PropertyComponentType componentType, Callback&& callback) const { switch (componentType) { case PropertyComponentType::Int8: callback( - propertyName, + propertyId, getPropertyViewImpl< PropertyArrayView>, - Normalized>(propertyName, classProperty)); + Normalized>(propertyId, classProperty)); break; case PropertyComponentType::Uint8: callback( - propertyName, + propertyId, getPropertyViewImpl< PropertyArrayView>, - Normalized>(propertyName, classProperty)); + Normalized>(propertyId, classProperty)); break; case PropertyComponentType::Int16: callback( - propertyName, + propertyId, getPropertyViewImpl< PropertyArrayView>, - Normalized>(propertyName, classProperty)); + Normalized>(propertyId, classProperty)); break; case PropertyComponentType::Uint16: callback( - propertyName, + propertyId, getPropertyViewImpl< PropertyArrayView>, - Normalized>(propertyName, classProperty)); + Normalized>(propertyId, classProperty)); break; case PropertyComponentType::Int32: callback( - propertyName, + propertyId, getPropertyViewImpl< PropertyArrayView>, - Normalized>(propertyName, classProperty)); + Normalized>(propertyId, classProperty)); break; case PropertyComponentType::Uint32: callback( - propertyName, + propertyId, getPropertyViewImpl< PropertyArrayView>, - Normalized>(propertyName, classProperty)); + Normalized>(propertyId, classProperty)); break; case PropertyComponentType::Int64: callback( - propertyName, + propertyId, getPropertyViewImpl< PropertyArrayView>, - Normalized>(propertyName, classProperty)); + Normalized>(propertyId, classProperty)); break; case PropertyComponentType::Uint64: callback( - propertyName, + propertyId, getPropertyViewImpl< PropertyArrayView>, - Normalized>(propertyName, classProperty)); + Normalized>(propertyId, classProperty)); break; case PropertyComponentType::Float32: callback( - propertyName, + propertyId, getPropertyViewImpl>, false>( - propertyName, + propertyId, classProperty)); break; case PropertyComponentType::Float64: callback( - propertyName, + propertyId, getPropertyViewImpl>, false>( - propertyName, + propertyId, classProperty)); break; default: callback( - propertyName, + propertyId, PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch)); break; @@ -631,7 +631,7 @@ class PropertyTableView { template void getMatNArrayPropertyViewImpl( - const std::string& propertyName, + const std::string& propertyId, const ClassProperty& classProperty, PropertyType type, PropertyComponentType componentType, @@ -640,28 +640,28 @@ class PropertyTableView { switch (N) { case 2: getMatNArrayPropertyViewImpl( - propertyName, + propertyId, classProperty, componentType, std::forward(callback)); break; case 3: getMatNArrayPropertyViewImpl( - propertyName, + propertyId, classProperty, componentType, std::forward(callback)); break; case 4: getMatNArrayPropertyViewImpl( - propertyName, + propertyId, classProperty, componentType, std::forward(callback)); break; default: callback( - propertyName, + propertyId, PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorTypeMismatch)); break; @@ -670,47 +670,47 @@ class PropertyTableView { template void getArrayPropertyViewImpl( - const std::string& propertyName, + const std::string& propertyId, const ClassProperty& classProperty, PropertyType type, PropertyComponentType componentType, Callback&& callback) const { if (type == PropertyType::Scalar) { getScalarArrayPropertyViewImpl( - propertyName, + propertyId, classProperty, componentType, std::forward(callback)); } else if (isPropertyTypeVecN(type)) { getVecNArrayPropertyViewImpl( - propertyName, + propertyId, classProperty, type, componentType, std::forward(callback)); } else if (isPropertyTypeMatN(type)) { getMatNArrayPropertyViewImpl( - propertyName, + propertyId, classProperty, type, componentType, std::forward(callback)); } else if (type == PropertyType::Boolean) { callback( - propertyName, + propertyId, getPropertyViewImpl, false>( - propertyName, + propertyId, classProperty)); } else if (type == PropertyType::String) { callback( - propertyName, + propertyId, getPropertyViewImpl, false>( - propertyName, + propertyId, classProperty)); } else { callback( - propertyName, + propertyId, PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorTypeMismatch)); } @@ -718,7 +718,7 @@ class PropertyTableView { template void getVecNPropertyViewImpl( - const std::string& propertyName, + const std::string& propertyId, const ClassProperty& classProperty, PropertyComponentType componentType, Callback&& callback) const { @@ -726,77 +726,77 @@ class PropertyTableView { switch (componentType) { case PropertyComponentType::Int8: callback( - propertyName, + propertyId, getPropertyViewImpl, Normalized>( - propertyName, + propertyId, classProperty)); break; case PropertyComponentType::Uint8: callback( - propertyName, + propertyId, getPropertyViewImpl, Normalized>( - propertyName, + propertyId, classProperty)); break; case PropertyComponentType::Int16: callback( - propertyName, + propertyId, getPropertyViewImpl, Normalized>( - propertyName, + propertyId, classProperty)); break; case PropertyComponentType::Uint16: callback( - propertyName, + propertyId, getPropertyViewImpl, Normalized>( - propertyName, + propertyId, classProperty)); break; case PropertyComponentType::Int32: callback( - propertyName, + propertyId, getPropertyViewImpl, Normalized>( - propertyName, + propertyId, classProperty)); break; case PropertyComponentType::Uint32: callback( - propertyName, + propertyId, getPropertyViewImpl, Normalized>( - propertyName, + propertyId, classProperty)); break; case PropertyComponentType::Int64: callback( - propertyName, + propertyId, getPropertyViewImpl, Normalized>( - propertyName, + propertyId, classProperty)); break; case PropertyComponentType::Uint64: callback( - propertyName, + propertyId, getPropertyViewImpl, Normalized>( - propertyName, + propertyId, classProperty)); break; case PropertyComponentType::Float32: callback( - propertyName, + propertyId, getPropertyViewImpl, false>( - propertyName, + propertyId, classProperty)); break; case PropertyComponentType::Float64: callback( - propertyName, + propertyId, getPropertyViewImpl, false>( - propertyName, + propertyId, classProperty)); break; default: callback( - propertyName, + propertyId, PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch)); break; @@ -805,7 +805,7 @@ class PropertyTableView { template void getVecNPropertyViewImpl( - const std::string& propertyName, + const std::string& propertyId, const ClassProperty& classProperty, PropertyType type, PropertyComponentType componentType, @@ -814,28 +814,28 @@ class PropertyTableView { switch (N) { case 2: getVecNPropertyViewImpl( - propertyName, + propertyId, classProperty, componentType, std::forward(callback)); break; case 3: getVecNPropertyViewImpl( - propertyName, + propertyId, classProperty, componentType, std::forward(callback)); break; case 4: getVecNPropertyViewImpl( - propertyName, + propertyId, classProperty, componentType, std::forward(callback)); break; default: callback( - propertyName, + propertyId, PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorTypeMismatch)); break; @@ -844,84 +844,84 @@ class PropertyTableView { template void getMatNPropertyViewImpl( - const std::string& propertyName, + const std::string& propertyId, const ClassProperty& classProperty, PropertyComponentType componentType, Callback&& callback) const { switch (componentType) { case PropertyComponentType::Int8: callback( - propertyName, + propertyId, getPropertyViewImpl, Normalized>( - propertyName, + propertyId, classProperty)); break; case PropertyComponentType::Uint8: callback( - propertyName, + propertyId, getPropertyViewImpl, Normalized>( - propertyName, + propertyId, classProperty)); break; case PropertyComponentType::Int16: callback( - propertyName, + propertyId, getPropertyViewImpl, Normalized>( - propertyName, + propertyId, classProperty)); break; case PropertyComponentType::Uint16: callback( - propertyName, + propertyId, getPropertyViewImpl, Normalized>( - propertyName, + propertyId, classProperty)); break; case PropertyComponentType::Int32: callback( - propertyName, + propertyId, getPropertyViewImpl, Normalized>( - propertyName, + propertyId, classProperty)); break; case PropertyComponentType::Uint32: callback( - propertyName, + propertyId, getPropertyViewImpl, Normalized>( - propertyName, + propertyId, classProperty)); break; case PropertyComponentType::Int64: callback( - propertyName, + propertyId, getPropertyViewImpl, Normalized>( - propertyName, + propertyId, classProperty)); break; case PropertyComponentType::Uint64: callback( - propertyName, + propertyId, getPropertyViewImpl, Normalized>( - propertyName, + propertyId, classProperty)); break; case PropertyComponentType::Float32: callback( - propertyName, + propertyId, getPropertyViewImpl, false>( - propertyName, + propertyId, classProperty)); break; case PropertyComponentType::Float64: callback( - propertyName, + propertyId, getPropertyViewImpl, false>( - propertyName, + propertyId, classProperty)); break; default: callback( - propertyName, + propertyId, PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch)); break; @@ -930,7 +930,7 @@ class PropertyTableView { template void getMatNPropertyViewImpl( - const std::string& propertyName, + const std::string& propertyId, const ClassProperty& classProperty, PropertyType type, PropertyComponentType componentType, @@ -939,28 +939,28 @@ class PropertyTableView { switch (N) { case 2: getMatNPropertyViewImpl( - propertyName, + propertyId, classProperty, componentType, std::forward(callback)); break; case 3: getMatNPropertyViewImpl( - propertyName, + propertyId, classProperty, componentType, std::forward(callback)); break; case 4: getMatNPropertyViewImpl( - propertyName, + propertyId, classProperty, componentType, std::forward(callback)); break; default: callback( - propertyName, + propertyId, PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorTypeMismatch)); break; @@ -969,78 +969,64 @@ class PropertyTableView { template void getScalarPropertyViewImpl( - const std::string& propertyName, + const std::string& propertyId, const ClassProperty& classProperty, PropertyComponentType componentType, Callback&& callback) const { switch (componentType) { case PropertyComponentType::Int8: callback( - propertyName, - getPropertyViewImpl(propertyName, classProperty)); + propertyId, + getPropertyViewImpl(propertyId, classProperty)); return; case PropertyComponentType::Uint8: callback( - propertyName, - getPropertyViewImpl( - propertyName, - classProperty)); + propertyId, + getPropertyViewImpl(propertyId, classProperty)); return; case PropertyComponentType::Int16: callback( - propertyName, - getPropertyViewImpl( - propertyName, - classProperty)); + propertyId, + getPropertyViewImpl(propertyId, classProperty)); return; case PropertyComponentType::Uint16: callback( - propertyName, - getPropertyViewImpl( - propertyName, - classProperty)); + propertyId, + getPropertyViewImpl(propertyId, classProperty)); break; case PropertyComponentType::Int32: callback( - propertyName, - getPropertyViewImpl( - propertyName, - classProperty)); + propertyId, + getPropertyViewImpl(propertyId, classProperty)); break; case PropertyComponentType::Uint32: callback( - propertyName, - getPropertyViewImpl( - propertyName, - classProperty)); + propertyId, + getPropertyViewImpl(propertyId, classProperty)); break; case PropertyComponentType::Int64: callback( - propertyName, - getPropertyViewImpl( - propertyName, - classProperty)); + propertyId, + getPropertyViewImpl(propertyId, classProperty)); break; case PropertyComponentType::Uint64: callback( - propertyName, - getPropertyViewImpl( - propertyName, - classProperty)); + propertyId, + getPropertyViewImpl(propertyId, classProperty)); break; case PropertyComponentType::Float32: callback( - propertyName, - getPropertyViewImpl(propertyName, classProperty)); + propertyId, + getPropertyViewImpl(propertyId, classProperty)); break; case PropertyComponentType::Float64: callback( - propertyName, - getPropertyViewImpl(propertyName, classProperty)); + propertyId, + getPropertyViewImpl(propertyId, classProperty)); break; default: callback( - propertyName, + propertyId, PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch)); break; @@ -1049,10 +1035,10 @@ class PropertyTableView { template PropertyTablePropertyView getPropertyViewImpl( - const std::string& propertyName, + const std::string& propertyId, const ClassProperty& classProperty) const { auto propertyTablePropertyIter = - _pPropertyTable->properties.find(propertyName); + _pPropertyTable->properties.find(propertyId); if (propertyTablePropertyIter == _pPropertyTable->properties.end()) { if (!classProperty.required && classProperty.defaultProperty) { // If the property was omitted from the property table, it is still diff --git a/CesiumGltf/include/CesiumGltf/PropertyTextureView.h b/CesiumGltf/include/CesiumGltf/PropertyTextureView.h index ccaa04312..f7d58cdbc 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyTextureView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyTextureView.h @@ -86,13 +86,13 @@ class PropertyTextureView { /** * @brief Finds the {@link ClassProperty} that - * describes the type information of the property with the specified name. - * @param propertyName The name of the property to retrieve the class for. + * describes the type information of the property with the specified id. + * @param propertyId The id of the property to retrieve the class for. * @return A pointer to the {@link ClassProperty}. * Return nullptr if the PropertyTextureView is invalid or if no class * property was found. */ - const ClassProperty* getClassProperty(const std::string& propertyName) const; + const ClassProperty* getClassProperty(const std::string& propertyId) const; /** * @brief Gets a {@link PropertyTexturePropertyView} that views the data of a @@ -113,34 +113,34 @@ class PropertyTextureView { * @tparam T The C++ type corresponding to the type of the data retrieved. * @tparam Normalized Whether the property is normalized. Only applicable to * types with integer components. - * @param propertyName The name of the property to retrieve data from + * @param propertyId The id of the property to retrieve data from * @return A {@link PropertyTexturePropertyView} of the property. If no valid * property is found, the property view will be invalid. */ template PropertyTexturePropertyView - getPropertyView(const std::string& propertyName) const { + getPropertyView(const std::string& propertyId) const { if (this->_status != PropertyTextureViewStatus::Valid) { return PropertyTexturePropertyView( PropertyTexturePropertyViewStatus::ErrorInvalidPropertyTexture); } - const ClassProperty* pClassProperty = getClassProperty(propertyName); + const ClassProperty* pClassProperty = getClassProperty(propertyId); if (!pClassProperty) { return PropertyTexturePropertyView( PropertyTexturePropertyViewStatus::ErrorNonexistentProperty); } - return getPropertyViewImpl(propertyName, *pClassProperty); + return getPropertyViewImpl(propertyId, *pClassProperty); } /** * @brief Gets a {@link PropertyTexturePropertyView} through a callback that accepts a - * property name and a {@link PropertyTexturePropertyView} that views the data - * of the property with the specified name. + * property id and a {@link PropertyTexturePropertyView} that views the data + * of the property with the specified id. * * This method will validate the EXT_structural_metadata format to ensure - * {@link PropertyTexturePropertyView} retrieves the correct data. . T must + * {@link PropertyTexturePropertyView} retrieves the correct data. T must * be a scalar with a supported component type (int8_t, uint8_t, int16_t, * uint16_t, int32_t, uint32_t, float), a glm vecN composed of one of the * scalar types, or a PropertyArrayView containing one of the scalar types. @@ -149,25 +149,25 @@ class PropertyTextureView { * with an error status will be passed to the callback. Otherwise, a valid * property view will be passed to the callback. * - * @param propertyName The name of the property to retrieve data from - * @tparam callback A callback function that accepts a property name and a + * @param propertyId The id of the property to retrieve data from + * @tparam callback A callback function that accepts a property id and a * {@link PropertyTexturePropertyView} */ template void - getPropertyView(const std::string& propertyName, Callback&& callback) const { + getPropertyView(const std::string& propertyId, Callback&& callback) const { if (this->_status != PropertyTextureViewStatus::Valid) { callback( - propertyName, + propertyId, PropertyTexturePropertyView( PropertyTexturePropertyViewStatus::ErrorInvalidPropertyTexture)); return; } - const ClassProperty* pClassProperty = getClassProperty(propertyName); + const ClassProperty* pClassProperty = getClassProperty(propertyId); if (!pClassProperty) { callback( - propertyName, + propertyId, PropertyTexturePropertyView( PropertyTexturePropertyViewStatus::ErrorNonexistentProperty)); return; @@ -184,7 +184,7 @@ class PropertyTextureView { if (normalized && !isPropertyComponentTypeInteger(componentType)) { // Only integer components may be normalized. callback( - propertyName, + propertyId, PropertyTexturePropertyView( PropertyTexturePropertyViewStatus::ErrorInvalidNormalization)); return; @@ -193,14 +193,14 @@ class PropertyTextureView { if (pClassProperty->array) { if (normalized) { getArrayPropertyViewImpl( - propertyName, + propertyId, *pClassProperty, type, componentType, std::forward(callback)); } else { getArrayPropertyViewImpl( - propertyName, + propertyId, *pClassProperty, type, componentType, @@ -212,13 +212,13 @@ class PropertyTextureView { if (type == PropertyType::Scalar) { if (normalized) { getScalarPropertyViewImpl( - propertyName, + propertyId, *pClassProperty, componentType, std::forward(callback)); } else { getScalarPropertyViewImpl( - propertyName, + propertyId, *pClassProperty, componentType, std::forward(callback)); @@ -229,14 +229,14 @@ class PropertyTextureView { if (isPropertyTypeVecN(type)) { if (normalized) { getVecNPropertyViewImpl( - propertyName, + propertyId, *pClassProperty, type, componentType, std::forward(callback)); } else { getVecNPropertyViewImpl( - propertyName, + propertyId, *pClassProperty, type, componentType, @@ -246,7 +246,7 @@ class PropertyTextureView { } callback( - propertyName, + propertyId, PropertyTexturePropertyView( PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty)); return; @@ -254,21 +254,21 @@ class PropertyTextureView { /** * @brief Iterates over each property in the {@link PropertyTexture} with a callback - * that accepts a property name and a {@link PropertyTexturePropertyView} to view + * that accepts a property id and a {@link PropertyTexturePropertyView} to view * the data stored in the {@link PropertyTextureProperty}. * * This method will validate the EXT_structural_metadata format to ensure * {@link PropertyTexturePropertyView} retrieves the correct data. T must be - * a scalar with a supported component type (uint8_t, uint16_t, uint32_t, - * float), a glm vecN composed of one of the scalar types, or a - * PropertyArrayView containing one of the scalar types. + * a scalar with a supported component type (int8_t, uint8_t, int16_t, + * uint16_t, int32_t, uint32_t, float), a glm vecN composed of one of the + * scalar types, or a PropertyArrayView containing one of the scalar types. * * If the property is invalid, an empty {@link PropertyTexturePropertyView} with an * error status will be passed to the callback. Otherwise, a valid property * view will be passed to the callback. * - * @param propertyName The name of the property to retrieve data from - * @tparam callback A callback function that accepts property name and + * @param propertyId The id of the property to retrieve data from + * @tparam callback A callback function that accepts property id and * {@link PropertyTexturePropertyView} */ template void forEachProperty(Callback&& callback) const { @@ -280,10 +280,10 @@ class PropertyTextureView { private: template PropertyTexturePropertyView getPropertyViewImpl( - const std::string& propertyName, + const std::string& propertyId, const ClassProperty& classProperty) const { auto propertyTexturePropertyIter = - _pPropertyTexture->properties.find(propertyName); + _pPropertyTexture->properties.find(propertyId); if (propertyTexturePropertyIter == _pPropertyTexture->properties.end()) { if (!classProperty.required && classProperty.defaultProperty) { // If the property was omitted from the property texture, it is still @@ -320,7 +320,7 @@ class PropertyTextureView { template void getArrayPropertyViewImpl( - const std::string& propertyName, + const std::string& propertyId, const ClassProperty& classProperty, PropertyType type, PropertyComponentType componentType, @@ -328,7 +328,7 @@ class PropertyTextureView { // Only scalar arrays are supported. if (type != PropertyType::Scalar) { callback( - propertyName, + propertyId, PropertyTexturePropertyView( PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty)); return; @@ -337,7 +337,7 @@ class PropertyTextureView { int64_t count = classProperty.count.value_or(0); if (count <= 0 || count > 4) { callback( - propertyName, + propertyId, PropertyTexturePropertyView( PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty)); return; @@ -346,35 +346,35 @@ class PropertyTextureView { switch (componentType) { case PropertyComponentType::Int8: callback( - propertyName, + propertyId, getPropertyViewImpl, Normalized>( - propertyName, + propertyId, classProperty)); break; case PropertyComponentType::Uint8: callback( - propertyName, + propertyId, getPropertyViewImpl, Normalized>( - propertyName, + propertyId, classProperty)); break; case PropertyComponentType::Int16: callback( - propertyName, + propertyId, getPropertyViewImpl, Normalized>( - propertyName, + propertyId, classProperty)); break; case PropertyComponentType::Uint16: callback( - propertyName, + propertyId, getPropertyViewImpl, Normalized>( - propertyName, + propertyId, classProperty)); break; default: callback( - propertyName, + propertyId, PropertyTexturePropertyView( PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty)); break; @@ -383,59 +383,49 @@ class PropertyTextureView { template void getScalarPropertyViewImpl( - const std::string& propertyName, + const std::string& propertyId, const ClassProperty& classProperty, PropertyComponentType componentType, Callback&& callback) const { switch (componentType) { case PropertyComponentType::Int8: callback( - propertyName, - getPropertyViewImpl(propertyName, classProperty)); + propertyId, + getPropertyViewImpl(propertyId, classProperty)); return; case PropertyComponentType::Uint8: callback( - propertyName, - getPropertyViewImpl( - propertyName, - classProperty)); + propertyId, + getPropertyViewImpl(propertyId, classProperty)); return; case PropertyComponentType::Int16: callback( - propertyName, - getPropertyViewImpl( - propertyName, - classProperty)); + propertyId, + getPropertyViewImpl(propertyId, classProperty)); return; case PropertyComponentType::Uint16: callback( - propertyName, - getPropertyViewImpl( - propertyName, - classProperty)); + propertyId, + getPropertyViewImpl(propertyId, classProperty)); break; case PropertyComponentType::Int32: callback( - propertyName, - getPropertyViewImpl( - propertyName, - classProperty)); + propertyId, + getPropertyViewImpl(propertyId, classProperty)); break; case PropertyComponentType::Uint32: callback( - propertyName, - getPropertyViewImpl( - propertyName, - classProperty)); + propertyId, + getPropertyViewImpl(propertyId, classProperty)); break; case PropertyComponentType::Float32: callback( - propertyName, - getPropertyViewImpl(propertyName, classProperty)); + propertyId, + getPropertyViewImpl(propertyId, classProperty)); break; default: callback( - propertyName, + propertyId, PropertyTexturePropertyView( PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty)); break; @@ -444,31 +434,31 @@ class PropertyTextureView { template void getVecNPropertyViewImpl( - const std::string& propertyName, + const std::string& propertyId, const ClassProperty& classProperty, PropertyComponentType componentType, Callback&& callback) const { switch (componentType) { case PropertyComponentType::Int8: callback( - propertyName, + propertyId, getPropertyViewImpl, Normalized>( - propertyName, + propertyId, classProperty)); break; case PropertyComponentType::Uint8: callback( - propertyName, + propertyId, getPropertyViewImpl, Normalized>( - propertyName, + propertyId, classProperty)); break; case PropertyComponentType::Int16: if constexpr (N == 2) { callback( - propertyName, + propertyId, getPropertyViewImpl, Normalized>( - propertyName, + propertyId, classProperty)); break; } @@ -476,16 +466,16 @@ class PropertyTextureView { case PropertyComponentType::Uint16: if constexpr (N == 2) { callback( - propertyName, + propertyId, getPropertyViewImpl, Normalized>( - propertyName, + propertyId, classProperty)); break; } [[fallthrough]]; default: callback( - propertyName, + propertyId, PropertyTexturePropertyView( PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty)); break; @@ -494,7 +484,7 @@ class PropertyTextureView { template void getVecNPropertyViewImpl( - const std::string& propertyName, + const std::string& propertyId, const ClassProperty& classProperty, PropertyType type, PropertyComponentType componentType, @@ -503,28 +493,28 @@ class PropertyTextureView { switch (N) { case 2: getVecNPropertyViewImpl( - propertyName, + propertyId, classProperty, componentType, std::forward(callback)); break; case 3: getVecNPropertyViewImpl( - propertyName, + propertyId, classProperty, componentType, std::forward(callback)); break; case 4: getVecNPropertyViewImpl( - propertyName, + propertyId, classProperty, componentType, std::forward(callback)); break; default: callback( - propertyName, + propertyId, PropertyTexturePropertyView( PropertyTexturePropertyViewStatus::ErrorTypeMismatch)); break; diff --git a/CesiumGltf/include/CesiumGltf/PropertyView.h b/CesiumGltf/include/CesiumGltf/PropertyView.h index b912b9abc..78ffb8b67 100644 --- a/CesiumGltf/include/CesiumGltf/PropertyView.h +++ b/CesiumGltf/include/CesiumGltf/PropertyView.h @@ -415,7 +415,7 @@ template class PropertyView { /** * @brief Gets the name of the property being viewed. Returns std::nullopt if - * no description was provided. + * no name was specified. */ const std::optional& name() const noexcept { return _name; } @@ -431,7 +431,7 @@ template class PropertyView { /** * @brief Gets the description of the property being viewed. Returns - * std::nullopt if no name was specified. + * std::nullopt if no description was specified. */ const std::optional& description() const noexcept { return _description; @@ -1596,7 +1596,7 @@ class PropertyView, false> { } if (property.scale) { - // Only floating point types can specify an offset. + // Only floating point types can specify a scale. switch (TypeToPropertyType::component) { case PropertyComponentType::Float32: case PropertyComponentType::Float64: diff --git a/CesiumGltf/src/PropertyAttributeView.cpp b/CesiumGltf/src/PropertyAttributeView.cpp index 331ddfe2f..c21f42f1a 100644 --- a/CesiumGltf/src/PropertyAttributeView.cpp +++ b/CesiumGltf/src/PropertyAttributeView.cpp @@ -78,12 +78,12 @@ PropertyAttributeView::PropertyAttributeView( } const ClassProperty* -PropertyAttributeView::getClassProperty(const std::string& propertyName) const { +PropertyAttributeView::getClassProperty(const std::string& propertyId) const { if (_status != PropertyAttributeViewStatus::Valid) { return nullptr; } - auto propertyIter = _pClass->properties.find(propertyName); + auto propertyIter = _pClass->properties.find(propertyId); if (propertyIter == _pClass->properties.end()) { return nullptr; } diff --git a/CesiumGltf/src/PropertyTableView.cpp b/CesiumGltf/src/PropertyTableView.cpp index 3ba261999..fb2ff97db 100644 --- a/CesiumGltf/src/PropertyTableView.cpp +++ b/CesiumGltf/src/PropertyTableView.cpp @@ -134,12 +134,12 @@ PropertyTableView::PropertyTableView( } const ClassProperty* -PropertyTableView::getClassProperty(const std::string& propertyName) const { +PropertyTableView::getClassProperty(const std::string& propertyId) const { if (_status != PropertyTableViewStatus::Valid) { return nullptr; } - auto propertyIter = _pClass->properties.find(propertyName); + auto propertyIter = _pClass->properties.find(propertyId); if (propertyIter == _pClass->properties.end()) { return nullptr; } diff --git a/CesiumGltf/src/PropertyTextureView.cpp b/CesiumGltf/src/PropertyTextureView.cpp index c81247318..f5ac76a11 100644 --- a/CesiumGltf/src/PropertyTextureView.cpp +++ b/CesiumGltf/src/PropertyTextureView.cpp @@ -32,12 +32,12 @@ PropertyTextureView::PropertyTextureView( } const ClassProperty* -PropertyTextureView::getClassProperty(const std::string& propertyName) const { +PropertyTextureView::getClassProperty(const std::string& propertyId) const { if (_status != PropertyTextureViewStatus::Valid) { return nullptr; } - auto propertyIter = _pClass->properties.find(propertyName); + auto propertyIter = _pClass->properties.find(propertyId); if (propertyIter == _pClass->properties.end()) { return nullptr; } diff --git a/CesiumGltf/test/TestPropertyAttributeView.cpp b/CesiumGltf/test/TestPropertyAttributeView.cpp index 3447b0f82..e5b16e460 100644 --- a/CesiumGltf/test/TestPropertyAttributeView.cpp +++ b/CesiumGltf/test/TestPropertyAttributeView.cpp @@ -1534,7 +1534,7 @@ TEST_CASE("Test callback on invalid property Attribute view") { primitive, "TestClassProperty", [&invokedCallbackCount]( - const std::string& /*propertyName*/, + const std::string& /*propertyId*/, auto propertyValue) mutable { invokedCallbackCount++; REQUIRE( @@ -1578,7 +1578,7 @@ TEST_CASE("Test callback on invalid PropertyAttributeProperty") { uint32_t invokedCallbackCount = 0; auto testCallback = [&invokedCallbackCount]( - const std::string& /*propertyName*/, + const std::string& /*propertyId*/, auto propertyValue) mutable { invokedCallbackCount++; REQUIRE( @@ -1623,7 +1623,7 @@ TEST_CASE("Test callback on invalid normalized PropertyAttributeProperty") { uint32_t invokedCallbackCount = 0; auto testCallback = [&invokedCallbackCount]( - const std::string& /*propertyName*/, + const std::string& /*propertyId*/, auto propertyValue) mutable { invokedCallbackCount++; REQUIRE( @@ -1680,7 +1680,7 @@ TEST_CASE("Test callback for scalar PropertyAttributeProperty") { primitive, "TestClassProperty", [&data, &invokedCallbackCount]( - const std::string& /*propertyName*/, + const std::string& /*propertyId*/, auto propertyValue) mutable { invokedCallbackCount++; if constexpr (std::is_same_v< @@ -1748,7 +1748,7 @@ TEST_CASE("Test callback for scalar PropertyAttributeProperty (normalized)") { primitive, "TestClassProperty", [&data, &invokedCallbackCount]( - const std::string& /*propertyName*/, + const std::string& /*propertyId*/, auto propertyValue) mutable { invokedCallbackCount++; if constexpr (std::is_same_v< @@ -1821,7 +1821,7 @@ TEST_CASE("Test callback for vecN PropertyAttributeProperty") { primitive, "TestClassProperty", [&data, &invokedCallbackCount]( - const std::string& /*propertyName*/, + const std::string& /*propertyId*/, auto propertyValue) mutable { invokedCallbackCount++; if constexpr (std::is_same_v< @@ -1893,7 +1893,7 @@ TEST_CASE("Test callback for vecN PropertyAttributeProperty (normalized)") { primitive, "TestClassProperty", [&data, &invokedCallbackCount]( - const std::string& /*propertyName*/, + const std::string& /*propertyId*/, auto propertyValue) mutable { invokedCallbackCount++; if constexpr (std::is_same_v< @@ -1976,7 +1976,7 @@ TEST_CASE("Test callback for matN PropertyAttributeProperty") { primitive, "TestClassProperty", [&data, &invokedCallbackCount]( - const std::string& /*propertyName*/, + const std::string& /*propertyId*/, auto propertyValue) mutable { invokedCallbackCount++; if constexpr (std::is_same_v< @@ -2062,7 +2062,7 @@ TEST_CASE("Test callback for matN PropertyAttributeProperty (normalized)") { primitive, "TestClassProperty", [&data, &invokedCallbackCount]( - const std::string& /*propertyName*/, + const std::string& /*propertyId*/, auto propertyValue) mutable { invokedCallbackCount++; if constexpr (std::is_same_v< @@ -2144,7 +2144,7 @@ TEST_CASE("Test callback on unsupported PropertyAttributeProperty") { primitive, "DoubleClassProperty", [&invokedCallbackCount]( - const std::string& /*propertyName*/, + const std::string& /*propertyId*/, auto propertyValue) mutable { invokedCallbackCount++; REQUIRE( @@ -2157,7 +2157,7 @@ TEST_CASE("Test callback on unsupported PropertyAttributeProperty") { primitive, "ArrayClassProperty", [&invokedCallbackCount]( - const std::string& /*propertyName*/, + const std::string& /*propertyId*/, auto propertyValue) mutable { invokedCallbackCount++; REQUIRE( @@ -2216,7 +2216,7 @@ TEST_CASE( primitive, "TestClassProperty", [defaultValue, &data, &invokedCallbackCount]( - const std::string& /*propertyName*/, + const std::string& /*propertyId*/, auto propertyValue) mutable { invokedCallbackCount++; if constexpr (std::is_same_v< diff --git a/CesiumGltf/test/TestPropertyTableView.cpp b/CesiumGltf/test/TestPropertyTableView.cpp index e15d2c835..a9fdf7414 100644 --- a/CesiumGltf/test/TestPropertyTableView.cpp +++ b/CesiumGltf/test/TestPropertyTableView.cpp @@ -3953,7 +3953,7 @@ TEST_CASE("Test callback on invalid property table view") { uint32_t invokedCallbackCount = 0; auto callback = [&invokedCallbackCount]( - const std::string& /*propertyName*/, + const std::string& /*propertyId*/, auto property) mutable { invokedCallbackCount++; REQUIRE( @@ -3998,7 +3998,7 @@ TEST_CASE("Test callback for invalid PropertyTableProperty") { uint32_t invokedCallbackCount = 0; auto testCallback = [&invokedCallbackCount]( - const std::string& /*propertyName*/, + const std::string& /*propertyId*/, auto propertyValue) mutable { invokedCallbackCount++; REQUIRE(propertyValue.status() != PropertyTablePropertyViewStatus::Valid); @@ -4049,7 +4049,7 @@ TEST_CASE("Test callback for invalid normalized PropertyTableProperty") { view.getPropertyView( "TestClassProperty", [&invokedCallbackCount]( - const std::string& /*propertyName*/, + const std::string& /*propertyId*/, auto propertyValue) mutable { invokedCallbackCount++; REQUIRE( @@ -4102,7 +4102,7 @@ TEST_CASE("Test callback for scalar PropertyTableProperty") { view.getPropertyView( "TestClassProperty", [&values, &invokedCallbackCount]( - const std::string& /*propertyName*/, + const std::string& /*propertyId*/, auto propertyValue) mutable { invokedCallbackCount++; if constexpr (std::is_same_v< @@ -4170,7 +4170,7 @@ TEST_CASE("Test callback for scalar PropertyTableProperty (normalized)") { view.getPropertyView( "TestClassProperty", [&values, &invokedCallbackCount]( - const std::string& /*propertyName*/, + const std::string& /*propertyId*/, auto propertyValue) mutable { invokedCallbackCount++; if constexpr (std::is_same_v< @@ -4241,7 +4241,7 @@ TEST_CASE("Test callback for vecN PropertyTableProperty") { view.getPropertyView( "TestClassProperty", [&values, &invokedCallbackCount]( - const std::string& /*propertyName*/, + const std::string& /*propertyId*/, auto propertyValue) mutable { invokedCallbackCount++; REQUIRE( @@ -4313,7 +4313,7 @@ TEST_CASE("Test callback for vecN PropertyTableProperty (normalized)") { view.getPropertyView( "TestClassProperty", [&values, &invokedCallbackCount]( - const std::string& /*propertyName*/, + const std::string& /*propertyId*/, auto propertyValue) mutable { invokedCallbackCount++; REQUIRE( @@ -4392,7 +4392,7 @@ TEST_CASE("Test callback for matN PropertyTableProperty") { view.getPropertyView( "TestClassProperty", [&values, &invokedCallbackCount]( - const std::string& /*propertyName*/, + const std::string& /*propertyId*/, auto propertyValue) mutable { REQUIRE( propertyValue.status() == PropertyTablePropertyViewStatus::Valid); @@ -4472,7 +4472,7 @@ TEST_CASE("Test callback for matN PropertyTableProperty (normalized)") { view.getPropertyView( "TestClassProperty", [&values, &invokedCallbackCount]( - const std::string& /*propertyName*/, + const std::string& /*propertyId*/, auto propertyValue) mutable { REQUIRE( propertyValue.status() == PropertyTablePropertyViewStatus::Valid); @@ -4554,7 +4554,7 @@ TEST_CASE("Test callback for boolean PropertyTableProperty") { view.getPropertyView( "TestClassProperty", [&expected, &invokedCallbackCount]( - const std::string& /*propertyName*/, + const std::string& /*propertyId*/, auto propertyValue) mutable { invokedCallbackCount++; @@ -4645,7 +4645,7 @@ TEST_CASE("Test callback for string PropertyTableProperty") { view.getPropertyView( "TestClassProperty", [&expected, &invokedCallbackCount]( - const std::string& /*propertyName*/, + const std::string& /*propertyId*/, auto propertyValue) mutable { invokedCallbackCount++; REQUIRE( @@ -4716,7 +4716,7 @@ TEST_CASE("Test callback for scalar array PropertyTableProperty") { view.getPropertyView( "TestClassProperty", [&values, &invokedCallbackCount]( - const std::string& /*propertyName*/, + const std::string& /*propertyId*/, auto propertyValue) { invokedCallbackCount++; REQUIRE( @@ -4792,7 +4792,7 @@ TEST_CASE("Test callback for scalar array PropertyTableProperty (normalized)") { view.getPropertyView( "TestClassProperty", [&values, &invokedCallbackCount]( - const std::string& /*propertyName*/, + const std::string& /*propertyId*/, auto propertyValue) { invokedCallbackCount++; REQUIRE( @@ -4873,7 +4873,7 @@ TEST_CASE("Test callback for vecN array PropertyTableProperty") { view.getPropertyView( "TestClassProperty", [&values, &invokedCallbackCount]( - const std::string& /*propertyName*/, + const std::string& /*propertyId*/, auto propertyValue) { invokedCallbackCount++; REQUIRE( @@ -4955,7 +4955,7 @@ TEST_CASE("Test callback for vecN array PropertyTableProperty (normalized)") { view.getPropertyView( "TestClassProperty", [&values, &invokedCallbackCount]( - const std::string& /*propertyName*/, + const std::string& /*propertyId*/, auto propertyValue) { invokedCallbackCount++; REQUIRE( @@ -5049,7 +5049,7 @@ TEST_CASE("Test callback for matN array PropertyTableProperty") { view.getPropertyView( "TestClassProperty", [&values, &invokedCallbackCount]( - const std::string& /*propertyName*/, + const std::string& /*propertyId*/, auto propertyValue) { invokedCallbackCount++; REQUIRE( @@ -5140,7 +5140,7 @@ TEST_CASE("Test callback for boolean array PropertyTableProperty") { view.getPropertyView( "TestClassProperty", [&expected, &invokedCallbackCount]( - const std::string& /*propertyName*/, + const std::string& /*propertyId*/, auto propertyValue) { invokedCallbackCount++; REQUIRE( @@ -5242,7 +5242,7 @@ TEST_CASE("Test callback for string array PropertyTableProperty") { view.getPropertyView( "TestClassProperty", [&invokedCallbackCount]( - const std::string& /*propertyName*/, + const std::string& /*propertyId*/, auto propertyValue) { invokedCallbackCount++; REQUIRE( @@ -5326,7 +5326,7 @@ TEST_CASE("Test callback for empty PropertyTableProperty with default value") { view.getPropertyView( "TestClassProperty", [defaultValue, count = propertyTable.count, &invokedCallbackCount]( - const std::string& /*propertyName*/, + const std::string& /*propertyId*/, auto propertyValue) mutable { invokedCallbackCount++; if constexpr (std::is_same_v< diff --git a/CesiumGltf/test/TestPropertyTextureView.cpp b/CesiumGltf/test/TestPropertyTextureView.cpp index 5ea2436df..8b616f03b 100644 --- a/CesiumGltf/test/TestPropertyTextureView.cpp +++ b/CesiumGltf/test/TestPropertyTextureView.cpp @@ -1540,7 +1540,7 @@ TEST_CASE("Test callback on invalid property texture view") { view.getPropertyView( "TestClassProperty", [&invokedCallbackCount]( - const std::string& /*propertyName*/, + const std::string& /*propertyId*/, auto propertyValue) mutable { invokedCallbackCount++; REQUIRE( @@ -1580,7 +1580,7 @@ TEST_CASE("Test callback on invalid PropertyTextureProperty") { uint32_t invokedCallbackCount = 0; auto testCallback = [&invokedCallbackCount]( - const std::string& /*propertyName*/, + const std::string& /*propertyId*/, auto propertyValue) mutable { invokedCallbackCount++; REQUIRE(propertyValue.status() != PropertyTexturePropertyViewStatus::Valid); @@ -1622,7 +1622,7 @@ TEST_CASE("Test callback on invalid normalized PropertyTextureProperty") { uint32_t invokedCallbackCount = 0; auto testCallback = [&invokedCallbackCount]( - const std::string& /*propertyName*/, + const std::string& /*propertyId*/, auto propertyValue) mutable { invokedCallbackCount++; REQUIRE( @@ -1690,7 +1690,7 @@ TEST_CASE("Test callback for scalar PropertyTextureProperty") { view.getPropertyView( "TestClassProperty", [&expected, &texCoords, &invokedCallbackCount]( - const std::string& /*propertyName*/, + const std::string& /*propertyId*/, auto propertyValue) mutable { invokedCallbackCount++; if constexpr (std::is_same_v< @@ -1770,7 +1770,7 @@ TEST_CASE("Test callback for scalar PropertyTextureProperty (normalized)") { view.getPropertyView( "TestClassProperty", [&expected, &texCoords, &invokedCallbackCount]( - const std::string& /*propertyName*/, + const std::string& /*propertyId*/, auto propertyValue) mutable { invokedCallbackCount++; if constexpr (std::is_same_v< @@ -1860,7 +1860,7 @@ TEST_CASE("Test callback for vecN PropertyTextureProperty") { view.getPropertyView( "TestClassProperty", [&expected, &texCoords, &invokedCallbackCount]( - const std::string& /*propertyName*/, + const std::string& /*propertyId*/, auto propertyValue) mutable { invokedCallbackCount++; if constexpr (std::is_same_v< @@ -1951,7 +1951,7 @@ TEST_CASE("Test callback for vecN PropertyTextureProperty (normalized)") { view.getPropertyView( "TestClassProperty", [&expected, &texCoords, &invokedCallbackCount]( - const std::string& /*propertyName*/, + const std::string& /*propertyId*/, auto propertyValue) mutable { invokedCallbackCount++; if constexpr (std::is_same_v< @@ -2043,7 +2043,7 @@ TEST_CASE("Test callback for array PropertyTextureProperty") { view.getPropertyView( "TestClassProperty", [&expected, &texCoords, &invokedCallbackCount]( - const std::string& /*propertyName*/, + const std::string& /*propertyId*/, auto propertyValue) mutable { invokedCallbackCount++; if constexpr (std::is_same_v< @@ -2153,7 +2153,7 @@ TEST_CASE("Test callback for array PropertyTextureProperty (normalized)") { view.getPropertyView( "TestClassProperty", [&expected, &texCoords, &invokedCallbackCount]( - const std::string& /*propertyName*/, + const std::string& /*propertyId*/, auto propertyValue) mutable { invokedCallbackCount++; if constexpr ( @@ -2269,7 +2269,7 @@ TEST_CASE("Test callback on unsupported PropertyTextureProperty") { view.getPropertyView( "DoubleClassProperty", [&invokedCallbackCount]( - const std::string& /*propertyName*/, + const std::string& /*propertyId*/, auto propertyValue) mutable { invokedCallbackCount++; REQUIRE( @@ -2281,7 +2281,7 @@ TEST_CASE("Test callback on unsupported PropertyTextureProperty") { view.getPropertyView( "ArrayClassProperty", [&invokedCallbackCount]( - const std::string& /*propertyName*/, + const std::string& /*propertyId*/, auto propertyValue) mutable { invokedCallbackCount++; REQUIRE( @@ -2332,7 +2332,7 @@ TEST_CASE( view.getPropertyView( "TestClassProperty", [defaultValue, &texCoords, &invokedCallbackCount]( - const std::string& /*propertyName*/, + const std::string& /*propertyId*/, auto propertyValue) mutable { invokedCallbackCount++; if constexpr (std::is_same_v< From 4f51905d21170e043e252b5eb63c58a99eb66c02 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Tue, 14 Nov 2023 15:50:24 -0500 Subject: [PATCH 368/421] Add StatusFromAccessor visitor --- CHANGES.md | 2 +- .../include/CesiumGltf/AccessorUtility.h | 15 ++++++ CesiumGltf/test/TestAccessorUtility.cpp | 54 +++++++++++++++++++ 3 files changed, 70 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 49fb29825..ae23f52e6 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -15,7 +15,7 @@ - Added `MetadataConversions`, which enables metadata values to be converted to different types for better usability in runtime engines. - Added various `typedef`s to catch all possible types of `AccessorView`s for an attribute, including `FeatureIdAccessorType` for feature ID attribute accessors, `IndexAccessorType` for index accessors, and `TexCoordAccessorType` for texture coordinate attribute accessors. - Added `GetFeatureIdAccessorView`, `GetIndexAccessorView`, and `GetTexCoordAccessorView` to retrieve the `AccessorView` as a `FeatureIdAccessorType`, `IndexAccessorType`, or `TexCoordAccessorType` respectively. -- Added `CountFromAccessor` to retrieve the accessor size from a `FeatureIdAccessorType`, `IndexAccessorType`, or `TexCoordAccessorType`. +- Added `StatusFromAccessor` and `CountFromAccessor` visitors to retrieve the accessor status and size respectively. This can be used with `FeatureIdAccessorType`, `IndexAccessorType`, or `TexCoordAccessorType`. - Added `FeatureIdFromAccessor` to retrieve feature IDs from a `FeatureIdAccessorType`. - Added `IndicesForFaceFromAccessor` to retrieve the indices of the vertices that make up a face, as supplied by `IndexAccessorType`. - Added `TexCoordFromAccessor` to retrieve the texture coordinates from a `TexCoordAccessorType`. diff --git a/CesiumGltf/include/CesiumGltf/AccessorUtility.h b/CesiumGltf/include/CesiumGltf/AccessorUtility.h index 63524de34..1e5d597d4 100644 --- a/CesiumGltf/include/CesiumGltf/AccessorUtility.h +++ b/CesiumGltf/include/CesiumGltf/AccessorUtility.h @@ -19,6 +19,21 @@ struct CountFromAccessor { } }; +/** + * @brief Visitor that retrieves the status from the given accessor. Returns an + * invalid status for a std::monostate (interpreted as a nonexistent accessor). + */ +struct StatusFromAccessor { + AccessorViewStatus operator()(std::monostate) { + return AccessorViewStatus::InvalidAccessorIndex; + } + + template + AccessorViewStatus operator()(const AccessorView& value) { + return value.status(); + } +}; + /** * Type definition for all kinds of feature ID attribute accessors. */ diff --git a/CesiumGltf/test/TestAccessorUtility.cpp b/CesiumGltf/test/TestAccessorUtility.cpp index de760a5ea..6857482f1 100644 --- a/CesiumGltf/test/TestAccessorUtility.cpp +++ b/CesiumGltf/test/TestAccessorUtility.cpp @@ -30,17 +30,26 @@ TEST_CASE("Test CountFromAccessor") { // Wrong type TexCoordAccessorType texcoordAccessor = AccessorView>(model, accessor); + REQUIRE( + std::visit(StatusFromAccessor{}, texcoordAccessor) != + AccessorViewStatus::Valid); REQUIRE(std::visit(CountFromAccessor{}, texcoordAccessor) == 0); // Wrong component type FeatureIdAccessorType featureIdAccessor = AccessorView(model, accessor); + REQUIRE( + std::visit(StatusFromAccessor{}, featureIdAccessor) != + AccessorViewStatus::Valid); REQUIRE(std::visit(CountFromAccessor{}, featureIdAccessor) == 0); } SECTION("Retrieves from valid accessor") { FeatureIdAccessorType featureIdAccessor = AccessorView(model, accessor); + REQUIRE( + std::visit(StatusFromAccessor{}, featureIdAccessor) == + AccessorViewStatus::Valid); int64_t count = std::visit(CountFromAccessor{}, featureIdAccessor); REQUIRE(count == static_cast(featureIds.size())); } @@ -103,6 +112,9 @@ TEST_CASE("Test GetFeatureIdAccessorView") { SECTION("Handles invalid feature ID set index") { FeatureIdAccessorType featureIDAccessor = GetFeatureIdAccessorView(model, primitive, 2); + REQUIRE( + std::visit(StatusFromAccessor{}, featureIDAccessor) != + AccessorViewStatus::Valid); REQUIRE(std::visit(CountFromAccessor{}, featureIDAccessor) == 0); } @@ -111,6 +123,9 @@ TEST_CASE("Test GetFeatureIdAccessorView") { FeatureIdAccessorType featureIDAccessor = GetFeatureIdAccessorView(model, primitive, 0); + REQUIRE( + std::visit(StatusFromAccessor{}, featureIDAccessor) != + AccessorViewStatus::Valid); REQUIRE(std::visit(CountFromAccessor{}, featureIDAccessor) == 0); model.accessors[0].type = Accessor::Type::SCALAR; @@ -121,6 +136,9 @@ TEST_CASE("Test GetFeatureIdAccessorView") { FeatureIdAccessorType featureIDAccessor = GetFeatureIdAccessorView(model, primitive, 1); + REQUIRE( + std::visit(StatusFromAccessor{}, featureIDAccessor) != + AccessorViewStatus::Valid); REQUIRE(std::visit(CountFromAccessor{}, featureIDAccessor) == 0); model.accessors[1].normalized = false; @@ -129,11 +147,17 @@ TEST_CASE("Test GetFeatureIdAccessorView") { SECTION("Creates from valid feature ID sets") { FeatureIdAccessorType featureIDAccessor = GetFeatureIdAccessorView(model, primitive, 0); + REQUIRE( + std::visit(StatusFromAccessor{}, featureIDAccessor) == + AccessorViewStatus::Valid); REQUIRE( std::visit(CountFromAccessor{}, featureIDAccessor) == static_cast(featureIds0.size())); featureIDAccessor = GetFeatureIdAccessorView(model, primitive, 1); + REQUIRE( + std::visit(StatusFromAccessor{}, featureIDAccessor) == + AccessorViewStatus::Valid); REQUIRE( std::visit(CountFromAccessor{}, featureIDAccessor) == static_cast(featureIds1.size())); @@ -213,6 +237,9 @@ TEST_CASE("Test GetIndexAccessorView") { model.accessors[0].type = Accessor::Type::VEC2; IndexAccessorType indexAccessor = GetIndexAccessorView(model, primitive); + REQUIRE( + std::visit(StatusFromAccessor{}, indexAccessor) != + AccessorViewStatus::Valid); REQUIRE(std::visit(CountFromAccessor{}, indexAccessor) == 0); model.accessors[0].type = Accessor::Type::SCALAR; @@ -222,6 +249,9 @@ TEST_CASE("Test GetIndexAccessorView") { model.accessors[0].componentType = Accessor::ComponentType::BYTE; IndexAccessorType indexAccessor = GetIndexAccessorView(model, primitive); + REQUIRE( + std::visit(StatusFromAccessor{}, indexAccessor) != + AccessorViewStatus::Valid); REQUIRE(std::visit(CountFromAccessor{}, indexAccessor) == 0); model.accessors[0].componentType = Accessor::ComponentType::UNSIGNED_BYTE; @@ -231,6 +261,9 @@ TEST_CASE("Test GetIndexAccessorView") { model.accessors[0].normalized = true; IndexAccessorType indexAccessor = GetIndexAccessorView(model, primitive); + REQUIRE( + std::visit(StatusFromAccessor{}, indexAccessor) != + AccessorViewStatus::Valid); REQUIRE(std::visit(CountFromAccessor{}, indexAccessor) == 0); model.accessors[0].normalized = false; @@ -238,6 +271,9 @@ TEST_CASE("Test GetIndexAccessorView") { SECTION("Creates from valid accessor") { IndexAccessorType indexAccessor = GetIndexAccessorView(model, primitive); + REQUIRE( + std::visit(StatusFromAccessor{}, indexAccessor) == + AccessorViewStatus::Valid); REQUIRE( std::visit(CountFromAccessor{}, indexAccessor) == static_cast(indices.size())); @@ -411,6 +447,9 @@ TEST_CASE("Test GetTexCoordAccessorView") { SECTION("Handles invalid texture coordinate set index") { TexCoordAccessorType texCoordAccessor = GetTexCoordAccessorView(model, primitive, 2); + REQUIRE( + std::visit(StatusFromAccessor{}, texCoordAccessor) != + AccessorViewStatus::Valid); REQUIRE(std::visit(CountFromAccessor{}, texCoordAccessor) == 0); } @@ -419,6 +458,9 @@ TEST_CASE("Test GetTexCoordAccessorView") { TexCoordAccessorType texCoordAccessor = GetTexCoordAccessorView(model, primitive, 0); + REQUIRE( + std::visit(StatusFromAccessor{}, texCoordAccessor) != + AccessorViewStatus::Valid); REQUIRE(std::visit(CountFromAccessor{}, texCoordAccessor) == 0); model.accessors[0].type = Accessor::Type::VEC2; @@ -429,6 +471,9 @@ TEST_CASE("Test GetTexCoordAccessorView") { TexCoordAccessorType texCoordAccessor = GetTexCoordAccessorView(model, primitive, 0); + REQUIRE( + std::visit(StatusFromAccessor{}, texCoordAccessor) != + AccessorViewStatus::Valid); REQUIRE(std::visit(CountFromAccessor{}, texCoordAccessor) == 0); model.accessors[0].componentType = Accessor::ComponentType::FLOAT; @@ -439,6 +484,9 @@ TEST_CASE("Test GetTexCoordAccessorView") { TexCoordAccessorType texCoordAccessor = GetTexCoordAccessorView(model, primitive, 2); + REQUIRE( + std::visit(StatusFromAccessor{}, texCoordAccessor) != + AccessorViewStatus::Valid); REQUIRE(std::visit(CountFromAccessor{}, texCoordAccessor) == 0); model.accessors[1].normalized = true; @@ -447,11 +495,17 @@ TEST_CASE("Test GetTexCoordAccessorView") { SECTION("Creates from valid texture coordinate sets") { TexCoordAccessorType texCoordAccessor = GetTexCoordAccessorView(model, primitive, 0); + REQUIRE( + std::visit(StatusFromAccessor{}, texCoordAccessor) == + AccessorViewStatus::Valid); REQUIRE( std::visit(CountFromAccessor{}, texCoordAccessor) == static_cast(texCoords0.size())); texCoordAccessor = GetTexCoordAccessorView(model, primitive, 1); + REQUIRE( + std::visit(StatusFromAccessor{}, texCoordAccessor) == + AccessorViewStatus::Valid); REQUIRE( std::visit(CountFromAccessor{}, texCoordAccessor) == static_cast(texCoords1.size())); From 268ead240238d1556d02fff45127ea998b712a0e Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Tue, 14 Nov 2023 16:07:09 -0500 Subject: [PATCH 369/421] Add static casts --- CesiumGltf/test/TestAccessorUtility.cpp | 48 ++++++++++++++----------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/CesiumGltf/test/TestAccessorUtility.cpp b/CesiumGltf/test/TestAccessorUtility.cpp index 6857482f1..b52110e8d 100644 --- a/CesiumGltf/test/TestAccessorUtility.cpp +++ b/CesiumGltf/test/TestAccessorUtility.cpp @@ -2,6 +2,8 @@ #include +#include + using namespace CesiumGltf; TEST_CASE("Test CountFromAccessor") { @@ -14,7 +16,7 @@ TEST_CASE("Test CountFromAccessor") { buffer.cesium.data.data(), featureIds.data(), buffer.cesium.data.size()); - buffer.byteLength = buffer.cesium.data.size(); + buffer.byteLength = static_cast(buffer.cesium.data.size()); BufferView& bufferView = model.bufferViews.emplace_back(); bufferView.buffer = 0; @@ -24,7 +26,7 @@ TEST_CASE("Test CountFromAccessor") { accessor.bufferView = 0; accessor.componentType = Accessor::ComponentType::UNSIGNED_BYTE; accessor.type = Accessor::Type::SCALAR; - accessor.count = bufferView.byteLength / sizeof(uint8_t); + accessor.count = bufferView.byteLength; SECTION("Handles invalid accessor") { // Wrong type @@ -67,7 +69,7 @@ TEST_CASE("Test GetFeatureIdAccessorView") { buffer.cesium.data.data(), featureIds0.data(), buffer.cesium.data.size()); - buffer.byteLength = buffer.cesium.data.size(); + buffer.byteLength = static_cast(buffer.cesium.data.size()); BufferView& bufferView = model.bufferViews.emplace_back(); bufferView.buffer = 0; @@ -77,7 +79,7 @@ TEST_CASE("Test GetFeatureIdAccessorView") { accessor.bufferView = 0; accessor.componentType = Accessor::ComponentType::UNSIGNED_BYTE; accessor.type = Accessor::Type::SCALAR; - accessor.count = bufferView.byteLength / sizeof(uint8_t); + accessor.count = bufferView.byteLength; } std::vector featureIds1{5, 6, 7, 8}; @@ -90,7 +92,7 @@ TEST_CASE("Test GetFeatureIdAccessorView") { buffer.cesium.data.data(), featureIds1.data(), buffer.cesium.data.size()); - buffer.byteLength = buffer.cesium.data.size(); + buffer.byteLength = static_cast(buffer.cesium.data.size()); BufferView& bufferView = model.bufferViews.emplace_back(); bufferView.buffer = 1; @@ -100,7 +102,8 @@ TEST_CASE("Test GetFeatureIdAccessorView") { accessor.bufferView = 1; accessor.componentType = Accessor::ComponentType::UNSIGNED_SHORT; accessor.type = Accessor::Type::SCALAR; - accessor.count = bufferView.byteLength / sizeof(uint16_t); + accessor.count = + bufferView.byteLength / static_cast(sizeof(uint16_t)); } Mesh& mesh = model.meshes.emplace_back(); @@ -174,7 +177,7 @@ TEST_CASE("FeatureIdFromAccessor") { buffer.cesium.data.data(), featureIds.data(), buffer.cesium.data.size()); - buffer.byteLength = buffer.cesium.data.size(); + buffer.byteLength = static_cast(buffer.cesium.data.size()); BufferView& bufferView = model.bufferViews.emplace_back(); bufferView.buffer = 0; @@ -184,7 +187,7 @@ TEST_CASE("FeatureIdFromAccessor") { accessor.bufferView = 0; accessor.componentType = Accessor::ComponentType::BYTE; accessor.type = Accessor::Type::SCALAR; - accessor.count = bufferView.byteLength / sizeof(int8_t); + accessor.count = bufferView.byteLength; SECTION("Handles invalid accessor") { // Wrong component type @@ -216,7 +219,7 @@ TEST_CASE("Test GetIndexAccessorView") { buffer.cesium.data.data(), indices.data(), buffer.cesium.data.size()); - buffer.byteLength = buffer.cesium.data.size(); + buffer.byteLength = static_cast(buffer.cesium.data.size()); BufferView& bufferView = model.bufferViews.emplace_back(); bufferView.buffer = 0; @@ -226,7 +229,7 @@ TEST_CASE("Test GetIndexAccessorView") { accessor.bufferView = 0; accessor.componentType = Accessor::ComponentType::UNSIGNED_BYTE; accessor.type = Accessor::Type::SCALAR; - accessor.count = bufferView.byteLength / sizeof(uint8_t); + accessor.count = bufferView.byteLength; } Mesh& mesh = model.meshes.emplace_back(); @@ -298,7 +301,7 @@ TEST_CASE("Test FaceVertexIndicesFromAccessor") { buffer.cesium.data.data(), indices.data(), buffer.cesium.data.size()); - buffer.byteLength = buffer.cesium.data.size(); + buffer.byteLength = static_cast(buffer.cesium.data.size()); BufferView& bufferView = model.bufferViews.emplace_back(); bufferView.buffer = 0; @@ -308,7 +311,8 @@ TEST_CASE("Test FaceVertexIndicesFromAccessor") { accessor.bufferView = 0; accessor.componentType = Accessor::ComponentType::UNSIGNED_INT; accessor.type = Accessor::Type::SCALAR; - accessor.count = bufferView.byteLength / sizeof(uint32_t); + accessor.count = + bufferView.byteLength / static_cast(sizeof(uint32_t)); SECTION("Handles invalid accessor") { // Wrong component type @@ -397,7 +401,7 @@ TEST_CASE("Test GetTexCoordAccessorView") { buffer.cesium.data.data(), texCoords0.data(), buffer.cesium.data.size()); - buffer.byteLength = buffer.cesium.data.size(); + buffer.byteLength = static_cast(buffer.cesium.data.size()); BufferView& bufferView = model.bufferViews.emplace_back(); bufferView.buffer = 0; @@ -407,7 +411,8 @@ TEST_CASE("Test GetTexCoordAccessorView") { accessor.bufferView = 0; accessor.componentType = Accessor::ComponentType::FLOAT; accessor.type = Accessor::Type::VEC2; - accessor.count = bufferView.byteLength / sizeof(glm::vec2); + accessor.count = + bufferView.byteLength / static_cast(sizeof(glm::vec2)); } std::vector texCoords1{ @@ -424,7 +429,7 @@ TEST_CASE("Test GetTexCoordAccessorView") { buffer.cesium.data.data(), texCoords1.data(), buffer.cesium.data.size()); - buffer.byteLength = buffer.cesium.data.size(); + buffer.byteLength = static_cast(buffer.cesium.data.size()); BufferView& bufferView = model.bufferViews.emplace_back(); bufferView.buffer = 1; @@ -435,7 +440,8 @@ TEST_CASE("Test GetTexCoordAccessorView") { accessor.componentType = Accessor::ComponentType::UNSIGNED_BYTE; accessor.type = Accessor::Type::VEC2; accessor.normalized = true; - accessor.count = bufferView.byteLength / sizeof(glm::u8vec2); + accessor.count = + bufferView.byteLength / static_cast(sizeof(glm::u8vec2)); } Mesh& mesh = model.meshes.emplace_back(); @@ -528,7 +534,7 @@ TEST_CASE("Test TexCoordFromAccessor") { buffer.cesium.data.data(), texCoords0.data(), buffer.cesium.data.size()); - buffer.byteLength = buffer.cesium.data.size(); + buffer.byteLength = static_cast(buffer.cesium.data.size()); BufferView& bufferView = model.bufferViews.emplace_back(); bufferView.buffer = 0; @@ -538,7 +544,8 @@ TEST_CASE("Test TexCoordFromAccessor") { accessor.bufferView = 0; accessor.componentType = Accessor::ComponentType::FLOAT; accessor.type = Accessor::Type::VEC2; - accessor.count = bufferView.byteLength / sizeof(glm::vec2); + accessor.count = + bufferView.byteLength / static_cast(sizeof(glm::vec2)); } std::vector texCoords1{ @@ -555,7 +562,7 @@ TEST_CASE("Test TexCoordFromAccessor") { buffer.cesium.data.data(), texCoords1.data(), buffer.cesium.data.size()); - buffer.byteLength = buffer.cesium.data.size(); + buffer.byteLength = static_cast(buffer.cesium.data.size()); BufferView& bufferView = model.bufferViews.emplace_back(); bufferView.buffer = 1; @@ -566,7 +573,8 @@ TEST_CASE("Test TexCoordFromAccessor") { accessor.componentType = Accessor::ComponentType::UNSIGNED_BYTE; accessor.type = Accessor::Type::VEC2; accessor.normalized = true; - accessor.count = bufferView.byteLength / sizeof(glm::u8vec2); + accessor.count = + bufferView.byteLength / static_cast(sizeof(glm::u8vec2)); } Mesh& mesh = model.meshes.emplace_back(); From 761630d45a68cdb2cfa5b47bdfd7ff731c407744 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 15 Nov 2023 17:20:08 +1100 Subject: [PATCH 370/421] Add CesiumGltfContent library. --- CMakeLists.txt | 1 + Cesium3DTilesContent/CMakeLists.txt | 1 + .../src/QuantizedMeshLoader.cpp | 3 +- .../src/upsampleGltfForRasterOverlays.cpp | 3 +- .../test/TestSkirtMeshMetadata.cpp | 4 +- .../test/TestUpsampleGltfForRasterOverlay.cpp | 3 +- .../Cesium3DTilesSelection/TileContent.h | 17 +++-- .../Cesium3DTilesSelection/TileLoadResult.h | 5 +- .../TilesetContentLoader.h | 2 +- .../src/LayerJsonTerrainLoader.cpp | 5 +- Cesium3DTilesSelection/src/TileContent.cpp | 1 + .../src/TilesetContentManager.cpp | 6 +- .../test/TestTilesetContentManager.cpp | 1 - CesiumGltfContent/CMakeLists.txt | 68 +++++++++++++++++++ .../CesiumGltfContent}/GltfUtilities.h | 6 +- .../CesiumGltfContent}/ImageManipulation.h | 8 +-- .../include/CesiumGltfContent/Library.h | 16 +++++ .../CesiumGltfContent}/SkirtMeshMetadata.h | 2 +- .../src/GltfUtilities.cpp | 8 +-- .../src/ImageManipulation.cpp | 7 +- .../src/SkirtMeshMetadata.cpp | 6 +- .../test/TestImageManipulation.cpp | 5 +- CesiumRasterOverlays/CMakeLists.txt | 1 + .../RasterOverlayDetails.h | 6 +- .../RasterOverlayUtilities.h | 8 +-- .../src/QuadtreeRasterOverlayTileProvider.cpp | 3 +- .../src/RasterOverlayDetails.cpp | 6 +- .../src/RasterOverlayUtilities.cpp | 12 ++-- 28 files changed, 154 insertions(+), 60 deletions(-) create mode 100644 CesiumGltfContent/CMakeLists.txt rename {Cesium3DTilesContent/include/Cesium3DTilesContent => CesiumGltfContent/include/CesiumGltfContent}/GltfUtilities.h (96%) rename {CesiumGltfReader/include/CesiumGltfReader => CesiumGltfContent/include/CesiumGltfContent}/ImageManipulation.h (95%) create mode 100644 CesiumGltfContent/include/CesiumGltfContent/Library.h rename {Cesium3DTilesContent/include/Cesium3DTilesContent => CesiumGltfContent/include/CesiumGltfContent}/SkirtMeshMetadata.h (96%) rename {Cesium3DTilesContent => CesiumGltfContent}/src/GltfUtilities.cpp (96%) rename {CesiumGltfReader => CesiumGltfContent}/src/ImageManipulation.cpp (97%) rename {Cesium3DTilesContent => CesiumGltfContent}/src/SkirtMeshMetadata.cpp (97%) rename {Cesium3DTilesSelection/include/Cesium3DTilesSelection => CesiumRasterOverlays/include/CesiumRasterOverlays}/RasterOverlayDetails.h (95%) rename {Cesium3DTilesSelection/include/Cesium3DTilesSelection => CesiumRasterOverlays/include/CesiumRasterOverlays}/RasterOverlayUtilities.h (93%) rename {Cesium3DTilesSelection => CesiumRasterOverlays}/src/RasterOverlayDetails.cpp (93%) rename {Cesium3DTilesSelection => CesiumRasterOverlays}/src/RasterOverlayUtilities.cpp (97%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3192ced42..8a44ad99f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -142,6 +142,7 @@ add_subdirectory(CesiumGeometry) add_subdirectory(CesiumGeospatial) add_subdirectory(CesiumJsonReader) add_subdirectory(CesiumJsonWriter) +add_subdirectory(CesiumGltfContent) add_subdirectory(CesiumGltfReader) add_subdirectory(CesiumGltfWriter) add_subdirectory(CesiumAsync) diff --git a/Cesium3DTilesContent/CMakeLists.txt b/Cesium3DTilesContent/CMakeLists.txt index fd45b00a9..fd298ef82 100644 --- a/Cesium3DTilesContent/CMakeLists.txt +++ b/Cesium3DTilesContent/CMakeLists.txt @@ -59,6 +59,7 @@ target_link_libraries(Cesium3DTilesContent CesiumGeometry CesiumGeospatial CesiumGltf + CesiumGltfContent CesiumGltfReader CesiumUtility ) diff --git a/Cesium3DTilesContent/src/QuantizedMeshLoader.cpp b/Cesium3DTilesContent/src/QuantizedMeshLoader.cpp index 0258ab120..b3c43a92c 100644 --- a/Cesium3DTilesContent/src/QuantizedMeshLoader.cpp +++ b/Cesium3DTilesContent/src/QuantizedMeshLoader.cpp @@ -1,9 +1,9 @@ #include -#include #include #include #include #include +#include #include #include #include @@ -22,6 +22,7 @@ using namespace Cesium3DTilesContent; using namespace CesiumUtility; using namespace CesiumGeospatial; using namespace CesiumGeometry; +using namespace CesiumGltfContent; struct QuantizedMeshHeader { // The center of the tile in Earth-centered Fixed coordinates. diff --git a/Cesium3DTilesContent/src/upsampleGltfForRasterOverlays.cpp b/Cesium3DTilesContent/src/upsampleGltfForRasterOverlays.cpp index d1c996e8e..5071489b4 100644 --- a/Cesium3DTilesContent/src/upsampleGltfForRasterOverlays.cpp +++ b/Cesium3DTilesContent/src/upsampleGltfForRasterOverlays.cpp @@ -1,10 +1,10 @@ -#include #include #include #include #include #include #include +#include #include #include @@ -14,6 +14,7 @@ #include using namespace CesiumGltf; +using namespace CesiumGltfContent; namespace Cesium3DTilesContent { struct EdgeVertex { diff --git a/Cesium3DTilesContent/test/TestSkirtMeshMetadata.cpp b/Cesium3DTilesContent/test/TestSkirtMeshMetadata.cpp index d85e2c135..43099b08d 100644 --- a/Cesium3DTilesContent/test/TestSkirtMeshMetadata.cpp +++ b/Cesium3DTilesContent/test/TestSkirtMeshMetadata.cpp @@ -1,9 +1,9 @@ -#include +#include #include #include -using namespace Cesium3DTilesContent; +using namespace CesiumGltfContent; using namespace CesiumUtility; TEST_CASE("Test converting skirt mesh metadata to gltf extras") { diff --git a/Cesium3DTilesContent/test/TestUpsampleGltfForRasterOverlay.cpp b/Cesium3DTilesContent/test/TestUpsampleGltfForRasterOverlay.cpp index 9a70c1c5b..264b58a7d 100644 --- a/Cesium3DTilesContent/test/TestUpsampleGltfForRasterOverlay.cpp +++ b/Cesium3DTilesContent/test/TestUpsampleGltfForRasterOverlay.cpp @@ -1,8 +1,8 @@ -#include #include #include #include #include +#include #include #include @@ -15,6 +15,7 @@ using namespace Cesium3DTilesContent; using namespace CesiumUtility; using namespace CesiumGeospatial; using namespace CesiumGltf; +using namespace CesiumGltfContent; static void checkSkirt( const Ellipsoid& ellipsoid, diff --git a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/TileContent.h b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/TileContent.h index 848d2f836..74d25a7d3 100644 --- a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/TileContent.h +++ b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/TileContent.h @@ -1,11 +1,11 @@ #pragma once #include "Library.h" -#include "RasterOverlayDetails.h" #include "TilesetMetadata.h" #include #include +#include #include #include @@ -102,29 +102,32 @@ class CESIUM3DTILESSELECTION_API TileRenderContent { * * @return The {@link RasterOverlayDetails} that is owned by this content */ - const RasterOverlayDetails& getRasterOverlayDetails() const noexcept; + const CesiumRasterOverlays::RasterOverlayDetails& + getRasterOverlayDetails() const noexcept; /** * @brief Get the {@link RasterOverlayDetails} which is the result of generating raster overlay UVs for the glTF model * * @return The {@link RasterOverlayDetails} that is owned by this content */ - RasterOverlayDetails& getRasterOverlayDetails() noexcept; + CesiumRasterOverlays::RasterOverlayDetails& + getRasterOverlayDetails() noexcept; /** * @brief Set the {@link RasterOverlayDetails} which is the result of generating raster overlay UVs for the glTF model * * @param rasterOverlayDetails The {@link RasterOverlayDetails} that will be owned by this content */ - void - setRasterOverlayDetails(const RasterOverlayDetails& rasterOverlayDetails); + void setRasterOverlayDetails( + const CesiumRasterOverlays::RasterOverlayDetails& rasterOverlayDetails); /** * @brief Set the {@link RasterOverlayDetails} which is the result of generating raster overlay UVs for the glTF model * * @param rasterOverlayDetails The {@link RasterOverlayDetails} that will be owned by this content */ - void setRasterOverlayDetails(RasterOverlayDetails&& rasterOverlayDetails); + void setRasterOverlayDetails( + CesiumRasterOverlays::RasterOverlayDetails&& rasterOverlayDetails); /** * @brief Get the list of {@link Credit} of the content @@ -192,7 +195,7 @@ class CESIUM3DTILESSELECTION_API TileRenderContent { private: CesiumGltf::Model _model; void* _pRenderResources; - RasterOverlayDetails _rasterOverlayDetails; + CesiumRasterOverlays::RasterOverlayDetails _rasterOverlayDetails; std::vector _credits; float _lodTransitionFadePercentage; }; diff --git a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/TileLoadResult.h b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/TileLoadResult.h index dce5889f4..d3eacf4e9 100644 --- a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/TileLoadResult.h +++ b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/TileLoadResult.h @@ -1,12 +1,12 @@ #pragma once #include "BoundingVolume.h" -#include "RasterOverlayDetails.h" #include "TileContent.h" #include #include #include +#include #include #include @@ -98,7 +98,8 @@ struct CESIUM3DTILESSELECTION_API TileLoadResult { * @brief Holds details of the {@link TileRenderContent} that are useful * for raster overlays. */ - std::optional rasterOverlayDetails; + std::optional + rasterOverlayDetails; /** * @brief The request that is created to download the tile content. diff --git a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/TilesetContentLoader.h b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/TilesetContentLoader.h index 5b767456b..5564cc22e 100644 --- a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/TilesetContentLoader.h +++ b/Cesium3DTilesSelection/include/Cesium3DTilesSelection/TilesetContentLoader.h @@ -2,7 +2,6 @@ #include "BoundingVolume.h" #include "Library.h" -#include "RasterOverlayDetails.h" #include "TileContent.h" #include "TileLoadResult.h" #include "TilesetOptions.h" @@ -12,6 +11,7 @@ #include #include #include +#include #include diff --git a/Cesium3DTilesSelection/src/LayerJsonTerrainLoader.cpp b/Cesium3DTilesSelection/src/LayerJsonTerrainLoader.cpp index 8f44570c3..27b43fbc4 100644 --- a/Cesium3DTilesSelection/src/LayerJsonTerrainLoader.cpp +++ b/Cesium3DTilesSelection/src/LayerJsonTerrainLoader.cpp @@ -1,11 +1,11 @@ #include "LayerJsonTerrainLoader.h" -#include #include #include -#include #include #include +#include +#include #include #include @@ -17,6 +17,7 @@ using namespace Cesium3DTilesContent; using namespace Cesium3DTilesSelection; using namespace CesiumGeometry; using namespace CesiumGeospatial; +using namespace CesiumRasterOverlays; using namespace CesiumUtility; namespace { diff --git a/Cesium3DTilesSelection/src/TileContent.cpp b/Cesium3DTilesSelection/src/TileContent.cpp index 30ed49bbe..0766f88ef 100644 --- a/Cesium3DTilesSelection/src/TileContent.cpp +++ b/Cesium3DTilesSelection/src/TileContent.cpp @@ -1,5 +1,6 @@ #include +using namespace CesiumRasterOverlays; using namespace CesiumUtility; namespace Cesium3DTilesSelection { diff --git a/Cesium3DTilesSelection/src/TilesetContentManager.cpp b/Cesium3DTilesSelection/src/TilesetContentManager.cpp index 5f6274977..cbcac9add 100644 --- a/Cesium3DTilesSelection/src/TilesetContentManager.cpp +++ b/Cesium3DTilesSelection/src/TilesetContentManager.cpp @@ -5,15 +5,15 @@ #include "TileContentLoadInfo.h" #include "TilesetJsonLoader.h" -#include #include -#include #include #include +#include #include #include #include #include +#include #include #include @@ -22,7 +22,7 @@ #include -using namespace Cesium3DTilesContent; +using namespace CesiumGltfContent; using namespace CesiumRasterOverlays; using namespace CesiumUtility; diff --git a/Cesium3DTilesSelection/test/TestTilesetContentManager.cpp b/Cesium3DTilesSelection/test/TestTilesetContentManager.cpp index 8bc04fbac..ec842ec48 100644 --- a/Cesium3DTilesSelection/test/TestTilesetContentManager.cpp +++ b/Cesium3DTilesSelection/test/TestTilesetContentManager.cpp @@ -1,7 +1,6 @@ #include "SimplePrepareRendererResource.h" #include "TilesetContentManager.h" -#include #include #include #include diff --git a/CesiumGltfContent/CMakeLists.txt b/CesiumGltfContent/CMakeLists.txt new file mode 100644 index 000000000..b499be5de --- /dev/null +++ b/CesiumGltfContent/CMakeLists.txt @@ -0,0 +1,68 @@ +add_library(CesiumGltfContent "") +configure_cesium_library(CesiumGltfContent) + +cesium_glob_files(CESIUM_GLTF_CONTENT_SOURCES + ${CMAKE_CURRENT_LIST_DIR}/src/*.cpp + ${CMAKE_CURRENT_LIST_DIR}/generated/src/*.cpp +) +cesium_glob_files(CESIUM_GLTF_CONTENT_HEADERS + ${CMAKE_CURRENT_LIST_DIR}/src/*.h + ${CMAKE_CURRENT_LIST_DIR}/generated/src/*.h +) +cesium_glob_files(CESIUM_GLTF_CONTENT_PUBLIC_HEADERS + ${CMAKE_CURRENT_LIST_DIR}/include/CesiumGltfContent/*.h + ${CMAKE_CURRENT_LIST_DIR}/generated/include/CesiumGltfContent/*.h +) +cesium_glob_files(CESIUM_GLTF_CONTENT_TEST_SOURCES + ${CMAKE_CURRENT_LIST_DIR}/test/*.cpp +) +cesium_glob_files(CESIUM_GLTF_CONTENT_TEST_HEADERS + ${CMAKE_CURRENT_LIST_DIR}/test/*.h +) + +set_target_properties(CesiumGltfContent + PROPERTIES + TEST_SOURCES "${CESIUM_GLTF_CONTENT_TEST_SOURCES}" + TEST_HEADERS "${CESIUM_GLTF_CONTENT_TEST_HEADERS}" + TEST_DATA_DIR ${CMAKE_CURRENT_LIST_DIR}/test/data +) + +set_target_properties(CesiumGltfContent + PROPERTIES + PUBLIC_HEADER "${CESIUM_GLTF_CONTENT_PUBLIC_HEADERS}" +) + +target_sources( + CesiumGltfContent + PRIVATE + ${CESIUM_GLTF_CONTENT_SOURCES} + ${CESIUM_GLTF_CONTENT_HEADERS} + PUBLIC + ${CESIUM_GLTF_CONTENT_PUBLIC_HEADERS} +) + +target_include_directories( + CesiumGltfContent + SYSTEM PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/include + ${CMAKE_CURRENT_LIST_DIR}/generated/include + PRIVATE + ${CMAKE_CURRENT_LIST_DIR}/src + ${CMAKE_CURRENT_LIST_DIR}/generated/src + ${CESIUM_NATIVE_STB_INCLUDE_DIR} +) + +target_link_libraries(CesiumGltfContent + PUBLIC + CesiumAsync + CesiumGeometry + CesiumGeospatial + CesiumGltf + CesiumGltfReader + CesiumUtility +) + +install(TARGETS CesiumGltfContent + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/CesiumGltfContent +) diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfUtilities.h b/CesiumGltfContent/include/CesiumGltfContent/GltfUtilities.h similarity index 96% rename from Cesium3DTilesContent/include/Cesium3DTilesContent/GltfUtilities.h rename to CesiumGltfContent/include/CesiumGltfContent/GltfUtilities.h index 5bb0c1a63..267bb8255 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/GltfUtilities.h +++ b/CesiumGltfContent/include/CesiumGltfContent/GltfUtilities.h @@ -14,12 +14,12 @@ namespace CesiumGltf { struct Model; } -namespace Cesium3DTilesContent { +namespace CesiumGltfContent { /** * A collection of utility functions that are used to process and transform a * gltf model */ -struct CESIUM3DTILESCONTENT_API GltfUtilities { +struct CESIUMGLTFCONTENT_API GltfUtilities { /** * @brief Applies the glTF's RTC_CENTER, if any, to the given transform. * @@ -93,4 +93,4 @@ struct CESIUM3DTILESCONTENT_API GltfUtilities { static std::vector parseGltfCopyright(const CesiumGltf::Model& gltf); }; -} // namespace Cesium3DTilesContent +} // namespace CesiumGltfContent diff --git a/CesiumGltfReader/include/CesiumGltfReader/ImageManipulation.h b/CesiumGltfContent/include/CesiumGltfContent/ImageManipulation.h similarity index 95% rename from CesiumGltfReader/include/CesiumGltfReader/ImageManipulation.h rename to CesiumGltfContent/include/CesiumGltfContent/ImageManipulation.h index 26fa73b7b..8c338f118 100644 --- a/CesiumGltfReader/include/CesiumGltfReader/ImageManipulation.h +++ b/CesiumGltfContent/include/CesiumGltfContent/ImageManipulation.h @@ -1,6 +1,6 @@ #pragma once -#include "CesiumGltfReader/Library.h" +#include "Library.h" #include #include @@ -10,7 +10,7 @@ namespace CesiumGltf { struct ImageCesium; } -namespace CesiumGltfReader { +namespace CesiumGltfContent { /** * @brief Specifies a rectangle of pixels in an image. @@ -37,7 +37,7 @@ struct PixelRectangle { int32_t height; }; -class CESIUMGLTFREADER_API ImageManipulation { +class CESIUMGLTFCONTENT_API ImageManipulation { public: /** * @brief Directly copies pixels from a source to a target, without validating @@ -97,4 +97,4 @@ class CESIUMGLTFREADER_API ImageManipulation { const PixelRectangle& sourcePixels); }; -} // namespace CesiumGltfReader +} // namespace CesiumGltfContent diff --git a/CesiumGltfContent/include/CesiumGltfContent/Library.h b/CesiumGltfContent/include/CesiumGltfContent/Library.h new file mode 100644 index 000000000..7b8ad7942 --- /dev/null +++ b/CesiumGltfContent/include/CesiumGltfContent/Library.h @@ -0,0 +1,16 @@ +#pragma once + +/** + * @brief Classes that support manipulating the content of a glTF. + */ +namespace CesiumGltfContent {} + +#if defined(_WIN32) && defined(CESIUM_SHARED) +#ifdef CESIUMGLTFCONTENT_BUILDING +#define CESIUMGLTFCONTENT_API __declspec(dllexport) +#else +#define CESIUMGLTFCONTENT_API __declspec(dllimport) +#endif +#else +#define CESIUMGLTFCONTENT_API +#endif diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/SkirtMeshMetadata.h b/CesiumGltfContent/include/CesiumGltfContent/SkirtMeshMetadata.h similarity index 96% rename from Cesium3DTilesContent/include/Cesium3DTilesContent/SkirtMeshMetadata.h rename to CesiumGltfContent/include/CesiumGltfContent/SkirtMeshMetadata.h index 45759a44e..16c291b63 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/SkirtMeshMetadata.h +++ b/CesiumGltfContent/include/CesiumGltfContent/SkirtMeshMetadata.h @@ -4,7 +4,7 @@ #include -namespace Cesium3DTilesContent { +namespace CesiumGltfContent { struct SkirtMeshMetadata { SkirtMeshMetadata() noexcept : noSkirtIndicesBegin{0}, diff --git a/Cesium3DTilesContent/src/GltfUtilities.cpp b/CesiumGltfContent/src/GltfUtilities.cpp similarity index 96% rename from Cesium3DTilesContent/src/GltfUtilities.cpp rename to CesiumGltfContent/src/GltfUtilities.cpp index 2d19cffbb..0f9b5db1a 100644 --- a/Cesium3DTilesContent/src/GltfUtilities.cpp +++ b/CesiumGltfContent/src/GltfUtilities.cpp @@ -1,14 +1,14 @@ -#include -#include #include #include #include #include #include +#include +#include #include -namespace Cesium3DTilesContent { +namespace CesiumGltfContent { /*static*/ glm::dmat4x4 GltfUtilities::applyRtcCenter( const CesiumGltf::Model& gltf, const glm::dmat4x4& rootTransform) { @@ -150,4 +150,4 @@ GltfUtilities::parseGltfCopyright(const CesiumGltf::Model& gltf) { return result; } -} // namespace Cesium3DTilesContent +} // namespace CesiumGltfContent diff --git a/CesiumGltfReader/src/ImageManipulation.cpp b/CesiumGltfContent/src/ImageManipulation.cpp similarity index 97% rename from CesiumGltfReader/src/ImageManipulation.cpp rename to CesiumGltfContent/src/ImageManipulation.cpp index fcd748499..aa5971a24 100644 --- a/CesiumGltfReader/src/ImageManipulation.cpp +++ b/CesiumGltfContent/src/ImageManipulation.cpp @@ -1,6 +1,5 @@ -#include "CesiumGltfReader/ImageManipulation.h" - #include +#include #include #include @@ -8,7 +7,7 @@ #define STB_IMAGE_RESIZE_IMPLEMENTATION #include -namespace CesiumGltfReader { +namespace CesiumGltfContent { void ImageManipulation::unsafeBlitImage( std::byte* pTarget, @@ -114,4 +113,4 @@ bool ImageManipulation::blitImage( return true; } -} // namespace CesiumGltfReader +} // namespace CesiumGltfContent diff --git a/Cesium3DTilesContent/src/SkirtMeshMetadata.cpp b/CesiumGltfContent/src/SkirtMeshMetadata.cpp similarity index 97% rename from Cesium3DTilesContent/src/SkirtMeshMetadata.cpp rename to CesiumGltfContent/src/SkirtMeshMetadata.cpp index 5d5985e76..f514d7b9e 100644 --- a/Cesium3DTilesContent/src/SkirtMeshMetadata.cpp +++ b/CesiumGltfContent/src/SkirtMeshMetadata.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include @@ -6,7 +6,7 @@ using namespace CesiumUtility; -namespace Cesium3DTilesContent { +namespace CesiumGltfContent { std::optional SkirtMeshMetadata::parseFromGltfExtras(const JsonValue::Object& extras) { auto skirtIt = extras.find("skirtMeshMetadata"); @@ -112,4 +112,4 @@ JsonValue::Object SkirtMeshMetadata::createGltfExtras( {"skirtEastHeight", skirtMeshMetadata.skirtEastHeight}, {"skirtNorthHeight", skirtMeshMetadata.skirtNorthHeight}}}}; } -} // namespace Cesium3DTilesContent +} // namespace CesiumGltfContent diff --git a/CesiumGltfReader/test/TestImageManipulation.cpp b/CesiumGltfReader/test/TestImageManipulation.cpp index 5506ba80f..38833411b 100644 --- a/CesiumGltfReader/test/TestImageManipulation.cpp +++ b/CesiumGltfReader/test/TestImageManipulation.cpp @@ -1,13 +1,12 @@ -#include "CesiumGltfReader/ImageManipulation.h" - #include +#include #include #include using namespace CesiumGltf; -using namespace CesiumGltfReader; +using namespace CesiumGltfContent; TEST_CASE("ImageManipulation::unsafeBlitImage entire image") { size_t width = 10; diff --git a/CesiumRasterOverlays/CMakeLists.txt b/CesiumRasterOverlays/CMakeLists.txt index 7d30c4d86..284ddf06b 100644 --- a/CesiumRasterOverlays/CMakeLists.txt +++ b/CesiumRasterOverlays/CMakeLists.txt @@ -43,6 +43,7 @@ target_link_libraries(CesiumRasterOverlays CesiumGeospatial CesiumGeometry CesiumGltf + CesiumGltfContent CesiumGltfReader CesiumUtility ) diff --git a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/RasterOverlayDetails.h b/CesiumRasterOverlays/include/CesiumRasterOverlays/RasterOverlayDetails.h similarity index 95% rename from Cesium3DTilesSelection/include/Cesium3DTilesSelection/RasterOverlayDetails.h rename to CesiumRasterOverlays/include/CesiumRasterOverlays/RasterOverlayDetails.h index 14b2f2fab..a43a5af50 100644 --- a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/RasterOverlayDetails.h +++ b/CesiumRasterOverlays/include/CesiumRasterOverlays/RasterOverlayDetails.h @@ -8,12 +8,12 @@ #include -namespace Cesium3DTilesSelection { +namespace CesiumRasterOverlays { /** * @brief Holds details of the {@link TileRenderContent} that are useful * for raster overlays. */ -struct CESIUM3DTILESSELECTION_API RasterOverlayDetails { +struct CESIUMRASTEROVERLAYS_API RasterOverlayDetails { /** * @brief Construct an empty RasterOverlayDetails. */ @@ -76,4 +76,4 @@ struct CESIUM3DTILESSELECTION_API RasterOverlayDetails { */ CesiumGeospatial::BoundingRegion boundingRegion; }; -} // namespace Cesium3DTilesSelection +} // namespace CesiumRasterOverlays diff --git a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/RasterOverlayUtilities.h b/CesiumRasterOverlays/include/CesiumRasterOverlays/RasterOverlayUtilities.h similarity index 93% rename from Cesium3DTilesSelection/include/Cesium3DTilesSelection/RasterOverlayUtilities.h rename to CesiumRasterOverlays/include/CesiumRasterOverlays/RasterOverlayUtilities.h index 9b2aac33d..0719693db 100644 --- a/Cesium3DTilesSelection/include/Cesium3DTilesSelection/RasterOverlayUtilities.h +++ b/CesiumRasterOverlays/include/CesiumRasterOverlays/RasterOverlayUtilities.h @@ -14,12 +14,12 @@ namespace CesiumGltf { struct Model; } -namespace Cesium3DTilesSelection { +namespace CesiumRasterOverlays { -struct CESIUM3DTILESSELECTION_API RasterOverlayUtilities { +struct CESIUMRASTEROVERLAYS_API RasterOverlayUtilities { /** * @brief Creates texture coordinates for mapping {@link RasterOverlay} tiles - * to {@link Tileset} tiles. + * to a glTF model. * * Generates new texture coordinates for the `gltf` using the given * `projections`. The first new texture coordinate (`u` or `s`) will be 0.0 at @@ -63,4 +63,4 @@ struct CESIUM3DTILESSELECTION_API RasterOverlayUtilities { std::vector&& projections); }; -} // namespace Cesium3DTilesSelection +} // namespace CesiumRasterOverlays diff --git a/CesiumRasterOverlays/src/QuadtreeRasterOverlayTileProvider.cpp b/CesiumRasterOverlays/src/QuadtreeRasterOverlayTileProvider.cpp index 538618e0f..669716d27 100644 --- a/CesiumRasterOverlays/src/QuadtreeRasterOverlayTileProvider.cpp +++ b/CesiumRasterOverlays/src/QuadtreeRasterOverlayTileProvider.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include #include #include @@ -10,6 +10,7 @@ using namespace CesiumAsync; using namespace CesiumGeometry; using namespace CesiumGeospatial; using namespace CesiumGltf; +using namespace CesiumGltfContent; using namespace CesiumGltfReader; using namespace CesiumUtility; diff --git a/Cesium3DTilesSelection/src/RasterOverlayDetails.cpp b/CesiumRasterOverlays/src/RasterOverlayDetails.cpp similarity index 93% rename from Cesium3DTilesSelection/src/RasterOverlayDetails.cpp rename to CesiumRasterOverlays/src/RasterOverlayDetails.cpp index fc4cc4959..1c8275741 100644 --- a/Cesium3DTilesSelection/src/RasterOverlayDetails.cpp +++ b/CesiumRasterOverlays/src/RasterOverlayDetails.cpp @@ -1,9 +1,9 @@ -#include +#include #include #include -namespace Cesium3DTilesSelection { +namespace CesiumRasterOverlays { RasterOverlayDetails::RasterOverlayDetails() : boundingRegion{ CesiumGeospatial::GlobeRectangle::EMPTY, @@ -52,4 +52,4 @@ void RasterOverlayDetails::merge(const RasterOverlayDetails& other) { boundingRegion.computeUnion(other.boundingRegion); } -} // namespace Cesium3DTilesSelection +} // namespace CesiumRasterOverlays diff --git a/Cesium3DTilesSelection/src/RasterOverlayUtilities.cpp b/CesiumRasterOverlays/src/RasterOverlayUtilities.cpp similarity index 97% rename from Cesium3DTilesSelection/src/RasterOverlayUtilities.cpp rename to CesiumRasterOverlays/src/RasterOverlayUtilities.cpp index 028a1b769..3930e8634 100644 --- a/Cesium3DTilesSelection/src/RasterOverlayUtilities.cpp +++ b/CesiumRasterOverlays/src/RasterOverlayUtilities.cpp @@ -1,13 +1,13 @@ -#include -#include -#include #include #include #include +#include +#include +#include -using namespace Cesium3DTilesContent; +using namespace CesiumGltfContent; -namespace Cesium3DTilesSelection { +namespace CesiumRasterOverlays { /*static*/ std::optional RasterOverlayUtilities::createRasterOverlayTextureCoordinates( @@ -265,4 +265,4 @@ RasterOverlayUtilities::createRasterOverlayTextureCoordinates( computedBounds.toRegion()}; } -} // namespace Cesium3DTilesSelection +} // namespace CesiumRasterOverlays From 37374b52ca09d5ad4d701b6545c0c5cdf23904e7 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 15 Nov 2023 22:42:29 +1100 Subject: [PATCH 371/421] computeDesiredScreenPixels, example of glTF raster overlay baking. --- .../include/CesiumNativeTests/waitForFuture.h | 18 +++ .../RasterOverlayTileProvider.h | 18 ++- .../RasterOverlayUtilities.h | 47 ++++++ .../src/RasterOverlayTileProvider.cpp | 24 +-- .../src/RasterOverlayUtilities.cpp | 46 ++++++ .../test/TestAddRasterOverlayToGltf.cpp | 152 ++++++++++++++++++ .../test/data/Cesium_Logo_Color/0/0/0.png | Bin 0 -> 1635 bytes .../test/data/Cesium_Logo_Color/1/0/1.png | Bin 0 -> 2963 bytes .../test/data/Cesium_Logo_Color/2/0/2.png | Bin 0 -> 3706 bytes .../test/data/Cesium_Logo_Color/2/1/2.png | Bin 0 -> 2499 bytes .../test/data/Cesium_Logo_Color/3/1/4.png | Bin 0 -> 7940 bytes .../test/data/Cesium_Logo_Color/3/2/4.png | Bin 0 -> 4567 bytes .../test/data/Cesium_Logo_Color/4/2/8.png | Bin 0 -> 406 bytes .../test/data/Cesium_Logo_Color/4/2/9.png | Bin 0 -> 8229 bytes .../test/data/Cesium_Logo_Color/4/3/8.png | Bin 0 -> 397 bytes .../test/data/Cesium_Logo_Color/4/3/9.png | Bin 0 -> 14579 bytes .../test/data/Cesium_Logo_Color/4/4/8.png | Bin 0 -> 397 bytes .../test/data/Cesium_Logo_Color/4/4/9.png | Bin 0 -> 8783 bytes .../test/data/Cesium_Logo_Color/4/5/8.png | Bin 0 -> 416 bytes .../test/data/Cesium_Logo_Color/4/5/9.png | Bin 0 -> 2794 bytes .../Cesium_Logo_Color/tilemapresource.xml | 17 ++ .../test/data/Shadow_Tester.glb | Bin 0 -> 42976 bytes 22 files changed, 310 insertions(+), 12 deletions(-) create mode 100644 CesiumNativeTests/include/CesiumNativeTests/waitForFuture.h create mode 100644 CesiumRasterOverlays/test/TestAddRasterOverlayToGltf.cpp create mode 100644 CesiumRasterOverlays/test/data/Cesium_Logo_Color/0/0/0.png create mode 100644 CesiumRasterOverlays/test/data/Cesium_Logo_Color/1/0/1.png create mode 100644 CesiumRasterOverlays/test/data/Cesium_Logo_Color/2/0/2.png create mode 100644 CesiumRasterOverlays/test/data/Cesium_Logo_Color/2/1/2.png create mode 100644 CesiumRasterOverlays/test/data/Cesium_Logo_Color/3/1/4.png create mode 100644 CesiumRasterOverlays/test/data/Cesium_Logo_Color/3/2/4.png create mode 100644 CesiumRasterOverlays/test/data/Cesium_Logo_Color/4/2/8.png create mode 100644 CesiumRasterOverlays/test/data/Cesium_Logo_Color/4/2/9.png create mode 100644 CesiumRasterOverlays/test/data/Cesium_Logo_Color/4/3/8.png create mode 100644 CesiumRasterOverlays/test/data/Cesium_Logo_Color/4/3/9.png create mode 100644 CesiumRasterOverlays/test/data/Cesium_Logo_Color/4/4/8.png create mode 100644 CesiumRasterOverlays/test/data/Cesium_Logo_Color/4/4/9.png create mode 100644 CesiumRasterOverlays/test/data/Cesium_Logo_Color/4/5/8.png create mode 100644 CesiumRasterOverlays/test/data/Cesium_Logo_Color/4/5/9.png create mode 100644 CesiumRasterOverlays/test/data/Cesium_Logo_Color/tilemapresource.xml create mode 100644 CesiumRasterOverlays/test/data/Shadow_Tester.glb diff --git a/CesiumNativeTests/include/CesiumNativeTests/waitForFuture.h b/CesiumNativeTests/include/CesiumNativeTests/waitForFuture.h new file mode 100644 index 000000000..eb2f24a82 --- /dev/null +++ b/CesiumNativeTests/include/CesiumNativeTests/waitForFuture.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +namespace CesiumNativeTests { + +template +T waitForFuture( + CesiumAsync::AsyncSystem& asyncSystem, + CesiumAsync::Future&& future) { + while (!future.isReady()) { + asyncSystem.dispatchMainThreadTasks(); + } + + return future.wait(); +} + +} // namespace CesiumNativeTests diff --git a/CesiumRasterOverlays/include/CesiumRasterOverlays/RasterOverlayTileProvider.h b/CesiumRasterOverlays/include/CesiumRasterOverlays/RasterOverlayTileProvider.h index d07b07670..d42fb8cae 100644 --- a/CesiumRasterOverlays/include/CesiumRasterOverlays/RasterOverlayTileProvider.h +++ b/CesiumRasterOverlays/include/CesiumRasterOverlays/RasterOverlayTileProvider.h @@ -113,6 +113,19 @@ struct LoadTileImageFromUrlOptions { bool allowEmptyImages = false; }; +class RasterOverlayTileProvider; + +/** + * @brief Holds a tile and its corresponding tile provider. Used as the return + * value of {@link RasterOverlayTileProvider::loadTile}. + */ +struct TileProviderAndTile { + CesiumUtility::IntrusivePointer pTileProvider; + CesiumUtility::IntrusivePointer pTile; + + ~TileProviderAndTile() noexcept; +}; + /** * @brief Provides individual tiles for a {@link RasterOverlay} on demand. * @@ -314,8 +327,7 @@ class CESIUMRASTEROVERLAYS_API RasterOverlayTileProvider * * @param tile The tile to load. */ - CesiumAsync::Future> - loadTile(RasterOverlayTile& tile); + CesiumAsync::Future loadTile(RasterOverlayTile& tile); /** * @brief Loads a tile, unless there are too many tile loads already in @@ -366,7 +378,7 @@ class CESIUMRASTEROVERLAYS_API RasterOverlayTileProvider LoadTileImageFromUrlOptions&& options = {}) const; private: - CesiumAsync::Future> + CesiumAsync::Future doLoad(RasterOverlayTile& tile, bool isThrottledLoad); /** diff --git a/CesiumRasterOverlays/include/CesiumRasterOverlays/RasterOverlayUtilities.h b/CesiumRasterOverlays/include/CesiumRasterOverlays/RasterOverlayUtilities.h index 0719693db..3112930ef 100644 --- a/CesiumRasterOverlays/include/CesiumRasterOverlays/RasterOverlayUtilities.h +++ b/CesiumRasterOverlays/include/CesiumRasterOverlays/RasterOverlayUtilities.h @@ -3,6 +3,7 @@ #include "Library.h" #include "RasterOverlayDetails.h" +#include #include #include @@ -61,6 +62,52 @@ struct CESIUMRASTEROVERLAYS_API RasterOverlayUtilities { int32_t firstTextureCoordinateID, const std::optional& globeRectangle, std::vector&& projections); + + /** + * @brief Computes the desired screen pixels for a raster overlay texture. + * + * This method is used to determine the appropriate number of "screen pixels" + * to use for a raster overlay texture to be attached to a glTF (which is + * usually a 3D Tiles tile). In other words, how detailed should the texture + * be? The answer depends, of course, on how close we'll get to the model. If + * we're going to get very close, we'll need a higher-resolution raster + * overlay texture than if we will stay far away from it. + * + * In 3D Tiles, we can only get so close to a model before it switches to the + * next higher level-of-detail by showing its children instead. The switch + * distance is controlled by the `geometric error` of the tile, as well as by + * the `maximum screen space error` of the tileset. So this method requires + * both of those parameters. + * + * The answer also depends on the size of the model on the screen at this + * switch distance. To determine that, this method takes a projection and a + * rectangle that bounds the tile, expressed in that projection. This + * rectangle is projected onto the screen at the switch distance, and the size + * of that rectangle on the screen is the `target screen pixels` returned by + * this method. + * + * The `target screen pixels` returned here may be further modified by the + * raster overlay's {@link RasterOverlay::getTile} method. In particular, it + * will usually be divided by the raster overlay's `maximum screen space + * error` of the raster overlay (not to be confused with the `maximum screen + * space error` of the tileset, mentioned above). + * + * @param geometricError The geometric error of the tile. + * @param maximumScreenSpaceError The maximum screen-space error used to + * render the tileset. + * @param projection The projection in which the `rectangle` parameter is + * provided. + * @param rectangle The 2D extent of the tile, expressed in the `projection`. + * @param ellipsoid The ellipsoid with which computations are performed. + * @return The desired screen pixels. + */ + static glm::dvec2 computeDesiredScreenPixels( + double geometricError, + double maximumScreenSpaceError, + const CesiumGeospatial::Projection& projection, + const CesiumGeometry::Rectangle& rectangle, + const CesiumGeospatial::Ellipsoid& ellipsoid = + CesiumGeospatial::Ellipsoid::WGS84); }; } // namespace CesiumRasterOverlays diff --git a/CesiumRasterOverlays/src/RasterOverlayTileProvider.cpp b/CesiumRasterOverlays/src/RasterOverlayTileProvider.cpp index 8dc4c416d..d6e81a3fa 100644 --- a/CesiumRasterOverlays/src/RasterOverlayTileProvider.cpp +++ b/CesiumRasterOverlays/src/RasterOverlayTileProvider.cpp @@ -94,12 +94,12 @@ void RasterOverlayTileProvider::removeTile(RasterOverlayTile* pTile) noexcept { this->_tileDataBytes -= int64_t(pTile->getImage().pixelData.size()); } -CesiumAsync::Future> +CesiumAsync::Future RasterOverlayTileProvider::loadTile(RasterOverlayTile& tile) { if (this->_pPlaceholder) { // Refuse to load placeholders. - return this->getAsyncSystem() - .createResolvedFuture>(nullptr); + return this->getAsyncSystem().createResolvedFuture( + TileProviderAndTile{this, nullptr}); } return this->doLoad(tile, false); @@ -298,14 +298,13 @@ static LoadResult createLoadResultFromLoadedImage( } // namespace -CesiumAsync::Future> -RasterOverlayTileProvider::doLoad( +CesiumAsync::Future RasterOverlayTileProvider::doLoad( RasterOverlayTile& tile, bool isThrottledLoad) { if (tile.getState() != RasterOverlayTile::LoadState::Unloaded) { // Already loading or loaded, do nothing. - return this->getAsyncSystem() - .createResolvedFuture>(nullptr); + return this->getAsyncSystem().createResolvedFuture( + TileProviderAndTile{this, nullptr}); } // CESIUM_TRACE_USE_TRACK_SET(this->_loadingSlots); @@ -348,7 +347,7 @@ RasterOverlayTileProvider::doLoad( thiz->finalizeTileLoad(isThrottledLoad); - return pTile; + return TileProviderAndTile{thiz, pTile}; }) .catchInMainThread( [thiz, pTile, isThrottledLoad](const std::exception& /*e*/) { @@ -361,7 +360,7 @@ RasterOverlayTileProvider::doLoad( thiz->finalizeTileLoad(isThrottledLoad); - return pTile; + return TileProviderAndTile{thiz, pTile}; }); } @@ -379,4 +378,11 @@ void RasterOverlayTileProvider::finalizeTileLoad( --this->_throttledTilesCurrentlyLoading; } } + +TileProviderAndTile::~TileProviderAndTile() noexcept { + // Ensure the tile is released before the tile provider. + pTile = nullptr; + pTileProvider = nullptr; +} + } // namespace CesiumRasterOverlays diff --git a/CesiumRasterOverlays/src/RasterOverlayUtilities.cpp b/CesiumRasterOverlays/src/RasterOverlayUtilities.cpp index 3930e8634..dc8d65cab 100644 --- a/CesiumRasterOverlays/src/RasterOverlayUtilities.cpp +++ b/CesiumRasterOverlays/src/RasterOverlayUtilities.cpp @@ -6,6 +6,8 @@ #include using namespace CesiumGltfContent; +using namespace CesiumGeometry; +using namespace CesiumGeospatial; namespace CesiumRasterOverlays { @@ -265,4 +267,48 @@ RasterOverlayUtilities::createRasterOverlayTextureCoordinates( computedBounds.toRegion()}; } +/*static*/ glm::dvec2 RasterOverlayUtilities::computeDesiredScreenPixels( + double geometricError, + double maximumScreenSpaceError, + const CesiumGeospatial::Projection& projection, + const CesiumGeometry::Rectangle& rectangle, + const CesiumGeospatial::Ellipsoid& ellipsoid) { + // We're aiming to estimate the maximum number of pixels (in each projected + // direction) the tile will occupy on the screen. The will be determined by + // the tile's geometric error, because when less error is needed (i.e. the + // viewer moved closer), the LOD will switch to show the tile's children + // instead of this tile. + // + // It works like this: + // * Estimate the size of the projected rectangle in world coordinates. + // * Compute the distance at which tile will switch to its children, based on + // its geometric error and the tileset SSE. + // * Compute the on-screen size of the projected rectangle at that distance. + // + // For the two compute steps, we use the usual perspective projection SSE + // equation: + // screenSize = (realSize * viewportHeight) / (distance * 2 * tan(0.5 * fovY)) + // + // Conveniently a bunch of terms cancel out, so the screen pixel size at the + // switch distance is not actually dependent on the screen dimensions or + // field-of-view angle. + + // We can get a more accurate estimate of the real-world size of the projected + // rectangle if we consider the rectangle at the true height of the geometry + // rather than assuming it's on the ellipsoid. This will make basically no + // difference for small tiles (because surface normals on opposite ends of + // tiles are effectively identical), and only a small difference for large + // ones (because heights will be small compared to the total size of a large + // tile). So we're skipping this complexity for now and estimating geometry + // width/height as if it's on the ellipsoid surface. + const double heightForSizeEstimation = 0.0; + + glm::dvec2 diameters = computeProjectedRectangleSize( + projection, + rectangle, + heightForSizeEstimation, + ellipsoid); + return diameters * maximumScreenSpaceError / geometricError; +} + } // namespace CesiumRasterOverlays diff --git a/CesiumRasterOverlays/test/TestAddRasterOverlayToGltf.cpp b/CesiumRasterOverlays/test/TestAddRasterOverlayToGltf.cpp new file mode 100644 index 000000000..3fc41bcee --- /dev/null +++ b/CesiumRasterOverlays/test/TestAddRasterOverlayToGltf.cpp @@ -0,0 +1,152 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace CesiumAsync; +using namespace CesiumGeospatial; +using namespace CesiumGltf; +using namespace CesiumGltfContent; +using namespace CesiumGltfReader; +using namespace CesiumGltfWriter; +using namespace CesiumRasterOverlays; +using namespace CesiumUtility; +using namespace CesiumNativeTests; + +TEST_CASE("Add raster overlay to glTF") { + std::filesystem::path dataDir(CesiumRasterOverlays_TEST_DATA_DIR); + std::vector bytes = readFile(dataDir / "Shadow_Tester.glb"); + GltfReader reader; + GltfReaderResult result = reader.readGltf(bytes); + REQUIRE(result.model); + + Model& gltf = *result.model; + + // Place the glTF in Philly and make it huge. + glm::dmat4 enuToFixed = GlobeTransforms::eastNorthUpToFixedFrame( + Ellipsoid::WGS84.cartographicToCartesian( + Cartographic::fromDegrees(-75.14777, 39.95021, 200.0))); + glm::dmat4 scale = + glm::scale(glm::dmat4(1.0), glm::dvec3(100000.0, 100000.0, 100000.0)); + glm::dmat4 modelToEcef = enuToFixed * scale; + + // Set up some mock resources. + auto pMockTaskProcessor = std::make_shared(); + CesiumAsync::AsyncSystem asyncSystem{pMockTaskProcessor}; + + std::map> mapUrlToRequest; + for (const auto& entry : std::filesystem::recursive_directory_iterator( + dataDir / "Cesium_Logo_Color")) { + if (!entry.is_regular_file()) + continue; + auto pResponse = std::make_unique( + uint16_t(200), + "application/binary", + CesiumAsync::HttpHeaders{}, + readFile(entry.path())); + std::string url = "file:///" + entry.path().generic_u8string(); + auto pRequest = std::make_unique( + "GET", + url, + CesiumAsync::HttpHeaders{}, + std::move(pResponse)); + mapUrlToRequest[url] = std::move(pRequest); + } + + auto pMockAssetAccessor = + std::make_shared(std::move(mapUrlToRequest)); + + // Create a raster overlay to drape over the glTF. + std::string tmr = + "file:///" + std::filesystem::directory_entry( + dataDir / "Cesium_Logo_Color" / "tilemapresource.xml") + .path() + .generic_u8string(); + IntrusivePointer pRasterOverlay = + new TileMapServiceRasterOverlay("test", tmr); + + auto future = + pRasterOverlay + ->createTileProvider( + asyncSystem, + pMockAssetAccessor, + nullptr, + nullptr, + spdlog::default_logger(), + nullptr) + .thenInMainThread([&gltf, &modelToEcef]( + RasterOverlay::CreateTileProviderResult&& + tileProviderResult) { + REQUIRE(tileProviderResult); + + IntrusivePointer pTileProvider = + tileProviderResult.value(); + + std::optional details = + RasterOverlayUtilities::createRasterOverlayTextureCoordinates( + gltf, + modelToEcef, + 0, + std::nullopt, + {pTileProvider->getProjection()}); + REQUIRE(details); + REQUIRE(details->rasterOverlayProjections.size() == 1); + REQUIRE(details->rasterOverlayRectangles.size() == 1); + + // The geometric error will usually come from the tile, but here + // we're hard-coding it. + double geometricError = 100000.0; + + // Determine the maximum number of screen pixels we expect to be + // covered by this raster overlay. + glm::dvec2 targetScreenPixels = + RasterOverlayUtilities::computeDesiredScreenPixels( + geometricError, + 16.0, + details->rasterOverlayProjections[0], + details->rasterOverlayRectangles[0]); + + // Get a raster overlay texture of the proper dimensions. + IntrusivePointer pRasterTile = + pTileProvider->getTile( + details->rasterOverlayRectangles[0], + targetScreenPixels); + + // And go load the texture. + return pTileProvider->loadTile(*pRasterTile); + }) + .thenInMainThread([&gltf](TileProviderAndTile&& loadResult) { + Image& image = gltf.images.emplace_back(); + image.cesium = loadResult.pTile->getImage(); + + Sampler& sampler = gltf.samplers.emplace_back(); + sampler.magFilter = Sampler::MagFilter::LINEAR; + sampler.minFilter = Sampler::MinFilter::LINEAR_MIPMAP_LINEAR; + sampler.wrapS = Sampler::WrapS::CLAMP_TO_EDGE; + sampler.wrapT = Sampler::WrapT::CLAMP_TO_EDGE; + + Texture& texture = gltf.textures.emplace_back(); + texture.sampler = int32_t(gltf.samplers.size() - 1); + texture.source = int32_t(gltf.images.size() - 1); + }); + + waitForFuture(asyncSystem, std::move(future)); + + GltfWriter writer; + GltfWriterResult writeResult = + writer.writeGlb(gltf, gltf.buffers[0].cesium.data); +} diff --git a/CesiumRasterOverlays/test/data/Cesium_Logo_Color/0/0/0.png b/CesiumRasterOverlays/test/data/Cesium_Logo_Color/0/0/0.png new file mode 100644 index 0000000000000000000000000000000000000000..9a8874e199122ad55f076a4d9cf2263c0b98d004 GIT binary patch literal 1635 zcmd5+`&SYM6vnw^nd!F9viYiW!bh1hU-{ORHbe4}`6yqw96BGVsVF|umX#+{Q&fC` ztl`^)l*Bi>xg;NeismHIe1M7|DHWT^RL(zN^$GuI@b05;)O9X>txqwU*u&}PV9a0-Gm4Gqx7EvE)LWH4 zsVRwMZNFGK6k(iaLtTA{iD&O2S!*I*w=h@A%?L?2>>^01ScQiF5()bG&4~NZnJEQW zELPyAPWkfcNQof>=bTM>Yx~|fd}FE(3dP>+N?7_Ukf9lz_c#y;h;ASp8(gCN#y^~u zsHv3&y8YH;+4ELOuIX}CAIz5+3HLBi)T=^mEG*?3&Dx-_>8^Pr7}5O&a%GUJd0GyV zh$YYaUasNQ3T_@9%*Jo24tDddrs&x1btgH?Ki29vJ5~(nI0oT;y>Yy_V+rr~C29qw zmR5zMji8w&6yaG51eMdTnMk!ZT%B&ZktUa{F=uDXC-H6#K25(2=YK!<^5>z~QcG*= zwKl5W*l>ZiXesx!0i@oe8cnCuUj%iUiszHO7P6!QhyC|C5EQ#NwP9eunayT5`M2Dn zWTv5NOHtI**$&nV$ncXFZYIDDmF2KZmP)3KP1*T?9)b&tPrzZb+wJ_lA;Vhuo2}+iQ?))#zV3BC=R|7^@H_GyV;YUd zA@pvYFLSSZMJA(LgF4YJR^zWSPqJcjvg)4wKq@Vjyp*Cq)KzRdwP1hIf6G0 z9{=Iq!EWCHuO)4F(oHl41=scW%MvxTO`rB;04L_BiweolV;9A}{4Ak{WUF+gyBVIW z;w3YS1ZVEVg@uKkDKYf&L|IFs5**;uT7YJ{Sp zZSuOeC(a2wl&jHz$Dhsk$cLcvwXDw1MDHy-I!QAY`qDN80$Sd8#))GlovR0%#DUBB zTyOyUj*<#cN|FkLz5^RLCn$8YtE7sjisjH!jXP!|^$E*)FV87prf62YAd=`nFV8}%6bR8!*s z!nKY-4=5BG!KlH4=K9h^TxFoTPSQ^eE&y=RaauTT{dPy8 z5~3web&#Skv-K7!!lN}QI8P=O3yFx&Kl=ZJ{hd!hNXI#kpR9}?e^v##;^|7b9QNQ} DQPlXy literal 0 HcmV?d00001 diff --git a/CesiumRasterOverlays/test/data/Cesium_Logo_Color/1/0/1.png b/CesiumRasterOverlays/test/data/Cesium_Logo_Color/1/0/1.png new file mode 100644 index 0000000000000000000000000000000000000000..8fbc7a62be020b381319e96bab067f219319f280 GIT binary patch literal 2963 zcmeH}`8(9z8^>o_WQ%%;$&$)cBwLb74Hap~8rip)u|-)1ktj8i@H~UmSQCaBOH5<3 z^T=*&VT`d<3^T@%n8wnK@25ZDdtKi@;QRjNoO7M)T-SZ>`+mR9Nwl&s*)J+53Ic)l z!*3edfIwhi3I+)a0fS3O2@(VnbAlUPwT;Z?Ohwv|>Jm4DQflphyp zOdK=wW3>m>h0~dLZrj*MMNci&oToveiBo(NV%EuGxd?f^*7NfBOtl-oB8Q?><=!Qa zopwU0ekh+f-eZ1H!RO6eV(ce{OA?W#THcIp?UT8@n1$?!l%|1;E1_;Fe*uyFU;jM= zdf;Aw6ruSSy@@VasH>b9|4Vwjg3i!8^jUtB_$5vg{Jyp+T|3No_J(#s#MmqU%ozbFwqj1C_q zv6r65@t-8jbju{ zG_b9uRWtA|x$Vxp)}{Nmyw21?_E@0TCjHmF_Z^TO|m zEA)p*k7SD|xg=J#r7xw3sHsiQlyPQivgp4&Wpe5vx%*1+_~CWd7X+Ek2&tV5sXg~- zZHu$U;`3&xi=%|fhq3l+EL`5= zWNp`E$5^>Dw@uPuogVyv^JbU$2jaBHc>Zq+N5tQE>qe7C<&o~UcvQ3nK}{OwyYiNK zr=g8-C1IzifxcjdNm001a2sa<$8@A95~{p9P3ywAW##2|(b4rl+D9fP{FhH@YFbKO zS+_FEyYUKg3_d~hNrn1NehQA7f9vB-r1hQGk60e*&(SAE^Ogk{_G1dgWHp#TJR`&b znEah4&o=a9eW40TutywfBff5Z`p|W$zZ9`^Rcum$eb_X;c(i${q9IXv{h{kX`HmLB zn1}pa8pbn?u_IE%L7k~enNSK=*DNC=!!vX)B}D?^?ac)0>K_oG)9XXQ7E#?}8wZ+D zWY=o%#OX(Cln-^CO0h2TdO@}!AwQFIcfrr%_81!*dxG<;s;m(RM76`2pOKd@h0fn< zR*1*Ut*bOkQiZGPiw*vQ<^&hFRZimE?X^OtRy~Bs_ZZHFCd=uXYGj(-Z&jDr$86>Xd(^mve&;4rE?_2LCSg%C7ZZ`4GscqZWv1Q2NW2D|7A~Q2>)~+s;wCifK zg7%fSTgwmYdKrt{3$?Yi2N7B41BZ*vvyS&H4CGa&8VDR{G}=TRih#%CZSCw1jdl2w zPV4C0&U^GTvAVjtVkgP{q?(!;&=sSt@%u=@vo{*I773Wbf`a4l;ir4W)@eR@Q$K8vELI{n3|cP zn1k=~b{Lw($kn^$4prAsC=|eYD_(%$$m0!9ozg_+agb3fB*LnlzEg0!e8zZW@Aa#V z^<2v+0X23^fUUH(o(k=SJ^U@cHZYYG18`!Btqr8U*sC*z^|dt~>FDo|NA_pae3~Vc zU}e94g)`>nRE{0H8XO#qy%1ag?f&+lr>#q6PHy8w|`jxWf7pl{V=k-zNM# zaG>Wc&Y@E{R9|1;*#dXlt|?PityY z(fj4M9?8Vg1IfnejD>~XATj}{W0H*eB5HMLiAF?_O`5(j1=}lB=FodeeZuD20#-NUS7&V&Fy!Go{30(4ChR^@%%_m+_^MMY@nvQ^fTsn zlFsh@0>Ci_1_nDnf07NNuDZv_Z@LI@XE3=rWK-VcszW6gmS>qDsdPRTauf5Hl$7qn zpWbVx6N7{KTU%SOR8KOUGHD3d1BD{h&NpX$VU3KG-lh;Df%OEPt{XUUu68PO>{Mi< zo8pzIb7!;_1)4r?C9=ZiADgq^0#{m(kFJ1m|8QO`F!3-VCkdj<556*Xc4O)ScHOgbWH^E}5m%%G%a{^xVG1UlInj9O#ht0B!Ie@3i;2 pS0~7SZyq+2`15=IA9Zv>AO_BECJz5=qWI?s+}OgX;+n_P{{WeIUy1+# literal 0 HcmV?d00001 diff --git a/CesiumRasterOverlays/test/data/Cesium_Logo_Color/2/0/2.png b/CesiumRasterOverlays/test/data/Cesium_Logo_Color/2/0/2.png new file mode 100644 index 0000000000000000000000000000000000000000..ab9cdec454c905f71d44a57a978f8e481460e5af GIT binary patch literal 3706 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 SWQfBb1ao7MQMJMSg#Q2^P4sL4 literal 0 HcmV?d00001 diff --git a/CesiumRasterOverlays/test/data/Cesium_Logo_Color/2/1/2.png b/CesiumRasterOverlays/test/data/Cesium_Logo_Color/2/1/2.png new file mode 100644 index 0000000000000000000000000000000000000000..5d8426918b82b1519b6fe8d3a52ec3c5a78cda66 GIT binary patch literal 2499 zcmc&$`8%827LMB<*I*nPRN2F8;S|o_LyZ1l1=a;*F`S!E+v)1?QcfIescj6@{dwJP?vJePF z9)A9;3j`tomJ$$YN$^337x_UTGGzE!8@HJ3MOH9LnX4_e%r>Xh^XN5xL4$pA0cmQu zXYD!;s>*Z&zuR8HnY-<-l_O(6J$;oX9WP6+`nl&$e2ucKQtZQYy8z_g#3wf%*9Xy` zWL3_KF!8fXbq##;;j7~t_|N$Ry(R-9w=cY26BGaa;OGD6h((94Of!<=8c1TlzAV$= z8Z5iHsg`$(Hbc>c$AWA@4TuU5|4qgJ*&C&&C$KVTl&H!x3iX-Xn?rt3LG2dxK8;U=rpbbR_D5@XV?e6K>uj4&Y zb0bV7Txq5E7_~Ic*%HMYv&=f5j^sp}yONAVC4FypGhuO4QSI2T3Gx~qt3v&#_2^4p zUh2xq%6h3%1Y|@+L}kF>^BK|i0ff%;9)uy)!LaW0otZ&fWg>K{(Hu_E9joxMzaN#6o~~z!TR0e~4N^%e2J?S3zH*?dSa;+^ zPyO3}K*W0wd(`qJE7*Ii(zhadY3ljO^Aueq5;=WO^(qPe=m5hLLm2AQ%gyoWe83